134 lines
8.3 KiB
Markdown
134 lines
8.3 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`.
|
|
|
|
## Structured parameter processors
|
|
|
|
`xtc_processor.h` is the public C99 parameter-only extension interface (ABI 2).
|
|
It complements the ABI 1 graph adapter without changing its layout or behavior.
|
|
`examples/processors.c` implements a mono gain effect and a monophonic MIDI sine
|
|
instrument using only public headers; `make test-processor` builds and exercises
|
|
both. The instrument has immediate note-off and no voice stealing/release tail.
|
|
|
|
Descriptors have an exact ABI version and structure size, a persistent identifier,
|
|
and up to 16 parameters with unique unsigned IDs, names, units, finite ranges and
|
|
defaults. Unsupported ABI versions/sizes are rejected before initialization.
|
|
Changing identifier, parameter order, IDs or ranges requires an explicit migration
|
|
by the extension author. There is no promise of binary compatibility across
|
|
compiler architectures, and no C++/FLTK types in either API.
|
|
|
|
Create prepares a copied descriptor on the control thread. Failed preparation
|
|
calls destroy, including with NULL: callbacks must implement partial cleanup.
|
|
Reset/process/parameter have one audio owner and must never allocate, free, block,
|
|
perform I/O or call GUI functions. Destroy is control-thread only after callback
|
|
quiescence. Reconfiguration prepares a new independent instance; publish it with
|
|
an appropriate owner handoff and retire the old instance after quiescence. The
|
|
callback code must remain loaded throughout that lifetime.
|
|
|
|
Each instance has one mono output and either one optional input (effect) or no
|
|
input (source/instrument). NULL input means silence. Stereo effects use two
|
|
independent instances; multichannel interdependent effects have no adapter.
|
|
Instruments receive at most 256 sorted sample-offset MIDI events per block.
|
|
The API intentionally has no channel-count argument: unsupported layouts cannot
|
|
be represented. Graph ABI 1 rejects instrument descriptors; explicitly render
|
|
instrument output and feed the existing instrument source buffers.
|
|
|
|
Up to 256 sorted parameter events split the process block at exact sample
|
|
positions. Equal-offset parameters execute in array order, then MIDI at that
|
|
sample. Values outside the descriptor range, bad IDs and malformed events reject
|
|
the entire call before touching state/output. Values are stepped; extension
|
|
callbacks own any smoothing. Transport discontinuities reset transient state;
|
|
reset retains current parameters. Callback errors silence the whole output and
|
|
reset voices/history; already applied parameters persist. Nonfinite/subnormal
|
|
successful samples become zero. Input/output may alias exactly.
|
|
|
|
`xtc_processor_save`/`restore` run only while quiescent and serialize configuration,
|
|
not DSP history. Format: `XTCN`, little-endian uint32 version 2, zero-padded
|
|
64-byte identifier, uint32 parameter count, zero uint32 reserved, then ordered
|
|
pairs of uint32 ID and IEEE float32 bits. Header is 80 bytes, pairs 8 bytes.
|
|
Restore validates exact length, identity, IDs, ranges and defaults/descriptor
|
|
compatibility before preparing a new instance; failure leaves existing handles
|
|
intact. Opaque assets and hidden state are outside this ABI. ABI 1 graph nodes
|
|
remain session-only and fail project saves explicitly; ABI 2 configuration blobs
|
|
are a separate public facility, not automatically embedded in project files.
|
|
GUI graph access continues through public C APIs only.
|