Docs file normalization.

This commit is contained in:
Johannes Findeisen 2026-11-06 15:20:20 +01:00
commit 619c2d0808
46 changed files with 3587 additions and 2579 deletions

View file

@ -1,18 +1,24 @@
# Decision 0001: Modular engine first
# Decision 0001: Modular Engine First
**Status:** Accepted
---
## Context
The project aims to preserve energyXT's combination of a simple workflow and a
flexible modular routing model.
---
## Decision
Use one unified processing graph for sources, instruments, effects, mixers,
and outputs. The sequencer is a timeline/event source integrated with that
graph. The default GUI is simple and sequencer-oriented, while a graph editor
exposes routing when needed.
Use one unified processing graph for sources, instruments, effects, mixers, and
outputs. The sequencer is a timeline/event source integrated with that graph.
The default GUI is simple and sequencer-oriented, while a graph editor exposes
routing when needed.
---
## Consequences

View file

@ -1,11 +1,15 @@
# Decision 0002: C core with FLTK/C++ GUI
# Decision 0002: C Core with FLTK/C++ GUI
**Status:** Accepted
---
## Context
The project prioritizes a readable, small C implementation while using FLTK
for a lightweight desktop GUI.
The project prioritizes a readable, small C implementation while using FLTK for
a lightweight desktop GUI.
---
## Decision
@ -13,6 +17,8 @@ Keep the engine, DSP, project model, graph, and public core API in C (C99
baseline). Use C++ only for FLTK windows and widgets. Communicate through
documented C headers with `extern "C"` guards.
---
## Consequences
- No C++ classes, STL containers, exceptions, or FLTK types cross the C API.

View file

@ -1,19 +1,25 @@
# Decision 0003: Real-time thread rules
# Decision 0003: Real-Time Thread Rules
**Status:** Accepted
---
## Context
Audio glitches and unsafe graph changes can undermine the whole application.
---
## Decision
The audio callback performs bounded processing using preallocated resources.
It performs no allocation/free, blocking locks, file I/O, logging, GUI calls,
The audio callback performs bounded processing using preallocated resources. It
performs no allocation/free, blocking locks, file I/O, logging, GUI calls,
plugin loading, or waits. Graphs are prepared outside the callback and active
graph resources are retired only after safe reclamation.
---
## Consequences
Thread ownership and publication mechanisms must be documented and tested.
Any exception to these rules requires explicit evidence and review.
Thread ownership and publication mechanisms must be documented and tested. Any
exception to these rules requires explicit evidence and review.

View file

@ -1,7 +1,9 @@
# Decision 0004: JACK backend and bounded plan handoff
# Decision 0004: JACK Backend and Bounded Plan Handoff
**Status:** Accepted
---
## Backend
Use the JACK C client API. The inspected system provides PipeWire's maintained
@ -22,10 +24,12 @@ framework or second driver implementation.
The optional dependency provides actual audio callbacks and port routing.
PipeWire's installed JACK library is MIT licensed (`pipewire-jack/COPYING`).
Traditional JACK client libraries use LGPL licensing; review the actual linked
implementation, transitive libraries and notices when distributing. Nothing
is bundled or installed here. ALSA is still a future MIDI candidate.
implementation, transitive libraries and notices when distributing. Nothing is
bundled or installed here. ALSA is still a future MIDI candidate.
## C99 and atomic operations
---
## C99 and Atomic Operations
Retain C99 and use GCC/Clang `__atomic` builtins. This is a documented compiler
extension, avoiding a language-standard change and keeping the existing core
@ -34,12 +38,15 @@ unsigned-int atomics to be always lock-free, or returns unsupported before
callbacks are installed. Naturally aligned fields reside in calloc-allocated
objects. There is no mutex or libatomic fallback in the callback path.
## Ownership and publication
---
## Ownership and Publication
One serialized control producer and one audio consumer share two pointer slots:
- `pending`: producer publishes a fully compiled plan with release; consumer
exchanges it for NULL with acquire/release at the next eligible block boundary.
exchanges it for NULL with acquire/release at the next eligible block
boundary.
- `retired`: consumer releases its previous active plan into this slot only
after its final access. Producer exchanges with NULL using acquire/release,
then destroys that acknowledged plan on the control thread.
@ -50,40 +57,47 @@ callers never receive plan pointers through the realtime/backend API.
A full pending slot returns capacity without replacing anything. A full retired
slot defers adoption while the current plan continues processing. The consumer
performs at most one adoption per block and never loops waiting for the producer.
Producer collection can overlap processing because the retired plan is no
longer reachable from the consumer. Collection clears the slot before freeing;
consumer may then retire another, distinct plan. Publication never immediately
frees the previously active plan.
performs at most one adoption per block and never loops waiting for the
producer. Producer collection can overlap processing because the retired plan is
no longer reachable from the consumer. Collection clears the slot before
freeing; consumer may then retire another, distinct plan. Publication never
immediately frees the previously active plan.
Submitting collects any already retired plan, so a producer repeatedly submitting
also reclaims memory. If it becomes idle, one pending, one active, and one retired
plan bound retained resources (at most three plans, each with up to 64 MiB of
sample buffers). Periodic collect is recommended. Shutdown closes the JACK client
and synchronizes its callbacks before destroying any active/pending/retired plan.
Standalone realtime-engine callers must stop and join their consumer themselves.
Submitting collects any already retired plan, so a producer repeatedly
submitting also reclaims memory. If it becomes idle, one pending, one active,
and one retired plan bound retained resources (at most three plans, each with up
to 64 MiB of sample buffers). Periodic collect is recommended. Shutdown closes
the JACK client and synchronizes its callbacks before destroying any
active/pending/retired plan. Standalone realtime-engine callers must stop and
join their consumer themselves.
## Status, errors and recovery
---
Unsigned-int status fields use lock-free atomics. Values are individual snapshots,
not one transactional state. Counters wrap naturally; no string formatting,
logging, allocation, GUI operation or blocking synchronization occurs in project
callbacks. JACK's shutdown notification only stores a flag and leaves cleanup to
the control thread. Other notifications store the current format and xrun count.
## Status, Errors and Recovery
Unsigned-int status fields use lock-free atomics. Values are individual
snapshots, not one transactional state. Counters wrap naturally; no string
formatting, logging, allocation, GUI operation or blocking synchronization
occurs in project callbacks. JACK's shutdown notification only stores a flag and
leaves cleanup to the control thread. Other notifications store the current
format and xrun count.
Buffer changes within the configured maximum require no allocation. Larger
blocks are fully silenced and reported as capacity errors; smaller blocks can
resume processing. Sample-rate changes latch a flag and silence processing
rather than replaying an oscillator compiled for a different rate. Close/reopen
and resubmit after a rate change or server loss. There is no automatic reconnect.
JACK-deactivate pauses the retained plan; restart continues its oscillator phase.
Plan replacement starts the newly compiled oscillator at phase zero, without
crossfade. Smooth parameter ramps and click-free graph transitions are future work.
and resubmit after a rate change or server loss. There is no automatic
reconnect. JACK-deactivate pauses the retained plan; restart continues its
oscillator phase. Plan replacement starts the newly compiled oscillator at phase
zero, without crossfade. Smooth parameter ramps and click-free graph transitions
are future work.
No stereo, recording, MIDI, native PipeWire API, GUI engine controls or hardware
configuration dialogs are included.
## Adoption acknowledgement and stopped editing
---
## Adoption Acknowledgement and Stopped Editing
The pending pointer has an associated atomic revision. Producer stores the
revision before release-publishing the pointer. Consumer acquire-loads a
@ -97,10 +111,10 @@ snapshots, not plan pointers or a multi-field transactional snapshot.
While JACK is stopped, a backend submission builds a separate realtime engine,
submits/compiles the graph there, and replaces the retained engine only on
success. Process callbacks cannot run on an inactive client or after successful
deactivation, and notification callbacks do not access the engine pointer.
The control thread can therefore reclaim all old state safely. Counters and
oscillator phases restart for that new engine epoch. The standalone realtime
API keeps its one-pending-slot contract regardless of external stop state.
deactivation, and notification callbacks do not access the engine pointer. The
control thread can therefore reclaim all old state safely. Counters and
oscillator phases restart for that new engine epoch. The standalone realtime API
keeps its one-pending-slot contract regardless of external stop state.
The callback still adopts at most one plan and never allocates/frees. Revision
ordering is tested alongside the plan's rendered gain under 2,000 concurrent

View file

@ -1,4 +1,4 @@
# 0005: Preloaded WAV clips and control-thread recording consumer
# 0005: Preloaded WAV Clips and Control-Thread Recording Consumer
Status: accepted.
@ -11,16 +11,19 @@ codec library needs its own dependency and licensing review.
Compile musical clip ranges and decoded, shared-per-path mono buffers while
stopped. The callback renders the timeline into a preallocated buffer consumed
by an explicit graph source node, preserving graph routing rather than mixing
outside the graph. The old snapshot remains until candidate preparation succeeds.
All track clips currently share one source; per-track routing/mixing is deferred.
outside the graph. The old snapshot remains until candidate preparation
succeeds. All track clips currently share one source; per-track routing/mixing
is deferred.
Use a bounded SPSC ring for callback input capture, with release/acquire index
publication and a GUI/control-thread consumer. This adds no worker-thread
dependency; a two-second ring tolerates normal 100 ms polling but exposes
overflow if the GUI or disk stalls. Sticky errors prevent silent time compression.
Finalization requires callback quiescence. Future long-session/background I/O
work can reuse the ring contract with a dedicated consumer.
overflow if the GUI or disk stalls. Sticky errors prevent silent time
compression. Finalization requires callback quiescence. Future
long-session/background I/O work can reuse the ring contract with a dedicated
consumer.
Store external absolute paths in version 2 metadata and retain version 1 loads.
Missing/unreadable assets are reported silence, with explicit relinking; portable
bundles and content-addressed assets are deferred. See AUDIO_FILES.md for limits.
Missing/unreadable assets are reported silence, with explicit relinking;
portable bundles and content-addressed assets are deferred. See AUDIO_FILES.md
for limits.

View file

@ -1,16 +1,16 @@
# 0006 — Bounded MIDI notes and internal instrument
# 0006 — Bounded MIDI Notes and Internal Instrument
Status: accepted.
Use the existing JACK backend for timestamped MIDI input, avoiding another
backend/dependency. Store source-relative notes in the C project and v3 format;
read v1/v2 without MIDI. Prepare sample spans on the control thread and render
a small sine instrument into its own graph source buffer. Use a piano roll to
meet draw/play/save/reopen acceptance before adding MIDI capture or SMF import.
read v1/v2 without MIDI. Prepare sample spans on the control thread and render a
small sine instrument into its own graph source buffer. Use a piano roll to meet
draw/play/save/reopen acceptance before adding MIDI capture or SMF import.
Keep explicit bounds: 4096 project notes, 32 overlapping scheduled notes, 32 live
voices and 256 events/block. Reject scheduled overcapacity at preparation; steal
oldest live voices. Panic handles event loss, disconnects and callback failures;
Stop/seek resets live state. Immediate note-off and a fixed sine voice keep this
prototype small, at the cost of possible clicks and no expressive controllers.
See ../MIDI.md for ownership, detailed limits and validation.
Keep explicit bounds: 4096 project notes, 32 overlapping scheduled notes, 32
live voices and 256 events/block. Reject scheduled overcapacity at preparation;
steal oldest live voices. Panic handles event loss, disconnects and callback
failures; Stop/seek resets live state. Immediate note-off and a fixed sine voice
keep this prototype small, at the cost of possible clicks and no expressive
controllers. See ../MIDI.md for ownership, detailed limits and validation.

View file

@ -1,4 +1,4 @@
# 0007 — Track mixing, paired stereo buses and snapshot history
# 0007 — Track Mixing, Paired Stereo Buses and Snapshot History
Status: accepted.

View file

@ -1,19 +1,19 @@
# 0008 — Native processors and source release preparation
# 0008 — Native Processors and Source Release Preparation
Status: accepted.
Add a versioned copied C descriptor with control-thread prepare/destroy and
single-owner realtime reset/process. Prove effects/sources in the existing graph,
with two independent mono instances for stereo, and event instruments rendered
through the public instrument-source buffer boundary. Keep trusted code statically
linked. Native project serialization and graph event ports need more contracts;
reject unsupported saves without replacing files. Defer LV2/CLAP/VST3 after a
concrete evaluation rather than linking SDKs speculatively.
single-owner realtime reset/process. Prove effects/sources in the existing
graph, with two independent mono instances for stereo, and event instruments
rendered through the public instrument-source buffer boundary. Keep trusted code
statically linked. Native project serialization and graph event ports need more
contracts; reject unsupported saves without replacing files. Defer LV2/CLAP/VST3
after a concrete evaluation rather than linking SDKs speculatively.
Keep format v4 and test migration plus process-crash/write-failure behavior.
Provide deterministic source exports and optimized release builds, staged
core/GUI installation, exact uninstall and installed C/C++ consumer tests. Use
Python standard-library tools only for packaging/testing; no application runtime
dependency or system changes. Document dependency licenses, Linux routing and
measured offline performance separately from unverified hardware/endurance.
See NATIVE_API.md, PLUGIN_HOSTING.md, RELEASING.md and RECOVERY.md.
measured offline performance separately from unverified hardware/endurance. See
NATIVE_API.md, PLUGIN_HOSTING.md, RELEASING.md and RECOVERY.md.

View file

@ -1,52 +1,55 @@
# 0009 — Parameter processors and bounded LADSPA hosting
# 0009 — Parameter Processors and Bounded LADSPA Hosting
Status: accepted (2026-10-27).
Native ABI 1 remains compatible. ABI 2 adds explicitly parameter-only configuration,
metadata, validation and sample-offset parameter/MIDI ordering. It is a public C
facility; opaque state and automatic native descriptor registration in project
files remain unsupported. Native instrument and gain examples exercise the
interface before adding a dynamically loaded module lifetime.
Native ABI 1 remains compatible. ABI 2 adds explicitly parameter-only
configuration, metadata, validation and sample-offset parameter/MIDI ordering.
It is a public C facility; opaque state and automatic native descriptor
registration in project files remain unsupported. Native instrument and gain
examples exercise the interface before adding a dynamically loaded module
lifetime.
Select LADSPA 1.1 for the first external host. Reconsidering earlier candidates,
LV2 needs RDF/features/URIDs/workers, CLAP needs host scheduling/extension
negotiation, and VST3 needs a separate C++ SDK adapter and release-specific license
review. LADSPA adds a mature Linux C ABI, installed real plugins, straightforward
port negotiation and no RDF/SDK runtime library. Its specification is old and
stable, not an actively expanding instrument/event ecosystem. This trades MIDI,
editors, opaque state and modern automation for a small testable first host.
No general multi-format abstraction is introduced.
negotiation, and VST3 needs a separate C++ SDK adapter and release-specific
license review. LADSPA adds a mature Linux C ABI, installed real plugins,
straightforward port negotiation and no RDF/SDK runtime library. Its
specification is old and stable, not an actively expanding instrument/event
ecosystem. This trades MIDI, editors, opaque state and modern automation for a
small testable first host. No general multi-format abstraction is introduced.
Use the installed LGPL-2.1-or-later header, system dynamic loader (`-ldl`), and
user-selected, trusted module files. Neither the header nor plugin binaries are
vendored. Header absence or `XTC_DISABLE_LADSPA` yields unsupported hosting stubs;
metadata can still be loaded/saved. Plugin licenses are independent of the host
and require review before distribution. See LICENSES.md.
vendored. Header absence or `XTC_DISABLE_LADSPA` yields unsupported hosting
stubs; metadata can still be loaded/saved. Plugin licenses are independent of
the host and require review before distribution. See LICENSES.md.
Support zero/one mono audio input and exactly one mono output, at most 64 ports
and 16 input controls. Graph stereo consists of independent instances, not a
stereo LADSPA layout. Reject incompatible layouts and identities explicitly.
Control outputs are connected to private storage, not exposed as graph events.
LADSPA has no MIDI or opaque state API: save control values only. Parameter
changes rebuild plans on the control thread; sample automation is provided through
parameter processors. Rate-scaled bounds are recalculated at compile. Instance history/tails
reset on recompile, not every transport seek (LADSPA has no realtime reset rule).
Latency compensation is unsupported: the supported set is zero-latency amp,
inverter and sine source, not arbitrary lookahead/delay/routing plugins.
changes rebuild plans on the control thread; sample automation is provided
through parameter processors. Rate-scaled bounds are recalculated at compile.
Instance history/tails reset on recompile, not every transport seek (LADSPA has
no realtime reset rule). Latency compensation is unsupported: the supported set
is zero-latency amp, inverter and sine source, not arbitrary
lookahead/delay/routing plugins.
Discovery, dlopen, descriptor calls, instantiate, connect, activate, deactivate,
cleanup and dlclose are control-thread operations. Only preallocated copies and
run execute in audio; the usual pending/retired plan handoff owns module handles.
Partial initialization unwinds on the control thread. The host cannot enforce
third-party callback behavior, bound CPU time, catch native crashes/hangs, or
undo memory corruption. There is no sandbox or crash recovery inside the audio
process. A fault may kill xtc; previously saved documents/recording recovery files
remain subject to their ordinary durability guarantees. Test only trusted modules.
run execute in audio; the usual pending/retired plan handoff owns module
handles. Partial initialization unwinds on the control thread. The host cannot
enforce third-party callback behavior, bound CPU time, catch native
crashes/hangs, or undo memory corruption. There is no sandbox or crash recovery
inside the audio process. A fault may kill xtc; previously saved
documents/recording recovery files remain subject to their ordinary durability
guarantees. Test only trusted modules.
Project v5 stores plain identity/layout/control metadata. Parsing never opens
modules. Compile failure preserves the source document and active plan. GUI Open
rejects missing/incompatible modules, retains its current document, and reports
saved module identity/path. Restore the compatible module at that path and retry;
a metadata-only C client can preserve/save the document without the module.
No silent replacement or deletion occurs. Same-ID/label/ports do not authenticate
binaries or guarantee unchanged sound across plugin upgrades.
saved module identity/path. Restore the compatible module at that path and
retry; a metadata-only C client can preserve/save the document without the
module. No silent replacement or deletion occurs. Same-ID/label/ports do not
authenticate binaries or guarantee unchanged sound across plugin upgrades.

View file

@ -1,13 +1,14 @@
# 0010 — Bounded CLAP, VST2 and VST3 effect loading
# 0010 — Bounded CLAP, VST2 and VST3 Effect Loading
Accepted. The user's request to support these three formats
supersedes the earlier recommendation to choose only one external format.
Accepted. The user's request to support these three formats supersedes the
earlier recommendation to choose only one external format.
Keep the engine/model/host adapters in C99. CLAP uses pinned MIT headers and VST3
uses the upstream BSD-3-Clause generated C ABI. No SDK runtime library is needed;
Linux dynamic loading and POSIX thread identity are existing platform facilities.
VST2 uses original minimal compatibility declarations, not the discontinued SDK.
See third_party/README.md and LICENSES.md for exact revisions and notices.
Keep the engine/model/host adapters in C99. CLAP uses pinned MIT headers and
VST3 uses the upstream BSD-3-Clause generated C ABI. No SDK runtime library is
needed; Linux dynamic loading and POSIX thread identity are existing platform
facilities. VST2 uses original minimal compatibility declarations, not the
discontinued SDK. See third_party/README.md and LICENSES.md for exact revisions
and notices.
Reuse the existing plugin graph node, paired mono instances and immutable plan
ownership. The common facade routes LADSPA to its existing implementation and
@ -18,25 +19,27 @@ configuration is used; no parameter/controller/editor or opaque-state access is
exposed. These are usable effect loaders, not complete implementations of every
extension in the formats. Unsupported layouts fail explicitly.
Project v11 adds format plus CLAP stable ID or VST3 class ID to each saved plugin.
Older plugins migrate to LADSPA with an empty extra ID. VST2 retains its integer
unique ID. Reopening checks format/index/identity/name/layout; metadata operations
never open modules. This does not authenticate a binary or freeze defaults across
plugin releases. No modified opaque state exists in this initial API to persist.
Project v11 adds format plus CLAP stable ID or VST3 class ID to each saved
plugin. Older plugins migrate to LADSPA with an empty extra ID. VST2 retains its
integer unique ID. Reopening checks format/index/identity/name/layout; metadata
operations never open modules. This does not authenticate a binary or freeze
defaults across plugin releases. No modified opaque state exists in this initial
API to persist.
Discovery and instance creation execute trusted module code on one serialized
main/control thread. A module cache balances CLAP entry init/deinit and Linux
VST3 ModuleEntry/ModuleExit across overlapping graph instances and retired plans.
CLAP start_processing runs on the processing owner; stop_processing runs after
quiescence with the symbolic audio-thread context. CLAP callback requests use
lock-free flags and are serviced by xtc_plugin_idle on main/control (the GUI timer
calls it). Restart/I/O-change requests latch an error for re-preparation; the audio
callback does not reconfigure or wait for GUI work. CLAP process wakeups need no
queue because playback processes every block, including silence.
VST3 ModuleEntry/ModuleExit across overlapping graph instances and retired
plans. CLAP start_processing runs on the processing owner; stop_processing runs
after quiescence with the symbolic audio-thread context. CLAP callback requests
use lock-free flags and are serviced by xtc_plugin_idle on main/control (the GUI
timer calls it). Restart/I/O-change requests latch an error for re-preparation;
the audio callback does not reconfigure or wait for GUI work. CLAP process
wakeups need no queue because playback processes every block, including silence.
Only preallocated buffers and plugin processing occur in audio. Error status
silences the failed block; nonfinite/subnormal samples are sanitized. Plugin code
can still allocate, block, corrupt memory or crash; there is no sandbox, scanner
subprocess or recoverable crash boundary. Modules unload only after destruction
on a quiescent/control owner. Real third-party plugins and hardware timing remain
unqualified; original ABI fixtures demonstrate the bounded contracts.
silences the failed block; nonfinite/subnormal samples are sanitized. Plugin
code can still allocate, block, corrupt memory or crash; there is no sandbox,
scanner subprocess or recoverable crash boundary. Modules unload only after
destruction on a quiescent/control owner. Real third-party plugins and hardware
timing remain unqualified; original ABI fixtures demonstrate the bounded
contracts.

View file

@ -1,27 +1,31 @@
# 0011 — Prepared MIDI track instruments
# 0011 — Prepared MIDI Track Instruments
Accepted. Extend the modern C99 adapters with zero-audio-input,
mono-output instruments and one event input. Keep graph effects separate from
track bindings. Each assigned track owns one prepared instance; the existing
Accepted. Extend the modern C99 adapters with zero-audio-input, mono-output
instruments and one event input. Keep graph effects separate from track
bindings. Each assigned track owns one prepared instance; the existing
instrument source sums their mixed output and the sine output of unassigned
tracks. A stable saved live target prevents accidental broadcast/double playback.
tracks. A stable saved live target prevents accidental broadcast/double
playback.
Bindings are copied metadata in the project/history model, serialized in v12.
Loading metadata never executes modules. GUI edits prepare a candidate before
publication, preserving the previous project on missing/invalid binaries. Existing
quiescent plan retirement owns destruction; no module operation belongs in audio.
publication, preserving the previous project on missing/invalid binaries.
Existing quiescent plan retirement owns destruction; no module operation belongs
in audio.
Preallocate 256-frame audio scratch and 256 incoming events per chunk. Stable
insertion orders reset, scheduled off/on and live events without callback sort or
allocation. VST3 translates into a bounded 2304-event IEventList (2048 possible
held keys plus 256 incoming events); panic expands held keys to note-offs. CLAP
prefers MIDI where supported, otherwise native note events; VST2 uses MIDI ABI
messages. Native CLAP and VST3 currently omit controller mapping. Existing
32 simultaneous scheduled notes and 4096 stored notes remain validation bounds.
insertion orders reset, scheduled off/on and live events without callback sort
or allocation. VST3 translates into a bounded 2304-event IEventList (2048
possible held keys plus 256 incoming events); panic expands held keys to
note-offs. CLAP prefers MIDI where supported, otherwise native note events; VST2
uses MIDI ABI messages. Native CLAP and VST3 currently omit controller mapping.
Existing 32 simultaneous scheduled notes and 4096 stored notes remain validation
bounds.
Discontinuities send all-off and chase spanning notes with fresh note-ons.
Panic suppresses already-started scheduled notes. Neither operation promises
opaque DSP reconstruction or removal of release tails. Overflow/process errors
silence the block and arm reset for the following render. Hosts cannot guarantee
that third-party processing is realtime safe. Qualification covers original ABI
fixtures; arbitrary third-party plugins, physical audio and MIDI remain unverified.
Discontinuities send all-off and chase spanning notes with fresh note-ons. Panic
suppresses already-started scheduled notes. Neither operation promises opaque
DSP reconstruction or removal of release tails. Overflow/process errors silence
the block and arm reset for the following render. Hosts cannot guarantee that
third-party processing is realtime safe. Qualification covers original ABI
fixtures; arbitrary third-party plugins, physical audio and MIDI remain
unverified.