File size: 10,542 Bytes
312ea17
 
 
 
dcfc9c9
 
 
 
 
312ea17
6f6ce59
47e9eca
dcfc9c9
 
 
 
 
6f6ce59
 
dcfc9c9
6f6ce59
dcfc9c9
 
61c48cb
312ea17
dcfc9c9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312ea17
 
dcfc9c9
312ea17
dcfc9c9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47e9eca
dcfc9c9
312ea17
2aa5050
dcfc9c9
312ea17
 
dcfc9c9
 
312ea17
dcfc9c9
 
2aa5050
dcfc9c9
 
 
 
 
 
 
 
 
 
 
 
2aa5050
dcfc9c9
 
 
2aa5050
dcfc9c9
 
 
 
 
 
 
 
 
 
29b4c18
dcfc9c9
 
312ea17
2aa5050
dcfc9c9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2aa5050
dcfc9c9
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# 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