136 lines
8 KiB
Markdown
136 lines
8 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.
|
|
The offline API remains single-owner. The realtime/backend APIs below add
|
|
publication and JACK processing; offline tests alone do not qualify hardware.
|
|
|
|
## Phase 2 backend and realtime bridge
|
|
|
|
Use `xtc_backend.h` on one serialized control thread:
|
|
|
|
1. Open a config with `XTC_BACKEND_JACK`, a client name, channel_count 1, and
|
|
max_frames 1..65536. It must accommodate the server's current buffer size.
|
|
The actual rate and buffer size are returned through get_status. Open does
|
|
not start the server or acquire devices directly. Invalid config returns
|
|
invalid argument, missing build support returns unsupported, and JACK setup
|
|
failures return I/O error. If cleanup itself fails, a non-NULL output handle
|
|
is preserved for close retry; no other operation is allowed on that handle.
|
|
2. Submit a graph (copied/compiled at the server rate), then start. Starting
|
|
an already running client is an invalid-state error; stopping a stopped
|
|
client succeeds. With no submitted graph, callbacks produce silence.
|
|
3. Enumerate available audio ports with visit_ports. Names are borrowed only
|
|
during the visitor; do not reenter backend operations from the visitor.
|
|
Connect the client's input to a remote output or its output to a remote
|
|
input explicitly. JACK maps device selection to server/device port routes;
|
|
ALSA device identifiers are not accepted here. The server controls rate,
|
|
period and hardware configuration outside this API. Names/ports can disappear
|
|
between enumeration and connection; route failures are returned as I/O errors.
|
|
4. During playback, submit edited graphs and periodically collect retired
|
|
resources. Capacity means one pending graph is still awaiting adoption;
|
|
keep the current graph and retry later. get_status is a nonblocking snapshot
|
|
of rate, frames, running/server-loss/rate-change flags, xruns, processed
|
|
blocks and last process result. No active DSP memory is exposed.
|
|
5. Stop pauses the retained DSP state after deactivation. Restart resumes it.
|
|
Close removes the JACK client, synchronizes all callbacks, then destroys
|
|
plans. Stop/close failures preserve the handle for recovery/retry. No plan
|
|
is freed on the audio thread. Sample-rate change or server loss requires
|
|
close/reopen and a fresh submission; there is no automatic reconnect.
|
|
|
|
Buffers can change size. In-limit changes process normally; oversized blocks
|
|
are silent and report capacity errors until the size is supported again.
|
|
Rate changes latch silence and invalid-state processing results. A missing input
|
|
buffer is treated as unavailable input (silence). A missing output buffer is
|
|
reported as an I/O process error. Shutdown callbacks only set the loss flag;
|
|
the control thread closes the client. No JACK or GUI types cross the C API.
|
|
|
|
The standalone `xtc_realtime.h` API allows hardware-independent consumers and
|
|
stress tests. Submit compiles on the producer; process adopts at a block
|
|
boundary without allocation/free, I/O, locks or waits. All process calls must
|
|
come from one consumer thread, and destruction requires that consumer stopped
|
|
and joined. Release/acquire publication, retirement backpressure and atomic
|
|
requirements are specified in decision 0004. Ordinary project processing and
|
|
JACK notification callbacks were audited for prohibited operations; JACK's own
|
|
internal realtime guarantees depend on the installed implementation/server.
|
|
|
|
Validation commands and results are recorded in BUILD.md and the integration
|
|
test README. Current limitations: mono input/output, no parameter ramping or
|
|
crossfaded graph changes, no realtime meter bridge, no automatic reconnect,
|
|
no physical-device or long-duration endurance qualification. Phase 3 connects
|
|
the FLTK shell to backend controls and status; see GUI.md. Its editors remain
|
|
placeholders and the GUI submits no playback graph yet.
|