Update CLAUDE.md — reflect Sprint 2 complete, fix API notes, update status
Browse files
CLAUDE.md
CHANGED
|
@@ -7,14 +7,22 @@ A JUCE-based VST3/AU plugin that:
|
|
| 7 |
3. Provides real-time audio analysis (LUFS, FFT bands, stereo width)
|
| 8 |
4. Any AI that speaks MCP (Claude Code, Codex CLI, Gemini CLI) can control any plugin
|
| 9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
## Project Decisions (Confirmed)
|
| 11 |
- **Platform:** macOS only (for now)
|
| 12 |
-
- **License:** GPL (JUCE GPL mode — no commercial license needed)
|
| 13 |
- **VST3 install path:** `~/Library/Audio/Plug-Ins/VST3/`
|
| 14 |
- **AU install path:** `~/Library/Audio/Plug-Ins/Components/`
|
| 15 |
-
- **Test plugins:** FabFilter Pro-Q 4 (EQ,
|
| 16 |
-
- **Xcode:**
|
|
|
|
| 17 |
- **DAW:** Ableton Live (primary), Logic Pro (secondary AU test)
|
|
|
|
| 18 |
|
| 19 |
## Architecture (Single Plugin Host, Not a Graph)
|
| 20 |
```
|
|
@@ -23,39 +31,49 @@ PluginBridgeProcessor (AudioProcessor — the outer VST3/AU shell)
|
|
| 23 |
├── McpServer (httplib) → JSON-RPC 2.0 on http://127.0.0.1:16620/mcp
|
| 24 |
├── AudioAnalyser → LUFS + 7-band FFT + stereo width in processBlock
|
| 25 |
└── processBlock():
|
| 26 |
-
buffer →
|
| 27 |
-
→
|
| 28 |
-
→ output
|
| 29 |
```
|
| 30 |
|
| 31 |
## Build System
|
| 32 |
- **JUCE 8** via CMake (git submodule at `/JUCE`)
|
| 33 |
- **cpp-httplib** — single header `libs/httplib.h` (MIT)
|
| 34 |
- **nlohmann/json** — single header `libs/json.hpp` (MIT)
|
| 35 |
-
- **LUFSMeter** — 4 files in `Source/LufsMeter/` (MIT, from klangfreund/LUFSMeter)
|
| 36 |
- **Target formats:** VST3 + AU (macOS only)
|
| 37 |
-
- **Build:** `
|
| 38 |
-
- **Install:** `COPY_PLUGIN_AFTER_BUILD=TRUE`
|
| 39 |
|
| 40 |
## MCP Protocol
|
| 41 |
- Transport: Streamable HTTP (NOT stdio — we're inside a DAW process)
|
| 42 |
- Endpoint: `POST http://127.0.0.1:16620/mcp`
|
|
|
|
| 43 |
- Wire format: JSON-RPC 2.0
|
| 44 |
- Handshake: `initialize` → `notifications/initialized` → then tools/list, tools/call
|
| 45 |
-
- 5 tools
|
| 46 |
-
- Session: Server returns `Mcp-Session-Id` header on init, client echoes it back
|
| 47 |
-
- Security: bind 127.0.0.1 only, validate Origin header
|
| 48 |
|
| 49 |
-
## Key JUCE APIs (
|
| 50 |
-
- `
|
| 51 |
-
- `formatManager.
|
| 52 |
- `instance->enableAllBuses()` — MUST call before using
|
| 53 |
- `plugin->getParameters()` → `Array<AudioProcessorParameter*>`
|
| 54 |
-
- Cast to `AudioPluginInstance::HostedParameter*` for `getParameterID()`
|
| 55 |
-
- `param->beginChangeGesture()` / `setValueNotifyingHost(val)` / `endChangeGesture()`
|
| 56 |
-
- `juce::dsp::FFT` for spectral analysis
|
| 57 |
-
- `juce::
|
| 58 |
-
- `juce::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
|
| 60 |
## Design Laws (Non-Negotiable)
|
| 61 |
- Never return all parameters — search first, fetch by ID
|
|
@@ -67,58 +85,35 @@ PluginBridgeProcessor (AudioProcessor — the outer VST3/AU shell)
|
|
| 67 |
|
| 68 |
## Thread Safety Rules
|
| 69 |
- HTTP handlers run on httplib's thread pool — NOT audio thread
|
| 70 |
-
-
|
| 71 |
-
- Use `CriticalSection` / `ScopedLock` for plugin hot-swap
|
| 72 |
-
-
|
| 73 |
-
- Cap httplib thread pool to 2-4 threads (reduce OS pressure in plugin host)
|
| 74 |
- `httplib::Server` lifecycle: constructor starts thread, destructor calls stop()+join()
|
| 75 |
|
| 76 |
-
## Sprint Order
|
| 77 |
-
1. **Sprint 1**: Empty plugin shell + httplib server responding to curl ← START HERE
|
| 78 |
-
2. **Sprint 2**: Host Pro-Q 4, route audio, enumerate params
|
| 79 |
-
3. **Sprint 3**: MCP JSON-RPC dispatch, all 5 tools working from Claude Code
|
| 80 |
-
4. **Sprint 4**: Audio analysis (LUFS, FFT bands, stereo, True Peak)
|
| 81 |
-
|
| 82 |
## What NOT To Do
|
| 83 |
- Don't use `AudioProcessorGraph` — we host ONE plugin, not a graph
|
| 84 |
-
- Don't
|
| 85 |
-
- Don't
|
| 86 |
-
- Don't use Gist or Sound-Analyser code — GPL license conflict (we're already GPL but want MIT deps)
|
| 87 |
- Don't use stdio MCP transport — plugin is inside DAW, not a subprocess
|
| 88 |
-
- Don't use `getParameter(int)` / `setParameter(int, float)` — deprecated, asserts in debug
|
| 89 |
- Don't target Windows yet — macOS only for now
|
| 90 |
-
- Don't scan system plugins on load — use file picker for explicit selection
|
| 91 |
-
|
| 92 |
-
## First Steps for Claude Code
|
| 93 |
-
1. Run `xcode-select --version` to verify Xcode 14+
|
| 94 |
-
2. Run `cmake --version` to verify CMake 3.22+
|
| 95 |
-
3. Clone JUCE 8 as submodule: `git submodule add https://github.com/juce-framework/JUCE.git`
|
| 96 |
-
4. Download `httplib.h` from yhirose/cpp-httplib into `libs/`
|
| 97 |
-
5. Download `json.hpp` from nlohmann/json into `libs/`
|
| 98 |
-
6. Create CMakeLists.txt (see ROADMAP.md for skeleton)
|
| 99 |
-
7. Create Source/ files (empty stubs first)
|
| 100 |
-
8. Build: `mkdir build && cd build && cmake .. -G Xcode && cmake --build . --config Release`
|
| 101 |
-
9. Verify plugin appears in `~/Library/Audio/Plug-Ins/VST3/`
|
| 102 |
-
10. Load in Ableton → verify audio passthrough
|
| 103 |
-
11. Verify `curl -X POST http://localhost:16620/mcp -d '{}'` gets a response
|
| 104 |
|
| 105 |
## Repo Structure
|
| 106 |
```
|
| 107 |
pluginbridge/
|
| 108 |
-
├── CLAUDE.md ← this file
|
| 109 |
-
├── ROADMAP.md ← full technical roadmap
|
| 110 |
-
├──
|
|
|
|
|
|
|
| 111 |
├── JUCE/ ← git submodule
|
| 112 |
├── libs/
|
| 113 |
-
│ ├──
|
|
|
|
| 114 |
│ └── json.hpp ← nlohmann/json
|
| 115 |
└── Source/
|
| 116 |
-
├── PluginBridgeProcessor.h/.cpp
|
| 117 |
-
├── PluginBridgeEditor.h/.cpp
|
| 118 |
-
├── HostedPluginManager.h/.cpp
|
| 119 |
-
├── McpServer.h/.cpp
|
| 120 |
-
|
| 121 |
-
└── LufsMeter/
|
| 122 |
-
├── Ebu128LoudnessMeter.h/.cpp
|
| 123 |
-
└── SecondOrderIIRFilter.h/.cpp
|
| 124 |
```
|
|
|
|
| 7 |
3. Provides real-time audio analysis (LUFS, FFT bands, stereo width)
|
| 8 |
4. Any AI that speaks MCP (Claude Code, Codex CLI, Gemini CLI) can control any plugin
|
| 9 |
|
| 10 |
+
## Current Status
|
| 11 |
+
- **Sprint 1:** ✅ Done — plugin shell + MCP server responding
|
| 12 |
+
- **Sprint 2:** ✅ Done — hosts Pro-Q 4 (737 params), routes audio, MCP tools wired
|
| 13 |
+
- **Sprint 3:** Next — full MCP client integration test from Claude Code
|
| 14 |
+
- **Sprint 4:** Pending — real-time audio analysis
|
| 15 |
+
|
| 16 |
## Project Decisions (Confirmed)
|
| 17 |
- **Platform:** macOS only (for now)
|
| 18 |
+
- **License:** GPL (JUCE GPL mode — no commercial license needed, doesn't hamper project)
|
| 19 |
- **VST3 install path:** `~/Library/Audio/Plug-Ins/VST3/`
|
| 20 |
- **AU install path:** `~/Library/Audio/Plug-Ins/Components/`
|
| 21 |
+
- **Test plugins:** FabFilter Pro-Q 4 (EQ, 737 params), LA-2A compressor (fewer params)
|
| 22 |
+
- **Xcode:** CLI tools 2410+ (no full Xcode.app needed)
|
| 23 |
+
- **CMake:** 3.28 via pip (system CMake 4.x incompatible with JUCE 8)
|
| 24 |
- **DAW:** Ableton Live (primary), Logic Pro (secondary AU test)
|
| 25 |
+
- **Build generator:** Unix Makefiles or Ninja (NOT Xcode generator — no Xcode.app)
|
| 26 |
|
| 27 |
## Architecture (Single Plugin Host, Not a Graph)
|
| 28 |
```
|
|
|
|
| 31 |
├── McpServer (httplib) → JSON-RPC 2.0 on http://127.0.0.1:16620/mcp
|
| 32 |
├── AudioAnalyser → LUFS + 7-band FFT + stereo width in processBlock
|
| 33 |
└── processBlock():
|
| 34 |
+
buffer → hostManager.processBlock(buffer, midi)
|
| 35 |
+
→ output (audio routed through hosted plugin)
|
|
|
|
| 36 |
```
|
| 37 |
|
| 38 |
## Build System
|
| 39 |
- **JUCE 8** via CMake (git submodule at `/JUCE`)
|
| 40 |
- **cpp-httplib** — single header `libs/httplib.h` (MIT)
|
| 41 |
- **nlohmann/json** — single header `libs/json.hpp` (MIT)
|
| 42 |
+
- **LUFSMeter** — 4 files in `Source/LufsMeter/` (MIT, from klangfreund/LUFSMeter) — Sprint 4
|
| 43 |
- **Target formats:** VST3 + AU (macOS only)
|
| 44 |
+
- **Build:** `cd build && cmake .. -DCMAKE_C_COMPILER=/usr/bin/cc -DCMAKE_CXX_COMPILER=/usr/bin/c++ && cmake --build . --config Release`
|
| 45 |
+
- **Install:** `COPY_PLUGIN_AFTER_BUILD=TRUE` copies to `~/Library/Audio/Plug-Ins/VST3/`
|
| 46 |
|
| 47 |
## MCP Protocol
|
| 48 |
- Transport: Streamable HTTP (NOT stdio — we're inside a DAW process)
|
| 49 |
- Endpoint: `POST http://127.0.0.1:16620/mcp`
|
| 50 |
+
- Health check: `GET http://127.0.0.1:16620/health`
|
| 51 |
- Wire format: JSON-RPC 2.0
|
| 52 |
- Handshake: `initialize` → `notifications/initialized` → then tools/list, tools/call
|
| 53 |
+
- 5 tools: `list_plugins`, `search_param`, `get_params`, `set_params`, `get_analysis`
|
|
|
|
|
|
|
| 54 |
|
| 55 |
+
## Key JUCE 8 APIs (Verified — these WORK)
|
| 56 |
+
- `formatManager.addFormat(new juce::VST3PluginFormat())` — explicit registration
|
| 57 |
+
- `formatManager.addFormat(new juce::AudioUnitPluginFormat())` — explicit registration
|
| 58 |
- `instance->enableAllBuses()` — MUST call before using
|
| 59 |
- `plugin->getParameters()` → `Array<AudioProcessorParameter*>`
|
| 60 |
+
- Cast to `juce::AudioPluginInstance::HostedParameter*` for `getParameterID()`
|
| 61 |
+
- `param->beginChangeGesture()` / `setValueNotifyingHost(val)` / `endChangeGesture()`
|
| 62 |
+
- `juce::dsp::FFT` for spectral analysis
|
| 63 |
+
- `juce::PluginDirectoryScanner` for system plugin scan
|
| 64 |
+
- `juce::KnownPluginList` for storing scan results
|
| 65 |
+
|
| 66 |
+
## Key JUCE 8 APIs (BROKEN — do NOT use)
|
| 67 |
+
- ❌ `addDefaultFormats()` — deleted in JUCE 8, use explicit addFormat() calls
|
| 68 |
+
- ❌ `addDefaultFormatsToManager()` — also deleted
|
| 69 |
+
- ❌ `getParameter(int)` / `setParameter(int, float)` — deprecated, asserts
|
| 70 |
+
|
| 71 |
+
## Plugin Discovery UX
|
| 72 |
+
- Click "Select Plugin..." button → first click triggers background scan
|
| 73 |
+
- Button shows "Scanning..." while scanning (background thread, non-blocking)
|
| 74 |
+
- After scan: popup menu grouped by manufacturer → plugin name
|
| 75 |
+
- Subsequent clicks: instant menu (cached scan results)
|
| 76 |
+
- NO file browser — clean DAW-style UX
|
| 77 |
|
| 78 |
## Design Laws (Non-Negotiable)
|
| 79 |
- Never return all parameters — search first, fetch by ID
|
|
|
|
| 85 |
|
| 86 |
## Thread Safety Rules
|
| 87 |
- HTTP handlers run on httplib's thread pool — NOT audio thread
|
| 88 |
+
- Plugin scan runs in background thread — NOT message thread (crashes DAW otherwise)
|
| 89 |
+
- Use `CriticalSection` / `ScopedLock` for plugin hot-swap in processBlock
|
| 90 |
+
- Cap httplib thread pool to 2 threads
|
|
|
|
| 91 |
- `httplib::Server` lifecycle: constructor starts thread, destructor calls stop()+join()
|
| 92 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
## What NOT To Do
|
| 94 |
- Don't use `AudioProcessorGraph` — we host ONE plugin, not a graph
|
| 95 |
+
- Don't scan plugins on editor open — scan only on button click, in background thread
|
| 96 |
+
- Don't use `addDefaultFormats()` — deleted in JUCE 8
|
|
|
|
| 97 |
- Don't use stdio MCP transport — plugin is inside DAW, not a subprocess
|
|
|
|
| 98 |
- Don't target Windows yet — macOS only for now
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
|
| 100 |
## Repo Structure
|
| 101 |
```
|
| 102 |
pluginbridge/
|
| 103 |
+
├── CLAUDE.md ← this file
|
| 104 |
+
├── ROADMAP.md ← full technical roadmap
|
| 105 |
+
├── SYNC.md ← coordination between HF Claude and Claude Code
|
| 106 |
+
├── CMakeLists.txt ← JUCE 8 CMake (LANGUAGES C CXX)
|
| 107 |
+
├── .gitignore
|
| 108 |
├── JUCE/ ← git submodule
|
| 109 |
├── libs/
|
| 110 |
+
│ ├── README.md ← download instructions
|
| 111 |
+
│ ├── httplib.h ← cpp-httplib
|
| 112 |
│ └── json.hpp ← nlohmann/json
|
| 113 |
└── Source/
|
| 114 |
+
├── PluginBridgeProcessor.h/.cpp ← main AudioProcessor, owns HostedPluginManager + McpServer
|
| 115 |
+
├── PluginBridgeEditor.h/.cpp ← GUI: status + plugin selector popup menu
|
| 116 |
+
├── HostedPluginManager.h/.cpp ← loads VST3/AU, enumerates params, routes audio
|
| 117 |
+
├── McpServer.h/.cpp ← HTTP server, JSON-RPC dispatch, 5 tools
|
| 118 |
+
└── AudioAnalyser.h/.cpp ← stub (Sprint 4)
|
|
|
|
|
|
|
|
|
|
| 119 |
```
|