// ZPWR-SYNTH — ENGINEERING REPORT

module map · dependency footprint · test status — a patch is the voice

>_EXECUTIVE SUMMARY

zpwr-synth is a fully modular polyphonic synthesizer plugin built on JUCE 8.0.13 (C++20, CMake 3.22+), shipping as AU, VST3, CLAP and Standalone. There is no fixed signal path: the plugin runs the shared modular patch graph from the zpwr-patch-core submodule, and a patch is the voice, instantiated across the voice pool. dsp/SynthModules.cpp registers 309 synth-specific DSP node types onto the core's zpc::ModuleRegistry — the same registry the live reference is generated from — spanning virtual-analog and wavetable oscillators, FM/additive/granular generators, hardstyle and physical-modelling voices, filters, shapers and CV modulators. The DSP core (dsp/ + src/) is roughly 12.3k lines of own source; the pure-DSP layer (zpwr_dsp) carries no JUCE dependency and is headless-testable. Version 0.2.23.

309
Synth DSP Node Types
32
Voice-Pool Ceiling (kMaxVoices)
128
CC-Mappable Automation Slots
5
CTest Suites
~1,365
Test LOC
~12.3k
Own DSP Source Lines
4
Plugin Formats (AU/VST3/CLAP/SA)
256
General Factory Voices

~MODULE MAP

The processor hosts a polyphonic pool of voices, each running the shared signal-agnostic node graph from zpwr-patch-core. The synth-specific generators, voices, filters and modulators are a DSP pack registered onto that core registry.

ModuleSourceRole
ZpwrSynthProcessorsrc/PluginProcessor.cpp/.hJUCE AudioProcessor; hosts the WebView editor, host params (soft keys + auto pool), MIDI/MPE intake, preset state.
PolyEnginedsp/PolyEngine.cpp/.hVoice pool (ceiling kMaxVoices = 32); poly / mono / legato / duophonic modes; unison stacking (kMaxUnison = 11); parallel per-voice rendering via worker threads.
RuntimeGraph (per voice)libs/zpwr-patch-coreShared signal-agnostic node graph evaluated per sample; nodes carry type + 4 inputs + up to 12 params + 1 output, fed per-voice external sources.
Voice / Layer / Instrumentdsp/Voice.cpp, Layer.cpp, Instrument.cppVoice lifecycle and layering (kMaxLayers = 16) over the graph engine.
Oscillators & generatorsdsp/SynthModules.cppOsc, Wt (wavetable), Supersaw, FM, Sub, Sync, Additive, Vector, ChordOsc, DCO/VCO, Vosim, PAF, Shepard, Buzz, Bytebeat, Granular, Sample — most with unison Voices + Detune.
Physical / instrument voicesdsp/SynthModules.cppKarplus and a large family of waveguide / modal voices (strings, winds, brass, mallets, organs, world instruments) registered as self-contained note-driven nodes.
Hardstyle / genre voicesdsp/SynthModules.cppHardKick, Screech, Hoover, Reese and drum-machine voices (TR-808/909/606) for techno / hardstyle / DnB material.
Filters & shapersdsp/SynthModules.cppTPT state-variable Filter, DiodeLadder (303-style), Folder, Waveshaper, Crusher (bit/SR reduction), Drive, RingMod.
Envelopes & modulatorsdsp/Envelope.h, LFO.h, ModMatrix.h, SynthModules.cppADSR Env, LFO, StepLFO, NoiseLFO, SampleHold, Glide, Scaler; per-param mod matrix routing source → (node, param) × depth.
Utility / routingdsp/SynthModules.cppVCA (in1 × in2), Gain, Mixer, Delay, Noise — signal plumbing inside the graph.
Editor & preset UIsrc/PluginEditor.cpp, PresetManager.cpp, PresetBrowser.cppWebView patcher (served from BinaryData), factory + user preset management, NKS export.
Sample / wavetable storessrc/SampleBank.cpp, SampleStore.cpp, UserWavetableStore.hBacking stores for Sample/Granular slots and user wavetables (resources/samples.bin, wavetables.bin).

~ARCHITECTURE

Headline structural facts about the engine — how a single patch definition becomes a polyphonic voice and what feeds it each sample.

Patch is the voice

No fixed signal path. The default voice is Osc(Note) → VCA ← Env(Gate); each node carries a type, 4 inputs, up to 12 params and one output. The patch is saved as JSON in plugin state.

Shared graph core

The graph engine is zpwr-patch-core (libs/zpwr-patch-core, a git submodule) — the same signal-agnostic cable-routing core that powers zpwr-fx. PolyEngine holds one zpc::RuntimeGraph per voice.

Per-voice external sources

Fed each sample: In L/R, Noise, Soft Keys (expandable pool), Note, Gate, Velocity, Mod Wheel, Pitch Bend, Aftertouch, MPE Pressure / Slide / Bend, plus a first-class Trigger impulse on each note-on edge.

Unison & layering

Every oscillator carries a Voices (1–11) unison param plus Detune (cents). Pool ceiling kMaxVoices = 32, kMaxUnison = 11, kMaxLayers = 16; Voices = 1 keeps classic mono behaviour.

Soft keys & automation

32 soft keys (16 active by default) + a reserved pool of 96 Auto N host params = 128 CC-mappable slots, one per MIDI CC. Right-click any block param to bind it (the Kontakt/Reaktor model).

Headless DSP layer

The pure-DSP layer (zpwr_dsp) carries no JUCE dependency and is headless-testable. Generators read each voice's Note/Gate from the core's ComputeContext::external array.


~DEPENDENCY FOOTPRINT

JUCE and the CLAP wrapper are vendored via FetchContent; the patch graph and clip engine are in-tree git submodules. No system installs are required.

DependencyPinNotes
JUCE8.0.13Vendored via FetchContent (no system install). Plugin formats AU / VST3 / Standalone; WebView editor (NEEDS_WEB_BROWSER).
clap-juce-extensionspinned commitAdds the CLAP format on top of the JUCE wrapper (same path zpwr-fx uses).
zpwr-patch-coresubmodule (libs/)Shared modular patch graph + DSP primitives + script engine; the synth registers its nodes onto this registry.
zpwr-clip-enginesubmodule (libs/)CLIP / note-stream engine, reuses zpwr_patch_core.
C++ / buildC++20, CMake ≥ 3.22macOS default universal binary (x86_64;arm64); Linux x86_64 / aarch64; Windows VST3 + CLAP via WebView2.

~VERIFICATION

Validation is a CTest suite of 5 headless test executables (≈1,365 lines) registered in CMakeLists.txt, using a lightweight in-file assertion harness — no external test framework, failures print to std::cerr and the binary exits non-zero.

CTest targetSourceCoverage
dsp_smoketests/dsp_smoke.cppCore DSP primitives produce finite, in-range output.
synth_modulestests/synth_modules_test.cppThe registered synth node types (largest suite, ~629 lines): compute behaviour, params, voice state.
poly_enginetests/poly_engine_test.cppPolyEngine voice allocation, unison, poly/mono/legato/duo modes.
factory_presetstests/factory_presets_test.cppFactory preset JSON loads and instantiates cleanly.
midifx_stagetests/midifx_stage_test.cppMIDI / MPE input staging into the engine.

There is also an offline benchmark (tools/synth_bench.cpp) and generator tools (tools/gen_*_bank.cpp, gen_reference.cpp, gen_wavetables.cpp, gen_samples.cpp) that build factory banks, wavetables, sample data and the module reference from the same source the plugin runs. No dedicated CI workflow file ships in this repo; tests are run locally via ctest against the headless DSP libraries.