YUP Audio Plugin Review
YUP looks JUCE-like in places, but its plugin API has its own parameter handles, bus layout, editor contract, and wrapper behavior. Review those directly instead of applying APVTS rules.
Step 0 - Run universal checks first
Invoke audio-dsp-review and audio-numerics-review before this skill. This skill adds YUP-specific checks on top; it does not replace realtime-safety or numerics reviews.
Step 1 - Identify plugin structure
Locate the core classes and boundaries:
yup::AudioProcessor- audio thread ownsprocessBlock; wrapper/host code calls lifecycle, state, and preset methodsyup::AudioProcessorEditor- UI/message thread only; never call from audio threadyup::AudioParameter- atomic scalar value plus listeners for host/UI notificationyup::AudioParameterHandle- audio-thread smoothing/read path; initialise inprepareToPlayyup::AudioBusLayout- immutable I/O contract exposed to plugin wrappersextern "C" yup::AudioProcessor* createPluginProcessor()- plugin entry point used by wrappers
Step 2 - Scan for YUP violations
| Violation | Where to look | Risk |
|---|---|---|
AudioParameterHandle default-constructed and used before prepareToPlay | Members and processBlock | Null parameter assertion/crash; wrong smoothing state |
Missing handle.updateNextAudioBlock() | processBlock | Automation and smoothing lag or never update |
Recreating AudioParameterHandle in processBlock | Audio callback | Reinitialises smoothing and may add avoidable work/glitches |
setValueNotifyingHost(), beginChangeGesture(), or endChangeGesture() from audio code | processBlock, MIDI handlers, DSP helpers | Listener/host notification path is not a realtime data path |
Allocating voices, buffers, MemoryBlock, String, std::vector, or smart-pointer objects in processBlock | MIDI note-on, scratch buffers, metering | Heap traffic causes xruns |
YUP_DBG, Logger::writeToLog, File, URL, JSON/XML, or string formatting in audio code | Debug and error paths | Logging/I/O/allocation on audio thread |
| Hard-coded channel pointers without layout checks | getWritePointer(0/1) and bus setup | Mono, sidechain, or unusual host layout crashes/corrupts audio |
Mutating MidiBuffer while iterating it | MIDI loops | Iterator invalidation or unbounded allocation |
prepareToPlay not resetting all DSP and note state | Lifecycle methods | Host reactivation leaves stale filter history, held notes, or ramps |
flush() missing for synths/effects with tails or voices | CLAP reset handling | Host reset leaves hanging notes or stale delay/reverb tails |
| State load/save left unimplemented | loadStateFromMemory, saveStateIntoMemory | DAW session recall and presets fail |
Long work under getProcessLock() or state swaps with no handoff | UI/state/background code | Processing may skip under CLAP try-lock or race under other wrappers |
| Editor stores raw processor-owned UI pointers or processor stores editor pointer | Editor/processor members | Dangling pointer when host closes editor |
Step 3 - Check YUP API contracts
- Constructor: parameters are added once; IDs are unique and stable; bus layout matches
PLUGIN_IS_SYNTH/PLUGIN_IS_MONOintent. prepareToPlay: allocates and resets all state; constructs handles with current sample rate; tolerates repeated calls.processBlock: reads smoothed parameters through handles; respectsaudioBuffer.getNumSamples()and actual channel count; clears or writes every output sample it owns.- MIDI: consumes events in sample order; uses bounded voice/event storage; clears or rewrites outgoing MIDI deliberately.
- Editor: uses gesture begin/end around drags; calls
setValueNotifyingHost()for user changes; polls parameter values with a timer usingdontSendNotification. - State/presets: serialises all host-visible parameters and preset data; validates memory before applying; does not assume state callbacks are the audio thread.
- Format wrappers: CLAP and VST3 builds are enabled only as needed; CLAP/VST3/standalone targets all link the same
${target_name}_sharedcode.
Step 4 - Write the review
## YUP Plugin Review: `[file / class]`
### Verdict
[Safe | Has critical violations | Warnings only] - [one sentence summary]
### Critical Violations
**[Category]: [description]**
`file:line` - `offending code`
Why: [one sentence on YUP lifecycle/thread/API risk]
Fix: [concrete YUP-idiomatic suggestion]
### Warnings
[same format]
### What's Done Well
[correct patterns observed]
### Recommended Fixes (priority order)
1. ...
Quick fix table
| Violation | Fix |
|---|---|
| Direct unsmoothed parameter read for DSP | Keep AudioParameter::Ptr, initialise AudioParameterHandle in prepareToPlay, read via the handle |
| Missing gesture pairing | Call beginChangeGesture() on drag start and endChangeGesture() on drag end |
UI value changes with setValue() | Use setValueNotifyingHost() for user edits so automation/host state is notified |
| Allocating voices on note-on | Preallocate voice slots and reuse them; reject/steal voices when full |
| Hard-coded stereo | Declare stereo in AudioBusLayout and guard getNumChannels() before channel access |
| Missing reset | Clear voices, delay lines, filters, smoothers, and pending MIDI in both prepareToPlay and flush() where relevant |
| Unimplemented state recall | Serialise parameters and presets into MemoryBlock; validate and apply from non-audio state callbacks |
| Audio-to-UI updates | Write scalars to atomics or a preallocated lock-free queue; have editor Timer poll them |
For full code examples and YUP-specific BAD/GOOD patterns, see references/yup-violations.md.