pluginbridge / CLAUDE.md
Sitar118
docs: bump CLAUDE.md to v0.6.0, add Sprint 4-6 features + Sprint 7 warning
6f6ce59
|
Raw
History Blame Contribute Delete
10.5 kB
# 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