| # PluginBridge β Claude Code Context |
|
|
| ## What This Is |
| A JUCE-based VST3/AU plugin that: |
| 1. Hosts any third-party VST3/AU plugin with full GUI embedded in Ableton's window |
| 2. Crash-isolates unsafe plugins by loading them in a separate Helper process (audio-only mode) |
| 3. Exposes ALL hosted plugin parameters via a local MCP server (port 16620) |
| 4. Audio routes through shared memory (zero added latency) |
| 5. Any AI that speaks MCP (Claude Code, Codex CLI, Gemini CLI) can control any plugin |
|
|
| ## Current Status (v0.6.0 β 2026-05-17) |
|
|
| - β
In-process GUI hosting (full GUI for safe plugins) |
| - β
Safety scanning system (Helper-based pre-test, results cached in safety_db.json) |
| - β
Audio-only mode for unsafe plugins (iZotope, etc.) |
| - β
Ableton-style plugin picker (manufacturer TreeView, search, safety badges) |
| - β
MCP server (port 16620, HTTP JSON-RPC 2.0) |
| - β
Live spectrum analyzer (LUFS / FFT bands / stereo width / centroid / true peak) |
| - β
Multi-instance support + channel dropdown (any PluginBridge track addressable by name) |
| - β
Verified working: Pro-Q 4, God Particle, Little MicroShift, LIMITER |
| - β οΈ Sprint 7 in progress β MCP param ops route through Helper (not in-process) β EQ changes invisible in GUI |
| |
| ## Architecture: HYBRID SAFETY-SCAN |
| |
| ``` |
| ββ Ableton Process βββββββββββββββββββββββββββββββββββββββββββββββ |
| β β |
| β PluginBridge.vst3 β |
| β βββ PluginBridgeProcessor β |
| β β βββ getInstalledPluginFiles() β scans VST3+AU dirs β |
| β β βββ getPluginManufacturer() β reads Info.plist, cached β |
| β β βββ PluginSafetyDB β safe/unsafe/unknown JSON DB β |
| β β βββ startBackgroundScan() β pre-tests all unknown plugins β |
| β β βββ loadPlugin() β orchestrates load flow β |
| β β βββ McpServer β HTTP MCP port 16620 β |
| β β β |
| β βββ PluginBridgeEditor β |
| β β βββ PluginPickerComponent β manufacturer TreeView + searchβ |
| β β βββ pluginEditor β hosted plugin's own editor β |
| β β β |
| β βββ HelperConnection β IPC + shared memory to Helperβ |
| β β |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| β Unix socket + shared memory |
| βΌ |
| ββ PluginBridgeHelper.app (separate process) ββββββββββββββββββββββ |
| β Bundled in PluginBridge.vst3/Contents/Resources/ β |
| β β |
| β Modes: β |
| β --load <path> Load plugin, route audio via shared memory β |
| β --scan <path> Test-load plugin, exit 0=safe / crash=unsafe β |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| ``` |
| |
| ## Plugin Load Flow |
| |
| ``` |
| User selects plugin in picker |
| β |
| βΌ |
| loadPlugin() checks PluginSafetyDB |
| β |
| ββββββ΄βββββββββββββββββββ |
| β β |
| Unknown Safe/Unsafe already known |
| β β |
| βΌ β |
| Wait for Helper to exit β |
| Spawn: Helper --scan β |
| β β |
| Crashed? β mark Unsafe β |
| Exit 0? β mark Safe β |
| β β |
| ββββββββββββββ¬βββββββββββ |
| β |
| βββββββββ΄βββββββββ |
| β β |
| Safe Unsafe |
| β β |
| βΌ βΌ |
| Helper --load Helper --load |
| (audio routing) (audio routing) |
| β β |
| βΌ βΌ |
| loadInProcess() showAudioOnlyUI() |
| createEditor() (MCP still works) |
| embed in window |
| ``` |
| |
| **Key constraint:** Only ONE Helper process at a time. Scanner must wait for live Helper to finish before spawning a scan subprocess (same shared memory name β conflict). |
| |
| ## File Structure |
| |
| ``` |
| pluginbridge/ |
| βββ CMakeLists.txt |
| βββ JUCE/ (git submodule) |
| βββ libs/ |
| β βββ httplib.h (MIT, header-only HTTP server) |
| β βββ json.hpp (MIT, header-only JSON) |
| βββ Source/ |
| β βββ Plugin/ β VST3/AU (runs inside Ableton) |
| β β βββ PluginBridgeProcessor.h/.cpp β main logic |
| β β βββ PluginBridgeEditor.h/.mm β editor window |
| β β βββ PluginPickerComponent.h/.mm β plugin browser UI (new v0.5) |
| β β βββ HelperConnection.h/.cpp β IPC + shared memory |
| β β βββ McpServer.h/.cpp β HTTP MCP server |
| β β βββ PluginSafetyDB.h β safe/unsafe JSON DB |
| β β βββ Blocklist.h β persistent crash blocklist |
| β β βββ PBLog.h β timestamped logger |
| β βββ Helper/ β Standalone exe (separate process) |
| β β βββ main.cpp β --load and --scan modes |
| β β βββ HelperPluginHost.h/.mm |
| β β βββ HelperIPC.h/.cpp |
| β βββ Shared/ |
| β βββ SharedAudioBuffer.h β shared memory layout |
| β βββ IPCProtocol.h β JSON message formats |
| β βββ Constants.h |
| βββ CLAUDE.md β this file |
| βββ MACHINE-CONTEXT.md β system paths, verified APIs, plugin behaviors |
| βββ SYNC.md β what was built/fixed and when |
| βββ FAILED-APPROACHES.md β what didn't work and why |
| βββ ROADMAP.md |
| ``` |
| |
| ## Build |
| |
| ```bash |
| cd "/Volumes/T7 Shield/Users/Aditya/Downloads/AI MUSIC PRODUCTION/pluginbridge/build" |
| cmake --build . --target PluginBridge_VST3 -- -j4 |
| # Auto-installs to ~/Library/Audio/Plug-Ins/VST3/PluginBridge.vst3 |
| ``` |
| |
| **Two targets:** |
| - `PluginBridge_VST3` β builds + installs both plugin and Helper |
| - `PluginBridgeHelper` β Helper standalone only |
| |
| **CMake 3.28 required** (system CMake 4.x incompatible with JUCE 8). |
| |
| ## Key Runtime Data |
| |
| | File | Purpose | |
| |---|---| |
| | `~/Library/PluginBridge/safety_db.json` | Safe/unsafe status per plugin path+mtime | |
| | `~/Library/PluginBridge/debug.log` | Session-tagged timestamped log | |
| | `~/Library/PluginBridge/scan.log` | Background scan output | |
| | `~/Library/PluginBridge/blocklist.txt` | Plugins that crashed Helper during use | |
| |
| ## PluginPickerComponent (v0.5) |
| |
| `Source/Plugin/PluginPickerComponent.h/.mm` |
| |
| - `Entry` struct: `{ file, name, manufacturer, blocked, safety }` |
| - Callbacks: `onPluginSelected`, `onClose`, `onClearBlocklist`, `onClearSafetyCache` |
| - `static constexpr int kWidth = 420, kHeight = 500` |
| - Tree: `ManufacturerTreeItem` (folder) β `PluginTreeItem` (leaf, 24px height) |
| - Safety badges: `Safety::Safe` β `β` green; `Safety::Unsafe` β `audio only` orange; `blocked` β `β ` red |
| - Search: `addChildComponent(searchList)` β NOT `addAndMakeVisible` (see FAILED-APPROACHES) |
| - Custom `TreeLF` LookAndFeel overrides `drawTreeviewPlusMinusBox` for Ableton-style arrows |
| |
| ## JUCE 8 APIs β Verified |
| |
| ### Working |
| - `formatManager.addFormat(new juce::VST3PluginFormat())` |
| - `formatManager.addFormat(new juce::AudioUnitPluginFormat())` |
| - `plugin->createEditor()` β message thread only |
| - `plugin->hasEditor()` |
| - `plugin->getParameters()` β `Array<AudioProcessorParameter*>` |
| - `juce::MessageManager::callAsync(lambda)` β cross-thread UI updates |
| - `addChildComponent(comp)` β adds hidden; use instead of `addAndMakeVisible` when starting invisible |
| - `juce::TreeView` + `juce::TreeViewItem` with custom `LookAndFeel` |
| - `juce::File::findChildFiles(findDirectories, false, "*.vst3")` |
| |
| ### Broken β Do NOT Use |
| - β `addDefaultFormats()` β deleted in JUCE 8 |
| - β `juce::Thread::setCurrentThreadPriority()` β removed, use native pthread |
| - β `getParameter(int)` / `setParameter(int, float)` β deprecated |
| - β `getStringWidth()` β use `getStringWidthFloat()` |
| - β `juce::Rectangle<float>::getTransformToFit()` β does not exist |
| - β `juce::Array<CustomStruct>` initializer list β use `std::vector<CustomStruct>` |
| |
| ## macOS Gotchas |
| |
| - β `MSG_NOSIGNAL` β Linux only β `signal(SIGPIPE, SIG_IGN)` + `SO_NOSIGPIPE` |
| - β Two processes with same `shm_open` name β both crash β scanner must wait for Helper |
| - β `addAndMakeVisible(comp)` after `comp.setVisible(false)` β `addAndMakeVisible` forces visible, invisible comp sits on top intercepting all mouse events β use `addChildComponent(comp)` instead |
| - β `CAContext` + `CALayerHost` for cross-process GUI β doesn't work in Ableton (see FAILED-APPROACHES #10) |
| - β
`posix_spawn` β safe for spawning from plugin process |
| - β
`shm_open` / `mmap` β reliable cross-process shared memory |
| |
| ## MCP Protocol |
| |
| - Endpoint: `POST http://127.0.0.1:16620/mcp` |
| - Health: `GET http://127.0.0.1:16620/health` |
| - Wire format: JSON-RPC 2.0 |
| - Tools: `list_plugins`, `search_param`, `get_params`, `set_params`, `get_analysis` |
| |
| ## What NOT To Do |
| |
| - Don't load plugins in-process without safety scan β crash-prone plugins (iZotope, some Waves) call `abort()` and kill Ableton |
| - Don't use `PluginDirectoryScanner` β triggers plugin code on scan β crash |
| - Don't spawn Helper --scan while live Helper is running β shared memory conflict β false UNSAFE results |
| - Don't use `addAndMakeVisible` on a component that should start hidden β use `addChildComponent` |
| - Don't try CAContext/CALayerHost for cross-process GUI β see FAILED-APPROACHES #10 |
| |