Skip to content

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 (26 items across 18 capabilities)

Capability Limitation Tracked in
timeline_engine.cli_mcp The headless render path emits arrangement audio only; it does not instantiate hosted devices or MIDI instruments, and unknown plugin-delay compensation remains null rather than being invented. link
timeline_engine.dawproject_import DAWproject import is a bounded linear subset: nested groups, warps, seconds-timed lanes, and unsupported timeline constructs fail the import rather than being dropped. link
timeline_engine.dawproject_export DAWproject export is a bounded writer, not Tier-1 support: it emits flat tracks, beats-timed clips, inline notes, referenced audio, and a single tempo and meter. Markers, clip gain and fades, embedded media, mixer state, automation, devices, take lanes, and freeze are declared lost in the capability table and refused unless the caller accepts each concept by name. A neutral channel is emitted so a receiving DAW can register the track; that is not an export of the document's authored mixer state, which is still reported as dropped. link
timeline_engine.smf_interop Standard MIDI File import accepts format 0/1 with a metrical division, note on/off, and the tempo, time-signature, track-name, and end-of-track meta events; SMPTE divisions, format 2, and any other event fail the import unless the caller opts into ignoring non-note events. link
timeline_engine.smf_interop Raw SMF export strictly rejects unsupported clip, event, and time-grid shapes it visits, but it is not a project-wide loss audit. The separate smf-interchange adapter performs the full concept census and requires per-concept consent before omitting unsupported containers or project state, stripping note modifiers, quantizing velocity, or stepping tempo ramps; run_export appends a versioned loss manifest. link
timeline_engine.capture Capture owns fixed-capacity callback buffers only; device I/O, durable media publication, and submission of materialized Timeline commands remain application responsibilities. link
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; setBusArrangements only renegotiates the primary stereo bus today. Descriptor-declared secondary output buses are routed through ProcessBuffers, so a multi-out processor that overrides process(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 to Processor::set_sidechain(). Descriptor-declared secondary output buses are routed through ProcessBuffers, so a multi-out processor that overrides process(ProcessBuffers&) writes each aux output bus; additional input buses beyond the sidechain are not exposed through the simple Processor process surface. CLAP PARAM_MOD note 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.md rows 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 I/O timing + graph-latency snapshot (CoreAudio) experimental audio modules
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_io_timing.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
Fixed-capacity MIDI utility kernels experimental midi Channel/filter/split, balanced note length, monophonic priority, CC mapping, and scale-aware MPE pitch
MIDI note/MPE to voice modulation adapter experimental audio Reuses caller-owned voice slots and VoiceModulationBuffer
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, pulp/midi/utility_kernels.hpp, pulp/audio/midi_voice_modulation_adapter.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
Six-band parametric EQ usable signal eq-curve-demo
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
Explicit frequency/Q resonator bank usable signal Processor-composition primitive; no standalone Forge node
Race-free spectral delay matrix usable signal Processor-composition primitive; frame-boundary table publication
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/explicit_q_resonator_bank.hpp, pulp/signal/spectral_delay_matrix.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; stateful custom nodes require matching create/load lifecycle
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


Creative Timeline Engine

The Timeline stack is an experimental, installed SDK surface. Its model, playback, and tooling layers are independently usable; the application retains device I/O, media publication, plugin instantiation, and UI ownership.

Capability Status Module Docs Examples
Immutable project model, typed transactions, undo/redo experimental timeline Timeline SDK Timeline Phase 1
Canonical snapshots, release downgrade, bounded project peek experimental timeline Timeline SDK
Crash-consistent native FileJournal experimental timeline Timeline SDK
Tempo/meter transport and immutable playback programs experimental playback Timeline SDK Timeline Phase 1
Arrangement audio, note, and automation rendering experimental playback Timeline SDK multitrack arrangement
Audible playhead scrubbing (repeated transport windows) experimental playback Timeline SDK
Takes, comp selection, and sealed track freeze experimental timeline / playback Timeline SDK
Bounded audio/MIDI capture and recording commit experimental playback Timeline SDK
DAWproject linear-subset import experimental timeline Timeline SDK SDK consumer source
DAWproject bounded export (per-concept consent + loss manifest) experimental dawproject / interchange Timeline SDK interchange matrix
Standard MIDI File strict import/export plus consent-gated export experimental smf / interchange Timeline SDK interchange matrix
pulp seq schema/validate/explain/apply/import/export and pulp render experimental Tooling CLI
Ten Timeline MCP operations, including bounded session diff/undo/redo, + Claude Timeline skill experimental Agent tooling Claude plugin
SequenceProcessor plugin-format adapter experimental sequence Timeline SDK

The headless CLI/MCP renderer resolves sealed local assets and renders arrangement audio to Float32 WAV. It does not instantiate hosted devices or MIDI instruments and does not invent plugin-delay compensation. The DAWproject importer accepts a fail-closed linear subset rather than silently downgrading arbitrary sessions.


Agent / Automation

The checked baseline and stable capability IDs for the in-progress development inspector are documented in Development inspector capabilities.

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 (low-level client; no normal runtime endpoint yet) experimental 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 seq (validate, explain, and transactionally edit Timeline projects) experimental cli
pulp render (device-free Timeline arrangement render) experimental cli
pulp ci-local (local CI runner — Mac + VM validation) experimental local-ci

Agent capability manifest

SDK installs include share/pulp/agent-capabilities.json, a versioned, multi-domain inventory of public APIs intended for generators and agents. Each curated row provides a stable key, per-capability contract version and digest, support status, evolution state, typed C++ bindings and exported targets, RT class, lifecycle, state and seed model, data domains, units, and latency/tail/scheduling categories. Signal rows may reference a semantic node key in forge-catalog.json; they never copy that catalog's numeric ranges, defaults, choices, or product policy.

Official release SDKs additionally include share/pulp/agent-capability-handoff.json. This schema-validated marker binds the release's exact source commit and platform to the SHA-256 of the installed bin/pulp-import-design executable and to both the exact installed capability manifest content and its byte hash. Release packaging and archive inspection fail closed if the handoff, importer, manifest, or either installed schema is missing or disagrees. Consumers should verify this marker from their selected SDK rather than combining a manifest or importer from another checkout.

Schema minor 1 adds the required feature determinism-contract-v1. Its per-row determinism object is deliberately separate from seed_model and answers four different questions:

  • repeatability is bit_exact, tolerance_bounded, statistical, or not_promised for identical declared inputs and initial state;
  • block_partition says whether results are invariant, require the same fixed_partition_only, or are not_applicable to the API;
  • platform_scope limits the promise to cross_platform, same_backend, or same_build; and
  • transport_history says whether prior transport is an input or irrelevant.

tolerance_bounded is a determinism class, not a universal numeric tolerance; the algorithm's typed contract and tests remain authoritative for its actual error bounds. Minor-0 documents still parse against the installed schema and a missing determinism field means unspecified, not deterministic. A consumer that requires determinism must require determinism-contract-v1, reject a manifest that does not advertise it, and reject every unknown entry in required_features. When the feature is advertised, every live row must carry all four fields; the installed schema enforces that implication while retaining an explicit minor-0 branch for the pre-determinism document.

The initial six promises are intentionally no broader than their implementation and tests support:

Key Repeatability Partition Platform Transport Basis
audio.instrument-voice-allocator bit_exact not_applicable cross_platform irrelevant Integer, fixed-slot event state machine; no floating-point or external history
midi.mpe-voice-tracker not_promised not_applicable same_build irrelevant Synchronous event state machine, but floating expression normalization has neither a published numerical bound nor a whole-sequence repeatability proof
sequence.host-transport-projector not_promised fixed_partition_only same_build input Floating beat/tick projection depends on callback partition and prior transport state, without a published repeatability bound or repeated-history proof
signal.saturator bit_exact not_applicable same_build irrelevant Per-sample API whose reset/re-render test is bit-identical across every shape and alias policy; same-build scope contains platform math-library variance
timebase.swing bit_exact not_applicable cross_platform irrelevant Pure rational/integer tick transform with tested bounded integer recovery
timebase.tick bit_exact not_applicable cross_platform irrelevant Fixed-width integer value type with saturating arithmetic

The inventory includes timebase.tempo-map, whose four-binding contract exposes the immutable compiled type, its validating control-thread factory, and the two allocation-free integer lookup operations needed by scheduling generators. Its same-build determinism claim is proved with independently compiled installed-SDK consumers for every binding; cursor and fractional helpers are deliberately not part of v1.0.

The first inventory covers representative public signal processing, instrument voice allocation, MPE note ownership, exact tick/swing timebase types, and host transport projection. Swing projection uses an exact rational ratio; inverse recovery is bounded by integer-tick rounding rather than exact. A checked C++ fixture mechanically references every advertised type or function. A separate acceptance test installs the SDK, configures outside consumers, reads the installed manifest and schema, and independently compiles, links, and runs each capability and each binding against only its declared minimal exported target. The maintenance surface ledger and its schema are intentionally not installed.

Coverage is explicitly partial. An absent key means unknown, not unsupported. A live row's status is the positive support claim; an unsupported_capability surface disposition is an explicit negative claim; legacy_unreviewed means only that the public header has not yet received a machine-readable capability review.

docs/status/agent-capability-surface.json is the maintenance ledger for all public headers in the covered audio, MIDI, signal, timebase, and sequence roots. It records SHA-256 file fingerprints and reviewed dispositions. The original unreviewed surface is frozen in tools/agent-capabilities/legacy-unreviewed-baseline.json: that set may shrink as headers are reviewed but may never grow. Consequently, CI fails closed when a public header is added, removed, or changed—including a new symbol inside an existing header—until the owner explicitly registers a capability, classifies the header as support/infrastructure/unsupported, or records a removal tombstone.

For a new generator-facing algorithm, add a curated row and typed binding; the tool deliberately does not guess from a header or class name. Start a new key at contract version 1.0. Increase the minor version for compatible additions and the major version for removed/renamed bindings or incompatible semantic changes. Any installed-manifest change also increases manifest_revision; any ledger change increases its inventory_version. Removed keys and headers leave permanent tombstones, so consumers can distinguish removal from an incomplete inventory and stable keys cannot be silently reused.

Determinism participates in each row's canonical contract_digest. An additive or stronger promise requires at least a capability minor increase. Weakening or removing an existing promise is a breaking same-key change and requires a major increase; alternatively, retain the original key and publish the weaker semantics under a new successor key. The evolution selftests exercise every determinism axis, missing-field downgrade, strengthening, and successor-key paths.

Capability removal has a published-window rule: a live capability must first ship with both status: deprecated and evolution.state: deprecated in the protected base revision. Its introduction and deprecation versions must be ordered within its last contract version. Only a later manifest revision may replace it with a status: removed tombstone. Replacement keys must name live capabilities and may not form self-references or cycles.

tools/agent-capabilities/contract-history.json is the append-only maintenance history. Normal --check validates every recorded transition and compares its prefix plus the current manifest and ledger directly with the protected Git tip (fetching the immutable GitHub base SHA in shallow CI checkouts). Editing generator source, current snapshots, and local history together cannot bypass a published tombstone, key-reuse prohibition, version increase, or deprecation-window rule.

This is a design-time discovery contract. Runtime operations, grants, policy, risk decisions, instances, activation, sessions, revocation, and receipts remain owned by the unified control platform and are rejected if added here.

Maintainers validate or regenerate the checked artifacts with:

python3 tools/scripts/agent_capability_manifest.py --check
python3 tools/scripts/agent_capability_manifest.py --json
python3 tools/scripts/agent_capability_manifest.py --write

Use the agent-capabilities skill for the add/change/remove workflow. The surface ledger currently uses a conservative full-header byte fingerprint. A future pinned-Clang AST inventory may reduce non-semantic churn, but it must retain the same fail-closed mutation coverage.

tools/dsp_vocabulary.py remains available with its exact signal-only JSON and Markdown output while consumers migrate. That renderer reads the complete compatibility projection stored in the manifest; its private header scan is used only to regenerate and freshness-check that payload.


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_thread and state.load call it unconditionally, and process() peeks non_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.