Phase 2.
This commit is contained in:
parent
c0ca1ff396
commit
577d80f018
16 changed files with 1100 additions and 41 deletions
84
docs/decisions/0004-jack-and-plan-handoff.md
Normal file
84
docs/decisions/0004-jack-and-plan-handoff.md
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
# Decision 0004: JACK backend and bounded plan handoff
|
||||
|
||||
**Status:** Accepted for Phase 2
|
||||
|
||||
## Backend
|
||||
|
||||
Use the JACK C client API. The inspected system provides PipeWire's maintained
|
||||
JACK compatibility library (`pkg-config jack` reports 3.1609.0), headers, a live
|
||||
server, and mono audio ports. This is JACK client integration, not native
|
||||
PipeWire or ALSA PCM. JACK owns device access, timing, sample rate and block
|
||||
size. The application registers one mono input/output pair and exposes port
|
||||
inspection and explicit routing. It neither starts the server nor changes its
|
||||
configuration or automatically connects hardware.
|
||||
|
||||
JACK support is optional at build time. Without JACK development metadata,
|
||||
backend open returns unsupported; offline and handoff tests still work.
|
||||
Consumers that call backend APIs link `pkg-config --libs jack` as well as `-lm`.
|
||||
No backend headers appear in public headers. The kind/configuration/status API
|
||||
is the boundary for adding another backend; there is no speculative plugin
|
||||
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.
|
||||
|
||||
## 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
|
||||
C99-compatible on the selected compilers. Initialization requires pointer and
|
||||
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
|
||||
|
||||
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 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.
|
||||
|
||||
The active pointer and all active DSP state belong exclusively to the consumer.
|
||||
Producer never reads them. Caller graph memory is copied before publication;
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
No stereo, recording, MIDI, native PipeWire API, GUI engine controls or hardware
|
||||
configuration dialogs are part of this phase.
|
||||
Loading…
Add table
Add a link
Reference in a new issue