YUP Audio Plugin - Build Guide
Five steps from empty folder to a working CLAP/VST3/standalone plugin. Do each step in order; skip none.
Step 1 - Project setup (CMake + YUP)
- Add YUP as a git submodule or
FetchContenttarget; disable examples/tests for plugin projects unless needed. - Use
yup_audio_plugin (...)to declare the plugin target. - Set
TARGET_NAME,TARGET_VERSION,TARGET_APP_ID,TARGET_APP_NAMESPACE, andTARGET_CXX_STANDARD 20. - Set plugin metadata in the same call:
PLUGIN_ID,PLUGIN_NAME,PLUGIN_VENDOR,PLUGIN_VERSION,PLUGIN_DESCRIPTION,PLUGIN_IS_SYNTH, andPLUGIN_IS_MONO. - Enable only the formats you need:
PLUGIN_CREATE_CLAP,PLUGIN_CREATE_VST3,PLUGIN_CREATE_STANDALONE. - Link at least
yup::yup_guiandyup::yup_audio_processorsinMODULES. - Add plugin sources to
${target_name}_shared; the format targets link against that shared interface target.
Step 2 - AudioProcessor skeleton
- Subclass
yup::AudioProcessor. - Construct it with a processor name and immutable
yup::AudioBusLayout, e.g. stereo output:AudioProcessor ("MyPlugin", yup::AudioBusLayout ({}, { yup::AudioBus ("main", yup::AudioBus::Audio, yup::AudioBus::Output, 2) })). - Override
prepareToPlay (float sampleRate, int maxBlockSize),releaseResources(),processBlock (yup::AudioSampleBuffer& audioBuffer, yup::MidiBuffer& midiBuffer), andflush(). - In
prepareToPlay: store sample rate, allocate worst-case scratch storage, initialiseAudioParameterHandles, and reset all filters, envelopes, oscillators, delay lines, MIDI/voice state, and smoothers. - In
processBlock: assume realtime constraints; no allocation, locks,yup::Stringformatting, logging, file/network access, or UI calls. - Implement preset and state methods:
getCurrentPreset,setCurrentPreset,getNumPresets,getPresetName,setPresetName,loadStateFromMemory, andsaveStateIntoMemory. - Export the plugin entry point:
extern "C" yup::AudioProcessor* createPluginProcessor()
{
return new MyPluginProcessor();
}
Step 3 - Parameters via AudioParameterBuilder
- Add parameters in the processor constructor with
addParameter (...). - Keep each
yup::AudioParameter::Ptras a processor member; parameter IDs must be stable and unique. - Use
yup::AudioParameterBuilder().withID (...).withName (...).withRange (...).withDefault (...).withSmoothing (...).build(). - For audio-thread reads, construct a
yup::AudioParameterHandleinprepareToPlaywith the parameter and sample rate. - In
processBlock, callhandle.updateNextAudioBlock()once per block, then usegetNextValue()per sample orskip (numSamples)for block ramps. - From the editor, wrap user drags with
beginChangeGesture()andendChangeGesture(), and usesetValueNotifyingHost()for host-visible parameter changes. - For presets or state restore, prefer setting the real value with
setValue()orsetNormalizedValue()from a non-audio thread, then let the audio handle pick it up next block.
Step 4 - Editor and UI
- Subclass
yup::AudioProcessorEditor. - Implement
isResizable(),getPreferredSize(),paint (yup::Graphics&), andresized(). - The editor owns UI components; the processor must work correctly with no editor open.
- The editor may keep
AudioParameter::Ptrs and poll values from ayup::Timerfor display sync. - Do not store a raw editor pointer in the processor. Use
hasEditor()andcreateEditor()as the factory boundary. - UI controls should notify parameters through gestures, e.g. slider drag start/end plus
setValueNotifyingHost().
Step 5 - Build, test, package
- Configure:
cmake -B build -DCMAKE_BUILD_TYPE=Release -DYUP_BUILD_TESTS=OFF -DYUP_BUILD_EXAMPLES=OFF - Build:
cmake --build build --config Release - Validate the produced CLAP/VST3/standalone targets in at least one host.
- Test sample-rate changes, buffer-size changes, transport reset, preset recall, automation, MIDI timing, bypass/suspend/resume, and editor open/close.
- If the plugin reports latency or tail, override
getLatencySamples()andgetTailSamples()and verify hosts receive correct values.
Common pitfalls
| Pitfall | Correct approach |
|---|---|
Reading raw AudioParameter values directly for smoothed DSP | Use AudioParameterHandle initialised in prepareToPlay |
Forgetting updateNextAudioBlock() | Call it once at the top of each audio block before getNextValue() or skip() |
Calling setValueNotifyingHost() from processBlock | Use it from UI/host-control paths; keep audio-thread changes local or lock-free |
| Allocating MIDI voices on note-on | Preallocate a bounded voice pool in prepareToPlay |
Hard-coding stereo processing without matching AudioBusLayout | Declare the layout explicitly and guard channel access |
| Missing state methods | Implement loadStateFromMemory / saveStateIntoMemory; hosts depend on them for recall |
Holding getProcessLock() from UI/background work | Keep state swaps short; the CLAP wrapper uses a try-lock around processing |
Leaving state stale after prepareToPlay or flush() | Reset voices, smoothers, filter history, delay buffers, and pending MIDI unconditionally |
Use the yup-review skill to audit finished processor code for YUP-specific thread, parameter, wrapper, and bus-layout issues.