Capabilities Reference¶
A categorized inventory of what Pulp can do today. Each capability lists its current status, the module that provides it, and pointers to relevant documentation and examples.
Status vocabulary: stable | usable | experimental | partial | planned | unsupported
See docs/guides/status-ladder.md for the evidence rule on usable labels.
Known Limitations¶
The following section is auto-generated from the limitations: block of docs/status/support-matrix.yaml. Run python3 tools/docs_generate.py generate to refresh.
Known limitations (20 items across 13 capabilities)¶
| Capability | Limitation | Tracked in |
|---|---|---|
formats.clap |
Bus 0 routes to Processor::process(), bus 1 routes to Processor::set_sidechain(), and descriptor-declared secondary output buses are routed through ProcessBuffers; a multi-out processor that overrides process(ProcessBuffers&) writes each aux output bus, while additional input buses beyond the sidechain are not exposed. | planning/production-readiness/01-format-adapters.md#1.1 |
formats.clap |
CLAP PARAM_MOD note_id/port/channel/key fields are accepted as parameter modulation but are not routed with per-note modulation scope. | planning/production-readiness/01-format-adapters.md#1.1 |
formats.vst3 |
Bus 0, one sidechain input, and descriptor-declared secondary output buses are routed through ProcessBuffers; a multi-out processor that overrides process(ProcessBuffers&) writes each aux output bus, and single-output processors leave aux buses silent. | planning/production-readiness/01-format-adapters.md#1.2 |
formats.vst3 |
Dynamic bus arrangements are limited to descriptor-declared bus counts and mono/stereo layouts; unsupported layouts require host-quirk silence accommodation. | planning/production-readiness/01-format-adapters.md#1.2 |
formats.vst3 |
Controller and per-note expression input is host-mediated: CC, pitch bend, and channel aftertouch arrive when the host honors IMidiMapping hidden parameters, and per-note tuning/pressure/timbre arrive for MPE-enabled plug-ins through INoteExpressionController. There is no separate raw poly-pressure event route. | planning/production-readiness/01-format-adapters.md#1.2 |
formats.au_v2 |
Plugin-side parameter changes do not propagate back to the host (no AUParameterListenerNotify). | planning/production-readiness/01-format-adapters.md#1.3 |
formats.au_v2 |
Outbound MIDI from AU v2 effects is not wired yet; HandleMIDIEvent / HandleSysEx feed the adapter's MidiBuffer, but effects that set produces_midi=true have no render-notify path to emit MIDI back to the host. | planning/production-readiness/01-format-adapters.md#1.3 |
formats.auv3 |
Bus 0 and descriptor-declared input bus 1 are routed through ProcessBuffers; additional input buses and secondary output buses are not exposed through the AUv3 adapter surface yet. | planning/production-readiness/01-format-adapters.md#1.4 |
formats.auv3 |
MIDI arrives as raw bytes; no type dispatch to note/CC/pitchbend/aftertouch. | planning/production-readiness/01-format-adapters.md#1.4 |
formats.auv3 |
iOS validation is stale — no on-device example or AVAudioSession ↔ C++ bridge. | planning/production-readiness/05-auv3-mobile.md |
formats.lv2 |
Atom sysex events are not routed — only 1–3-byte short MIDI messages in the atom input sequence reach Processor::process(). | planning/production-readiness/01-format-adapters.md#1.5 |
audio_io.wasapi |
Full-duplex render/capture is not exposed as one synchronized WASAPI device: a WasapiDevice wraps either one render endpoint or one capture endpoint, so callers that need synchronized input/output must open and synchronize two devices. | planning/production-readiness/02-audio-midi-io.md#2.1 |
audio_io.alsa |
No input capture path. | planning/production-readiness/02-audio-midi-io.md#2.2 |
audio_io.alsa |
Hardcoded sample-rate list; no real enumeration. | planning/production-readiness/02-audio-midi-io.md#2.2 |
audio_io.jack |
Server-backed open/start/stop smoke depends on a reachable JACK server and skips otherwise; JACK hotplug/device-manager policy remains pending. | planning/production-readiness/02-audio-midi-io.md#2.2 |
midi_io.coremidi |
MIDI 2.0 channel-voice input is flattened to MIDI 1.0 where representable; per-note and other unsupported UMP statuses are not delivered through MidiInputCallback. | planning/production-readiness/02-audio-midi-io.md#2.6 |
midi_io.win32_midi |
Default legacy mmeapi path has no Windows MIDI Services / MIDI 2.0 transport and no hotplug; SysEx input is routed via MIM_LONGDATA. The opt-in WinRT MIDI 2.0 backend requires PULP_HAS_WINRT_MIDI and the Windows MIDI Services SDK. | planning/production-readiness/02-audio-midi-io.md#2.4 |
midi_io.alsa_midi |
Hotplug notifications depend on runtime libudev/udevd; when unavailable, the port-change callback is stored but will not fire, so clients must re-enumerate manually. | planning/production-readiness/02-audio-midi-io.md#2.5 |
platform_maturity.accessibility.windows |
Direct UIA client and screen-reader regression tests remain pending; provider tree, WM_GETOBJECT, and value/focus/name event helpers are implemented in source. | planning/production-readiness/04-accessibility.md#4.1 |
platform_maturity.accessibility.linux |
Real AT-SPI registry/Orca signal receipt remains pending; per-widget Accessible/Component/Value objects and event-hook marshalling are loopback-tested on the session bus. | planning/production-readiness/04-accessibility.md#4.2 |
Plugin Formats¶
Pulp wraps a single Processor subclass and exposes it through multiple plugin format adapters.
| Capability | Status | Module | Docs | Examples |
|---|---|---|---|---|
| VST3 effect | usable | format | getting-started | pulp-gain, pulp-effect, pulp-compressor |
| VST3 instrument | usable | format | PulpTone, PulpPluck | |
| Audio Unit v2 effect | usable | format | getting-started | pulp-gain, pulp-effect, pulp-compressor |
| Audio Unit v2 instrument | usable | format | PulpTone, PulpPluck | |
| CLAP effect | usable | format | pulp-gain, pulp-effect, pulp-compressor | |
| CLAP instrument | usable | format | PulpSynth | |
| Standalone app | usable | format | pulp-gain | |
| Headless host | usable | format | testing | |
| AU v3 (macOS + iOS) | usable | format | ||
| LV2 (Linux) | experimental | format | ||
| WAM v2 (Web) | experimental | format | web-plugins | |
| WebCLAP (Web) | experimental | format | ||
| AAX native (optional, macOS/Windows) | experimental | format | aax | pulp-gain |
Key headers: pulp/format/processor.hpp, pulp/format/vst3_adapter.hpp, pulp/format/clap_adapter.hpp, pulp/format/headless.hpp
AAX support is intentionally opt-in. It requires a developer-supplied AAX SDK, is not bundled by Pulp, and is unsupported on Linux and Ubuntu.
Known Limitations (plugin formats)¶
These are the production gaps currently tracked per adapter. The authoritative
list lives at docs/status/support-matrix.yaml under format_limitations:;
this section mirrors it for human readers. A status of usable means the
adapter compiles and loads — not that every host-facing feature is wired.
- VST3 — Dynamic bus arrangements limited;
setBusArrangementsonly renegotiates the primary stereo bus today. Descriptor-declared secondary output buses are routed throughProcessBuffers, so a multi-out processor that overridesprocess(ProcessBuffers&)writes each aux output bus. Tracked: production-readiness workstream 01. - Audio Unit v2 — Outbound parameter changes are not emitted to the host; automation read on AU v2 effects only flows host → plugin. Tracked: workstream 01.
- CLAP — Bus 0 routes to
Processor::process()and bus 1 routes toProcessor::set_sidechain(). Descriptor-declared secondary output buses are routed throughProcessBuffers, so a multi-out processor that overridesprocess(ProcessBuffers&)writes each aux output bus; additional input buses beyond the sidechain are not exposed through the simpleProcessorprocess surface. CLAPPARAM_MODnote identity fields are accepted but not routed with per-note modulation scope. - LV2 — Atom sysex is ignored; the
run()loop only promotes 1–3-byte short MIDI messages out of the input atom sequence. - AAX — Custom editor surface not wired; Pro Tools shows the
auto-generated parameter strip rather than a Pulp ViewBridge editor.
AudioSuite role is declared but not exercised end-to-end. Tracked:
planning/signalgraph-and-bridge-followups-plan.mdrows 7 and 13. - AUv3 — iOS jetsam pressure not modeled; heavy V8 / WebView workloads in the AUv3 process risk termination at ~50 MB. Tracked: workstream 05.
- WAM v2 / WebCLAP — Browser origin sandbox only; no Pulp-side capability
manifest yet, and the Pulp browser-host runtime wiring is still scaffolded.
Tracked:
planning/security/container-and-sandbox-strategy-v4.md.
If you hit a limitation not listed, check
planning/production-readiness/01-format-adapters.md and file an issue.
Platforms¶
| Capability | Status | Module | Notes |
|---|---|---|---|
| macOS (ARM64) | usable | platform | Primary development platform |
| Windows | experimental | platform | WASAPI, Win32 MIDI, NSIS installer, CI |
| Linux | experimental | platform | ALSA, JACK, LV2, CI |
| iOS | experimental | platform | AVAudioSession, AUv3, UIKit, Metal |
| Web / WASM | experimental | platform | WAMv2 (Emscripten) and WebCLAP (wasi-sdk) plugins, both exercised in a headless-Chrome CI lane. Pulp's view tree also renders in the browser via Skia Ganesh on WebGL2. DSP is CPU-only today — GPU compute does not yet compile to wasm. Browser WebGPU does support compute shaders, so GPU DSP is reachable (compute in a Worker, blocks handed to the AudioWorklet over a SharedArrayBuffer ring); it is simply not wired yet. |
Key headers: pulp/platform/detect.hpp, pulp/platform/native_handle.hpp
Audio I/O¶
| Capability | Status | Module | Docs |
|---|---|---|---|
| BufferView (non-owning channel pointer wrapper) | usable | audio | modules |
| Audio device enumeration and streaming (CoreAudio) | usable | audio | |
| Audio file read/write | usable | audio | |
| WASAPI device I/O | experimental | audio | |
| ALSA device I/O | experimental | audio | |
| AVAudioSession (iOS) | experimental | audio | |
| Live Audio Inspector (RT output-boundary probe, dev window + JSON dump) | usable | audio | audio-inspector |
Audio Scope acquisition and measurements (pulp.audio.scope.v1, CLI/MCP live or offline WAV) |
experimental | audio | cli |
Key headers: pulp/audio/buffer.hpp, pulp/audio/device.hpp, pulp/audio/audio_file.hpp, pulp/audio/wav_decoder.hpp, pulp/audio/audio_scope.hpp
MIDI I/O¶
| Capability | Status | Module | Docs |
|---|---|---|---|
| MidiEvent / MidiBuffer | usable | midi | modules |
| MIDI device I/O (CoreMIDI) | usable | midi | |
| MIDI file read/write | usable | midi | |
| Provider-neutral microtuning | usable | midi | Optional MTS-ESP session/SysEx provider and Scala SCL/KBM local-file provider |
| Win32 MIDI | experimental | midi | |
| ALSA Raw MIDI | experimental | midi |
Key headers: pulp/midi/message.hpp, pulp/midi/buffer.hpp, pulp/midi/device.hpp, pulp/midi/midi_file.hpp, pulp/midi/tuning.hpp, pulp/midi/mts_esp_tuning.hpp, pulp/midi/scala_tuning.hpp
DSP / Signal Processing¶
All signal processors live in the signal module. Each is a standalone, stateless-friendly C++ class.
| Capability | Status | Module | Examples |
|---|---|---|---|
| Gain | usable | signal | pulp-gain |
| ADSR envelope | usable | signal | PulpSynth |
| Biquad filter | usable | signal | pulp-effect |
| State-variable filter (SVF) | usable | signal | |
| Ladder filter | usable | signal | PulpSynth |
| Linkwitz-Riley crossover | usable | signal | |
| Oscillator | usable | signal | pulp-tone, PulpSynth |
| Delay line | usable | signal | pulp-effect |
| Chorus | usable | signal | |
| Phaser | usable | signal | |
| Compressor | usable | signal | pulp-compressor |
| Noise gate | usable | signal | |
| Reverb | usable | signal | pulp-effect |
| Waveshaper | usable | signal | |
| Panner | usable | signal | |
| Oversampling | usable | signal | |
| FFT | usable | signal | |
| Windowing functions | usable | signal | |
| SmoothedValue | usable | signal | |
| Modal synthesis bank + strike/pickup weighting | usable | signal | modal specification fixtures |
| Versioned modal-spec JSON loading and validation | usable | signal | modal specification fixtures |
| Circuit-derived bridged-T resonator | usable | signal | |
| Band-limited square-oscillator bank | usable | signal |
Key headers: all under pulp/signal/ -- e.g., pulp/signal/compressor.hpp,
pulp/signal/modal_bank.hpp, pulp/signal/modal_spec.hpp,
pulp/signal/bridged_t_resonator.hpp, pulp/signal/osc/va.hpp
Oscillators live in pulp/signal/osc/: a phase accumulator, the polyBLEP/BLAMP
kernels, and the virtual-analog shapes built from them, with hard sync and
through-zero FM. The older pulp/signal/oscillator.hpp is superseded and kept
for existing callers.
DSP DSLs¶
| Capability | Status | Module | Docs | Examples |
|---|---|---|---|---|
| FAUST offline codegen via external compiler + checked-in generated C++ | experimental | dsl | faust guide, pulp-dsl contract | faust-gain, faust-filter, faust-tremolo |
| Cmajor external-toolchain support lane | experimental | dsl | cmajor guide, pulp-dsl contract | cmajor-gain (source-only) |
| JSFX bounded subset support lane | experimental | dsl | jsfx guide, pulp-dsl contract | jsfx-gain, jsfx-tremolo, jsfx-delay (source-only) |
Key headers: pulp/dsl/dsl_processor.hpp, pulp/dsl/faust_processor.hpp
Native Components¶
Opt-in native-language audio components behind a language-neutral C ABI (Rust first). OFF by default — a default build needs no Rust toolchain. See the native-components reference for scope, the C-ABI contract, and the iOS static-only limits.
| Capability | Status | Module | Docs | Examples |
|---|---|---|---|---|
Rust/C/Zig DSP behind a C++ Processor adapter via a private C-shaped FFI |
experimental | format | native-components | rust-gain (opt-in) |
Native non-RT domain logic behind EditorBridge |
experimental | view | native-components, editor-bridge | |
Source-built custom SignalGraph nodes (toward pulp_node_v1 C ABI) |
experimental | host | node-abi, signal-graph | |
Bake a lowerable SignalGraph into an optimized BakedGraphProcessor (bit-identical to live) |
usable | host | signal-graph | in-process bake(), trusted |
Signed on-disk .pulpbake artifact (write_baked_signed / verify-before-parse load_baked) |
usable | host | signal-graph | Ed25519 trust-set; no unsigned load; v1 stateless custom |
Live plugin-instance swap in a SignalGraph node while audio plays (set_node_live_swap_policy / stage_plugin_replacement / prepare_swap) |
usable | host | signal-graph | opt-in, fail-closed; loads no new code (no trust surface); eager-prepare fallback |
State and Automation¶
| Capability | Status | Module | Docs | Examples |
|---|---|---|---|---|
| ParamValue (lock-free atomic float) | stable | state | modules | all |
| ParamInfo (metadata, range, units) | stable | state | all | |
| ParamRange (normalize / denormalize, linear + skew/symmetric shaped) | stable | state | ||
| StateStore (centralized parameter registry) | stable | state | all | |
| Parameter groups | stable | state | ||
| Binding (reactive UI-parameter link) | stable | state | ||
| Gesture begin/end (host undo grouping) | stable | state | ||
| State serialization / deserialization | stable | state | ||
| CLAP modulation offset | stable | state | ||
| Change listeners | stable | state |
Key headers: pulp/state/parameter.hpp, pulp/state/store.hpp, pulp/state/binding.hpp
View / UI¶
Core¶
| Capability | Status | Module | Docs |
|---|---|---|---|
| View hierarchy (tree, bounds, hit-testing) | usable | view | modules |
| Flex layout (full CSS Flexbox L1) | usable | view | web-compat |
| Grid layout (CSS Grid L1 — templates, fr, gaps) | usable | view | |
| Theme system (color/dimension tokens, inheritance) | usable | view | design-tokens |
| JS scripting (QuickJS default, V8 and JavaScriptCore available) | usable | view | js-bridge |
| Hot reload | partial | view | Scripted UI live reload is runtime-validated in the standalone macOS lane. Plugin targets can load UI_SCRIPT, but live reload is not yet guaranteed across hosts/platforms. |
| Screenshot capture (headless PNG) | partial | view | Native on mac/iOS; Windows/Linux/Android require a host-registered provider via set_screenshot_provider. |
| Component inspector | usable | view | |
| Animation (FrameClock, ValueAnimation, motion tokens) | usable | view | animation |
| Design export (JSON, SVG) | usable | view | |
| App framework (commands, menus, key bindings) | usable | view | |
Interactive AI design tool (pulp design) |
experimental | view | cli |
Design debug harness (pulp design-debug) |
experimental | view | cli |
Widgets¶
| Capability | Status | Module | Notes |
|---|---|---|---|
| Knob, Fader, Toggle, Checkbox, ToggleButton | usable | view | Full interaction + accessibility roles |
| Label (multi-line, text-transform, decoration) | usable | view | |
| TextEditor (selection, clipboard, undo, IME) | usable | view | |
| ComboBox (dropdown, keyboard nav) | usable | view | |
| ListBox (virtualized, scroll, keyboard) | usable | view | |
| ScrollView (smooth scroll, fade bars) | usable | view | |
| Meter (RMS + peak hold), ProgressBar | usable | view | |
| XYPad, WaveformView, SpectrumView | usable | view | |
| ImageView | usable | view | File-backed decode with placeholder fallback |
| TreeView, Tooltip, Panel, Icon | usable | view | |
| SpectrogramView (scrolling STFT) | usable | view | |
| MultiMeter, CorrelationMeter | usable | view | |
| PresetBrowser (search, categories, nav) | usable | view | |
| MidiKeyboard (display + interaction) | usable | view | |
| WaveformEditor (selection, zoom, regions) | usable | view | |
| EqCurveView (draggable band handles) | usable | view | |
| FileBrowser, FileDropZone | usable | view | |
| GraphEditorView (canvas-based node editor) | usable | view | SignalGraph UI |
| ConcertinaPanel, SplitView, Breadcrumb, Toolbar | usable | view | |
| ColorPicker, Lasso, PropertyList, CodeEditor | usable | view | |
| CanvasWidget (25 draw commands) | usable | view | custom-rendering |
| ModulationMatrixWidget (source-to-destination routes) | usable | view | widgets |
| TableListBox (sortable columns) | partial | view | Click-to-sort columns and themed rows are implemented; built-in table scrolling/scrollbar remains planned |
| A/B compare | planned | view | Production-readiness workstream 07 |
Web-Compat Layer¶
| Capability | Status | Module | Docs |
|---|---|---|---|
| document.createElement / appendChild / remove | usable | view | web-compat |
| element.style (81 CSS properties) | usable | view | web-compat |
| CSS calc() / min() / max() / clamp() | usable | view | |
| CSS unit resolution (em, rem, %, vw, vh) | usable | view | |
| CSS colors (L4: hex, rgb, hsl, 148 named) | usable | view | |
| StyleSheet (class rules, pseudo-classes) | usable | view | |
| Selectors (:nth-child, :not, descendant, child) | usable | view | |
| closest / matches / innerHTML | usable | view | |
| matchMedia (responsive breakpoints) | usable | view | |
| Pointer events (W3C Level 2) | usable | view | js-bridge |
| Gesture events (scale, rotation) | partial | view | macOS trackpad gestures shipped; iOS multi-touch gesture analysis is still incomplete |
Platform Maturity¶
| Capability | Status | Platform | Notes |
|---|---|---|---|
| Cursor management (7 styles) | usable | macOS | NSCursor in mouseMoved |
| Tab focus traversal | usable | all | Tab/Shift+Tab cycles focusable views |
| VoiceOver accessibility | usable | macOS | NSAccessibilityElement + AccessRole |
| VoiceOver accessibility | usable | iOS | UIAccessibilityElement with slider increment/decrement |
| TalkBack accessibility | usable | Android | JNI bridge: role, label, value, table metadata, actions |
| UIA accessibility | partial | Windows | Provider tree, WM_GETOBJECT, and value/focus/name event helpers exist; direct UIA client regression still pending |
| AT-SPI accessibility | partial | Linux | Direct D-Bus provider exposes per-widget tree/value paths with loopback tests; real registry/Orca signal receipt still pending |
| IME composition (marked text) | usable | macOS | Full NSTextInputClient |
| Right-click context menu | partial | all | on_context_menu/registerContextMenu fire; view-tree ContextMenu is actionable; native showContextMenu currently renders only on macOS and does not report selection |
| Keyboard shortcuts | usable | all | registerShortcut bridge |
| File dialogs (open, save, folder) | partial | macOS, Windows, Linux | macOS NSOpenPanel/NSSavePanel; opt-in Windows IFileDialog and Linux xdg-desktop-portal backends via FileDialog::install_native_backend() |
| Drag and drop | usable | macOS | File + text drop targets |
| Plugin view hosting | usable | macOS/iOS, Windows, Linux | Native NSView/UIView/HWND/X11 plugin-editor hosts. Windows requires Skia; Linux requires Skia + X11 and degrades to headless capture when no display is available. Android/custom targets still require a host-registered PluginViewHost::Factory. Native child attach/bounds/detach inside plugin editors is built in on macOS/iOS only; non-Apple child embedding remains factory-backed. |
| Native child view embedding (WindowHost) | partial | macOS + factory-backed non-Apple | Built-in standalone support is macOS-only. Built-in iOS WindowHost does not expose the embedding handles. Windows/Linux/Android require a host-registered WindowHost::Factory that implements attach/bounds/detach. |
| SDL window host | partial | all | Cross-platform windowing via SDL3. SDL does not provide first-party native child embedding; non-Apple platforms require a host-registered WindowHost::Factory for native presentation. Recording-canvas-only for now. |
Key headers: pulp/view/view.hpp, pulp/view/widgets.hpp, pulp/view/theme.hpp, pulp/view/script_engine.hpp, pulp/view/widget_bridge.hpp
Rendering / GPU¶
| Capability | Status | Module | Docs |
|---|---|---|---|
| Dawn/Metal GPU surface | experimental | render | modules |
| Skia Graphite rendering | experimental | render | |
| Dawn/Metal iOS surface | experimental | render | |
| Dawn/D3D12 surface (Windows) | experimental | render | Surface creation implemented, not runtime-validated |
| Dawn/Vulkan surface (Linux) | experimental | render | X11 surface creation implemented; Wayland extraction exists but presentation is not wired; not runtime-validated |
| CoreGraphics fallback | usable | render | Default render path on macOS |
| Skia Ganesh on WebGL2 (browser) | experimental | render | The wasm render path. Paints core/view's widget tree into a browser canvas, driven by a requestAnimationFrame render loop; DOM pointer/key events are translated into the View tree. |
Native GPU rendering is Skia Graphite over Dawn. The browser is the one
exception: the published Skia wasm slice is Ganesh on WebGL2 and ships no
Dawn at all, so Graphite/Dawn-on-wasm is not what runs there. The backend is
confined to core/render/src/skia_surface_ganesh.cpp — nothing above the render
boundary knows which one it got. WebGL context loss is handled (the surface
reports unavailable, the loop keeps pumping, and Ganesh is rebuilt on restore),
and a browser without a WebGL2 context is a supported configuration that the host
page must be prepared to fall back from
(pulp::view::web::browser_host_gpu_available()).
Key headers: pulp/render/gpu_surface.hpp, pulp/render/skia_surface.hpp, pulp/view/web/web_event_translate.hpp
GPU Audio¶
Offload heavy DSP to the GPU from a real-time process() block via a
fixed-latency, lock-free bridge. The GPU node implementations are gated on the
GPU render stack (pulp::render); the GpuAudioTransport bridge and the public
node classes still compile in a non-GPU build and report gpu_available() ==
false, falling back to the signal::* CPU path.
| Capability | Status | Module | Docs |
|---|---|---|---|
| GPU audio transport (fixed-latency RT↔GPU bridge) | experimental | gpu_audio | GPU Audio SDK |
GPU convolution (GpuConvolver) |
experimental | gpu_audio | SuperConvolver example |
| GPU spectral toolkit (STFT / freeze / morph) | experimental | gpu_audio | Spectral Lab example |
| GPU WaveNet neural-inference primitive | experimental | render | GPU NAM example |
Key headers: pulp/gpu_audio/gpu_audio_transport.hpp, pulp/gpu_audio/gpu_convolver.hpp, pulp/render/gpu_compute.hpp
Not available on the web. No GPU-compute code compiles to wasm. The Skia wasm
slice is Ganesh on WebGL2, and WebGL2 has no compute shaders, so a plugin with a
GPU engine runs its CPU engine in the browser (SuperConvolver's WAM/WebCLAP
builds run the CPU PartitionedConvolver). A GPU-DSP lane in the browser would
need WebGPU in a dedicated worker; that is not implemented. Note also that GPU is
not automatically faster than CPU for audio — a measured spike found a competent
CPU implementation beats or ties the GPU at all musical settings.
Canvas / 2D Drawing¶
| Capability | Status | Module | Docs |
|---|---|---|---|
| Canvas abstraction (paths, fills, strokes, text) | usable | canvas | modules |
| RecordingCanvas (command capture for testing) | usable | canvas | |
| CoreGraphics backend | usable | canvas | |
| Skia backend | experimental | canvas | |
| SVG rendering | experimental | canvas | |
| Effects (shadow, blur, gradients) | usable | canvas | |
Backend capability query (Canvas::supports(CanvasCapability)) |
usable | canvas |
Key headers: pulp/canvas/canvas.hpp, pulp/canvas/cg_canvas.hpp, pulp/canvas/skia_canvas.hpp, pulp/canvas/svg.hpp, pulp/canvas/view_effect.hpp
Runtime Primitives¶
| Capability | Status | Module | Docs |
|---|---|---|---|
| SeqLock (coherent multi-field reads) | stable | runtime | architecture |
| TripleBuffer (latest-value publication) | stable | runtime | architecture |
| SPSCQueue (single-producer single-consumer FIFO) | stable | runtime | |
| ActivityChannel (realtime-to-UI occurrence feedback) | stable | runtime | runtime guide |
SpscRingIndex (lock-free ring index; the <pulp/runtime/spsc_ring_index.hpp> type behind a ring buffer) |
stable | runtime | |
| ScopeGuard | stable | runtime | |
| Logging | stable | runtime | |
| Assertions | stable | runtime |
Key headers: pulp/runtime/activity_channel.hpp, pulp/runtime/seqlock.hpp, pulp/runtime/triple_buffer.hpp, pulp/runtime/spsc_queue.hpp, pulp/runtime/log.hpp
Events¶
| Capability | Status | Module | Docs |
|---|---|---|---|
| Event loop | usable | events | modules |
| Timers | usable | events |
Key headers: pulp/events/event_loop.hpp, pulp/events/timer.hpp
Platform Services¶
| Capability | Status | Module |
|---|---|---|
| OS detection | usable | platform |
| Clipboard access | partial | platform |
| Native file dialogs | partial | platform |
| Popup menus | usable | platform |
| Native window handle | usable | platform |
Key headers: pulp/platform/detect.hpp, pulp/platform/clipboard.hpp, pulp/platform/file_dialog.hpp, pulp/platform/popup_menu.hpp
OSC (Open Sound Control)¶
| Capability | Status | Module | Docs |
|---|---|---|---|
| OSC 1.0 message encode/decode | experimental | osc | modules |
| UDP sender | experimental | osc | |
| UDP receiver | experimental | osc |
Key header: pulp/osc/osc.hpp
Agent / Automation¶
| Capability | Status | Docs | Notes |
|---|---|---|---|
Repo-level MCP server (pulp-mcp) |
experimental | Project/repo automation server in tools/mcp/pulp_mcp.cpp; not a per-plugin control surface |
|
| Plugin CLI harness pattern | usable | cli | tools/plugin-cli/plugin_cli.hpp; usable for batch/headless workflows, but not auto-generated for every plugin |
| Per-plugin/app MCP control contract | planned | claim-audit-baseline | Not a shipped default capability |
Tooling / CLI¶
The pulp CLI wraps common development workflows.
| Capability | Status | Docs |
|---|---|---|
pulp build (configure + build) |
usable | cli |
pulp test (run test suite) |
usable | cli |
pulp validate (pluginval, clap-validator, auval, optional AAX validator) |
usable | cli |
pulp status (show project info, build state, source counts) |
usable | cli |
pulp clean (remove build directory) |
usable | cli |
pulp ship sign |
usable | cli |
pulp ship package |
usable | cli |
pulp ship check |
usable | cli |
pulp docs (local docs lookup) |
usable | cli |
pulp create (new project from template) |
usable | cli |
pulp run (launch standalone binary) |
usable | cli |
pulp upgrade (self-update) |
usable | cli |
pulp doctor (check system dependencies) |
usable | cli |
pulp inspect (component inspector) |
usable | cli |
pulp audit (dependency license check) |
usable | cli |
pulp add (add dependency) |
usable | cli |
pulp cache (build cache management) |
usable | cli |
pulp design (interactive AI design tool) |
experimental | cli |
pulp design-debug (design debug harness) |
experimental | cli |
pulp import-design (import from external design tools) |
experimental | cli |
pulp import-design --from designmd (Google DESIGN.md import, Apache-2.0; tokens-only in Phase 1) |
partial | imports/designmd |
pulp export-tokens (export design tokens) |
experimental | cli |
pulp ci-local (local CI runner — Mac + VM validation) |
experimental | local-ci |
Shipping / Release¶
| Capability | Status | Module | Docs |
|---|---|---|---|
| Code signing (macOS) | usable | ship | cli |
| Notarization submit/check/staple | usable | ship | |
| DMG creation | usable | ship | |
| PKG installer creation | usable | ship | |
| Combined multi-format PKG | usable | ship | |
| Entitlements generation | usable | ship | |
| Signing identity listing | usable | ship | |
| Appcast feed generation (Sparkle-compatible) | usable | ship | |
| Appcast XML parsing | usable | ship | |
| Ed25519 update signing | usable | ship | Sparkle appcast signatures via sign_file_ed25519() and pulp ship appcast --sign-key |
| Semantic version comparison | usable | ship | |
| Windows code signing | partial | ship | windows — Authenticode via Windows SDK signtool; signing is implemented, but certificate provisioning and real signing validation remain host-owned |
| Linux packaging | usable | ship | linux — tested .deb path with .tar.gz fallback independent of runtime platform maturity, plus standalone AppImage wrapping when appimagetool is installed |
Key headers: pulp/ship/codesign.hpp, pulp/ship/appcast.hpp
Processor Interface¶
The Processor base class defines what plugin developers implement.
| Capability | Status | Module | Docs | Examples |
|---|---|---|---|---|
| Plugin descriptor (name, category, buses, MIDI flags) | usable | format | getting-started | all |
| Multi-bus I/O (sidechain, aux) | usable | format | pulp-compressor | |
| Effect / Instrument / MidiEffect categories | usable | format | ||
| Transport context (tempo, time sig, position) | usable | format | ||
| Latency reporting | usable | format | ||
| Tail time | usable | format | ||
Non-realtime maintenance tick (on_non_realtime_tick() / non_realtime_tick_pending()) |
experimental | format | super-convolver | |
| Plugin registry (multi-plugin bundles) | usable | format |
Non-realtime maintenance tick¶
Processor::on_non_realtime_tick() (default no-op) gives a processor a place to
do control-driven work that process() must never do — decode, resample,
FFT-plan, allocate — when the host provides no thread to do it on. The
motivating case is the browser: a WAM v2 module lives entirely inside an
AudioWorklet and a WebCLAP module has no std::thread.
Processor::non_realtime_tick_pending() (default false) is the realtime-safe
query the adapter peeks from process() to discover that a tick is owed. It
must be RT-safe: read atomics and compare — no locks, no allocation.
Who calls it — the contract is narrower than "any host":
- WAM v2 — the adapter marks the processor dirty on a control write and calls the tick once per render turn, so a burst of control messages (a knob drag delivers many in one turn) collapses into a single pass over the latest values. Do not rely on one tick per parameter write; rely on "eventually, with the latest values."
- CLAP — including native CLAP, not only WebCLAP.
clap_on_main_threadandstate.loadcall it unconditionally, andprocess()peeksnon_realtime_tick_pending()each block to request the main-thread callback (the same mechanism already used for latency/tail changes). A processor that already owns a worker thread should do nothing in the tick, or the worker and the host race. - VST3, AU, and the standalone host do not call it at all. A processor that
depends on it for correctness must also have a worker (or do the work in
prepare()), or it will never reconcile in those formats.
It is never called from inside process(). In a worklet-only host it does run on
the same OS thread as the render callback (just outside it), so a long tick can
still make the next quantum late — keep the work bounded.
Both methods are appended at the end of the Processor vtable to keep vtable
ordering additive-only.