2026-10-26 22:51:25 +01:00
|
|
|
# Architecture
|
|
|
|
|
|
|
|
|
|
## Decision: modular engine first
|
|
|
|
|
|
|
|
|
|
The processing graph is the common model for audio sources, instruments,
|
|
|
|
|
effects, mixers, and outputs. The sequencer produces time-based content and
|
|
|
|
|
events consumed by graph nodes. The default workspace remains sequencer-first;
|
|
|
|
|
the graph editor is an advanced view of the same project.
|
|
|
|
|
|
|
|
|
|
## Layers
|
|
|
|
|
|
|
|
|
|
1. **FLTK/C++ GUI:** input, drawing, windows, view state.
|
|
|
|
|
2. **C core:** project model, commands, parameters, persistence, validation.
|
|
|
|
|
3. **C engine:** prepared processing graph, DSP, transport timing, event delivery.
|
|
|
|
|
4. **Backend:** hardware and system audio/MIDI integration.
|
|
|
|
|
|
|
|
|
|
The GUI must not directly modify engine internals. It calls a stable C API.
|
|
|
|
|
|
|
|
|
|
## Graph execution
|
|
|
|
|
|
|
|
|
|
The first implementation should reject cycles. Compile a graph off the audio
|
|
|
|
|
thread: validate, topologically sort, determine buffer requirements, allocate
|
|
|
|
|
resources, and publish a prepared immutable execution plan. The audio thread
|
|
|
|
|
must not traverse a graph being edited.
|
|
|
|
|
|
2026-10-26 23:20:00 +01:00
|
|
|
The offline engine now copies a validated graph into a prepared plan with
|
|
|
|
|
stable node IDs, topological order, precomputed input adjacency, per-node mono
|
|
|
|
|
buffers and oscillator/meter state. Each plan has one processing owner. Graph
|
|
|
|
|
edits and destruction cannot affect an existing plan. All nodes are scheduled,
|
|
|
|
|
including disconnected nodes; only explicit output nodes are rendered (or
|
2026-10-26 23:55:35 +01:00
|
|
|
terminal nodes for legacy callers that do not require explicit outputs). See AUDIO_ENGINE.md for contracts.
|
2026-10-26 23:32:04 +01:00
|
|
|
`xtc_realtime` now provides a one-pending/one-retired handoff around an
|
|
|
|
|
exclusively audio-owned active plan. `xtc_backend` is the C backend boundary;
|
|
|
|
|
the optional JACK implementation drives that same realtime engine.
|
2026-10-26 22:51:25 +01:00
|
|
|
|
|
|
|
|
## Ownership and threads
|
|
|
|
|
|
|
|
|
|
- GUI thread owns FLTK widgets and view state.
|
|
|
|
|
- Project/control thread owns mutable project data and graph compilation.
|
|
|
|
|
- Audio thread owns the active processing plan and DSP state.
|
|
|
|
|
- Backend callbacks follow the backend's documented threading contract.
|
|
|
|
|
|
2026-10-26 23:32:04 +01:00
|
|
|
Only acknowledged retired plans are reclaimed during processing. Backend close
|
|
|
|
|
synchronizes callbacks before reclaiming remaining plans. See decision 0004
|
|
|
|
|
for the release/acquire protocol, bounded resource retention and recovery.
|
2026-10-26 23:40:28 +01:00
|
|
|
|
|
|
|
|
## GUI backend ownership
|
|
|
|
|
|
|
|
|
|
The Phase 3 window owns one C backend handle and serializes all control calls
|
|
|
|
|
on the GUI thread. A GUI timer reads snapshots and collects retired resources;
|
|
|
|
|
no audio callback calls FLTK. Successful window close first closes the JACK
|
|
|
|
|
client (synchronizing callbacks), then removes the GUI timer and hides the
|
|
|
|
|
window. Failed close leaves both handle and window available for retry. View
|
2026-10-26 23:55:35 +01:00
|
|
|
switching and node positions are GUI state only. Semantic edits use transactional
|
|
|
|
|
C commands and publish independent compiled snapshots. See GUI.md.
|
|
|
|
|
|
|
|
|
|
## Editable graph and canvas
|
|
|
|
|
|
|
|
|
|
Phase 4 adds read-only C snapshots, monotonic node IDs, and a small set of C
|
|
|
|
|
edit commands. The GUI owns an opaque graph handle, never private arrays.
|
|
|
|
|
An edit produces a candidate, and the GUI commits it only after backend submit
|
|
|
|
|
succeeds (or locally while offline). A busy pending slot or invalid edit leaves
|
|
|
|
|
the prior model/plan unchanged. Active/submitted revisions distinguish the
|
|
|
|
|
queued drawing from the engine's acknowledged plan. Empty/output-less editor
|
|
|
|
|
graphs render silence; legacy offline terminal fallback remains available.
|
|
|
|
|
|
|
|
|
|
GraphWidget owns only selection, node positions, pointer gestures and drawing.
|
|
|
|
|
Its fixed canvas is hosted by FLTK scrolling. C owns nodes, ports, edges,
|
|
|
|
|
validation, ID assignment, compilation and rendering. No audio thread accesses
|
|
|
|
|
GUI-owned state. Reconnect republishes the same retained editable graph.
|
2026-10-27 00:10:45 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
## Project ownership and persistence
|
|
|
|
|
|
|
|
|
|
Phase 5 introduces a C-owned project containing the graph, bounded track/clip
|
|
|
|
|
arrays, stable ID allocators and constant tempo. Read APIs return copies;
|
|
|
|
|
GUI canvases borrow the opaque project/graph on the control thread. A graph
|
|
|
|
|
edit prepares a candidate project before submitting its graph, then commits
|
|
|
|
|
ownership only after acceptance. Arrangement edits currently change silent
|
|
|
|
|
metadata and require no audio publication.
|
|
|
|
|
|
|
|
|
|
Persistence is private C/POSIX code; loading validates an isolated candidate
|
|
|
|
|
and swaps ownership only after a successful close. The GUI similarly prepares
|
|
|
|
|
a loaded document before stopping/submitting audio. File operations never run
|
|
|
|
|
in the callback. See PROJECT_FORMAT.md for schema, bounds and durability.
|
|
|
|
|
SequencerWidget owns selection, zoom and gestures only; musical range checks,
|
|
|
|
|
source-preserving trims and ID allocation remain C responsibilities.
|