|
DSPark 1.6.1
Header-only audio DSP framework in pure C++20 — zero dependencies
|
Format-agnostic plugin layer: write ONE class, ship every format. More...
#include "../Core/AudioSpec.h"#include "../Core/AudioBuffer.h"#include "../Core/DenormalGuard.h"#include <array>#include <concepts>#include <cstdint>#include <cstdio>#include <cstring>#include <type_traits>#include <vector>

Go to the source code of this file.
Classes | |
| struct | dspark::plugin::Descriptor |
Static identity of a plugin. All fields are plain literals so the whole descriptor can live in a static constexpr of the user class. More... | |
| struct | dspark::plugin::Param |
One automatable parameter. Plain values run [min, max]; hosts see the normalized [0, 1] projection. steps == 0 means continuous, steps == 1 a toggle, steps == N an N+1-position discrete control. More... | |
| struct | dspark::plugin::TransportInfo |
| Host transport snapshot, delivered once per audio block. More... | |
| struct | dspark::plugin::MidiEvent |
| One incoming MIDI-ish event, normalised across formats. More... | |
| struct | dspark::plugin::PresetDef< NumValues > |
| One factory preset: a name plus a PLAIN value for every parameter, in parameter-table order. Build the table with preset()/presets() so the value count is checked at compile time against the parameter count. More... | |
| struct | dspark::plugin::BlockEvent |
| One timestamped in-block event, normalised across formats: a parameter point, a bypass point, or a MIDI event. The backends collect them per block, sort them, and either split processing at quantum boundaries (sample-accurate mode) or apply them all up front. More... | |
| struct | dspark::plugin::EditorSize |
| Editor window size in logical pixels (physical = logical x host scale). More... | |
| struct | dspark::plugin::PluginBase< Derived > |
| Optional convenience base that makes EVERY contract method visible and overridable from one place - IDE-discoverable, without the virtuals. More... | |
Namespaces | |
| namespace | dspark |
| Main namespace for the DSPark framework. | |
| namespace | dspark::plugin |
Concepts | |
| concept | dspark::plugin::HasReset |
| concept | dspark::plugin::HasLatency |
| concept | dspark::plugin::HasTail |
| concept | dspark::plugin::HasGetState |
| concept | dspark::plugin::HasSetState |
| concept | dspark::plugin::HasEditor |
Custom-editor switch. With hasEditor = false (or absent) hosts show their generic parameter UI. With hasEditor = true AND plugin/webview/DSParkWebViewEditor.h included BEFORE the format headers, the WebView editor layer serves editorHtml() inside the host window. | |
| concept | dspark::plugin::HasSidechain |
| Sidechain capability. Implement the two-buffer process - the same shape DSPark's own dynamics take: | |
| concept | dspark::plugin::HasTransport |
Transport capability: void setTransport(const TransportInfo&) noexcept. Called on the audio thread, before processBlock, whenever the host supplied transport data for the block (no call means "same as last
block"). This is how tempo-synced delays, LFOs and gates follow the song. | |
| concept | dspark::plugin::HasMidi |
MIDI capability: void handleMidiEvent(const MidiEvent&) noexcept. Its presence grows a note/event input in every format (VST3 event bus, CLAP note port, AU music-device selectors) - which is also why PluginBase deliberately ships no default for it. Required for Category::Instrument, optional for MIDI-driven effects. Audio thread; allocation-free. | |
| concept | dspark::plugin::HasOfflineMode |
Render-mode capability: void setOfflineRendering(bool) noexcept. The host flips it to true for non-realtime bounces (VST3 kOffline setup, the CLAP render extension, AU OfflineRender) - the moment to switch to more expensive algorithms (higher oversampling, longer lookahead). Called outside the audio thread, before processing (re)starts. Default: assume realtime. | |
| concept | dspark::plugin::HasChannelSupport |
| concept | dspark::plugin::HasFactoryPresets |
Factory-preset capability: a static constexpr auto factoryPresets table built with presets(). The backends publish it natively - a VST3 program list, CLAP preset-load + preset-discovery, AU factory presets - so the host's own preset browser offers them. No PluginBase default on purpose: the table's presence changes what hosts display. | |
| concept | dspark::plugin::HasSampleAccurateOptOut |
Sample-accurate opt-out: static constexpr bool sampleAccurateAutomation = false;. By default the wrappers split each audio block at automation points (snapped to kAutomationQuantum frames) so fast curves land where the host drew them instead of stepping once per block. Opt out when your plugin has a high fixed cost per processBlock CALL and block-rate automation is acceptable. | |
| concept | dspark::plugin::HasEditorHtml |
static const char* editorHtml() - the editor page (HTML/CSS/JS), usually a raw string literal. Required when hasEditor is true. | |
| concept | dspark::plugin::HasEditorSize |
Optional static constexpr EditorSize editorSize { w, h };. | |
| concept | dspark::plugin::HasEditorResizable |
Optional static constexpr bool editorResizable = true; - shorthand for editorResize = EditorResize::Free (default: fixed size). | |
| concept | dspark::plugin::HasEditorResize |
Optional static constexpr EditorResize editorResize = ...; - full resize policy; takes precedence over editorResizable. | |
| concept | dspark::plugin::HasEditorDebug |
Optional static constexpr bool editorDebug = true; - enables the browser DevTools in the editor (WebView2; development builds only). | |
| concept | dspark::plugin::HasEditorDevFile |
Optional static const char* editorDevFile() - absolute path of an HTML file to load INSTEAD of editorHtml() while developing: edit the file, reopen the editor, no recompile. Falls back to editorHtml() when the file is missing, so the same build still works elsewhere. Strip from releases. | |
Enumerations | |
| enum class | dspark::plugin::Category { dspark::plugin::Fx , dspark::plugin::Instrument } |
| Plugin category (reflected into each format's class metadata). More... | |
| enum class | dspark::plugin::ChannelSupport { dspark::plugin::MonoAndStereo , dspark::plugin::StereoOnly , dspark::plugin::MonoOnly } |
Bus widths a plugin offers to the host. The DSPark contract is channel-agnostic by construction (prepare() receives numChannels and processBlock honours io.numChannels()), so the DEFAULT is mono+stereo: the plugin appears on every track type. Declare static constexpr auto channels = dspark::plugin::ChannelSupport::...; only to restrict it (e.g. StereoOnly for M/S wideners). All buses of an instance run the same width - a sidechain follows the main pair. More... | |
| enum class | dspark::plugin::EditorResize { dspark::plugin::Fixed , dspark::plugin::Free , dspark::plugin::KeepAspect } |
How the host may resize the editor window. Declare static constexpr EditorResize editorResize = ...; in the plugin class; without it the window is Fixed (or Free if the simpler editorResizable = true shorthand is present). More... | |
Functions | |
| constexpr Param | dspark::plugin::param (const char *id, const char *name, float minValue, float maxValue, float defValue, const char *unit) noexcept |
| Continuous parameter helper. | |
| constexpr Param | dspark::plugin::toggle (const char *id, const char *name, bool defaultOn) noexcept |
| On/off parameter helper. | |
| template<typename... Ps> | |
| constexpr std::array< Param, sizeof...(Ps)> | dspark::plugin::params (Ps... ps) noexcept |
Builds the parameter table (use inside static constexpr auto). | |
| constexpr double | dspark::plugin::toNormalized (const Param &p, double plain) noexcept |
| Plain [min, max] -> normalized [0, 1] (steps snap on the way back). | |
| constexpr double | dspark::plugin::toPlain (const Param &p, double normalized) noexcept |
| Normalized [0, 1] -> plain [min, max], snapped for stepped params. | |
| int | dspark::plugin::parseToggleText (const char *text) noexcept |
| Case-insensitive "On"/"Off" recognition - the inverse of the toggle display below. Returns 1 for On, 0 for Off, -1 for anything else. Hosts round-trip displayed strings through text-to-value (automation lanes, typed values), so toggles must parse their own output. | |
| void | dspark::plugin::formatValue (const Param &p, double plain, char *out, int outSize) noexcept |
| Formats a plain value for host display ("3.5 dB", "On", "440 Hz"). | |
| constexpr uint32_t | dspark::plugin::hash32 (const char *s) noexcept |
| FNV-1a 32-bit over a C string - the per-parameter host id. | |
| constexpr uint64_t | dspark::plugin::hash64 (const char *s, uint64_t salt) noexcept |
| FNV-1a 64-bit with a salt (two runs build a 128-bit class UID). | |
| template<typename P > | |
| constexpr bool | dspark::plugin::paramIdsUnique () noexcept |
Compile-time guarantee that every parameter id hashes uniquely and collides with neither reserved state id ('PRGM', 'BYPS'). A collision would silently cross-wire automation and state between two parameters, so the backends refuse to build: static_assert(paramIdsUnique<MyPlugin>());. | |
| constexpr std::array< uint8_t, 16 > | dspark::plugin::makeUid (const char *productId, uint64_t salt) noexcept |
| Deterministic 16-byte class UID from the stable productId. Each backend salts differently so VST3/CLAP/AU ids never collide. | |
| template<typename P > | |
| constexpr ChannelSupport | dspark::plugin::channelSupportOf () noexcept |
The declared channel support of P (default: mono+stereo). | |
| template<typename P > | |
| constexpr bool | dspark::plugin::supportsChannelCount (int numChannels) noexcept |
True when P accepts a bus width of numChannels (1 or 2). | |
| template<typename P > | |
| constexpr int | dspark::plugin::defaultChannelCount () noexcept |
| The width every backend starts in before the host negotiates. | |
| template<typename... Vs> | |
| constexpr PresetDef< sizeof...(Vs)> | dspark::plugin::preset (const char *name, Vs... values) noexcept |
Builds one factory preset: preset("Warm", 6.0f, 0.8f, ...) - one PLAIN value per parameter, in table order. | |
| template<typename First , typename... Rest> | |
| constexpr std::array< First, 1+sizeof...(Rest)> | dspark::plugin::presets (First first, Rest... rest) noexcept |
Builds the factory preset table (all presets must cover the same parameter count - enforced here; matched against the parameter table by the backends). Declare as static constexpr auto factoryPresets = .... | |
| template<typename P > | |
| constexpr int | dspark::plugin::factoryPresetCountOf () noexcept |
Number of factory presets of P (0 when none declared). | |
| template<typename P > | |
| constexpr double | dspark::plugin::presetNormalized (int presetIndex, size_t paramIndex) noexcept |
Normalized value of parameter paramIndex in factory preset presetIndex (bounds already validated by the caller). | |
| template<typename P > | |
| constexpr bool | dspark::plugin::sampleAccurateOf () noexcept |
| void | dspark::plugin::sortBlockEvents (BlockEvent *events, int count) noexcept |
| Stable insertion sort by offset (tiny N, allocation-free - audio-thread safe; equal offsets keep arrival order). | |
| template<typename P > | |
| constexpr EditorSize | dspark::plugin::editorSizeOf () noexcept |
| The declared (logical) editor size, or the 480x320 default. | |
| template<typename P > | |
| constexpr EditorResize | dspark::plugin::editorResizeOf () noexcept |
The effective resize policy for plugin class P. | |
| template<typename P > | |
| constexpr void | dspark::plugin::constrainEditorSize (double &width, double &height, double scale) noexcept |
Applies the resize policy of P to a size the host proposes: Fixed pins it, Free clamps each axis to the 0.5x..3x window, KeepAspect fits the declared ratio INSIDE the proposal - never exceeding it on either axis. That last property is essential: hosts size the plugin area inside a window the user controls, so answering "larger" on any axis pushes the plugin outside its own window (clipped bottom in REAPER). | |
| template<typename P > | |
| std::vector< uint8_t > | dspark::plugin::buildState (const P &user, const double *normalized, size_t numParams, int programIndex=-1, int bypassState=-1) |
| template<typename P > | |
| bool | dspark::plugin::applyState (P &user, const uint8_t *data, size_t size, double *normalized, int *programIndex=nullptr, int *bypassState=nullptr) |
Parses a state blob; fills normalized (defaults pre-loaded by the caller), reports a persisted program index / bypass through programIndex and bypassState (left untouched when absent) and forwards the user section. Returns false on a foreign blob. | |
Variables | |
| constexpr int | dspark::plugin::kAutomationQuantum = 32 |
| Sub-block grain: automation/event splits snap to this many frames (bounds the per-call overhead; ~0.7 ms at 48 kHz is inaudible). | |
| constexpr int | dspark::plugin::kMaxBlockEvents = 256 |
| Hard cap of timestamped events handled per block; the (very unlikely) excess applies at the position of the last captured event. | |
| constexpr double | dspark::plugin::kEditorMinSizeFactor = 0.5 |
| Resize bounds: hosts may shrink to half and grow to 3x the declared size. | |
| constexpr double | dspark::plugin::kEditorMaxSizeFactor = 3.0 |
| constexpr uint32_t | dspark::plugin::kStateMagic = 0x4453504Bu |
| constexpr uint32_t | dspark::plugin::kStateVersion = 1u |
| constexpr uint32_t | dspark::plugin::kProgramStateId = 0x5052474Du |
| Reserved entry id persisting the active factory-preset index ('PRGM'); never a user parameter. Older builds skip it (unknown id). | |
| constexpr uint32_t | dspark::plugin::kBypassStateId = 0x42595053u |
| Reserved entry id persisting the host bypass ('BYPS' - the same value every backend uses as the bypass ParamID). Hosts treat bypass as one more parameter, so it must survive a state round-trip like any other. Older builds skip it (unknown id). | |
Format-agnostic plugin layer: write ONE class, ship every format.
The developer describes the plugin declaratively (a Descriptor and a constexpr parameter table) and implements the familiar DSPark contract (prepare / processBlock plus a setParameter switch). The format backends (plugin/vst3/..., later CLAP and AU) translate that single class into each plugin ABI - no external SDK to install, no base class to inherit from.
Capability detection is by C++20 concepts: implement only what applies. The full optional menu (each one detected structurally, see the concepts below and docs/plugins.md):
State: the wrapper always serialises the parameter table itself (stable text ids hashed to 32 bits, version-tolerant); a user getState/setState blob - e.g. DSPark's StateBlob - rides along as an extra section.
Definition in file DSParkPlugin.h.