83 lines
5.1 KiB
Markdown
83 lines
5.1 KiB
Markdown
|
|
# Native C processor API, version 1
|
||
|
|
|
||
|
|
`xtc_native.h` defines a small compile-time extension boundary for trusted C99
|
||
|
|
sources, effects and instruments. Build `make build/native-gain` and run it for a
|
||
|
|
complete graph-effect example in `examples/native_gain.c`. This is a source API
|
||
|
|
for this release, not a promise of binary compatibility across builds/platforms.
|
||
|
|
There is no module scanner, dynamic loader, SDK dependency or sandbox.
|
||
|
|
|
||
|
|
## Descriptor and ownership
|
||
|
|
|
||
|
|
Set `abi_version = XTC_NATIVE_ABI_VERSION`, `struct_size = sizeof(descriptor)`,
|
||
|
|
a terminated printable non-space ASCII identifier (1..63 bytes), kind and all
|
||
|
|
four callbacks. Unknown ABI/size returns UNSUPPORTED; invalid fields return
|
||
|
|
INVALID_ARGUMENT. The host copies the entire descriptor, including its name.
|
||
|
|
Callback code must remain loaded until all graphs, plans and instances are gone.
|
||
|
|
No plugin/global registry is consulted or mutated by rendering.
|
||
|
|
|
||
|
|
`xtc_native_create` validates rate (positive, <=768000), frames (1..65536) and a
|
||
|
|
finite scalar parameter. It runs prepare on the control thread with a NULL
|
||
|
|
initial state pointer. Prepare may allocate/preload; on failure the host calls
|
||
|
|
destroy even for NULL state. Destroy must accept NULL. The host then resets the
|
||
|
|
new instance. On failure the output handle is NULL; successful instances belong
|
||
|
|
to the caller. Destroy only after the processing owner is stopped/joined.
|
||
|
|
|
||
|
|
Reset and process have one audio owner (or run while stopped). They must never
|
||
|
|
allocate/free, perform I/O/logging, block, call GUI functions or mutate shared
|
||
|
|
control data. Reset must be idempotent. Prepare/destroy run only on the control
|
||
|
|
thread, including failed compilation and retired-plan collection. Code executed
|
||
|
|
inside callbacks is trusted: xtc cannot enforce its realtime behavior or protect
|
||
|
|
against memory corruption. C++ processors must use C linkage and never throw
|
||
|
|
across this boundary; domain/DSP code in this repository remains C.
|
||
|
|
|
||
|
|
## Block contract
|
||
|
|
|
||
|
|
A block contains mono input/output, frames, absolute sample position and optional
|
||
|
|
MIDI events. Input NULL means silence; exact input/output aliasing is allowed and
|
||
|
|
processors must support it. Output is writable for every frame, including silent
|
||
|
|
frames; do not retain any borrowed block pointers. Other overlapping buffers are
|
||
|
|
not supported. Only instruments receive events; event count <=256, offsets are
|
||
|
|
sorted and strictly less than frames, with equal offsets retaining caller order.
|
||
|
|
Pitch/velocity/channel/type bounds follow xtc_midi.h. Zero frames is a no-op.
|
||
|
|
|
||
|
|
The host rejects invalid blocks before changing state/output, and reports
|
||
|
|
CAPACITY above the prepared block limit. Position arithmetic cannot overflow.
|
||
|
|
A discontinuous position resets held voices/state before processing. Call reset
|
||
|
|
for Stop/panic when hosting instruments yourself; send channel ALL_OFF for
|
||
|
|
channel-specific panic. The native host does not implement sustain or routing.
|
||
|
|
|
||
|
|
On successful processing, nonfinite samples become silence. Callback failure
|
||
|
|
silences the block, resets that processor and returns its error. Already advanced
|
||
|
|
processors in a graph are not rolled back. Never rely on failure to transactionally
|
||
|
|
rewind DSP; prepare fallible resources before publication. There is no latency
|
||
|
|
compensation, tail protocol, parameter ramp or dynamic port negotiation in ABI 1.
|
||
|
|
|
||
|
|
## Graph and instrument integration
|
||
|
|
|
||
|
|
`xtc_graph_add_native` adds effects (input 0/output 1) or sources (output 1), with
|
||
|
|
a scalar immutable parameter. Compilation creates two independent mono instances
|
||
|
|
for stereo; no mutable state is shared between sides. Graph-only edits retain
|
||
|
|
the existing pending/retired publication contract. Reset clears native state and
|
||
|
|
its graph-local frame clock; graph positions count successfully rendered frames
|
||
|
|
since compilation/reset, independently of backend seek/transport. Sources needing
|
||
|
|
transport time should be hosted explicitly with a block position.
|
||
|
|
|
||
|
|
For an instrument, use create/process directly with sample-offset events, then
|
||
|
|
feed its output to an existing XTC_NODE_INSTRUMENT through process_sources or
|
||
|
|
process_stereo. The host owns event routing and panic/reset delivery. Graph-native
|
||
|
|
instrument addition returns UNSUPPORTED because current graph ports carry audio,
|
||
|
|
not MIDI. This separation avoids silently dropping instrument events. The built-in
|
||
|
|
JACK MIDI sine instrument remains the GUI's instrument; custom instrument selection
|
||
|
|
and event graph ports are future work.
|
||
|
|
|
||
|
|
Descriptors survive graph/project cloning and transactional remove/connect edits;
|
||
|
|
custom ADD_NODE commands use the dedicated constructor rather than a GUI menu.
|
||
|
|
Native nodes have no project-file representation: Save returns UNSUPPORTED and
|
||
|
|
preserves the destination. Format v4 still accepts only builtin types 1..6.
|
||
|
|
A durable identifier/registry, versioned processor state and missing-module policy
|
||
|
|
must precede serialized native extensions or external plugin hosting.
|
||
|
|
|
||
|
|
Tests cover ABI/size/field rejection, descriptor copying, aliased buffers,
|
||
|
|
prepare/process failures, stereo compile cleanup, retirement/destruction outside
|
||
|
|
processing, event offsets, seek note reset, source ports, instrument-buffer graph
|
||
|
|
routing and unsupported-save preservation. Run `make test`.
|