Update CLAUDE.md with confirmed project decisions
Browse files
CLAUDE.md
CHANGED
|
@@ -7,6 +7,15 @@ 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 |
## Architecture (Single Plugin Host, Not a Graph)
|
| 11 |
```
|
| 12 |
PluginBridgeProcessor (AudioProcessor β the outer VST3/AU shell)
|
|
@@ -24,7 +33,9 @@ PluginBridgeProcessor (AudioProcessor β the outer VST3/AU shell)
|
|
| 24 |
- **cpp-httplib** β single header `libs/httplib.h` (MIT)
|
| 25 |
- **nlohmann/json** β single header `libs/json.hpp` (MIT)
|
| 26 |
- **LUFSMeter** β 4 files in `Source/LufsMeter/` (MIT, from klangfreund/LUFSMeter)
|
| 27 |
-
-
|
|
|
|
|
|
|
| 28 |
|
| 29 |
## MCP Protocol
|
| 30 |
- Transport: Streamable HTTP (NOT stdio β we're inside a DAW process)
|
|
@@ -32,17 +43,21 @@ PluginBridgeProcessor (AudioProcessor β the outer VST3/AU shell)
|
|
| 32 |
- Wire format: JSON-RPC 2.0
|
| 33 |
- Handshake: `initialize` β `notifications/initialized` β then tools/list, tools/call
|
| 34 |
- 5 tools only: `list_plugins`, `search_param`, `get_params`, `set_params`, `get_analysis`
|
|
|
|
|
|
|
| 35 |
|
| 36 |
-
## Key JUCE APIs (Correct, Verified)
|
| 37 |
- `addDefaultFormatsToManager(formatManager)` β NOT deprecated `addDefaultFormats()`
|
| 38 |
-
- `formatManager.createPluginInstanceAsync(desc, sr, bs, callback)` β ALWAYS async
|
| 39 |
- `instance->enableAllBuses()` β MUST call before using
|
| 40 |
- `plugin->getParameters()` β `Array<AudioProcessorParameter*>`
|
| 41 |
-
- Cast to `AudioPluginInstance::HostedParameter*` for `getParameterID()` (stable ID)
|
| 42 |
-
- `param->beginChangeGesture()` / `setValueNotifyingHost(val)` / `endChangeGesture()`
|
| 43 |
- `juce::dsp::FFT` for spectral analysis (NOT KissFFT, NOT Gist β those are GPL)
|
|
|
|
|
|
|
| 44 |
|
| 45 |
-
## Design Laws
|
| 46 |
- Never return all parameters β search first, fetch by ID
|
| 47 |
- Delta only after param changes
|
| 48 |
- Single-line compact responses (numbers only)
|
|
@@ -55,10 +70,11 @@ PluginBridgeProcessor (AudioProcessor β the outer VST3/AU shell)
|
|
| 55 |
- Use `std::atomic<float>` for analysis values queried by HTTP
|
| 56 |
- Use `CriticalSection` / `ScopedLock` for plugin hot-swap
|
| 57 |
- Never call `getValue()` directly from HTTP thread β read from atomic cache
|
|
|
|
| 58 |
- `httplib::Server` lifecycle: constructor starts thread, destructor calls stop()+join()
|
| 59 |
|
| 60 |
## Sprint Order
|
| 61 |
-
1. **Sprint 1**: Empty plugin shell + httplib server responding to curl
|
| 62 |
2. **Sprint 2**: Host Pro-Q 4, route audio, enumerate params
|
| 63 |
3. **Sprint 3**: MCP JSON-RPC dispatch, all 5 tools working from Claude Code
|
| 64 |
4. **Sprint 4**: Audio analysis (LUFS, FFT bands, stereo, True Peak)
|
|
@@ -67,27 +83,35 @@ PluginBridgeProcessor (AudioProcessor β the outer VST3/AU shell)
|
|
| 67 |
- Don't use `AudioProcessorGraph` β we host ONE plugin, not a graph
|
| 68 |
- Don't use `AudioPluginHost`'s settings file β use runtime file picker
|
| 69 |
- Don't fork `getdunne/juce-plugin-wrapper` β dead, GPL, JUCE 6
|
| 70 |
-
- Don't use Gist or Sound-Analyser code β GPL license
|
| 71 |
- Don't use stdio MCP transport β plugin is inside DAW, not a subprocess
|
| 72 |
-
- Don't use `getParameter(int)` / `setParameter(int, float)` β deprecated, asserts
|
|
|
|
|
|
|
| 73 |
|
| 74 |
-
##
|
| 75 |
-
1.
|
| 76 |
-
2.
|
| 77 |
-
3.
|
| 78 |
-
4.
|
| 79 |
-
5.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
|
| 81 |
## Repo Structure
|
| 82 |
```
|
| 83 |
pluginbridge/
|
| 84 |
βββ CLAUDE.md β this file (Claude Code reads automatically)
|
| 85 |
βββ ROADMAP.md β full technical roadmap with code samples
|
| 86 |
-
βββ CMakeLists.txt
|
| 87 |
-
βββ JUCE/ β git submodule
|
| 88 |
βββ libs/
|
| 89 |
-
β βββ httplib.h
|
| 90 |
-
β βββ json.hpp
|
| 91 |
βββ Source/
|
| 92 |
βββ PluginBridgeProcessor.h/.cpp
|
| 93 |
βββ PluginBridgeEditor.h/.cpp
|
|
|
|
| 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, many params), LA-2A compressor (fewer params)
|
| 16 |
+
- **Xcode:** 14+ required (user should verify with `xcode-select --version`)
|
| 17 |
+
- **DAW:** Ableton Live (primary), Logic Pro (secondary AU test)
|
| 18 |
+
|
| 19 |
## Architecture (Single Plugin Host, Not a Graph)
|
| 20 |
```
|
| 21 |
PluginBridgeProcessor (AudioProcessor β the outer VST3/AU shell)
|
|
|
|
| 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:** `mkdir build && cd build && cmake .. && cmake --build .`
|
| 38 |
+
- **Install:** `COPY_PLUGIN_AFTER_BUILD=TRUE` in CMake copies to system plugin dirs
|
| 39 |
|
| 40 |
## MCP Protocol
|
| 41 |
- Transport: Streamable HTTP (NOT stdio β we're inside a DAW process)
|
|
|
|
| 43 |
- Wire format: JSON-RPC 2.0
|
| 44 |
- Handshake: `initialize` β `notifications/initialized` β then tools/list, tools/call
|
| 45 |
- 5 tools only: `list_plugins`, `search_param`, `get_params`, `set_params`, `get_analysis`
|
| 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 (Correct, Verified Against JUCE 8 Source)
|
| 50 |
- `addDefaultFormatsToManager(formatManager)` β NOT deprecated `addDefaultFormats()`
|
| 51 |
+
- `formatManager.createPluginInstanceAsync(desc, sr, bs, callback)` β ALWAYS async (AU requires it)
|
| 52 |
- `instance->enableAllBuses()` β MUST call before using
|
| 53 |
- `plugin->getParameters()` β `Array<AudioProcessorParameter*>`
|
| 54 |
+
- Cast to `AudioPluginInstance::HostedParameter*` for `getParameterID()` (stable string ID)
|
| 55 |
+
- `param->beginChangeGesture()` / `setValueNotifyingHost(val)` / `endChangeGesture()` β for DAW automation
|
| 56 |
- `juce::dsp::FFT` for spectral analysis (NOT KissFFT, NOT Gist β those are GPL)
|
| 57 |
+
- `juce::dsp::Oversampling<float>` for True Peak measurement (4Γ oversample)
|
| 58 |
+
- `juce::dsp::WindowingFunction<float>::hann` for FFT windowing
|
| 59 |
|
| 60 |
+
## Design Laws (Non-Negotiable)
|
| 61 |
- Never return all parameters β search first, fetch by ID
|
| 62 |
- Delta only after param changes
|
| 63 |
- Single-line compact responses (numbers only)
|
|
|
|
| 70 |
- Use `std::atomic<float>` for analysis values queried by HTTP
|
| 71 |
- Use `CriticalSection` / `ScopedLock` for plugin hot-swap
|
| 72 |
- Never call `getValue()` directly from HTTP thread β read from atomic cache
|
| 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)
|
|
|
|
| 83 |
- Don't use `AudioProcessorGraph` β we host ONE plugin, not a graph
|
| 84 |
- Don't use `AudioPluginHost`'s settings file β use runtime file picker
|
| 85 |
- Don't fork `getdunne/juce-plugin-wrapper` β dead, GPL, JUCE 6
|
| 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 (Claude Code reads automatically)
|
| 109 |
βββ ROADMAP.md β full technical roadmap with code samples
|
| 110 |
+
βββ CMakeLists.txt
|
| 111 |
+
βββ JUCE/ β git submodule
|
| 112 |
βββ libs/
|
| 113 |
+
β βββ httplib.h β cpp-httplib (yhirose/cpp-httplib)
|
| 114 |
+
β βββ json.hpp β nlohmann/json
|
| 115 |
βββ Source/
|
| 116 |
βββ PluginBridgeProcessor.h/.cpp
|
| 117 |
βββ PluginBridgeEditor.h/.cpp
|