1
0
Fork 0
forked from hanez/xtc
xtc/docs/ARCHITECTURE.md

3.6 KiB

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.

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 terminal nodes for legacy callers that do not require explicit outputs). See AUDIO_ENGINE.md for contracts. 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.

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.

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.

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 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.