xtc/docs/AUDIO_ENGINE.md

81 lines
4.4 KiB
Markdown

# Audio engine requirements
## Real-time callback rules
The callback must not allocate/free memory, lock blocking mutexes, perform
filesystem/network/console I/O, log, load plugins or samples, call GUI APIs,
or wait for another thread.
Use preallocated buffers and bounded work. Publish parameter and graph changes
through a documented real-time-safe mechanism. Verify atomics are lock-free
where required on the target platform.
## Processing model
Process a bounded number of frames per block. Do not assume a fixed block size
unless the backend guarantees it. Define maximum frame and event counts and
safe behavior when those limits are reached.
The initial milestone is offline processing, then a test oscillator -> gain ->
meter/output graph, then a hardware backend.
## Clock and transport
Audio sample position is the authoritative playback clock. Use integer or
rational timeline representations to avoid cumulative floating-point drift.
Define tempo-map conversion and behavior at tempo changes before implementing
tempo automation.
## Phase 1 offline API
`xtc_graph_create` accepts 1..256 nodes; IDs are insertion indexes, stable for
that graph's lifetime. Supported nodes are gain (any finite multiplier), sine
oscillator (finite nonnegative Hz), pass-through peak meter and audio output.
Ports carry mono float audio: input 0, output 1. Oscillators have no input and
output nodes have no output. Port lookup reports direction, signal type and
channel count. MIDI/control ports and channel conversion are not implemented;
invalid ports/directions, duplicate edges and cycles are rejected before an
edit becomes visible. Fan-out copies a source to consumers; fan-in sums inputs
without normalization. Failed additions leave IDs and graph contents unchanged.
`xtc_graph_compile(graph, sample_rate, max_frames, &plan)` runs on the control
thread. Empty graphs are rejected. Sample rate must be finite and positive,
oscillators must be at or below Nyquist, and max_frames is 1..65536. Compilation
allocates and copies topology/parameters, builds a topological schedule and
input adjacency, and allocates one max_frames float buffer per node. The
maximum buffer storage is 64 MiB per plan. Compilation may fail with an
allocation error; output plan is then NULL. Destroy a plan on the control thread.
`xtc_plan_process` uses only prepared state: no allocation/free, sorting,
logging, I/O or locks. Each call accepts up to max_frames; oversized calls
return capacity errors without modifying output or state. Output storage must
hold frame_count floats. NULL input means silence; input/output may alias.
Zero frames leaves all state unchanged. No plan operations may run concurrently
with processing, including reset, meter reads or destruction.
Unconnected non-oscillator nodes receive external input. Connected inputs
receive only the sum of their upstream buffers. Explicit output nodes are
summed to the caller's output. If none exist, terminal nodes are summed for
compatibility with the original gain-chain API. Disconnected components cannot
contribute when explicit outputs exist, but they are still processed. Floating
point overflow and nonfinite input become silence per sample. No clipping or
normalization is applied to finite samples.
Oscillators start at phase zero and carry phase continuously across blocks;
reset clears phases and meter peaks. Meter reads return the last nonempty
block's absolute peak. These are same-thread reads, not a thread-safe GUI
meter bridge. Rendering is deterministic on the same build/platform regardless
of block partitioning; libm results need not be bit-identical across platforms.
`xtc_graph_process_offline` remains a convenience helper, compiling at 48000 Hz
and destroying the plan after one block. It accepts a finite external-input
gain and requires non-NULL input/output. It now follows routing rather than
multiplying every gain in insertion order. It allocates and is not suitable for
an audio callback. Use a persistent plan for phase continuity and repeated blocks.
Tests in `tests/unit/test_offline.c` cover known oscillator samples, gain and
meter output, reset and partition equivalence, fan-out/fan-in, reverse insertion
order, disconnected nodes, in-place processing, nonfinite input, invalid ports,
cycles, capacity limits, plan lifetime and repeated compilation/cleanup.
Hardware playback, concurrent graph publication and backend stress tests remain
Phase 2 work; this offline API does not claim hardware real-time qualification.