Docs file normalization.

This commit is contained in:
Johannes Findeisen 2026-11-06 15:20:20 +01:00
commit 619c2d0808
46 changed files with 3587 additions and 2579 deletions

View file

@ -1,39 +1,46 @@
# Architecture
## Decision: modular engine first
## 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.
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
---
## 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.
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
---
## Ownership and Threads
- GUI thread owns FLTK widgets and view state.
- Project/control thread owns mutable project data and graph compilation.
@ -41,53 +48,59 @@ the optional JACK implementation drives that same realtime engine.
- 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.
synchronizes callbacks before reclaiming remaining plans. See decision 0004 for
the release/acquire protocol, bounded resource retention and recovery.
## GUI backend ownership
---
The GUI 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.
## GUI Backend Ownership
## Editable graph and canvas
The GUI 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
Read-only C snapshots, monotonic node IDs, and a small set of C edit commands
are provided. 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.
are provided. 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.
---
## Project ownership and persistence
## Project Ownership and Persistence
A C-owned project contains 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.
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.
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.
---
## Assets and recording
## Assets and Recording
Audio paths are C clip metadata. A stopped preparation copies range/offset
information, deduplicates paths, validates and preloads bounded WAV assets into
@ -102,12 +115,14 @@ and render silence, with repair through a C path setter and GUI relinking.
Version 2 persistence stores references and explicitly migrates version 1 loads.
Recording has one callback producer and one control-thread consumer. A bounded
SPSC ring publishes complete payload blocks through lock-free indices. Only
the consumer writes WAV bytes; finalization follows successful callback stop.
No editable project pointer, GUI object or FILE is accessed by the producer.
See decision 0005 and AUDIO_FILES.md for format, limits and recovery contracts.
SPSC ring publishes complete payload blocks through lock-free indices. Only the
consumer writes WAV bytes; finalization follows successful callback stop. No
editable project pointer, GUI object or FILE is accessed by the producer. See
decision 0005 and AUDIO_FILES.md for format, limits and recovery contracts.
## MIDI source
---
## MIDI Source
The C MIDI plan prepares bounded source-relative notes into sample spans and
renders a separate instrument buffer. `xtc_plan_process_sources` and
@ -116,10 +131,12 @@ instrument input; older wrappers supply silence for omitted sources. Graph
instrument nodes consume the shared mono instrument buffer. The backend owns
prepared MIDI runtime state exclusively while active, with lock-free panic
requests and no callback allocation/I/O. Stop/seek clear live voices; scheduled
notes chase their absolute phase. See MIDI.md for event ordering, panic semantics,
capacities and test coverage. No new dependency is required.
notes chase their absolute phase. See MIDI.md for event ordering, panic
semantics, capacities and test coverage. No new dependency is required.
## Mix snapshots and history
---
## Mix Snapshots and History
`src/mixer` owns validated per-track settings and bounded gain/pan automation.
Audio/MIDI preparation copies and indexes these into source snapshots; paired
@ -128,16 +145,19 @@ output-peak snapshots cross to the GUI without exposing DSP state. Mixer edits
stop transport before subsequent preparation. `src/project/history.c` provides
32-edit C project snapshots; checkout clones a candidate and accept moves the
cursor only after successful GUI/backend installation. No samples or FLTK state
are stored in history. `mixer_widget.cpp` is only controls/drawing/gestures.
See MIXER.md and PROJECT_FORMAT.md for ownership, limits and v4 migration.
are stored in history. `mixer_widget.cpp` is only controls/drawing/gestures. See
MIXER.md and PROJECT_FORMAT.md for ownership, limits and v4 migration.
## Native lifecycle and distribution
---
`xtc_native.h` defines copied versioned C descriptors and bounded block contexts.
Native effect/source nodes prepare independent instances on control-thread graph
compilation and reclaim them only on compile failure, quiescent destruction or
retirement collection. Event instruments can render into the public graph source
buffer. No descriptor/state pointers enter project files; unsupported native
saves preserve files. See NATIVE_API.md and decision 0008. Release tooling lives
outside the runtime, with source manifests, deterministic exports and staged
installation/consumer checks. See RELEASING.md for reproducibility scope.
## Native Lifecycle and Distribution
`xtc_native.h` defines copied versioned C descriptors and bounded block
contexts. Native effect/source nodes prepare independent instances on
control-thread graph compilation and reclaim them only on compile failure,
quiescent destruction or retirement collection. Event instruments can render
into the public graph source buffer. No descriptor/state pointers enter project
files; unsupported native saves preserve files. See NATIVE_API.md and decision
0008. Release tooling lives outside the runtime, with source manifests,
deterministic exports and staged installation/consumer checks. See RELEASING.md
for reproducibility scope.