# 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 Load plugin, route audio via shared memory │ │ --scan 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` - `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::getTransformToFit()` — does not exist - ❌ `juce::Array` initializer list — use `std::vector` ## 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