Skip to content

Latest commit

 

History

History
105 lines (75 loc) · 6.37 KB

File metadata and controls

105 lines (75 loc) · 6.37 KB

AGENTS.md — iPlug2 plugin workspace

Guidance for AI agents (and humans) working in this repo. Read this before editing.

What this repo is

Out-of-source iPlug2 plugin projects plus a shared DSP/MIR library. It is not an iPlug2 fork — it expects a sibling iPlug2 checkout and links against it.

Dev/
├── iPlug2/      <-- sibling SDK, NOT in this repo (fetched by scripts/setup-iplug2.ps1)
└── plugins/     <-- this repo
    ├── src/audioagent/   shared C++ DSP + MIR + Camelot wheel library (header-only, INTERFACE lib)
    ├── CamelotSynth/      reference iPlug2 plugin (links audioagent)
    ├── third_party/       vendored deps; upstream/ is gitignored, fetched by setup
    ├── assets/             shared audio (gitignored)
    └── scripts/            PowerShell build/setup + CMake helpers

The repo targets Windows + Visual Studio 2022 + CMake as the primary path. macOS/iOS Xcode project files exist (generated by iPlug2's duplicate.py) but the documented, supported workflow is Windows/PowerShell.

iPlug2 is current. iPlug3 is announced but not released — do not assume iPlug3 APIs exist. All code here is iPlug2.

First-time setup (run once)

.\scripts\setup-iplug2.ps1          # populates ../iPlug2 dependencies
.\scripts\setup-third-party.ps1     # fetches audioFlux source into third_party/audioFlux/upstream
code plugins.code-workspace

Build / install (the loop you'll use)

$p = "CamelotSynth"
.\scripts\build.ps1 -Plugin $p -Format vst3 -Config Release -Install
  • Build output: CamelotSynth/build/out/CamelotSynth.vst3/.
  • Reaper-locked-file gotcha: if Reaper has the plugin loaded, -Install stages to *.vst3.pending. Close Reaper, then re-run .\scripts\install-plugin.ps1 -Plugin $p -Format vst3.
  • -Deploy (CMake IPLUG_DEPLOY_PLUGINS=ON) copies straight into the AppData VST3 folder; off by default to avoid clobbering a plugin Reaper has open.
  • Two build methods exist: -Method cmake (default, preferred) and -Method vs (msbuild the generated .sln). Prefer cmake.

New plugin

.\scripts\new-plugin.ps1 -Name MySynth -Template IPlugInstrument -Manufacturer Vecnode

This calls iPlug2's Examples/duplicate.py, then patches the generated CMakeLists.txt so IPLUG2_DIR resolves to the sibling ../../iPlug2 and appends the Windows resource-embed include. After running: edit config.h (PLUG_MFR_ID, unique IDs, metadata) and link audioagent if you need the shared DSP (see its CMake snippet below).

The iPlug2 surface (important — keep it minimal)

audioagent is framework-agnostic C++ that touches a deliberately tiny slice of iPlug2/WDL so it stays host-portable. The entire dependency is:

Symbol / header Used in Why
IPlugConstants.hiplug::sample iplug_bridge.h RT sample type (double/float)
Smoothers.hiplug::LogParamSmooth iplug_bridge.h param de-zipper on the audio thread
heapbuf.hWDL_TypedBuf / WDL_String dsp/analysis buffers RT-safe pre-allocated buffers
IPlugPaths.hLocateResource / LoadWinResource / gHINSTANCE platform/ResourceLoader.h embedded WAV load on Windows

Rules:

  • Only iplug_bridge.h and platform/ResourceLoader.h may include iPlug headers. Everything else in audioagent/ is plain C++ (+ audioFlux in analysis/).
  • No IGraphics anywhere in audioagent/. UI lives only in the plugin (CamelotSynth/src/ui/, src/editor/). camelot/WheelLayout.h is pure geometry — the plugin maps IRECTBounds.
  • To port audioagent to a non-iPlug host: replace those two files (swap LoadEmbedded for AssignFromFloat on a decoded buffer).

Layer dependency rules are enforced by convention — see src/audioagent/ARCHITECTURE.md ("Module dependency rules").

Real-time audio contract (do not break)

The audio thread is SamplerEngine::ProcessBlock → SampleTransport::ProcessBlock → SamplePlayer::ProcessBlock. On that path:

  • No audioFlux calls, no heap allocation, no locks/mutexes, no IGraphics.
  • audioFlux (pitch shift, PitchYIN detect) runs only on PitchStreamWorker / OfflineSampleWorker background threads.
  • The worker scheduler is kicked from SamplerEngine::Tick() (UI timer), not from ProcessBlock.
  • CI greps for forbidden APIs: run .\scripts\check-rt-audio.ps1 before committing audio-path changes (also wired in .github/workflows/rt-audit.yml).

Linking audioagent from a plugin CMakeLists

add_subdirectory("${CMAKE_SOURCE_DIR}/../src/audioagent" "${CMAKE_BINARY_DIR}/audioagent")
# then, per format target:
target_link_libraries(${PROJECT_NAME}-vst3 PRIVATE audioagent)

audioagent is an INTERFACE library: headers + include paths + the audioflux static link, and it pulls iPlug2 headers from IPLUG2_DIR.

Known issues / things to verify before "done"

  • State persistence is param-based (no chunks). PLUG_DOES_STATE_CHUNKS is 0; HPF and pitch mode persist as real params (kParamHPF, kParamPitchMode) and are re-applied to the engine via OnParamChange (live) and OnReset (restore). If you ever add non-param state, set PLUG_DOES_STATE_CHUNKS 1 and implement SerializeState/UnserializeState so they also call SerializeParams/UnserializeParams — otherwise enabling chunks will stop params from saving.
  • Asset filename is rewritten at configure time. assets/AtmosSynth1 D#maj.wav (gitignored, has a space + #) is copied to CamelotSynth/resources/audio/AtmosSynth1_Dmaj.wav by the plugin CMakeLists. Code references the sanitized name via ATMOS_SAMPLE_FN in config.h.
  • config.h still ships placeholder metadata from the template (PLUG_MFR_ID 'Acme', PLUG_URL_STR/PLUG_EMAIL_STR pointing at iplug2.github.io). Fix per real plugin before release.

Documentation map

Doc Contents
README.md Overview, setup, build quickstart
DEVELOPMENT_PLAN.md RT DSP roadmap, implemented phases
src/audioagent/README.md Library modules, thread model, iPlug integration
src/audioagent/ARCHITECTURE.md Data flow, transport de-clicking, dependency rules
CamelotSynth/README.md Plugin features, UI, processing chain

When you change module structure, the thread model, or the iPlug surface, update src/audioagent/ARCHITECTURE.md and this file in the same change.