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

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