ZDSP-CORE // SHARED AUDIO DSP ENGINE

// zdsp::core · header-only JUCE DSP · one engine for the whole audio stack

Engineering Report Source

zdsp-core is the shared real-time audio DSP engine behind the MenkeTechnologies audio stack — the audio-engine analog of zgui-core (the shared GUI toolkit). One implementation of the core DSP units and the playback pipeline, vendored by every audio app instead of each re-deriving its own: Audio-Haxor, zpwr-patch-core, and the plugin apps zpwr-daw / zpwr-synth / zpwr-fx / zpwr-midi-fx. Header-only, JUCE-based; every unit was verbatim-extracted from the Audio-Haxor audio-engine. Paid product — proprietary (UNLICENSED), part of the paid audio stack.

Header-Only

Pure C++ headers under include/zdsp/. Consumers add_subdirectory(zdsp-core) and link zdsp::core — no separate build artifact, no library to ship.

JUCE, Not Bundled

Includes JUCE headers directly, so each unit pulls its own modules — juce_audio_basics/juce_core for most, plus juce_dsp (channel strip, spectrogram), juce_audio_devices (peak meter), and juce_audio_formats (orchestrator). JUCE must already be present; the library does not fetch it, so every app links its own.

Extracted, Not Invented

Each unit is lifted from the Audio-Haxor audio-engine where it first shipped, then generalized so the patch-core and the plugin apps share one copy instead of forking divergent ones.

Real-Time Safe

The units are designed for the audio thread — fixed-work per block, no allocation in process(). prepare() sets up buffers once; reset() clears state between runs.

What's in include/zdsp/

Seven units, each a header under include/zdsp/: OlaTimeStretch (+ SpeedMode) — Hann-windowed overlap-add time-stretch, change speed without altering pitch; channel_strip — a lock-free 3-band IIR EQ (low-shelf 200 Hz / mid-peak 1 kHz / high-shelf 8 kHz) + gain/pan held in RT-safe DspAtomics, applied one stereo frame at a time by applyDspFrame; ToneAudioSource — a sine test-tone juce::AudioSource; InputPeakCallback — an input peak meter with exponential release; computeSpectrogramGrid — offline Hann-windowed STFT → heightPx × widthPx dBFS grid (the DSP half of the frontend ZGui.viz spectrogram/waterfall); LockFreeStreamSource — RT-safe ring-buffered file streaming with a glitch-free mid-playback stream→RAM reader hot-swap; and DspStereoFileSource — the transport-ready playback orchestrator that composes all of the above (tape-speed resample or OLA stretch, optional streaming, per-sample channel strip, an app-implemented InsertChain plugin rack, mono fold, seek-fade, reverse path, and meter/scope taps). See the engineering report for the full unit table.

How apps consume it

An app adds zdsp-core as a submodule (e.g. under libs/), calls add_subdirectory(libs/zdsp-core), and links the interface target: target_link_libraries(my_audio_target PRIVATE zdsp::core). Then #include <zdsp/ola_time_stretch.h> (or any of the seven unit headers) and drive it on the audio thread. See the engineering report for the full unit map and extraction provenance, and the MenkeTechnologiesMeta umbrella for the wider stack.

How it composes

DspStereoFileSource is the single orchestrator; every other unit is a piece it wires together. As a juce::PositionableAudioSource driven from the app's transport, its forward path per block runs: the AudioFormatReaderSource (optionally wrapped in LockFreeStreamSource to move disk I/O off the audio thread) → tape-speed ResamplingAudioSource or OlaTimeStretch.process depending on SpeedMode → mono-source upmix → seek-fade ramp → per-sample applyDspFrame channel strip (3-band EQ → gain → pan) → the app's InsertChain plugin rack when active → mono fold (deferred past the inserts so stereo plugins can't undo it) → peak/spectrum/scope taps. Reverse playback is a separate branch reading a pre-decoded RAM buffer backwards with no inserts.

The one app coupling that was abstracted out is InsertChain — a four-method pure-virtual interface (prepare / release / isActive / process) that zdsp-core declares and the consuming app implements to host VST/AU plugins. Plugin scanning, hosting, and state stay in the app; the orchestrator only knows the four methods and calls process after the channel strip when isActive().

Tests

A headless JUCE console app (tests/zdsp_tests.cpp) runs known-answer unit tests over the pure-math surface of every header — dB↔linear conversion, power-of-two reduction, the spectrogram STFT frequency response (DC → bottom row, Nyquist → top row), the channel-strip pan law + biquad EQ DC gain, the OLA Hann window + constant-overlap-add property, the tone oscillator period/amplitude, and the peak meter's exponential release. It returns non-zero on failure: cmake -S tests -B tests/build && cmake --build tests/build && ctest --test-dir tests/build --output-on-failure (add -DZDSP_JUCE_DIR=/path/to/JUCE to point at a JUCE checkout).