VST3 Plugin Review
VST3's split controller/processor model and bus negotiation are the most common sources of host incompatibility. Issues are rarely compile-time errors — they surface as broken recall, silence, or crashes in specific DAWs.
Step 0 — Run universal checks first
Invoke audio-dsp-review and audio-numerics-review before this skill. This skill adds VST3-specific spec compliance and host compatibility checks on top; it does not replace realtime-safety or numerics reviews.
Step 1 — Identify plugin structure
Locate the three core interfaces and their ownership:
| Interface | Thread | Responsibilities |
|---|---|---|
IComponent |
Main thread | Bus layout, activation, state save/load, unit info |
IAudioProcessor |
Audio thread in process(); main thread otherwise |
Bus arrangement negotiation, latency, processing setup |
IEditController |
Main thread | Parameter definitions, UI, MIDI mapping, setComponentState |
IConnectionPoint |
Main thread | Processor↔controller messaging via notify() |
Step 2 — Scan for VST3-specific violations
| Violation | Where | Risk |
|---|---|---|
process() allocating memory or acquiring a lock |
IAudioProcessor::process |
Realtime violation — causes xruns and glitches |
Latency not reported via getLatencySamples() |
IAudioProcessor |
Host delay compensation broken — early mixdown or timing drift |
Bus arrangement not matching what was negotiated in setBusArrangements |
setBusArrangements / process |
Channel count mismatch — buffer overread or silent output |
ParamID not stable across save/load and plugin versions |
IEditController |
Parameter recall broken — automation and presets silently map to wrong params |
Calling IHostApplication or IComponentHandler from audio thread |
process() |
Not thread-safe — host component handler is main-thread only |
IParamValueQueue not fully consumed each process() callback |
process() |
Parameter updates lost — automation steps skipped |
| Bypass parameter not passing audio through cleanly | IAudioProcessor / controller parameter setup |
Incorrect bypassed output — artifacts or silence when host automates bypass |
kOfflineProcessing mode not handled separately from realtime |
setProcessing / process |
Offline rendering broken — wrong timing, denormal handling, or lookahead |
setComponentState not applied by controller on load |
IEditController::setComponentState |
Controller shows stale values after preset load |
setState / getState on IComponent and IEditController not symmetric |
Both | Preset corruption — one side saves data the other ignores |
Step 3 — Write the review
## VST3 Plugin Review: `[file / processor class]`
### Verdict
[Spec-compliant | Has critical violations | Warnings only] — [one sentence summary]
### Critical Violations
**[Category]: [description]**
`file:line` — `offending code`
Why: [one sentence on spec / thread safety / host compat risk]
Fix: [concrete VST3-idiomatic suggestion]
### Warnings
[same format]
### What's Done Well
[correct patterns — clean bus negotiation, stable ParamIDs, latency reported, etc.]
### Recommended Fixes (priority order)
1. ...
Quick fix table
| Violation | Fix |
|---|---|
Allocation in process() |
Move to setupProcessing() or constructor; use pre-sized buffers |
| Missing latency report | Override getLatencySamples() and return a stable value; call restartComponent(kLatencyChanged) if it changes |
| Bus mismatch | Store the accepted arrangement from setBusArrangements; validate numInputs/numOutputs against it in process() |
Unstable ParamID |
Use a fixed enum or stable CRC of the param name string; never use a container index |
| Params not consumed | Iterate inputParameterChanges fully each block; apply at sample-accurate offsets |
| Unsafe bypass | Mark the bypass parameter with ParameterInfo::kIsBypass; when bypassed, copy inputs to outputs with no modification and keep consuming parameter changes |
For deep detail on bus negotiation, parameter ID stability, latency reporting, controller↔processor communication, and per-DAW compatibility quirks see references/vst3-pitfalls.md.