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 # Architecture
## Decision: modular engine first ## Decision: Modular Engine First
The processing graph is the common model for audio sources, instruments, The processing graph is the common model for audio sources, instruments,
effects, mixers, and outputs. The sequencer produces time-based content and effects, mixers, and outputs. The sequencer produces time-based content and
events consumed by graph nodes. The default workspace remains sequencer-first; events consumed by graph nodes. The default workspace remains sequencer-first;
the graph editor is an advanced view of the same project. the graph editor is an advanced view of the same project.
---
## Layers ## Layers
1. **FLTK/C++ GUI:** input, drawing, windows, view state. 1. **FLTK/C++ GUI:** input, drawing, windows, view state.
2. **C core:** project model, commands, parameters, persistence, validation. 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. 4. **Backend:** hardware and system audio/MIDI integration.
The GUI must not directly modify engine internals. It calls a stable C API. 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 The first implementation should reject cycles. Compile a graph off the audio
thread: validate, topologically sort, determine buffer requirements, allocate thread: validate, topologically sort, determine buffer requirements, allocate
resources, and publish a prepared immutable execution plan. The audio thread resources, and publish a prepared immutable execution plan. The audio thread
must not traverse a graph being edited. must not traverse a graph being edited.
The offline engine now copies a validated graph into a prepared plan with The offline engine now copies a validated graph into a prepared plan with stable
stable node IDs, topological order, precomputed input adjacency, per-node mono node IDs, topological order, precomputed input adjacency, per-node mono buffers
buffers and oscillator/meter state. Each plan has one processing owner. Graph and oscillator/meter state. Each plan has one processing owner. Graph edits and
edits and destruction cannot affect an existing plan. All nodes are scheduled, destruction cannot affect an existing plan. All nodes are scheduled, including
including disconnected nodes; only explicit output nodes are rendered (or disconnected nodes; only explicit output nodes are rendered (or terminal nodes
terminal nodes for legacy callers that do not require explicit outputs). See AUDIO_ENGINE.md for contracts. for legacy callers that do not require explicit outputs). See AUDIO_ENGINE.md
`xtc_realtime` now provides a one-pending/one-retired handoff around an for contracts. `xtc_realtime` now provides a one-pending/one-retired handoff
exclusively audio-owned active plan. `xtc_backend` is the C backend boundary; around an exclusively audio-owned active plan. `xtc_backend` is the C backend
the optional JACK implementation drives that same realtime engine. boundary; the optional JACK implementation drives that same realtime engine.
## Ownership and threads ---
## Ownership and Threads
- GUI thread owns FLTK widgets and view state. - GUI thread owns FLTK widgets and view state.
- Project/control thread owns mutable project data and graph compilation. - 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. - Backend callbacks follow the backend's documented threading contract.
Only acknowledged retired plans are reclaimed during processing. Backend close Only acknowledged retired plans are reclaimed during processing. Backend close
synchronizes callbacks before reclaiming remaining plans. See decision 0004 synchronizes callbacks before reclaiming remaining plans. See decision 0004 for
for the release/acquire protocol, bounded resource retention and recovery. 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 ## GUI Backend Ownership
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 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 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. are provided. The GUI owns an opaque graph handle, never private arrays. An edit
An edit produces a candidate, and the GUI commits it only after backend submit produces a candidate, and the GUI commits it only after backend submit succeeds
succeeds (or locally while offline). A busy pending slot or invalid edit leaves (or locally while offline). A busy pending slot or invalid edit leaves the prior
the prior model/plan unchanged. Active/submitted revisions distinguish the model/plan unchanged. Active/submitted revisions distinguish the queued drawing
queued drawing from the engine's acknowledged plan. Empty/output-less editor from the engine's acknowledged plan. Empty/output-less editor graphs render
graphs render silence; legacy offline terminal fallback remains available. silence; legacy offline terminal fallback remains available.
GraphWidget owns only selection, node positions, pointer gestures and drawing. GraphWidget owns only selection, node positions, pointer gestures and drawing.
Its fixed canvas is hosted by FLTK scrolling. C owns nodes, ports, edges, Its fixed canvas is hosted by FLTK scrolling. C owns nodes, ports, edges,
validation, ID assignment, compilation and rendering. No audio thread accesses validation, ID assignment, compilation and rendering. No audio thread accesses
GUI-owned state. Reconnect republishes the same retained editable graph. 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 A C-owned project contains the graph, bounded track/clip arrays, stable ID
allocators and constant tempo. Read APIs return copies; allocators and constant tempo. Read APIs return copies; GUI canvases borrow the
GUI canvases borrow the opaque project/graph on the control thread. A graph opaque project/graph on the control thread. A graph edit prepares a candidate
edit prepares a candidate project before submitting its graph, then commits project before submitting its graph, then commits ownership only after
ownership only after acceptance. Arrangement edits currently change silent acceptance. Arrangement edits currently change silent metadata and require no
metadata and require no audio publication. audio publication.
Persistence is private C/POSIX code; loading validates an isolated candidate Persistence is private C/POSIX code; loading validates an isolated candidate and
and swaps ownership only after a successful close. The GUI similarly prepares swaps ownership only after a successful close. The GUI similarly prepares a
a loaded document before stopping/submitting audio. File operations never run loaded document before stopping/submitting audio. File operations never run in
in the callback. See PROJECT_FORMAT.md for schema, bounds and durability. the callback. See PROJECT_FORMAT.md for schema, bounds and durability.
SequencerWidget owns selection, zoom and gestures only; musical range checks, SequencerWidget owns selection, zoom and gestures only; musical range checks,
source-preserving trims and ID allocation remain C responsibilities. 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 Audio paths are C clip metadata. A stopped preparation copies range/offset
information, deduplicates paths, validates and preloads bounded WAV assets into 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. Version 2 persistence stores references and explicitly migrates version 1 loads.
Recording has one callback producer and one control-thread consumer. A bounded Recording has one callback producer and one control-thread consumer. A bounded
SPSC ring publishes complete payload blocks through lock-free indices. Only SPSC ring publishes complete payload blocks through lock-free indices. Only the
the consumer writes WAV bytes; finalization follows successful callback stop. consumer writes WAV bytes; finalization follows successful callback stop. No
No editable project pointer, GUI object or FILE is accessed by the producer. editable project pointer, GUI object or FILE is accessed by the producer. See
See decision 0005 and AUDIO_FILES.md for format, limits and recovery contracts. 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 The C MIDI plan prepares bounded source-relative notes into sample spans and
renders a separate instrument buffer. `xtc_plan_process_sources` 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 instrument nodes consume the shared mono instrument buffer. The backend owns
prepared MIDI runtime state exclusively while active, with lock-free panic prepared MIDI runtime state exclusively while active, with lock-free panic
requests and no callback allocation/I/O. Stop/seek clear live voices; scheduled 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, notes chase their absolute phase. See MIDI.md for event ordering, panic
capacities and test coverage. No new dependency is required. 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. `src/mixer` owns validated per-track settings and bounded gain/pan automation.
Audio/MIDI preparation copies and indexes these into source snapshots; paired 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 stop transport before subsequent preparation. `src/project/history.c` provides
32-edit C project snapshots; checkout clones a candidate and accept moves the 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 cursor only after successful GUI/backend installation. No samples or FLTK state
are stored in history. `mixer_widget.cpp` is only controls/drawing/gestures. are stored in history. `mixer_widget.cpp` is only controls/drawing/gestures. See
See MIXER.md and PROJECT_FORMAT.md for ownership, limits and v4 migration. 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 Lifecycle and Distribution
Native effect/source nodes prepare independent instances on control-thread graph
compilation and reclaim them only on compile failure, quiescent destruction or `xtc_native.h` defines copied versioned C descriptors and bounded block
retirement collection. Event instruments can render into the public graph source contexts. Native effect/source nodes prepare independent instances on
buffer. No descriptor/state pointers enter project files; unsupported native control-thread graph compilation and reclaim them only on compile failure,
saves preserve files. See NATIVE_API.md and decision 0008. Release tooling lives quiescent destruction or retirement collection. Event instruments can render
outside the runtime, with source manifests, deterministic exports and staged into the public graph source buffer. No descriptor/state pointers enter project
installation/consumer checks. See RELEASING.md for reproducibility scope. 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.

View file

@ -1,66 +1,146 @@
# Audio source editor workflows # Audio Source Editor Workflows
The editor uses the existing bounded mono WAV reader. Stereo input is downmixed; The editor uses the existing bounded mono WAV reader. Stereo input is downmixed;
these are offline control-thread tools, not stereo restoration or time stretching. these are offline control-thread tools, not stereo restoration or time
The project format remains 16. No new dependencies are required. stretching. The project format remains 16. No new dependencies are required.
## Source inspection ---
## Source Inspection
Select an audio clip and use View / Sample editor or double-click it. The modal Select an audio clip and use View / Sample editor or double-click it. The modal
editor loads an independent snapshot of the complete source, up to 4,194,304 frames editor loads an independent snapshot of the complete source, up to 4,194,304
(about 87 seconds at 48 kHz). Drag or enter half-open native frame bounds; zoom frames (about 87 seconds at 48 kHz). Drag or enter half-open native frame
1..64 and View start control the visible region. Statistics show raw mono min/max, bounds; zoom 1..64 and View start control the visible region. Statistics show
RMS and mean/DC before clip gain, fades or mixer processing. Display clamps to raw mono min/max, RMS and mean/DC before clip gain, fades or mixer processing.
±1; sample statistics retain finite over-range values. Editing project MIDI still Display clamps to ±1; sample statistics retain finite over-range values. Editing
opens the piano roll. This editor does not audition or stream long files. project MIDI still opens the piano roll. This editor does not audition or stream
long files.
## Trim clip to selection ---
Trim clip to selection applies source-frame endpoints within the current audible clip. Start rounds up and end rounds down to musical ticks; empty sub-tick ranges reject. Leading removal moves the timeline start by the same amount, preserving alignment. Source content, reference and clip gain/fade settings remain intact; project Undo restores the prior trim. ## Trim Clip to Selection
## Export and relink Trim clip to selection applies source-frame endpoints within the current audible
clip. Start rounds up and end rounds down to musical ticks; empty sub-tick
ranges reject. Leading removal moves the timeline start by the same amount,
preserving alignment. Source content, reference and clip gain/fade settings
remain intact; project Undo restores the prior trim.
Export selected frames writes a new mono float32 WAV of the half-open selection at native rate. Save full copy and relink writes the complete source and relinks only the selected clip after successful preparation. The original source is never overwritten. Full-copy adoption requires matching source frame count/rate. Project undo restores the reference but retains the new file, which may be referenced by redo/history or other projects. Failed adoption removes its owned new file. Export/copy runs synchronously outside audio callbacks. ---
## Reverse selection ## Export and Relink
Reverse selection changes only the chosen source frames in a new local snapshot. Undo / redo toggles the most recent processing step; selection and view are unchanged. Processing does not affect playback until Save full copy and relink. Close discards unsaved local processing. Sources are bounded to 16 MiB of float samples; preparing a new edit can temporarily retain three snapshots (48 MiB), then two (32 MiB). Export selected frames writes a new mono float32 WAV of the half-open selection
at native rate. Save full copy and relink writes the complete source and relinks
only the selected clip after successful preparation. The original source is
never overwritten. Full-copy adoption requires matching source frame count/rate.
Project undo restores the reference but retains the new file, which may be
referenced by redo/history or other projects. Failed adoption removes its owned
new file. Export/copy runs synchronously outside audio callbacks.
## Invert polarity ---
Invert polarity negates selected float samples; it is not time reversal. Over-range finite samples remain over-range; no implicit limiting is performed. Outside-selection frames are bit-preserved in the copied snapshot. ## Reverse Selection
## Remove DC offset Reverse selection changes only the chosen source frames in a new local snapshot.
Undo / redo toggles the most recent processing step; selection and view are
unchanged. Processing does not affect playback until Save full copy and relink.
Close discards unsaved local processing. Sources are bounded to 16 MiB of float
samples; preparing a new edit can temporarily retain three snapshots (48 MiB),
then two (32 MiB).
Remove DC offset subtracts the mean of the selected range from only that range. This is a constant offset correction, not a high-pass filter. Selection boundaries may acquire discontinuities; use fades or zero-crossing tools where appropriate. Finite over-range results are retained; float overflow rejects the edit. ---
## Sample fades ## Invert Polarity
Sample fades offers four offline curves: linear in/out and equal-power in/out (sinusoidal amplitude). The entire selection is the fade duration; both endpoints are included in the curve. Fewer than two frames reject. These curves change copied sample data and stack with existing playback clip fades after adoption; they do not form a linked clip crossfade. Invert polarity negates selected float samples; it is not time reversal.
Over-range finite samples remain over-range; no implicit limiting is performed.
Outside-selection frames are bit-preserved in the copied snapshot.
## Gain and normalize ---
Gain / normalize processes only selected samples. Mode 0 accepts linear gain 0..16; mode 1 accepts a linear peak target above 0 and at most 1, rejecting silent selections or required gain above 16. Finite values above unity are not clipped; float overflow rejects atomically. This changes sample data in the local snapshot, unlike the existing clip-gain normalization command; it provides neither true-peak nor LUFS processing. ## Remove DC Offset
## Select audible bounds Remove DC offset subtracts the mean of the selected range from only that range.
This is a constant offset correction, not a high-pass filter. Selection
boundaries may acquire discontinuities; use fades or zero-crossing tools where
appropriate. Finite over-range results are retained; float overflow rejects the
edit.
Select audible bounds finds the first and last sample strictly above the chosen dBFS threshold and pads within the existing selection. It only changes selection; use Trim clip to selection to apply a nondestructive timeline trim, or export the selection. Entirely silent/below-threshold ranges leave selection unchanged. This is boundary detection, not automatic removal of internal gaps or a noise gate. ---
## Snap to zero crossings ## Sample Fades
Snap to zero crossings searches each selected boundary within a source-frame radius up to 65,536. It uses the mono source, chooses earlier positions on equal distance and changes both endpoints only if both searches yield a nonempty range. Source edges qualify only when their adjacent sample is zero. This can reduce boundary discontinuities but does not guarantee click-free output; musical-tick trimming/slicing can move a boundary away from the exact crossing. Sample fades offers four offline curves: linear in/out and equal-power in/out
(sinusoidal amplitude). The entire selection is the fade duration; both
endpoints are included in the curve. Fewer than two frames reject. These curves
change copied sample data and stack with existing playback clip fades after
adoption; they do not form a linked clip crossfade.
## Detect attacks ---
Detect attacks uses absolute sample amplitude, an initial armed state, rearming below one quarter of the threshold and a minimum frame gap between retained attacks. It marks up to 128 source positions in orange; hovering the statistics shows exact frame indices. This is a threshold detector, not spectral onset detection, beat detection or tempo estimation. Selection/processing changes invalidate the markers. Slice clip at attacks splits the current clip reference at those positions in one project undo step and closes the editor. Edits in the local buffer must be saved/relinked first if the slices should use processed audio. Source positions round to nearest musical ticks; out-of-trim, edge and coalesced cuts are skipped. No usable cuts rejects. Left ID, source references and outside fades remain; inner explicit fades clear. Long explicit fades and existing automatic region fades can sound different after splitting. Timeline/source rounding may differ by one sample, so this is not sample-accurate timeline slicing. The no-explicit-fade aligned regression fixture remains bit-identical across slicing. No media files are written by slicing. ## Gain and Normalize
## Validation and timing limits Gain / normalize processes only selected samples. Mode 0 accepts linear gain
0..16; mode 1 accepts a linear peak target above 0 and at most 1, rejecting
silent selections or required gain above 16. Finite values above unity are not
clipped; float overflow rejects atomically. This changes sample data in the
local snapshot, unlike the existing clip-gain normalization command; it provides
neither true-peak nor LUFS processing.
Focused model and GUI checks pass for each operation. ---
The integrated checks add allocation failure/cleanup, oversized sources, overflow,
clip ID rollback, exact selected samples, persistence, no-fade render comparison, ## Select Audible Bounds
plugin fixtures, software JACK, Clang analysis, ASan/UBSan and install layouts.
The actual source editor and attack markers are shown in the Select audible bounds finds the first and last sample strictly above the chosen
dBFS threshold and pads within the existing selection. It only changes
selection; use Trim clip to selection to apply a nondestructive timeline trim,
or export the selection. Entirely silent/below-threshold ranges leave selection
unchanged. This is boundary detection, not automatic removal of internal gaps or
a noise gate.
---
## Snap to Zero Crossings
Snap to zero crossings searches each selected boundary within a source-frame
radius up to 65,536. It uses the mono source, chooses earlier positions on equal
distance and changes both endpoints only if both searches yield a nonempty
range. Source edges qualify only when their adjacent sample is zero. This can
reduce boundary discontinuities but does not guarantee click-free output;
musical-tick trimming/slicing can move a boundary away from the exact crossing.
---
## Detect Attacks
Detect attacks uses absolute sample amplitude, an initial armed state, rearming
below one quarter of the threshold and a minimum frame gap between retained
attacks. It marks up to 128 source positions in orange; hovering the statistics
shows exact frame indices. This is a threshold detector, not spectral onset
detection, beat detection or tempo estimation. Selection/processing changes
invalidate the markers. Slice clip at attacks splits the current clip reference
at those positions in one project undo step and closes the editor. Edits in the
local buffer must be saved/relinked first if the slices should use processed
audio. Source positions round to nearest musical ticks; out-of-trim, edge and
coalesced cuts are skipped. No usable cuts rejects. Left ID, source references
and outside fades remain; inner explicit fades clear. Long explicit fades and
existing automatic region fades can sound different after splitting.
Timeline/source rounding may differ by one sample, so this is not
sample-accurate timeline slicing. The no-explicit-fade aligned regression
fixture remains bit-identical across slicing. No media files are written by
slicing.
---
## Validation and Timing Limits
Focused model and GUI checks pass for each operation. The integrated checks add
allocation failure/cleanup, oversized sources, overflow, clip ID rollback, exact
selected samples, persistence, no-fade render comparison, plugin fixtures,
software JACK, Clang analysis, ASan/UBSan and install layouts. The actual source
editor and attack markers are shown in the
[screenshot gallery](../screenshots/README.md). [screenshot gallery](../screenshots/README.md).
Trim placement preserves alignment in musical ticks; independent source/timeline Trim placement preserves alignment in musical ticks; independent source/timeline
@ -69,5 +149,5 @@ click-free transitions are promised. Clip source data outside a local processing
selection remains intact; exported selections contain only their chosen frames. selection remains intact; exported selections contain only their chosen frames.
Closing the editor discards unsaved local processing. Successful project Undo Closing the editor discards unsaved local processing. Successful project Undo
keeps generated files for redo/history; only failed adoption removes its owned keeps generated files for redo/history; only failed adoption removes its owned
new output. Rejecting sources above the editor's frame limit may temporarily new output. Rejecting sources above the editor's frame limit may temporarily use
use the existing WAV reader's larger decode bound before rejecting them. the existing WAV reader's larger decode bound before rejecting them.

View file

@ -1,128 +1,142 @@
# Audio engine requirements # Audio Engine Requirements
## Real-time callback rules ## Real-Time Callback Rules
The callback must not allocate/free memory, lock blocking mutexes, perform The callback must not allocate/free memory, lock blocking mutexes, perform
filesystem/network/console I/O, log, load plugins or samples, call GUI APIs, filesystem/network/console I/O, log, load plugins or samples, call GUI APIs, or
or wait for another thread. wait for another thread.
Use preallocated buffers and bounded work. Publish parameter and graph changes Use preallocated buffers and bounded work. Publish parameter and graph changes
through a documented real-time-safe mechanism. Verify atomics are lock-free through a documented real-time-safe mechanism. Verify atomics are lock-free
where required on the target platform. where required on the target platform.
## Processing model ---
## Processing Model
Process a bounded number of frames per block. Do not assume a fixed block size 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 unless the backend guarantees it. Define maximum frame and event counts and safe
safe behavior when those limits are reached. behavior when those limits are reached.
The initial milestone is offline processing, then a test oscillator -> gain -> The initial milestone is offline processing, then a test oscillator -> gain ->
meter/output graph, then a hardware backend. meter/output graph, then a hardware backend.
## Clock and transport ---
## Clock and Transport
Audio sample position is the authoritative playback clock. Use integer or Audio sample position is the authoritative playback clock. Use integer or
rational timeline representations to avoid cumulative floating-point drift. rational timeline representations to avoid cumulative floating-point drift.
Define tempo-map conversion and behavior at tempo changes before implementing Define tempo-map conversion and behavior at tempo changes before implementing
tempo automation. tempo automation.
---
## Offline API ## Offline API
`xtc_graph_create` accepts 1..256 nodes; IDs are monotonic stable identifiers, never reused `xtc_graph_create` accepts 1..256 nodes; IDs are monotonic stable identifiers,
within a graph's lifetime. Enumeration indexes are dense and distinct from IDs. Supported nodes are gain (any finite multiplier), sine never reused within a graph's lifetime. Enumeration indexes are dense and
distinct from IDs. Supported nodes are gain (any finite multiplier), sine
oscillator (finite nonnegative Hz), pass-through peak meter and audio output. 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 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 output nodes have no output. Port lookup reports direction, signal type and
channel count. MIDI/control ports and channel conversion are not implemented; channel count. MIDI/control ports and channel conversion are not implemented;
invalid ports/directions, duplicate edges and cycles are rejected before an invalid ports/directions, duplicate edges and cycles are rejected before an edit
edit becomes visible. Fan-out copies a source to consumers; fan-in sums inputs becomes visible. Fan-out copies a source to consumers; fan-in sums inputs
without normalization. Failed additions leave IDs and graph contents unchanged. without normalization. Failed additions leave IDs and graph contents unchanged.
`xtc_graph_compile(graph, sample_rate, max_frames, &plan)` runs on the control `xtc_graph_compile(graph, sample_rate, max_frames, &plan)` runs on the control
thread. Empty graphs compile to silence. Sample rate must be finite and positive, thread. Empty graphs compile to silence. Sample rate must be finite and
oscillators must be at or below Nyquist, and max_frames is 1..65536. Compilation positive, oscillators must be at or below Nyquist, and max_frames is 1..65536.
allocates and copies topology/parameters, builds a topological schedule and Compilation allocates and copies topology/parameters, builds a topological
input adjacency, and allocates paired max_frames float buffers per node for stereo. The schedule and input adjacency, and allocates paired max_frames float buffers per
maximum buffer storage is 128 MiB per plan (32 MiB for the GUI 64-node limit). Compilation may fail with an node for stereo. The maximum buffer storage is 128 MiB per plan (32 MiB for the
allocation error; output plan is then NULL. Destroy a plan on the control thread. GUI 64-node limit). 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, `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 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 return capacity errors without modifying output or state. Output storage must
hold frame_count floats. NULL input means silence; input/output may alias. hold frame_count floats. NULL input means silence; input/output may alias. Zero
Zero frames leaves all state unchanged. No plan operations may run concurrently frames leaves all state unchanged. No plan operations may run concurrently with
with processing, including reset, meter reads or destruction. processing, including reset, meter reads or destruction.
Unconnected non-oscillator nodes receive external input. Connected inputs Unconnected non-oscillator nodes receive external input. Connected inputs
receive only the sum of their upstream buffers. Explicit output nodes are receive only the sum of their upstream buffers. Explicit output nodes are summed
summed to the caller's output. If none exist, terminal nodes are summed for to the caller's output. If none exist, terminal nodes are summed for
compatibility with the original gain-chain API, unless compatibility with the original gain-chain API, unless
`xtc_graph_require_output_nodes(graph, 1)` is set. The GUI uses that policy; `xtc_graph_require_output_nodes(graph, 1)` is set. The GUI uses that policy; no
no output nodes means silence. Disconnected components cannot output nodes means silence. Disconnected components cannot contribute when
contribute when explicit outputs exist, but they are still processed. Floating explicit outputs exist, but they are still processed. Floating point overflow
point overflow and nonfinite input become silence per sample. No clipping or and nonfinite input become silence per sample. No clipping or normalization is
normalization is applied to finite samples. applied to finite samples.
Oscillators start at phase zero and carry phase continuously across blocks; Oscillators start at phase zero and carry phase continuously across blocks;
reset clears phases and meter peaks. Meter reads return the last nonempty 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 block's absolute peak. These are same-thread reads, not a thread-safe GUI meter
meter bridge. Rendering is deterministic on the same build/platform regardless bridge. Rendering is deterministic on the same build/platform regardless of
of block partitioning; libm results need not be bit-identical across platforms. block partitioning; libm results need not be bit-identical across platforms.
`xtc_graph_process_offline` remains a convenience helper, compiling at 48000 Hz `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 and destroying the plan after one block. It accepts a finite external-input gain
gain and requires non-NULL input/output. It now follows routing rather than 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 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. 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 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 meter output, reset and partition equivalence, fan-out/fan-in, reverse insertion
order, disconnected nodes, in-place processing, nonfinite input, invalid ports, order, disconnected nodes, in-place processing, nonfinite input, invalid ports,
cycles, capacity limits, plan lifetime and repeated compilation/cleanup. cycles, capacity limits, plan lifetime and repeated compilation/cleanup. The
The offline API remains single-owner. The realtime/backend APIs below add offline API remains single-owner. The realtime/backend APIs below add
publication and JACK processing; offline tests alone do not qualify hardware. publication and JACK processing; offline tests alone do not qualify hardware.
## Backend and realtime bridge ---
## Backend and Realtime Bridge
Use `xtc_backend.h` on one serialized control thread: Use `xtc_backend.h` on one serialized control thread:
1. Open a config with `XTC_BACKEND_JACK`, a client name, channel_count 1 (or 2 for stereo output), and 1. Open a config with `XTC_BACKEND_JACK`, a client name, channel_count 1 (or 2
max_frames 1..65536. It must accommodate the server's current buffer size. for stereo output), and max_frames 1..65536. It must accommodate the server's
The actual rate and buffer size are returned through get_status. Open does current buffer size. The actual rate and buffer size are returned through
not start the server or acquire devices directly. Invalid config returns get_status. Open does not start the server or acquire devices directly.
invalid argument, missing build support returns unsupported, and JACK setup Invalid config returns invalid argument, missing build support returns
failures return I/O error. If cleanup itself fails, a non-NULL output handle unsupported, and JACK setup failures return I/O error. If cleanup itself
is preserved for close retry; no other operation is allowed on that handle. fails, a non-NULL output handle is preserved for close retry; no other
2. Submit a graph (copied/compiled at the server rate), then start. Starting operation is allowed on that handle.
an already running client is an invalid-state error; stopping a stopped 2. Submit a graph (copied/compiled at the server rate), then start. Starting an
client succeeds. With no submitted graph, callbacks produce silence. 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 3. Enumerate available audio ports with visit_ports. Names are borrowed only
during the visitor; do not reenter backend operations from the visitor. 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 Connect the client's input to a remote output or its output to a remote input
input explicitly. JACK maps device selection to server/device port routes; explicitly. JACK maps device selection to server/device port routes; ALSA
ALSA device identifiers are not accepted here. The server controls rate, device identifiers are not accepted here. The server controls rate, period
period and hardware configuration outside this API. Names/ports can disappear and hardware configuration outside this API. Names/ports can disappear
between enumeration and connection; route failures are returned as I/O errors. between enumeration and connection; route failures are returned as I/O
errors.
4. During playback, submit edited graphs and periodically collect retired 4. During playback, submit edited graphs and periodically collect retired
resources. Capacity means one pending graph is still awaiting adoption; resources. Capacity means one pending graph is still awaiting adoption; keep
keep the current graph and retry later. Stopped submissions instead compile the current graph and retry later. Stopped submissions instead compile a
a replacement engine off-thread and swap only after success, while callbacks replacement engine off-thread and swap only after success, while callbacks
are quiescent. Its block/revision counters restart and oscillator phase resets; are quiescent. Its block/revision counters restart and oscillator phase
no edit is blocked behind a plan that cannot be adopted until playback. get_status is a nonblocking snapshot resets; no edit is blocked behind a plan that cannot be adopted until
of rate, frames, running/server-loss/rate-change flags, xruns, processed playback. get_status is a nonblocking snapshot of rate, frames,
blocks and last process result. No active DSP memory is exposed. 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. 5. Stop pauses the retained DSP state after deactivation. Restart resumes it.
Close removes the JACK client, synchronizes all callbacks, then destroys Close removes the JACK client, synchronizes all callbacks, then destroys
plans. Stop/close failures preserve the handle for recovery/retry. No plan plans. Stop/close failures preserve the handle for recovery/retry. No plan is
is freed on the audio thread. Sample-rate change or server loss requires freed on the audio thread. Sample-rate change or server loss requires
close/reopen and a fresh submission; there is no automatic reconnect. close/reopen and a fresh submission; there is no automatic reconnect.
Buffers can change size. In-limit changes process normally; oversized blocks Buffers can change size. In-limit changes process normally; oversized blocks are
are silent and report capacity errors until the size is supported again. silent and report capacity errors until the size is supported again. Rate
Rate changes latch silence and invalid-state processing results. A missing input changes latch silence and invalid-state processing results. A missing input
buffer is treated as unavailable input (silence). A missing output buffer is 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; reported as an I/O process error. Shutdown callbacks only set the loss flag; the
the control thread closes the client. No JACK or GUI types cross the C API. 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 The standalone `xtc_realtime.h` API allows hardware-independent consumers and
stress tests. Submit compiles on the producer; process adopts at a block stress tests. Submit compiles on the producer; process adopts at a block
@ -134,12 +148,15 @@ JACK notification callbacks were audited for prohibited operations; JACK's own
internal realtime guarantees depend on the installed implementation/server. internal realtime guarantees depend on the installed implementation/server.
Validation commands and results are recorded in BUILD.md and the integration Validation commands and results are recorded in BUILD.md and the integration
test README. Current limitations: mono input, no crossfaded graph changes, no automatic reconnect, test README. Current limitations: mono input, no crossfaded graph changes, no
no physical-device or long-duration endurance qualification. The FLTK shell is automatic reconnect, no physical-device or long-duration endurance
connected to backend controls and status; see GUI.md. Its editors remain qualification. The FLTK shell is connected to backend controls and status; see
placeholders except the modular editor described below. GUI.md. Its editors remain placeholders except the modular editor described
below.
## Graph snapshots and edits ---
## Graph Snapshots and Edits
The control-thread API exposes copies through get_counts, get_node(index) and The control-thread API exposes copies through get_counts, get_node(index) and
get_connection(index), including node IDs/types/values and port endpoints. No get_connection(index), including node IDs/types/values and port endpoints. No
@ -154,17 +171,17 @@ and returns a caller-owned candidate on success. On failure candidate is NULL,
source and added_id are unchanged. added_id is optional and written only for source and added_id are unchanged. added_id is optional and written only for
ADD_NODE. ADD_NODE validates type and the used parameter (finite gain, or finite ADD_NODE. ADD_NODE validates type and the used parameter (finite gain, or finite
nonnegative oscillator Hz). Meter/output do not use the value field. Compile nonnegative oscillator Hz). Meter/output do not use the value field. Compile
additionally checks the oscillator against the actual sample rate. Removing additionally checks the oscillator against the actual sample rate. Removing a
a node removes its incident connections; disconnecting a nonexistent edge node removes its incident connections; disconnecting a nonexistent edge returns
returns not-found. Missing nodes/ports, reversed directions, duplicate edges, not-found. Missing nodes/ports, reversed directions, duplicate edges, cycles and
cycles and unknown command/type values are rejected without visible edits. unknown command/type values are rejected without visible edits.
The GUI keeps a 64-node graph requiring explicit output nodes. It creates a The GUI keeps a 64-node graph requiring explicit output nodes. It creates a
candidate, submits that exact candidate to the backend, then swaps its owned candidate, submits that exact candidate to the backend, then swaps its owned
graph and refreshes the canvas only if submission succeeds. Failed compile or graph and refreshes the canvas only if submission succeeds. Failed compile or
pending-slot pressure destroys the candidate and retains both the displayed pending-slot pressure destroys the candidate and retains both the displayed
graph and previously submitted engine plan. When disconnected at startup, graph and previously submitted engine plan. When disconnected at startup, edits
edits can be stored locally and are submitted on reconnect. Lost/changed active can be stored locally and are submitted on reconnect. Lost/changed active
backends reject edits until reconnect. Reconnect submits the retained graph. backends reject edits until reconnect. Reconnect submits the retained graph.
Submitted/active revision counters expose adoption without exposing plan Submitted/active revision counters expose adoption without exposing plan
@ -181,20 +198,21 @@ silent under the GUI policy. Terminal fallback remains the default for earlier
offline/standalone callers. Input on unconnected nodes retains the existing offline/standalone callers. Input on unconnected nodes retains the existing
external-input contract; the GUI does not connect external JACK ports. external-input contract; the GUI does not connect external JACK ports.
---
## Transport ## Transport
Backend status includes a uint64 sample_position. Each successful processing Backend status includes a uint64 sample_position. Each successful processing
callback advances it by its exact frame count (including silence); errors do callback advances it by its exact frame count (including silence); errors do not
not advance it. The counter uses compiler atomics, verified always lock-free advance it. The counter uses compiler atomics, verified always lock-free before
before backend setup, with overflow detected rather than wrapped. No extra backend setup, with overflow detected rather than wrapped. No extra allocation,
allocation, I/O, mutex or GUI work occurs in callbacks. Fields remain individually I/O, mutex or GUI work occurs in callbacks. Fields remain individually coherent
coherent snapshots. snapshots.
Stop pauses the clock. Start resumes; stopped and running graph submissions Stop pauses the clock. Start resumes; stopped and running graph submissions
preserve position even when plan/revision counters reset. Reopen starts at zero. preserve position even when plan/revision counters reset. Reopen starts at zero.
xtc_backend_seek changes position only on a healthy stopped backend; it does xtc_backend_seek changes position only on a healthy stopped backend; it does not
not rewind oscillator phase. Project tick/sample conversions use integer floor rewind oscillator phase. Project tick/sample conversions use integer floor
arithmetic at constant tempo, with explicit capacity errors and rates 1..768000. arithmetic at constant tempo, with explicit capacity errors and rates 1..768000.
The GUI observes the counter every 100 ms, so visual updates are block-quantized The GUI observes the counter every 100 ms, so visual updates are block-quantized
and delayed by polling. No external JACK transport synchronization is provided. and delayed by polling. No external JACK transport synchronization is provided.
@ -203,8 +221,9 @@ Silent clip metadata is control-thread data, never traversed by the callback.
Audio/MIDI scheduling uses compiled event/clip plans rather than direct callback Audio/MIDI scheduling uses compiled event/clip plans rather than direct callback
access to the editable project. access to the editable project.
---
## Audio source and recording ## Audio Source and Recording
xtc_audio.h provides control-thread import and immutable preloaded clip plans. xtc_audio.h provides control-thread import and immutable preloaded clip plans.
The backend prepares a project only while stopped: build audio candidate, The backend prepares a project only while stopped: build audio candidate,
@ -214,31 +233,33 @@ assets produce explicit issue metadata and silence; OOM/capacity fails.
Graph-only edits still use the existing pending/retired mechanism while running. Graph-only edits still use the existing pending/retired mechanism while running.
Each callback renders the plan into a preallocated mono timeline buffer using Each callback renders the plan into a preallocated mono timeline buffer using
the authoritative sample position. xtc_realtime_process_audio passes that the authoritative sample position. xtc_realtime_process_audio passes that buffer
buffer into the compiled graph; XTC_NODE_AUDIO (output port only) reads it, into the compiled graph; XTC_NODE_AUDIO (output port only) reads it,
independently from external JACK input. The old process APIs remain wrappers independently from external JACK input. The old process APIs remain wrappers
with a silent timeline. Import creates a source/output route if necessary. with a silent timeline. Import creates a source/output route if necessary. No
No samples or GUI/project arrays are shared with editable state. Clip mixing, samples or GUI/project arrays are shared with editable state. Clip mixing, rate
rate conversion and graph processing are bounded; see AUDIO_FILES.md for limits. conversion and graph processing are bounded; see AUDIO_FILES.md for limits.
A borrowed recorder can be attached/detached only while stopped. Raw JACK input A borrowed recorder can be attached/detached only while stopped. Raw JACK input
is copied to its SPSC ring before graph processing. Producer and consumer index is copied to its SPSC ring before graph processing. Producer and consumer index
release/acquire operations protect payload ownership; all atomics are checked release/acquire operations protect payload ownership; all atomics are checked
always lock-free. The control thread drains to a temporary float32 WAV, then always lock-free. The control thread drains to a temporary float32 WAV, then
finalizes only after successful stop/close has synchronized the callback. finalizes only after successful stop/close has synchronized the callback. An
An overflowing ring or unavailable input creates a sticky error; later input is overflowing ring or unavailable input creates a sticky error; later input is
dropped, reported and never silently joined into a continuous-looking take. dropped, reported and never silently joined into a continuous-looking take. A
A partial overflow take is finalized for manual recovery. File errors retain partial overflow take is finalized for manual recovery. File errors retain the
the previous destination and remove unfinished temporaries. previous destination and remove unfinished temporaries.
GUI recording uses a two-second ring and 100 ms control-thread draining, with GUI recording uses a two-second ring and 100 ms control-thread draining, with no
no worker thread. A GUI stall can overflow it. No callback allocation/free, file worker thread. A GUI stall can overflow it. No callback allocation/free, file
I/O, logging, locks or FLTK calls were added. Backend stop failure preserves the I/O, logging, locks or FLTK calls were added. Backend stop failure preserves the
borrowed recorder; the caller must not free it before successful quiescence. borrowed recorder; the caller must not free it before successful quiescence.
Live software source/capture acceptance and concurrent ring tests pass; physical Live software source/capture acceptance and concurrent ring tests pass; physical
recording, latency compensation and disk endurance remain unverified. recording, latency compensation and disk endurance remain unverified.
## MIDI source ---
## MIDI Source
The C MIDI plan prepares bounded source-relative notes into sample spans and The C MIDI plan prepares bounded source-relative notes into sample spans and
renders a separate instrument buffer. `xtc_plan_process_sources` and renders a separate instrument buffer. `xtc_plan_process_sources` and
@ -247,10 +268,12 @@ instrument input; older wrappers supply silence for omitted sources. Graph
instrument nodes consume the shared mono instrument buffer. The backend owns instrument nodes consume the shared mono instrument buffer. The backend owns
prepared MIDI runtime state exclusively while active, with lock-free panic prepared MIDI runtime state exclusively while active, with lock-free panic
requests and no callback allocation/I/O. Stop/seek clear live voices; scheduled 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, notes chase their absolute phase. See MIDI.md for event ordering, panic
capacities and test coverage. No new dependency is required. semantics, capacities and test coverage. No new dependency is required.
## Stereo mixing and automation ---
## Stereo Mixing and Automation
Track settings and automation are copied into immutable source-plan snapshots. Track settings and automation are copied into immutable source-plan snapshots.
`xtc_audio_plan_render_stereo` and `xtc_midi_plan_render_stereo` pan track audio `xtc_audio_plan_render_stereo` and `xtc_midi_plan_render_stereo` pan track audio
@ -260,11 +283,13 @@ retain gain/mute/solo/automation but ignore pan. `xtc_plan_process_stereo` and
with one oscillator phase advance, preserving mono APIs and graph publication. with one oscillator phase advance, preserving mono APIs and graph publication.
The backend preallocates paired buffers, registers an optional output_right port The backend preallocates paired buffers, registers an optional output_right port
and publishes independently atomic output peak snapshots. Callback work remains and publishes independently atomic output peak snapshots. Callback work remains
bounded and allocation/I/O/lock free. Meter UI reads no active plan memory. bounded and allocation/I/O/lock free. Meter UI reads no active plan memory. See
See MIXER.md for exact pan law, automation interpolation, event/source exceptions, MIXER.md for exact pan law, automation interpolation, event/source exceptions,
memory/history bounds and verification. Input/recording remain mono. memory/history bounds and verification. Input/recording remain mono.
## Native processing ---
## Native Processing
Graph-native effects/sources use copied versioned descriptors, two independently Graph-native effects/sources use copied versioned descriptors, two independently
prepared mono instances and the existing plan ownership/retirement protocol. prepared mono instances and the existing plan ownership/retirement protocol.
@ -274,19 +299,24 @@ without rolling back already advanced DSP. Native event instruments can render
explicitly into graph instrument buffers. See NATIVE_API.md for exact lifecycle explicitly into graph instrument buffers. See NATIVE_API.md for exact lifecycle
and serialization limits and PERFORMANCE.md for measured workload and budgets. and serialization limits and PERFORMANCE.md for measured workload and budgets.
## Ownership audit ---
## Ownership Audit
[RELIABILITY.md](RELIABILITY.md) records resource owners, publication and [RELIABILITY.md](RELIABILITY.md) records resource owners, publication and
reclamation ordering, callback work bounds and repeatable diagnostic tests. reclamation ordering, callback work bounds and repeatable diagnostic tests.
Graph stereo rendering completes both channels before writing caller outputs; Graph stereo rendering completes both channels before writing caller outputs;
either output may equal a source, but the output buffers must not overlap. either output may equal a source, but the output buffers must not overlap.
## Quality policy ---
[AUDIO_QUALITY.md](AUDIO_QUALITY.md) defines absolute musical timing, tempo snapshot ## Quality Policy
changes, scheduled source envelopes and configurable master transitions. The GUI
enables 5 ms fades; low-level defaults remain raw. `xtc_backend_request_stop` is [AUDIO_QUALITY.md](AUDIO_QUALITY.md) defines absolute musical timing, tempo
asynchronous and does not establish quiescence; control-thread collection after snapshot changes, scheduled source envelopes and configurable master
its callback acknowledgement deactivates JACK. Immediate stop/close remain transitions. The GUI enables 5 ms fades; low-level defaults remain raw.
available. Sample rate and buffer frames are reported, but end-to-end latency `xtc_backend_request_stop` is asynchronous and does not establish quiescence;
and native-node delay compensation are not implemented. control-thread collection after its callback acknowledgement deactivates JACK.
Immediate stop/close remain available. Sample rate and buffer frames are
reported, but end-to-end latency and native-node delay compensation are not
implemented.

View file

@ -1,12 +1,12 @@
# Audio clips and recording # Audio Clips and Recording
## Import and playback ## Import and Playback
File/Import WAV (Ctrl+I) loads a WAV into the selected track at the playhead. File/Import WAV (Ctrl+I) loads a WAV into the selected track at the playhead.
With no selected track it creates an Audio track. Add region still creates a With no selected track it creates an Audio track. Add region still creates a
silent placeholder. Imported clips use the existing move/trim controls. silent placeholder. Imported clips use the existing move/trim controls. Import
Import stops playback, validates a candidate project, and commits only after stops playback, validates a candidate project, and commits only after successful
successful backend preparation (or locally when disconnected). backend preparation (or locally when disconnected).
Supported input: little-endian RIFF/WAVE, PCM16/24/32 and IEEE float32, one or Supported input: little-endian RIFF/WAVE, PCM16/24/32 and IEEE float32, one or
two channels, 1..768000 Hz. Stereo is averaged to mono. Compressed WAV, RF64, two channels, 1..768000 Hz. Stereo is averaged to mono. Compressed WAV, RF64,
@ -15,23 +15,22 @@ unsupported. Nonfinite float data and malformed/truncated files are rejected.
There is no new dependency: the bounded C decoder/writer is covered by known There is no new dependency: the bounded C decoder/writer is covered by known
sample, truncation and invalid-codec tests. See decision 0005. sample, truncation and invalid-codec tests. See decision 0005.
Audio is preloaded before playback: maximum 64 distinct paths per plan, Audio is preloaded before playback: maximum 64 distinct paths per plan, 16777216
16777216 mono frames per asset (64 MiB), 67108864 total frames (256 MiB). mono frames per asset (64 MiB), 67108864 total frames (256 MiB). Import rejects
Import rejects files over 256 MiB or over 4096 chunks. No streaming decoder, files over 256 MiB or over 4096 chunks. No streaming decoder, waveform cache,
waveform cache, peak drawing or background load worker exists yet. Preloading peak drawing or background load worker exists yet. Preloading can briefly block
can briefly block the GUI. Candidate compilation retains the old plan until the GUI. Candidate compilation retains the old plan until success; temporary
success; temporary build memory can include both old and candidate buffers. build memory can include both old and candidate buffers.
The callback renders only overlapping clip ranges from memory. Clips on all The callback renders only overlapping clip ranges from memory. Clips on all
tracks are summed through their mix/automation settings into the graph's tracks are summed through their mix/automation settings into the graph's **Audio
**Audio clips** source node. clips** source node. Import creates that source and connects it to an
Import creates that source and connects it to an existing/new output if no existing/new output if no audio source exists. Subsequent routing is explicit:
audio source exists. Subsequent routing is explicit: disconnect it or route it disconnect it or route it through gain/meter nodes in Modular Graph. Removing
through gain/meter nodes in Modular Graph. Removing the output silences editor the output silences editor graphs. This source does not monitor JACK input;
graphs. This source does not monitor JACK input; unconnected ordinary graph unconnected ordinary graph inputs retain their earlier external-input behavior.
inputs retain their earlier external-input behavior. Track faders/pan/mute Track faders/pan/mute are implemented by the track mixer (see MIXER.md);
are implemented by the track mixer (see MIXER.md); individual individual track graph routing remains deferred.
track graph routing remains deferred.
Linear interpolation converts source sample rate to backend rate. There is no Linear interpolation converts source sample rate to backend rate. There is no
antialiasing filter, time stretching, looping or fades. Musical durations round antialiasing filter, time stretching, looping or fades. Musical durations round
@ -45,7 +44,9 @@ Stop pauses the sample clock. Graph-only edits can still be submitted during
playback. Graph recompilation can reset oscillator phase as before. Playback playback. Graph recompilation can reset oscillator phase as before. Playback
does not stop automatically at the arrangement end. does not stop automatically at the arrangement end.
## Assets and recovery ---
## Assets and Recovery
Version 2 projects store external absolute canonical paths, not sample data. Version 2 projects store external absolute canonical paths, not sample data.
Save the project AND keep its referenced WAV files. Save As changes only the Save the project AND keep its referenced WAV files. Save As changes only the
@ -56,20 +57,24 @@ removed; the next preparation detects it. Unreadable, malformed and unsupported
assets are also reported, rather than substituted with unrelated files. assets are also reported, rather than substituted with unrelated files.
Capacity/OOM is a preparation failure and retains the old plans. Capacity/OOM is a preparation failure and retains the old plans.
Select the affected clip, then File/Relink selected audio to choose a replacement. Select the affected clip, then File/Relink selected audio to choose a
Relinking retains its original timeline/source bounds. The replacement is replacement. Relinking retains its original timeline/source bounds. The
validated on the next Play; a shorter replacement pads beyond its end with replacement is validated on the next Play; a shorter replacement pads beyond its
silence. Saving retains unresolved references for later repair. Missing assets end with silence. Saving retains unresolved references for later repair. Missing
are also checked on Open without a working backend, using a temporary plan. assets are also checked on Open without a working backend, using a temporary
Automatic asset search, content hashes and portable project bundles are deferred. plan. Automatic asset search, content hashes and portable project bundles are
deferred.
---
## Recording ## Recording
Record (or Audio/Record WAV, Shift+F5) opens a destination chooser, stops playback, Record (or Audio/Record WAV, Shift+F5) opens a destination chooser, stops
prepares the project, opens a temporary mono float32 WAV and starts JACK. playback, prepares the project, opens a temporary mono float32 WAV and starts
Connect a source to xtc's JACK input using an external patchbay; no physical or JACK. Connect a source to xtc's JACK input using an external patchbay; no
software route is created automatically. Without a route the take contains physical or software route is created automatically. Without a route the take
silence. Recording captures raw mono JACK input, not the output/effect mix. contains silence. Recording captures raw mono JACK input, not the output/effect
mix.
A preallocated two-second SPSC ring transfers complete callback blocks to the A preallocated two-second SPSC ring transfers complete callback blocks to the
GUI/control thread, which drains it every 100 ms. The producer uses verified GUI/control thread, which drains it every 100 ms. The producer uses verified
@ -80,53 +85,59 @@ error rather than hiding gaps or compressing time.
Stop first synchronizes the callback, detaches the recorder, drains remaining Stop first synchronizes the callback, detaches the recorder, drains remaining
samples, writes the final WAV header, flushes/fsyncs/closes and renames the samples, writes the final WAV header, flushes/fsyncs/closes and renames the
same-directory temporary using an atomic no-clobber link; an existing destination is refused. A successful nonempty take is same-directory temporary using an atomic no-clobber link; an existing
imported at its capture position on the selected track (or a new Recording destination is refused. A successful nonempty take is imported at its capture
track), then the document becomes unsaved. Save the project after Stop. Seeking position on the selected track (or a new Recording track), then the document
back to the take and pressing Play auditions it. Recording continues existing becomes unsaved. Save the project after Stop. Seeking back to the take and
arrangement playback, but there is no automatic input monitoring, punch, loop, pressing Play auditions it. Recording continues existing arrangement playback,
latency compensation, multitrack arming or overdub alignment compensation. but there is no automatic input monitoring, punch, loop, latency compensation,
multitrack arming or overdub alignment compensation.
Graph/arrangement edits, project file actions, reconnect and close are blocked Graph/arrangement edits, project file actions, reconnect and close are blocked
until Stop finishes the take. Deactivation failure retains the recorder for a until Stop finishes the take. Deactivation failure retains the recorder for a
safe retry. On overflow a finalized partial WAV is retained for manual recovery safe retry. On overflow a finalized partial WAV is retained for manual recovery
and is not automatically added. On import/preparation failure the completed and is not automatically added. On import/preparation failure the completed take
take remains at its chosen path with an explicit recovery message. I/O errors remains at its chosen path with an explicit recovery message. I/O errors are
are reported; failed temporary data is retained and its recovery path is reported. Recordings reported; failed temporary data is retained and its recovery path is reported.
are RIFF-limited to 1073741814 frames, but the smaller playback/import asset Recordings are RIFF-limited to 1073741814 frames, but the smaller
limit still applies: longer takes may need external splitting before import. playback/import asset limit still applies: longer takes may need external
There is no crash-recovery journal. The file is mode 0600; parent-directory splitting before import. There is no crash-recovery journal. The file is mode
fsync and preservation of previous permissions are not implemented. 0600; parent-directory fsync and preservation of previous permissions are not
implemented.
---
## Verification ## Verification
make test includes deterministic WAV/graph samples, resampling/overlap/trim, make test includes deterministic WAV/graph samples, resampling/overlap/trim,
malformed inputs, missing/relinked assets, SPSC concurrency/overflow/cleanup, malformed inputs, missing/relinked assets, SPSC concurrency/overflow/cleanup,
and record -> save/reopen -> exact playback through the backend callback. and record -> save/reopen -> exact playback through the backend callback. make
make test-gui covers the workflow and rollback with a fake backend. test-gui covers the workflow and rollback with a fake backend. make
make test-gui-jack records/reopens a silent take using real JACK callbacks. test-gui-jack records/reopens a silent take using real JACK callbacks. make
make test-jack-audio routes only two software clients: known source -> recorded test-jack-audio routes only two software clients: known source -> recorded WAV
WAV -> saved/reopened project -> graph playback -> capture assertion. -> saved/reopened project -> graph playback -> capture assertion. Physical
Physical input/output, device latency, slow-disk endurance and server loss input/output, device latency, slow-disk endurance and server loss during a real
during a real recording remain unverified. recording remain unverified.
## Recording protection ---
## Recording Protection
The default recorder and GUI require a new destination, including rejecting The default recorder and GUI require a new destination, including rejecting
symlinks. Publication also refuses files created during recording. The explicit symlinks. Publication also refuses files created during recording. The explicit
`xtc_recorder_create_replace` API retains authorized replacement for callers `xtc_recorder_create_replace` API retains authorized replacement for callers
that deliberately need it; the GUI never uses it. After a failed finish, copy that deliberately need it; the GUI never uses it. After a failed finish, copy
`xtc_recorder_recovery_path` before destruction. Disk-write/finalization failures `xtc_recorder_recovery_path` before destruction. Disk-write/finalization
retain the temporary. Destruction after producer quiescence attempts to drain failures retain the temporary. Destruction after producer quiescence attempts to
and finalize accepted data; normal code should call finish and inspect errors. drain and finalize accepted data; normal code should call finish and inspect
The GUI refuses closure while recording and prints the retained/finalized path errors. The GUI refuses closure while recording and prints the
on exceptional destruction. retained/finalized path on exceptional destruction.
Use `xtc-check --recover-recording TEMP NEW.wav` to copy complete float32 frames Use `xtc-check --recover-recording TEMP NEW.wav` to copy complete float32 frames
from an interrupted xtc WAV into a new WAV. Source and existing destinations are from an interrupted xtc WAV into a new WAV. Source and existing destinations are
never modified. A trailing partial frame is ignored. This handles xtc's canonical never modified. A trailing partial frame is ignored. This handles xtc's
44-byte mono float32 header only, not arbitrary damaged WAVs. Recovery is explicit canonical 44-byte mono float32 header only, not arbitrary damaged WAVs. Recovery
and control-thread only; no orphan scan runs during playback. is explicit and control-thread only; no orphan scan runs during playback.
`make test-recording` simulates 30 minutes at 8 kHz with repeated ring wrapping `make test-recording` simulates 30 minutes at 8 kHz with repeated ring wrapping
and draining, destination races/symlinks, a child-local disk-size limit, prefix and draining, destination races/symlinks, a child-local disk-size limit, prefix
@ -138,20 +149,22 @@ player still limits each asset to 16M frames. Longer valid takes may require
external splitting before import. No directory fsync/power-loss guarantee is external splitting before import. No directory fsync/power-loss guarantee is
claimed. WAV destinations are chosen explicitly by the user. claimed. WAV destinations are chosen explicitly by the user.
## Non-destructive clip editing ---
## Non-Destructive Clip Editing
Move clips by dragging their body; drag the left/right edge to trim within the Move clips by dragging their body; drag the left/right edge to trim within the
existing source length. Ctrl+D duplicates adjacent to the selected clip. Delete existing source length. Ctrl+D duplicates adjacent to the selected clip. Delete
removes the reference. With an audio clip selected, click the ruler while stopped removes the reference. With an audio clip selected, click the ruler while
to position the cursor strictly inside it, then choose **Edit / Split audio at stopped to position the cursor strictly inside it, then choose **Edit / Split
cursor**, press S with timeline focus, or use the clip context menu. The right audio at cursor**, press S with timeline focus, or use the clip context menu.
piece becomes selected. The four-pixel minimum draw width is also selectable for The right piece becomes selected. The four-pixel minimum draw width is also
very short clips; zoom in for precise edge gestures. selectable for very short clips; zoom in for precise edge gestures.
**Edit / Audio clip gain and fades** (A with timeline focus, also in the context **Edit / Audio clip gain and fades** (A with timeline focus, also in the context
menu) takes three numbers: linear gain 0..4, fade-in ticks, fade-out ticks. menu) takes three numbers: linear gain 0..4, fade-in ticks, fade-out ticks.
Defaults are `1 0 0`; 0 fade disables that explicit fade. Each duration can be up Defaults are `1 0 0`; 0 fade disables that explicit fade. Each duration can be
to the original source length, even after trimming; overlapping long fades up to the original source length, even after trimming; overlapping long fades
attenuate a short clip without extending it. MIDI clips reject these operations; attenuate a short clip without extending it. MIDI clips reject these operations;
silent metadata regions support them for later audio relinking. silent metadata regions support them for later audio relinking.
@ -165,9 +178,9 @@ No destructive audio processing or file deletion command is supplied.
A split retains the original source length/path/name/gain on both pieces. Its A split retains the original source length/path/name/gain on both pieces. Its
right offset advances by the left length, keeping trims reversible within the right offset advances by the left length, keeping trims reversible within the
same source. Left keeps its ID and fade-in; right gets a fresh ID and fade-out. same source. Left keeps its ID and fade-in; right gets a fresh ID and fade-out.
Inner explicit fades are cleared. Gain/fades are retained on duplication. Inner explicit fades are cleared. Gain/fades are retained on duplication. Moving
Moving does not alter offsets; trimming changes only timeline/source bounds and does not alter offsets; trimming changes only timeline/source bounds and makes
makes explicit fades follow the new audible boundaries. explicit fades follow the new audible boundaries.
The renderer maps tick boundaries by the existing absolute floor conversion, The renderer maps tick boundaries by the existing absolute floor conversion,
linearly resamples the source and never reads beyond its samples. Its audible linearly resamples the source and never reads beyond its samples. Its audible
@ -177,27 +190,27 @@ length up to a tick). A fade uses `age / fade_in_frames` at the start and
use the smaller value. The first/last audible samples are zero when their use the smaller value. The first/last audible samples are zero when their
respective fade has a positive sample duration. Durations rounding to zero respective fade has a positive sample duration. Durations rounding to zero
samples are disabled. A one-sample clip with any active fade is silent. Explicit samples are disabled. A one-sample clip with any active fade is silent. Explicit
fade gain multiplies the existing optional safety envelope, then clip gain, fade gain multiplies the existing optional safety envelope, then clip gain, then
then track gain/pan/automation. No limiter is added; gains/overlaps may clip. track gain/pan/automation. No limiter is added; gains/overlaps may clip.
The GUI retains its 5 ms boundary safety fades, including new split edges, so The GUI retains its 5 ms boundary safety fades, including new split edges, so
cuts can introduce a brief dip rather than an abrupt discontinuity. The raw C cuts can introduce a brief dip rather than an abrupt discontinuity. The raw C
plan still defaults to zero safety fade for exact-sample/offline consumers; plan still defaults to zero safety fade for exact-sample/offline consumers;
callers requesting raw cuts accept possible clicks. Tick quantization can place callers requesting raw cuts accept possible clicks. Tick quantization can place
an edit up to one output sample away from a separately rounded boundary; this an edit up to one output sample away from a separately rounded boundary; this is
is not sample-resolution audio editing. Manual listening remains pending. not sample-resolution audio editing. Manual listening remains pending.
Time-stretching, independent pitch shifting and crossfade tools are future work; Time-stretching, independent pitch shifting and crossfade tools are future work;
no suitable algorithm/quality target or new dependency has been selected. no suitable algorithm/quality target or new dependency has been selected.
### Cached waveform display ### Cached waveform display
Timeline audio clips show mono min/max peaks from an immutable C summary, Timeline audio clips show mono min/max peaks from an immutable C summary, shared
shared by reference path across split/duplicated clips. Up to 64 paths each by reference path across split/duplicated clips. Up to 64 paths each cache at
cache at most 1024 bins (fewer for short files). It uses playback's WAV parser, most 1024 bins (fewer for short files). It uses playback's WAV parser, downmix
downmix and file/frame bounds; creation temporarily loads one WAV then frees and file/frame bounds; creation temporarily loads one WAV then frees its sample
its sample storage. Bins cover every source sample, including a final partial storage. Bins cover every source sample, including a final partial bin. The
bin. The display follows source offsets and current tempo; it shows **raw source display follows source offsets and current tempo; it shows **raw source peaks
peaks before clip/track gain and fades**, clamped visually to +/-1. It is an before clip/track gain and fades**, clamped visually to +/-1. It is an
arrangement aid, not a sample editor or loudness meter. arrangement aid, not a sample editor or loudness meter.
Loading/rebuilding is synchronous on the GUI/control thread during project Loading/rebuilding is synchronous on the GUI/control thread during project
@ -219,4 +232,5 @@ cover short-clip selection, shared previews, split/duplicate/delete and undo.
The modal audio source editor also provides frame selection, reversible The modal audio source editor also provides frame selection, reversible
processing, selection export, copy adoption and transient slicing. See processing, selection export, copy adoption and transient slicing. See
[AUDIO_EDITOR.md](AUDIO_EDITOR.md) for commands, undo behavior and mono/size limits. [AUDIO_EDITOR.md](AUDIO_EDITOR.md) for commands, undo behavior and mono/size
limits.

View file

@ -1,19 +1,21 @@
# Timing, transitions and audio quality # Timing, Transitions and Audio Quality
Deterministic transition processing and tests are included. Musical time still Deterministic transition processing and tests are included. Musical time still
uses a single constant tempo, 960 ticks per quarter note and fixed 4/4 meter; uses a single constant tempo, 960 ticks per quarter note and fixed 4/4 meter; it
it does not introduce a tempo map, meter editor or JACK transport following. does not introduce a tempo map, meter editor or JACK transport following.
Project format remains v4 (reads v1–v3). Project format remains v4 (reads v1–v3).
## Timing policy ---
## Timing Policy
`xtc_project_set_tempo` accepts 20000–300000 milli-BPM and changes the editable `xtc_project_set_tempo` accepts 20000–300000 milli-BPM and changes the editable
model only. Build new audio/MIDI/mixer plans while stopped to adopt it. Existing model only. Build new audio/MIDI/mixer plans while stopped to adopt it. Existing
plans keep their prepared sample positions and mix snapshots. The backend retains plans keep their prepared sample positions and mix snapshots. The backend
its absolute sample counter through preparation; explicitly seek if the caller retains its absolute sample counter through preparation; explicitly seek if the
wants to preserve a particular musical tick after changing tempo. The GUI has no caller wants to preserve a particular musical tick after changing tempo. The GUI
tempo-change command yet. Meter is always 4/4, has no setter and does not vary has no tempo-change command yet. Meter is always 4/4, has no setter and does not
within a project. vary within a project.
Every musical boundary is converted from its **absolute** tick: Every musical boundary is converted from its **absolute** tick:
@ -25,10 +27,11 @@ Conversions use bounded unsigned integer arithmetic, not accumulated floating
beat durations. The inverse also floors; a sample-to-tick roundtrip can lose a beat durations. The inverse also floors; a sample-to-tick roundtrip can lose a
tick because of quantization. At very low supported rates several ticks can map tick because of quantization. At very low supported rates several ticks can map
to one sample. Collapsed note spans are silent. Automation uses the last point to one sample. Collapsed note spans are silent. Automation uses the last point
in tick order at a shared sample. Clip/note intervals are half-open `[start,end)`; in tick order at a shared sample. Clip/note intervals are half-open
a boundary exactly at the next block belongs to that next block. The MIDI input `[start,end)`; a boundary exactly at the next block belongs to that next block.
array preserves caller order for equal offsets (for example on/off versus off/on). The MIDI input array preserves caller order for equal offsets (for example
A discontinuous MIDI render position resets live voices before rendering. on/off versus off/on). A discontinuous MIDI render position resets live voices
before rendering.
Changing tempo shifts musical boundaries, including source offsets, on newly Changing tempo shifts musical boundaries, including source offsets, on newly
compiled plans. WAV samples keep their original playback speed with linear rate compiled plans. WAV samples keep their original playback speed with linear rate
@ -36,14 +39,17 @@ conversion; this is not audio time stretching. Scheduled note phase comes from
absolute note age, so seeks and block partitions reproduce the same waveform. absolute note age, so seeks and block partitions reproduce the same waveform.
The graph oscillator/native graph clock is plan-local, rather than project time. The graph oscillator/native graph clock is plan-local, rather than project time.
## Boundary fades and smoothing ---
## Boundary Fades and Smoothing
The GUI configures `xtc_backend_set_transition_frames(rate/200)`: approximately The GUI configures `xtc_backend_set_transition_frames(rate/200)`: approximately
5 ms at the current rate (integer frames, floor). Configuration is control-thread 5 ms at the current rate (integer frames, floor). Configuration is
only while stopped, before project preparation. The low-level backend and control-thread only while stopped, before project preparation. The low-level
source-plan APIs default to zero transition frames for source compatibility. backend and source-plan APIs default to zero transition frames for source
Call `xtc_audio_plan_set_fade_frames` and `xtc_midi_plan_set_fade_frames` before compatibility. Call `xtc_audio_plan_set_fade_frames` and
publication to enable the same behavior in an offline/custom host. `xtc_midi_plan_set_fade_frames` before publication to enable the same behavior
in an offline/custom host.
Each audio region and scheduled note gets an independent linear boundary Each audio region and scheduled note gets an independent linear boundary
envelope. Its first and last audible samples are zero. At distance `d` from the envelope. Its first and last audible samples are zero. At distance `d` from the
@ -60,33 +66,35 @@ per-clip editing metadata.
`xtc_gain_ramp` in `xtc_dsp.h` is a single-owner C primitive for smooth gain and `xtc_gain_ramp` in `xtc_dsp.h` is a single-owner C primitive for smooth gain and
parameter changes. Initialize with reset, set a finite target and length, then parameter changes. Initialize with reset, set a finite target and length, then
process preallocated mono/stereo buffers. It reaches the exact target on the Nth process preallocated mono/stereo buffers. It reaches the exact target on the Nth
sample, with the first sample one step toward the target. Retargeting starts from sample, with the first sample one step toward the target. Retargeting starts
the current gain; zero frames changes it immediately. Stereo shares one gain and from the current gain; zero frames changes it immediately. Stereo shares one
advances once per frame. Invalid calls leave buffers/state intact. It allocates gain and advances once per frame. Invalid calls leave buffers/state intact. It
nothing. Mixer automation already interpolates gain/pan per sample; the ramp allocates nothing. Mixer automation already interpolates gain/pan per sample;
primitive does not add a second smoothing delay to that timeline. Static mixer the ramp primitive does not add a second smoothing delay to that timeline.
edits stop playback and use the master ramp on restart. Live native parameter Static mixer edits stop playback and use the master ramp on restart. Live native
updates and live graph replacements have no automatic crossfade. parameter updates and live graph replacements have no automatic crossfade.
## Graceful and immediate stopping ---
## Graceful and Immediate Stopping
GUI Play fades the master from zero to unity. Ordinary GUI Pause/Stop request a GUI Play fades the master from zero to unity. Ordinary GUI Pause/Stop request a
fade to zero using `xtc_backend_request_stop`; repeated requests do not restart fade to zero using `xtc_backend_request_stop`; repeated requests do not restart
the fade. The callback publishes completion with a release atomic, then outputs the fade. The callback publishes completion with a release atomic, then outputs
silence and holds the sample position. `xtc_backend_collect` on the control thread silence and holds the sample position. `xtc_backend_collect` on the control
acquires completion and calls JACK deactivate, which establishes quiescence. thread acquires completion and calls JACK deactivate, which establishes
Until then, status reports `running` and `stopping`, and the GUI displays that it quiescence. Until then, status reports `running` and `stopping`, and the GUI
is fading. The 100 ms GUI timer completes deactivation; it does not control fade displays that it is fading. The 100 ms GUI timer completes deactivation; it does
timing. If deactivation fails, context/ownership remains intact, silence not control fade timing. If deactivation fails, context/ownership remains
continues, the error is reported and later collection retries. intact, silence continues, the error is reported and later collection retries.
The ramp length is sample-counted, but the last callback processes a whole block The ramp length is sample-counted, but the last callback processes a whole block
and advances the sample position by that block, including its zero-valued tail. and advances the sample position by that block, including its zero-valued tail.
A stop request starts at the next observed block boundary. Therefore Pause/Stop A stop request starts at the next observed block boundary. Therefore Pause/Stop
can consume up to approximately `fade_frames + block_frames` after the request, can consume up to approximately `fade_frames + block_frames` after the request,
plus server scheduling; this is not an immediate sample-exact stop command. plus server scheduling; this is not an immediate sample-exact stop command. Play
Play resumes at the retained block-end sample position with a fresh start ramp. resumes at the retained block-end sample position with a fresh start ramp. No
No callback waits for control or performs deactivation/allocation/I/O. callback waits for control or performs deactivation/allocation/I/O.
`xtc_backend_stop`/close remain synchronous immediate operations. Recording Stop `xtc_backend_stop`/close remain synchronous immediate operations. Recording Stop
uses immediate deactivation before draining/finalizing the input take; recording uses immediate deactivation before draining/finalizing the input take; recording
@ -97,31 +105,37 @@ material preservation take priority. If callbacks cease, use immediate stop or
close rather than waiting for fade acknowledgement. Live note-off/panic remains close rather than waiting for fade acknowledgement. Live note-off/panic remains
immediate with no release tail; scheduled notes use the boundary envelope. immediate with no release tail; scheduled notes use the boundary envelope.
## Numerical and channel policy ---
Core graph, native output, audio and MIDI rendering canonicalize nonfinite values ## Numerical and Channel Policy
and float subnormals to zero at output boundaries; graph node buffers also use
this policy. Native callbacks remain trusted and may have their own internal
denormal behavior. No process-wide FTZ/DAZ flags are modified. Normal values as
small as `FLT_MIN` are retained. The raw constant-gain helper is unchanged;
`xtc_gain_ramp_process` applies the canonical output policy.
Finite values above unity are preserved for mixing headroom. There is no limiter, Core graph, native output, audio and MIDI rendering canonicalize nonfinite
soft clipping or dither. The physical device/downstream system may clip; lower values and float subnormals to zero at output boundaries; graph node buffers
track gains if meters exceed unity. Ports remain mono with paired stereo passes; also use this policy. Native callbacks remain trusted and may have their own
external mono input is duplicated. Audio assets are downmixed on import, so internal denormal behavior. No process-wide FTZ/DAZ flags are modified. Normal
stereo assets do not retain stereo image. Output buffers must not overlap each values as small as `FLT_MIN` are retained. The raw constant-gain helper is
other; graph sources may alias outputs because both source passes finish first. unchanged; `xtc_gain_ramp_process` applies the canonical output policy.
Supported render sizes are 0–65536 frames within the prepared graph maximum.
## Latency and synchronization limits Finite values above unity are preserved for mixing headroom. There is no
limiter, soft clipping or dither. The physical device/downstream system may
clip; lower track gains if meters exceed unity. Ports remain mono with paired
stereo passes; external mono input is duplicated. Audio assets are downmixed on
import, so stereo assets do not retain stereo image. Output buffers must not
overlap each other; graph sources may alias outputs because both source passes
finish first. Supported render sizes are 0–65536 frames within the prepared
graph maximum.
---
## Latency and Synchronization Limits
Backend status reports sample rate and current buffer frames, not measured Backend status reports sample rate and current buffer frames, not measured
end-to-end device latency. `buffer_frames/sample_rate` is one process quantum, end-to-end device latency. `buffer_frames/sample_rate` is one process quantum,
not capture-to-speaker latency. JACK owns hardware/graph scheduling and external not capture-to-speaker latency. JACK owns hardware/graph scheduling and external
port latencies; xtc currently does not query/display JACK latency ranges or port latencies; xtc currently does not query/display JACK latency ranges or
publish an explicit per-node latency model. Fades shape amplitude without a publish an explicit per-node latency model. Fades shape amplitude without a
lookahead/delay buffer, so they add no timeline delay; they do attenuate attacks. lookahead/delay buffer, so they add no timeline delay; they do attenuate
attacks.
Built-in gain/meter/output, preloaded sources and the sine instrument have no Built-in gain/meter/output, preloaded sources and the sine instrument have no
algorithmic delay to compensate between tracks. The graph has no delay inserts, algorithmic delay to compensate between tracks. The graph has no delay inserts,
@ -131,7 +145,9 @@ tail contract and compensation are required before claiming synchronized delayed
inserts or external hosting. JACK transport sync, external MIDI clock, tempo inserts or external hosting. JACK transport sync, external MIDI clock, tempo
maps and physical recording-offset calibration remain unsupported. maps and physical recording-offset calibration remain unsupported.
## Verification and listening ---
## Verification and Listening
``` ```
make all test gui test-reliability test-workflow test-gui test-gui-jack make all test gui test-reliability test-workflow test-gui test-gui-jack
@ -140,21 +156,23 @@ make test-sanitize analyze
make quality-preview make quality-preview
``` ```
`test_quality` is part of `make test`. It covers a sample-rate/tempo matrix up to `test_quality` is part of `make test`. It covers a sample-rate/tempo matrix up
768 kHz and the maximum project tick, conversion errors, old/new tempo snapshots to 768 kHz and the maximum project tick, conversion errors, old/new tempo
for both audio and MIDI, exact offset/on/off order, sample boundary behavior, snapshots for both audio and MIDI, exact offset/on/off order, sample boundary
raw versus faded endpoints, media ends shorter than rounded clip ends, split/ behavior, raw versus faded endpoints, media ends shorter than rounded clip ends,
whole equivalence, seeks, ramps, stereo parity, overload, NaN/infinity/subnormals split/ whole equivalence, seeks, ramps, stereo parity, overload,
and block sizes 1, 2, 3, 17, 257, 4097, 65535 and 65536. Backend fault tests check NaN/infinity/subnormals and block sizes 1, 2, 3, 17, 257, 4097, 65535 and 65536.
exact master ramps, repeated stop requests, held position/silence after completion, Backend fault tests check exact master ramps, repeated stop requests, held
deactivation failure/retry, immediate escape and recording guards. The workflow position/silence after completion, deactivation failure/retry, immediate escape
render comparison now enables the GUI source fade policy before/after reopening. and recording guards. The workflow render comparison now enables the GUI source
fade policy before/after reopening.
Automated checks pass warning-enabled core/GUI and no-JACK builds, Automated checks pass warning-enabled core/GUI and no-JACK builds,
ASan/UBSan/leak checks, static analysis, pthread TSan tests, mock and live GUI, ASan/UBSan/leak checks, static analysis, pthread TSan tests, mock and live GUI,
software JACK audio/MIDI tests and staged installation. GUI leak checks retain software JACK audio/MIDI tests and staged installation. GUI leak checks retain
the existing fontconfig-only suppression; core leak checks are unsuppressed. the existing fontconfig-only suppression; core leak checks are unsuppressed.
Physical-device listening and human listening acceptance are **not yet verified**. Physical-device listening and human listening acceptance are **not yet
verified**.
`make quality-preview` writes ignored build artifacts `quality-raw.wav` and `make quality-preview` writes ignored build artifacts `quality-raw.wav` and
`quality-smooth.wav` in BUILD_DIR using `examples/quality_preview.c`. Compare at `quality-smooth.wav` in BUILD_DIR using `examples/quality_preview.c`. Compare at
@ -162,7 +180,7 @@ moderate playback level: first listen to the three-note phrase endings, then the
constant clip at two seconds, then a mid-period tone stop near three seconds. constant clip at two seconds, then a mid-period tone stop near three seconds.
Check that fades reduce edge clicks, do not add noise and do not unexpectedly Check that fades reduce edge clicks, do not add noise and do not unexpectedly
change pitch/timing. Also listen to the saved MIDI/audio workflow project with change pitch/timing. Also listen to the saved MIDI/audio workflow project with
repeated Play/Pause/Stop, Home/seek and representative gain/pan automation on the repeated Play/Pause/Stop, Home/seek and representative gain/pan automation on
actual target device. Record device, rate, block size and observations before the actual target device. Record device, rate, block size and observations
marking human listening acceptance complete. Generated previews are reproducible before marking human listening acceptance complete. Generated previews are
test material, not a claim that anyone has listened to them. reproducible test material, not a claim that anyone has listened to them.

View file

@ -1,9 +1,11 @@
# Build baseline # Build Baseline
The existing layout is retained: public C headers in `include/xtc`, The existing layout is retained: public C headers in `include/xtc`, C
C implementation modules under `src`, optional C++ GUI under `src/gui`, and implementation modules under `src`, optional C++ GUI under `src/gui`, and tests
tests under `tests`. `src/main.c` is a minimal headless startup/shutdown check; under `tests`. `src/main.c` is a minimal headless startup/shutdown check; it
it does not initialize audio devices or open a GUI. does not initialize audio devices or open a GUI.
---
## Commands ## Commands
@ -15,14 +17,17 @@ make gui
``` ```
`all` builds the static C library and startup check. `test` runs the existing `all` builds the static C library and startup check. `test` runs the existing
core/offline tests, concurrent plan-handoff stress, JACK fault injection core/offline tests, concurrent plan-handoff stress, JACK fault injection (when
(when JACK headers are available), and the startup check. `debug` builds and tests separately in JACK headers are available), and the startup check. `debug` builds and tests
`build/debug`. `gui` builds the FLTK shell with backend controls and status. separately in `build/debug`. `gui` builds the FLTK shell with backend controls
The shell starts with a silent graph and never connects physical ports. Generated files remain under `build` and header changes trigger rebuilds. and status. The shell starts with a silent graph and never connects physical
ports. Generated files remain under `build` and header changes trigger rebuilds.
Compiler, flags, `BUILD_DIR`, `PKG_CONFIG`, `FLTK_CONFIG`, and installation Compiler, flags, `BUILD_DIR`, `PKG_CONFIG`, `FLTK_CONFIG`, and installation
`PREFIX` can be overridden through Make variables. C uses C99; GUI uses C++17. `PREFIX` can be overridden through Make variables. C uses C99; GUI uses C++17.
## Inspected Linux toolchain (2026-10-26) ---
## Inspected Linux Toolchain (2026-10-26)
| Component | Available version | Role | | Component | Available version | Role |
| --- | --- | --- | | --- | --- | --- |
@ -35,24 +40,29 @@ Compiler, flags, `BUILD_DIR`, `PKG_CONFIG`, `FLTK_CONFIG`, and installation
| Clang | 23.1.3 | Alternative compiler / diagnostics | | Clang | 23.1.3 | Alternative compiler / diagnostics |
Valgrind is also available. The core tests use a small in-tree C test harness Valgrind is also available. The core tests use a small in-tree C test harness
and require no external test framework. FLTK has no `fltk.pc` on this host; and require no external test framework. FLTK has no `fltk.pc` on this host; the
the Makefile tries pkg-config first, then `fltk-config`. Makefile tries pkg-config first, then `fltk-config`.
## Dependencies and licensing ---
## Dependencies and Licensing
The offline core needs the C runtime and math library. Optional JACK support is The offline core needs the C runtime and math library. Optional JACK support is
selected with pkg-config; missing JACK does not prevent core/tests selected with pkg-config; missing JACK does not prevent core/tests from
from building. Realtime publication uses GCC/Clang lock-free atomic builtins in building. Realtime publication uses GCC/Clang lock-free atomic builtins in C99.
C99. Tests also use POSIX threads. Backend consumers must link JACK explicitly; Tests also use POSIX threads. Backend consumers must link JACK explicitly;
backend objects are pulled from the static library only when used. The repository is MIT licensed. The optional GUI uses maintained backend objects are pulled from the static library only when used. The
FLTK 1.4 (LGPL with FLTK's static-linking exception); its platform link flags repository is MIT licensed. The optional GUI uses maintained FLTK 1.4 (LGPL with
come from the installed package. Distribution still requires review of all FLTK's static-linking exception); its platform link flags come from the
linked dependencies and notices. The installed PipeWire JACK library is MIT; installed package. Distribution still requires review of all linked dependencies
traditional JACK client libraries use LGPL licensing. See decision 0004 for and notices. The installed PipeWire JACK library is MIT; traditional JACK client
the dependency choice and caveats. ALSA remains a future MIDI candidate. No packages or system settings libraries use LGPL licensing. See decision 0004 for the dependency choice and
caveats. ALSA remains a future MIDI candidate. No packages or system settings
are changed by these build commands. are changed by these build commands.
## Backend verification ---
## Backend Verification
```sh ```sh
make all test gui debug make all test gui debug
@ -72,11 +82,13 @@ Normal, sanitizer and no-JACK tests pass. ThreadSanitizer reports no race in
2,000 concurrent submissions. The 30-second live loopback test passed at 48 kHz 2,000 concurrent submissions. The 30-second live loopback test passed at 48 kHz
with zero xruns and ten restart cycles; see tests/integration/README.md. Live with zero xruns and ten restart cycles; see tests/integration/README.md. Live
server termination/format changes were not performed on the shared server; server termination/format changes were not performed on the shared server;
fault-injection tests cover those notification paths. No packages installed fault-injection tests cover those notification paths. No packages installed or
or system/device configuration changed. Sanitizers are not enabled in the live system/device configuration changed. Sanitizers are not enabled in the live
callback qualification run. callback qualification run.
## GUI shell verification ---
## GUI Shell Verification
```sh ```sh
make clean all test gui debug test-gui test-gui-jack make clean all test gui debug test-gui test-gui-jack
@ -92,7 +104,9 @@ exit-time font allocations; see tests/integration/README.md for the distinction
and command. No compiler warnings remain with GCC or Clang. Core/backend tests and command. No compiler warnings remain with GCC or Clang. Core/backend tests
pass with unsuppressed ASan/UBSan. No packages or system configuration changed. pass with unsuppressed ASan/UBSan. No packages or system configuration changed.
## Graph editor verification ---
## Graph Editor Verification
```sh ```sh
make clean all test gui debug test-gui test-gui-jack make clean all test gui debug test-gui test-gui-jack
@ -106,10 +120,12 @@ ASan/UBSan; ThreadSanitizer passes the concurrent handoff test with revision-to-
gain checks. GUI ASan/UBSan tests pass with the existing fontconfig-only leak gain checks. GUI ASan/UBSan tests pass with the existing fontconfig-only leak
suppression. Live GUI tests build a connected graph, adopt accepted edits during suppression. Live GUI tests build a connected graph, adopt accepted edits during
playback, reject cycles, reconnect and close active clients. Mouse/visual smoke playback, reject cycles, reconnect and close active clients. Mouse/visual smoke
checks cover node creation, wiring, cycle rejection, dragging and deletion. checks cover node creation, wiring, cycle rejection, dragging and deletion. No
No physical routes, new dependencies or system changes are introduced. physical routes, new dependencies or system changes are introduced.
## Project verification ---
## Project Verification
```sh ```sh
make clean all test gui debug test-gui test-gui-jack make clean all test gui debug test-gui test-gui-jack
@ -118,10 +134,10 @@ make BUILD_DIR=build/nojack PKG_CONFIG=false all test gui test-gui
The suite adds test_project: track/clip limits, stable IDs, trims, integer The suite adds test_project: track/clip limits, stable IDs, trims, integer
transport conversion, exact file roundtrip with graph rendering, exhaustive transport conversion, exact file roundtrip with graph rendering, exhaustive
fixture truncations, byte mutations, failed saves and temporary cleanup. fixture truncations, byte mutations, failed saves and temporary cleanup. GUI
GUI tests cover region gestures, zoom, save/load and backend rollback. tests cover region gestures, zoom, save/load and backend rollback. Live JACK
Live JACK tests verify playhead advance, pause and resume without creating routes. tests verify playhead advance, pause and resume without creating routes. The
The updated core and GUI tests pass Clang ASan/UBSan (the GUI uses the existing updated core and GUI tests pass Clang ASan/UBSan (the GUI uses the existing
fontconfig-only LSan suppression). An Xvfb/xdotool visual check created a track fontconfig-only LSan suppression). An Xvfb/xdotool visual check created a track
and region, moved it one bar and zoomed the canvas. No dependencies were added. and region, moved it one bar and zoomed the canvas. No dependencies were added.
@ -129,8 +145,9 @@ Audio assets and recording are supported. Silent arrangement regions, fixed
tempo/4/4, no undo, no persisted GUI layout, and unverified physical-device tempo/4/4, no undo, no persisted GUI layout, and unverified physical-device
playback/endurance remain explicit limits. playback/endurance remain explicit limits.
---
## Audio verification ## Audio Verification
```sh ```sh
make all test gui debug test-gui test-gui-jack test-jack-audio make all test gui debug test-gui test-gui-jack test-jack-audio
@ -150,12 +167,15 @@ migration and 1024 maximum-length asset paths in a valid v2 file.
The live test-jack-audio target connects only its software clients and checks The live test-jack-audio target connects only its software clients and checks
record -> WAV -> saved/reopened project -> graph playback. GUI/JACK tests also record -> WAV -> saved/reopened project -> graph playback. GUI/JACK tests also
record, finalize, save and reopen a take without hardware routing. Clang record, finalize, save and reopen a take without hardware routing. Clang
ASan/UBSan passes the core and GUI tests (GUI retains the existing fontconfig-only ASan/UBSan passes the core and GUI tests (GUI retains the existing
LSan suppression). ThreadSanitizer passes the concurrent recorder and existing fontconfig-only LSan suppression). ThreadSanitizer passes the concurrent
realtime handoff tests. Hardware capture/playback, disk endurance and real server recorder and existing realtime handoff tests. Hardware capture/playback, disk
loss during recording remain unverified. See AUDIO_FILES.md. endurance and real server loss during recording remain unverified. See
AUDIO_FILES.md.
## MIDI verification ---
## MIDI Verification
```sh ```sh
make all test gui debug test-gui test-gui-jack test-jack-audio test-jack-midi make all test gui debug test-gui test-gui-jack test-jack-audio test-jack-midi
@ -166,17 +186,20 @@ make BUILD_DIR=build/nojack PKG_CONFIG=false all test gui test-gui
voice capacities, stable notes and v3 malformed-file/roundtrip/v2 migration voice capacities, stable notes and v3 malformed-file/roundtrip/v2 migration
coverage. Backend fault injection checks event offsets, dropped/malformed input, coverage. Backend fault injection checks event offsets, dropped/malformed input,
disconnection, xruns, missing output buffers and stop/restart cleanup. GUI tests disconnection, xruns, missing output buffers and stop/restart cleanup. GUI tests
draw/move/resize notes, save/reopen and render them. The live MIDI target connects draw/move/resize notes, save/reopen and render them. The live MIDI target
only software clients and checks on/off, panic, disconnect, and saved-note connects only software clients and checks on/off, panic, disconnect, and
playback/seek. It needs an existing JACK-compatible server. No hardware routes saved-note playback/seek. It needs an existing JACK-compatible server. No
or system changes are made. MIDI recording/SMF and physical devices are deferred. hardware routes or system changes are made. MIDI recording/SMF and physical
devices are deferred.
A fresh `build/verify` build passed all targets above with no compiler warnings. A fresh `build/verify` build passed all targets above with no compiler warnings.
The no-JACK core/GUI suite and Clang ASan/UBSan core/GUI suite also passed; GUI The no-JACK core/GUI suite and Clang ASan/UBSan core/GUI suite also passed; GUI
leak checking uses only the existing fontconfig suppression documented in the leak checking uses only the existing fontconfig suppression documented in the
integration README. Physical MIDI and audio devices remain unverified. integration README. Physical MIDI and audio devices remain unverified.
## Mixer verification ---
## Mixer Verification
```sh ```sh
make all test gui debug test-gui test-gui-jack test-jack-audio test-jack-midi make all test gui debug test-gui test-gui-jack test-jack-audio test-jack-midi
@ -184,20 +207,23 @@ make BUILD_DIR=build/nojack PKG_CONFIG=false all test gui test-gui
``` ```
`test_mixer` covers stereo gain/pan, mute/solo, sample automation, block `test_mixer` covers stereo gain/pan, mute/solo, sample automation, block
independence, WAV and graph routing, v4 persistence failures and bounded history. independence, WAV and graph routing, v4 persistence failures and bounded
GUI tests add automation mouse edits, mixer keys, history rollback/branching and history. GUI tests add automation mouse edits, mixer keys, history
save/reopen. The existing live MIDI test now uses two software capture ports and rollback/branching and save/reopen. The existing live MIDI test now uses two
checks hard-right saved-note playback and output meter snapshots; mono audio software capture ports and checks hard-right saved-note playback and output
record/play acceptance remains covered. A native Xvfb mouse smoke check confirms meter snapshots; mono audio record/play acceptance remains covered. A native
sliders and automation drawing. See MIXER.md for capacities and deferred features. Xvfb mouse smoke check confirms sliders and automation drawing. See MIXER.md for
capacities and deferred features.
The fresh verification build, no-JACK core/GUI suite and Clang The fresh verification build, no-JACK core/GUI suite and Clang ASan/UBSan
ASan/UBSan core/GUI suite passed without compiler warnings. GUI leak checking core/GUI suite passed without compiler warnings. GUI leak checking retains only
retains only the existing fontconfig suppression. ThreadSanitizer also passed the existing fontconfig suppression. ThreadSanitizer also passed the recorder
the recorder concurrency and realtime publication tests. Physical devices and concurrency and realtime publication tests. Physical devices and long-running
long-running stereo performance remain unverified. stereo performance remain unverified.
## Release preparation ---
## Release Preparation
```sh ```sh
make all test gui debug test-gui test-gui-jack test-jack test-jack-audio test-jack-midi make all test gui debug test-gui test-gui-jack test-jack test-jack-audio test-jack-midi
@ -205,54 +231,63 @@ make test-install test-recovery test-performance
make test-release make test-release
``` ```
Core test now includes native ABI/lifecycle/retirement and the native-gain example. Core test now includes native ABI/lifecycle/retirement and the native-gain
Optional release tests use Python 3.12+ standard library, Git for indexed source example. Optional release tests use Python 3.12+ standard library, Git for
exports, GNU/LLVM deterministic ar and temporary directories. No live prefix is indexed source exports, GNU/LLVM deterministic ar and temporary directories. No
installed. test-install checks core/GUI files, a prefix containing spaces, live prefix is installed. test-install checks core/GUI files, a prefix
headless CLI, pkg-config C/C++ consumers, exact/idempotent uninstall and preservation containing spaces, headless CLI, pkg-config C/C++ consumers, exact/idempotent
of unrelated files. test-release compares two clean exported optimized builds and uninstall and preservation of unrelated files. test-release compares two clean
repacked archives byte-for-byte. test-recovery kills private child writers and exported optimized builds and repacked archives byte-for-byte. test-recovery
forces a child-local write limit, checking complete finalized destinations and kills private child writers and forces a child-local write limit, checking
explicit orphan temporaries. Performance timings are reported, not used as a complete finalized destinations and explicit orphan temporaries. Performance
universal realtime guarantee. See RELEASING.md, RECOVERY.md and PERFORMANCE.md. timings are reported, not used as a universal realtime guarantee. See
RELEASING.md, RECOVERY.md and PERFORMANCE.md.
Verification passed a fresh warning-enabled core/debug/GUI build, all Verification passed a fresh warning-enabled core/debug/GUI build, all software
software JACK targets, staged install/uninstall, recovery and performance tests. JACK targets, staged install/uninstall, recovery and performance tests. No-JACK
No-JACK core/GUI/install checks and Clang ASan/UBSan core/GUI checks also passed core/GUI/install checks and Clang ASan/UBSan core/GUI checks also passed (the
(the GUI retains only its existing fontconfig leak suppression). TSan recorder GUI retains only its existing fontconfig leak suppression). TSan recorder and
and realtime-handoff checks passed. Two independent optimized source exports realtime-handoff checks passed. Two independent optimized source exports and
and their repacked source archives were byte-identical on this toolchain. their repacked source archives were byte-identical on this toolchain.
## Reliability audit ---
## Reliability Audit
Run `make test-reliability` for the core suites, allocation-failure sweep, Run `make test-reliability` for the core suites, allocation-failure sweep,
callback allocation/free guard and native reclamation checks. Run callback allocation/free guard and native reclamation checks. Run
`make test-sanitize` for Clang ASan/UBSan/leak checks and `make analyze` for `make test-sanitize` for Clang ASan/UBSan/leak checks and `make analyze` for
Clang static analysis of all core sources. These need installed development Clang static analysis of all core sources. These need installed development
tools; the allocation guard uses a GNU-compatible linker. See tools; the allocation guard uses a GNU-compatible linker. See
[RELIABILITY.md](RELIABILITY.md) for ownership, audit findings, [RELIABILITY.md](RELIABILITY.md) for ownership, audit findings, full
full verification commands and remaining limits. verification commands and remaining limits.
## Workflow acceptance ---
## Workflow Acceptance
`make test-workflow` requires FLTK/JACK development files, Xvfb and an existing `make test-workflow` requires FLTK/JACK development files, Xvfb and an existing
JACK-compatible server. It does not start/configure the server or connect JACK-compatible server. It does not start/configure the server or connect
physical devices. See [WORKFLOW.md](WORKFLOW.md) for the full GUI composition physical devices. See [WORKFLOW.md](WORKFLOW.md) for the full GUI composition
acceptance sequence and transport semantics. acceptance sequence and transport semantics.
## Audio quality ---
## Audio Quality
`make test-quality` runs exact timing, source-boundary, gain-ramp, event-order `make test-quality` runs exact timing, source-boundary, gain-ramp, event-order
and numeric/layout tests (also included in `make test`). `make quality-preview` and numeric/layout tests (also included in `make test`). `make quality-preview`
builds the C example and writes raw/smoothed listening WAVs into BUILD_DIR without builds the C example and writes raw/smoothed listening WAVs into BUILD_DIR
playing them or configuring devices. See [AUDIO_QUALITY.md](AUDIO_QUALITY.md). without playing them or configuring devices. See
[AUDIO_QUALITY.md](AUDIO_QUALITY.md).
Parameter-processor and plugin checks: `make test-processor test-plugins test-gui-plugins`. Parameter-processor and plugin checks:
The latter two need the system LADSPA development header; real-plugin acceptance `make test-processor test-plugins test-gui-plugins`. The latter two need the
is separate (`make test-plugins-real`, paths listed in PLUGIN_HOSTING.md). With no system LADSPA development header; real-plugin acceptance is separate
(`make test-plugins-real`, paths listed in PLUGIN_HOSTING.md). With no
`ladspa.h`, the ordinary core/GUI still builds and plugin operations return `ladspa.h`, the ordinary core/GUI still builds and plugin operations return
UNSUPPORTED; existing plugin metadata can still be loaded/saved. To exercise that UNSUPPORTED; existing plugin metadata can still be loaded/saved. To exercise
configuration explicitly, use a fresh BUILD_DIR with that configuration explicitly, use a fresh BUILD_DIR with
`CPPFLAGS=-DXTC_DISABLE_LADSPA JACK_AVAILABLE=`. No dependency is downloaded. `CPPFLAGS=-DXTC_DISABLE_LADSPA JACK_AVAILABLE=`. No dependency is downloaded.
Link static consumers with `-lm -ldl` plus the backend dependencies when used. Link static consumers with `-lm -ldl` plus the backend dependencies when used.
See LICENSES.md for header/plugin licensing and decision 0009 for rationale. See LICENSES.md for header/plugin licensing and decision 0009 for rationale.
@ -264,23 +299,27 @@ ASan/UBSan. Run `make benchmark benchmark-project` separately without contention
or sanitizers for timing reports. Optional `make test-stress-plugins` requires or sanitizers for timing reports. Optional `make test-stress-plugins` requires
installed LADSPA headers; `make test-stress` supports minimal builds too. installed LADSPA headers; `make test-stress` supports minimal builds too.
## Installation layouts ---
## Installation Layouts
`make test-install test-install-layout` verifies default and custom layouts in `make test-install test-install-layout` verifies default and custom layouts in
throwaway staging directories, C/C++ installed consumers, examples and exact throwaway staging directories, C/C++ installed consumers, examples and exact
removal. Override `PREFIX`, `exec_prefix`, `bindir`, `libdir`, `includedir`, removal. Override `PREFIX`, `exec_prefix`, `bindir`, `libdir`, `includedir`,
`datarootdir`, `datadir`, `docdir`, `pkgconfigdir`, `licensedir`, `examplesdir` or `datarootdir`, `datadir`, `docdir`, `pkgconfigdir`, `licensedir`, `examplesdir`
`desktopdir` as needed. `DESTDIR` adds staging separately; pass the same layout or `desktopdir` as needed. `DESTDIR` adds staging separately; pass the same
to uninstall. See RELEASING.md for commands and RELEASE_NOTES.md for upgrades. layout to uninstall. See RELEASING.md for commands and RELEASE_NOTES.md for
upgrades.
Modern plugin adapters build in C99 using the pinned header-only CLAP Modern plugin adapters build in C99 using the pinned header-only CLAP and VST3 C
and VST3 C APIs under third_party; there is no network build or SDK runtime. APIs under third_party; there is no network build or SDK runtime. VST2
VST2 compatibility uses original ABI declarations. `make test-modern-plugins` compatibility uses original ABI declarations. `make test-modern-plugins` runs
runs original shared-library fixtures without a server; `make test-gui-modern-plugins` original shared-library fixtures without a server;
adds GUI/browser/undo/reopen tests under Xvfb. The fixtures also run in `make test` `make test-gui-modern-plugins` adds GUI/browser/undo/reopen tests under Xvfb.
and therefore in sanitizer/release builds. Notices are installed with LICENSE. The fixtures also run in `make test` and therefore in sanitizer/release builds.
Notices are installed with LICENSE.
The GUI uses a header-only FLTK theme and existing drawing primitives; no new The GUI uses a header-only FLTK theme and existing drawing primitives; no new
GUI dependency or generated asset is required. `make test-gui-instruments` also GUI dependency or generated asset is required. `make test-gui-instruments` also
exercises sidebar track creation, rollback, undo/reopen and minimum/large layouts. exercises sidebar track creation, rollback, undo/reopen and minimum/large
See WORKSPACE.md for the default size and manual visual verification. layouts. See WORKSPACE.md for the default size and manual visual verification.

View file

@ -1,21 +1,23 @@
# Internal instruments and effects # Internal Instruments and Effects
No plugin downloads, search paths or external libraries are needed. The original No plugin downloads, search paths or external libraries are needed. The original
built-in sine remains available for existing projects. built-in sine remains available for existing projects.
Choose **Internal: Soft Keys** or **Internal: Sub Bass** in a MIDI track's Choose **Internal: Soft Keys** or **Internal: Sub Bass** in a MIDI track's
instrument dropdown. Soft Keys blends a sine with a third harmonic; Sub Bass instrument dropdown. Soft Keys blends a sine with a third harmonic; Sub Bass
uses a pure sine. Both have 32 voices, velocity sensitivity, uses a pure sine. Both have 32 voices, velocity sensitivity, short
short attack/release envelopes, sustain pedal, note-off and panic handling. attack/release envelopes, sustain pedal, note-off and panic handling. High
High harmonics are omitted near Nyquist. The default sounds remain compatible with fixed presets from v13. See the harmonics are omitted near Nyquist. The default sounds remain compatible with
instrument controls below. A sample library is not provided by these oscillators. fixed presets from v13. See the instrument controls below. A sample library is
not provided by these oscillators.
In **Modular Graph**, select a wire and choose **Edit → Insert internal delay on In **Modular Graph**, select a wire and choose **Edit → Insert internal delay on
wire** or **Insert internal reverb on wire**. This preserves the route and inserts wire** or **Insert internal reverb on wire**. This preserves the route and
the effect into the signal path. **File → Add internal delay/reverb** creates an inserts the effect into the signal path. **File → Add internal delay/reverb**
unconnected device for manual wiring. Select its node and use **Edit → Selected creates an unconnected device for manual wiring. Select its node and use **Edit
plugin control** to edit it, or **Selected plugin automation** to automate it. → Selected plugin control** to edit it, or **Selected plugin automation** to
Changes use the existing stopped-engine preparation and undo workflow. automate it. Changes use the existing stopped-engine preparation and undo
workflow.
- **Delay:** time (1–2000 ms), feedback (0–0.95), dry/wet mix (0–1). - **Delay:** time (1–2000 ms), feedback (0–0.95), dry/wet mix (0–1).
- **Reverb:** four parallel damped feedback combs, with decay (0–0.95), damping - **Reverb:** four parallel damped feedback combs, with decay (0–0.95), damping
@ -28,37 +30,43 @@ Internal effect history resets on transport discontinuities. Configurable export
tails and optional playback stopping after tails are described below. tails and optional playback stopping after tails are described below.
Project format 13 stores internal devices as format 4 with reserved module path Project format 13 stores internal devices as format 4 with reserved module path
`/xtc/internal`, stable numeric identity and index (0 keys, 1 bass, 2 delay, `/xtc/internal`, stable numeric identity and index (0 keys, 1 bass, 2 delay, 3
3 reverb). This is a virtual identifier, never an external binary. Effect controls reverb). This is a virtual identifier, never an external binary. Effect controls
and automation are saved using the existing plugin records; instruments are fixed and automation are saved using the existing plugin records; instruments are
presets. Readers still accept formats 1–12. Older xtc builds reject format 13. fixed presets. Readers still accept formats 1–12. Older xtc builds reject format
All buffers are allocated during preparation; processing performs no allocation, 13. All buffers are allocated during preparation; processing performs no
file access, library loading, locking or GUI calls. allocation, file access, library loading, locking or GUI calls.
Validation: `make BUILD_DIR=build/builtins all gui test test-gui-instruments Validation:
test-gui-modern-plugins test-gui-plugins analyze` (join onto one line). `make BUILD_DIR=build/builtins all gui test test-gui-instruments test-gui-modern-plugins test-gui-plugins analyze`
`test-builtins` checks impulse tails, dry mix, feedback bounds, block partition (join onto one line). `test-builtins` checks impulse tails, dry mix, feedback
consistency, note release/panic and instrument/effect project round trips. bounds, block partition consistency, note release/panic and instrument/effect
project round trips.
## Acceptance checks ---
## Acceptance Checks
`make BUILD_DIR=build/builtin-acceptance all gui test test-reliability test-gui builtin-preview` `make BUILD_DIR=build/builtin-acceptance all gui test test-reliability test-gui builtin-preview`
checks all four device identities and GUI paths without external plugin binaries. checks all four device identities and GUI paths without external plugin
The reliability runner fails each internal preparation allocation in turn and binaries. The reliability runner fails each internal preparation allocation in
forbids allocation/free, direct file I/O and blocking locks during processing. turn and forbids allocation/free, direct file I/O and blocking locks during
Core tests compare persisted rendering for both instruments and both automated processing. Core tests compare persisted rendering for both instruments and both
effects, corrupt an internal module identity transactionally, and cover voice automated effects, corrupt an internal module identity transactionally, and
exhaustion, sustain, repeated notes, partitioning and transport reset/seek/panic. cover voice exhaustion, sustain, repeated notes, partitioning and transport
The existing portability suite verifies older project migrations. reset/seek/panic. The existing portability suite verifies older project
migrations.
For repeatable manual listening, run `make builtin-preview` and audition For repeatable manual listening, run `make builtin-preview` and audition
`build/builtin-{0,1,2,3}.wav` at a conservative level: 0 is Soft Keys, 1 Sub Bass, `build/builtin-{0,1,2,3}.wav` at a conservative level: 0 is Soft Keys, 1 Sub
2 Delay and 3 Reverb. Each six-second file includes held tones or two impulses Bass, 2 Delay and 3 Reverb. Each six-second file includes held tones or two
followed by silence for tails. Check onset/release, unwanted discontinuities, impulses followed by silence for tails. Check onset/release, unwanted
finite decay and the documented mono character. Generation is verified; discontinuities, finite decay and the documented mono character. Generation is
human listening and physical-device qualification remain pending. verified; human listening and physical-device qualification remain pending.
## Instrument controls and presets ---
## Instrument Controls and Presets
**Edit → Selected instrument controls** edits an assigned internal instrument. **Edit → Selected instrument controls** edits an assigned internal instrument.
The five ports are harmonic amount (0..1), attack (0.1..2000 ms), release The five ports are harmonic amount (0..1), attack (0.1..2000 ms), release
@ -77,8 +85,8 @@ with no parameters upgrade to defaults when opened in the editor.
are versioned single-instrument `.xtc` containers with no clips. Public C APIs are versioned single-instrument `.xtc` containers with no clips. Public C APIs
`xtc_plugin_preset_save/load` provide atomic saving and transactional metadata `xtc_plugin_preset_save/load` provide atomic saving and transactional metadata
loading; they never execute plugin code. Loading into a track is an undoable, loading; they never execute plugin code. Loading into a track is an undoable,
prepared edit. Parameter automation is included. Preset paths are ordinary files; prepared edit. Parameter automation is included. Preset paths are ordinary
saving uses the existing project replacement semantics. files; saving uses the existing project replacement semantics.
Live MIDI pitch bend uses standard 14-bit messages (8192 center) and is applied Live MIDI pitch bend uses standard 14-bit messages (8192 center) and is applied
at the event's sample offset per channel. Panic clears the affected channel's at the event's sample offset per channel. Panic clears the affected channel's
@ -91,7 +99,9 @@ old fixed internal assignments use their original defaults. Older builds reject
v14. Tests cover legacy-default sound equality, v13 migration, parameter/preset v14. Tests cover legacy-default sound equality, v13 migration, parameter/preset
round trips, pitch-bend offsets, automated block partitions, undo and GUI edits. round trips, pitch-bend offsets, automated block partitions, undo and GUI edits.
## Sample instrument ---
## Sample Instrument
**File → Load sampler WAV** assigns a Sample Player to the selected track or **File → Load sampler WAV** assigns a Sample Player to the selected track or
relinks its existing sample while retaining its controls. Use the instrument relinks its existing sample while retaining its controls. Use the instrument
@ -99,8 +109,8 @@ controls editor for root MIDI key (0..127), whole-sample loop (0/1), attack,
release and gain (0..4). One root mapping covers every MIDI key using linear release and gain (0..4). One root mapping covers every MIDI key using linear
rate conversion, including source/output sample-rate differences. Loop and root rate conversion, including source/output sample-rate differences. Loop and root
are integer controls and require hold automation. Live bend spans ±2 semitones. are integer controls and require hold automation. Live bend spans ±2 semitones.
This initial sampler has no multisample zones, loop-point editor or time stretch. This initial sampler has no multisample zones, loop-point editor or time
Extreme transposition can alias; use appropriate source material. stretch. Extreme transposition can alias; use appropriate source material.
The existing WAV reader supports PCM16/24/32 and float32, mono/stereo downmixed The existing WAV reader supports PCM16/24/32 and float32, mono/stereo downmixed
to mono. Each sample is at most 16M mono frames; the combined instrument plan to mono. Each sample is at most 16M mono frames; the combined instrument plan
@ -122,15 +132,17 @@ Tests cover source-rate conversion, one-shot endpoint, looping, release,
partitioning, preset references, missing assets, GUI relinking/undo/reload and partitioning, preset references, missing assets, GUI relinking/undo/reload and
preparation-allocation/callback guards. preparation-allocation/callback guards.
## Stereo effects ---
## Stereo Effects
**File → Add stereo delay/reverb** and **Edit → Insert stereo delay/reverb on **File → Add stereo delay/reverb** and **Edit → Insert stereo delay/reverb on
wire** add new devices (indices 5/6). Original Delay/Reverb (indices 2/3) retain wire** add new devices (indices 5/6). Original Delay/Reverb (indices 2/3) retain
their original algorithms, values and saved identities. Existing control and their original algorithms, values and saved identities. Existing control and
automation editors, undo and persistence support both generations. automation editors, undo and persistence support both generations.
Stereo Delay adds integer **Sync**: 0 milliseconds, 1 quarter, 2 eighth, Stereo Delay adds integer **Sync**: 0 milliseconds, 1 quarter, 2 eighth, 3
3 dotted eighth, 4 eighth triplet. Nonzero sync follows project tempo; changing dotted eighth, 4 eighth triplet. Nonzero sync follows project tempo; changing
tempo uses ordinary stopped project preparation. Time changes crossfade two tempo uses ordinary stopped project preparation. Time changes crossfade two
fractional read heads for 5 ms. A crossfade finishes before adopting the latest fractional read heads for 5 ms. A crossfade finishes before adopting the latest
automated target, bounding transition work and avoiding read-head jumps. automated target, bounding transition work and avoiding read-head jumps.
@ -139,29 +151,33 @@ Delay storage covers four seconds, sufficient for all divisions at 20..300 BPM.
Stereo Reverb adds **Room** (0..1, scales comb spacing) and **Diffusion** (0..1, Stereo Reverb adds **Room** (0..1, scales comb spacing) and **Diffusion** (0..1,
cross-couples damped feedback). The right comb timings differ from the left. cross-couples damped feedback). The right comb timings differ from the left.
Decay/damping retain 0..0.95 bounds. This is a simple algorithmic reverb, without Decay/damping retain 0..0.95 bounds. This is a simple algorithmic reverb,
physical room simulation or a measured RT60. Room automation can alter coloration. without physical room simulation or a measured RT60. Room automation can alter
coloration.
The graph evaluates both channels' inputs before calling a coupled internal The graph evaluates both channels' inputs before calling a coupled internal
processor. Each stereo device has one prepared state, with no callback allocation. processor. Each stereo device has one prepared state, with no callback
Mono processing excites both sides and folds the wet output to mono. External allocation. Mono processing excites both sides and folds the wet output to mono.
plugins and the original effects retain independent mono instances. External plugins and the original effects retain independent mono instances.
Tests compare whole/split blocks, tempo-synced impulse placement, left-to-right Tests compare whole/split blocks, tempo-synced impulse placement, left-to-right
energy, bounded extreme feedback/diffusion and graph/direct stereo output energy, bounded extreme feedback/diffusion and graph/direct stereo output
(including the engine's subnormal-to-zero policy). GUI tests save/reopen both (including the engine's subnormal-to-zero policy). GUI tests save/reopen both
identities; callback guards and ASan/UBSan cover processing. `make builtin-preview` identities; callback guards and ASan/UBSan cover processing.
also writes files 5/6 for repeatable listening; human listening remains pending. `make builtin-preview` also writes files 5/6 for repeatable listening; human
listening remains pending.
## History and tails ---
## History and Tails
Internal effects reset on a transport discontinuity (seek/loop wrap), stopped Internal effects reset on a transport discontinuity (seek/loop wrap), stopped
backend, or newly prepared plan. Pause uses backend Stop and therefore also backend, or newly prepared plan. Pause uses backend Stop and therefore also
resets history. Continuous playback retains tails. Reset invalidates delay resets history. Continuous playback retains tails. Reset invalidates delay
history with a valid-frame counter and clears only fixed-size filter/voice state; history with a valid-frame counter and clears only fixed-size filter/voice
it neither allocates nor clears a large delay buffer in the callback. External state; it neither allocates nor clears a large delay buffer in the callback.
plugins retain their existing history contract. MIDI discontinuities continue External plugins retain their existing history contract. MIDI discontinuities
to chase notes with fresh envelopes. continue to chase notes with fresh envelopes.
**File → Export WAV** renders the song plus the Preferences / transport **File → Export WAV** renders the song plus the Preferences / transport
`tail_ms` duration (0..60000, default 3000). Output is stereo float32 WAV at the `tail_ms` duration (0..60000, default 3000). Output is stereo float32 WAV at the
@ -169,9 +185,9 @@ offline sample rate. The destination must be a new absolute path. Rendering
prepares an independent snapshot, then advances in 256-frame control-thread prepares an independent snapshot, then advances in 256-frame control-thread
steps with a Cancel button; the project and playback instance stay independent. steps with a Cancel button; the project and playback instance stay independent.
Only a complete synced file is published, without replacing an existing file. Only a complete synced file is published, without replacing an existing file.
Cancellation/errors remove the temporary. Missing media rejects export. Cancellation/errors remove the temporary. Missing media rejects export. RIFF's
RIFF's 32-bit size limit is enforced before rendering. Rendering does not apply 32-bit size limit is enforced before rendering. Rendering does not apply loop
loop preferences, normalize, automatically detect silence or stop infinite preferences, normalize, automatically detect silence or stop infinite
oscillator/feedback sources early: the configured endpoint always bounds work. oscillator/feedback sources early: the configured endpoint always bounds work.
The public `xtc_export_create/step/progress/destroy` API supports incremental The public `xtc_export_create/step/progress/destroy` API supports incremental
@ -181,8 +197,9 @@ destruction. `xtc_project_render_length` returns the exact sample endpoint.
Set `stop_after_tail=1` in Preferences to stop ordinary playback after the song Set `stop_after_tail=1` in Preferences to stop ordinary playback after the song
and configured tail. Looping and recording disable this automatic stop. Playback and configured tail. Looping and recording disable this automatic stop. Playback
stop is serviced by the GUI refresh timer and normal stop fade, so it can exceed stop is serviced by the GUI refresh timer and normal stop fade, so it can exceed
the endpoint by a refresh interval plus fade; export has the exact sample length. the endpoint by a refresh interval plus fade; export has the exact sample
Uncheck the setting to retain indefinite playback. Stop always remains available. length. Uncheck the setting to retain indefinite playback. Stop always remains
available.
Tests compare stereo export against direct rendering in 31-frame blocks, verify Tests compare stereo export against direct rendering in 31-frame blocks, verify
nonzero tails, exact length, cancellation, existing-file retention, tail bounds, nonzero tails, exact length, cancellation, existing-file retention, tail bounds,

View file

@ -1,48 +1,124 @@
# Arrangement and composition workflows # Arrangement and Composition Workflows
All mutations run on the control thread through the existing stop, history and All mutations run on the control thread through the existing stop, history and
prepared-plan workflow. No new dependencies are required. prepared-plan workflow. No new dependencies are required.
---
## Persistent Arrangement Markers ## Persistent Arrangement Markers
Edit/Markers/Set at cursor adds or renames a marker; Remove at cursor removes the exact tick key. Markers draw names and vertical guides in the arrangement and survive project save/load and undo. Up to 128 markers are supported. Names may overlap visually when markers are close; markers do not change playback by themselves. Edit/Markers/Set at cursor adds or renames a marker; Remove at cursor removes
the exact tick key. Markers draw names and vertical guides in the arrangement
and survive project save/load and undo. Up to 128 markers are supported. Names
may overlap visually when markers are close; markers do not change playback by
themselves.
---
## Section Navigation and Looping ## Section Navigation and Looping
View/Previous marker and Next marker navigate without wrapping and stop playback before seeking. Loop current section uses the last marker at or before the cursor through its successor; the final section ends at the final clip endpoint. Empty final sections reject. Loop ranges remain existing UI preferences rather than marker metadata. View/Previous marker and Next marker navigate without wrapping and stop playback
before seeking. Loop current section uses the last marker at or before the
cursor through its successor; the final section ends at the final clip endpoint.
Empty final sections reject. Loop ranges remain existing UI preferences rather
than marker metadata.
---
## Ripple Time Editing ## Ripple Time Editing
Edit/Time inserts a gap or removes a tick range beginning at the cursor. Clips crossing either edit boundary reject the entire operation; split them first. Contained clips and their notes are removed; later clips, markers and absolute automation points shift together. Source notes within retained clips do not move. No interpolation guard points are generated, so automation ramps across the boundary may change slope. Loop preferences are unchanged. Edit/Time inserts a gap or removes a tick range beginning at the cursor. Clips
crossing either edit boundary reject the entire operation; split them first.
Contained clips and their notes are removed; later clips, markers and absolute
automation points shift together. Source notes within retained clips do not
move. No interpolation guard points are generated, so automation ramps across
the boundary may change slope. Loop preferences are unchanged.
---
## Complete Track Duplication ## Complete Track Duplication
Edit/Duplicate complete track copies the selected non-bus track adjacent to its source. Clips, notes, instrument settings, static mix, bus routing and mixer automation are independent model copies with fresh IDs; WAV references remain shared. The modular graph and live-input target are unchanged. Bus duplication is rejected. Capacity errors leave the original document unchanged. Edit/Duplicate complete track copies the selected non-bus track adjacent to its
source. Clips, notes, instrument settings, static mix, bus routing and mixer
automation are independent model copies with fresh IDs; WAV references remain
shared. The modular graph and live-input target are unchanged. Bus duplication
is rejected. Capacity errors leave the original document unchanged.
---
## Nondestructive MIDI Clip Splitting ## Nondestructive MIDI Clip Splitting
The existing Split clip at cursor command (S) now handles MIDI as well as audio. Left retains its ID; right gets fresh IDs, the same source content and an adjusted source offset. Hidden notes remain editable. Split is nondestructive, but instrument note envelopes may retrigger at the new boundary; original sine rendering retains source phase. Capacity failures preserve the project. The existing Split clip at cursor command (S) now handles MIDI as well as audio.
Left retains its ID; right gets fresh IDs, the same source content and an
adjusted source offset. Hidden notes remain editable. Split is nondestructive,
but instrument note envelopes may retrigger at the new boundary; original sine
rendering retains source phase. Capacity failures preserve the project.
---
## MIDI Clip Consolidation ## MIDI Clip Consolidation
Edit/Consolidate MIDI clips merges two or more selected MIDI clips on one track. The new clip spans their full bounding range; gaps and overlapping notes remain. Notes outside visible trims are discarded and crossing notes are cropped to audible boundaries. IDs are fresh. Cropped notes start anew, so envelope/phase behavior can change. Audio or cross-track selections reject atomically. Edit/Consolidate MIDI clips merges two or more selected MIDI clips on one track.
The new clip spans their full bounding range; gaps and overlapping notes remain.
Notes outside visible trims are discarded and crossing notes are cropped to
audible boundaries. IDs are fresh. Cropped notes start anew, so envelope/phase
behavior can change. Audio or cross-track selections reject atomically.
---
## Deterministic MIDI Humanization ## Deterministic MIDI Humanization
Edit/MIDI tools/Humanize applies timing variation up to 960 ticks and velocity variation up to 127, using a 32-bit seed and stable note IDs. Repeating from the same original state reproduces results; applying repeatedly accumulates edits. All source notes are affected, including hidden trimmed notes. Timing outside source bounds rejects the entire edit; velocities clamp to 1..127. This is offline editing, not live randomness. Edit/MIDI tools/Humanize applies timing variation up to 960 ticks and velocity
variation up to 127, using a 32-bit seed and stable note IDs. Repeating from the
same original state reproduces results; applying repeatedly accumulates edits.
All source notes are affected, including hidden trimmed notes. Timing outside
source bounds rejects the entire edit; velocities clamp to 1..127. This is
offline editing, not live randomness.
---
## Chord Construction ## Chord Construction
Edit/MIDI tools/Build chords accepts 1..8 unique nonzero semitone intervals, such as 4 7 for a major triad or 3 7 for minor. Originals remain; added notes copy timing, velocity and channel. Negative intervals support lower voicings. All source notes are processed, including hidden trimmed notes. Pitch overflow or capacity rejects the whole operation; existing unisons are not deduplicated. Edit/MIDI tools/Build chords accepts 1..8 unique nonzero semitone intervals,
such as 4 7 for a major triad or 3 7 for minor. Originals remain; added notes
copy timing, velocity and channel. Negative intervals support lower voicings.
All source notes are processed, including hidden trimmed notes. Pitch overflow
or capacity rejects the whole operation; existing unisons are not deduplicated.
---
## Offline Arpeggio Generation ## Offline Arpeggio Generation
Edit/MIDI tools/Generate arpeggio replaces source notes with a written pattern. Equal-onset notes on the same channel form a chord lasting until its longest note end. The pattern cycles sorted pitches upward or downward, with the selected step and gate. Each step inherits its chosen note velocity/channel. All source notes, including hidden trimmed notes, participate. This is offline generation with undo, not a live arpeggiator or tempo-synced MIDI effect. Edit/MIDI tools/Generate arpeggio replaces source notes with a written pattern.
Equal-onset notes on the same channel form a chord lasting until its longest
note end. The pattern cycles sorted pitches upward or downward, with the
selected step and gate. Each step inherits its chosen note velocity/channel. All
source notes, including hidden trimmed notes, participate. This is offline
generation with undo, not a live arpeggiator or tempo-synced MIDI effect.
---
## Audio Peak Normalization ## Audio Peak Normalization
Edit/Audio tools/Normalize source peak accepts a target from -60 to 0 dBFS. The C API accepts a linear target above 0 and at most 1. Analysis uses the audible native-rate source slice and the same mono downmix as playback, before fades or mixer controls. It replaces clip gain without writing WAV data. Silence, missing/invalid media, MIDI selections or required gain above 4 reject the entire batch. This is sample-peak normalization, not true-peak limiting or LUFS loudness normalization. Analysis runs synchronously on the control thread. Edit/Audio tools/Normalize source peak accepts a target from -60 to 0 dBFS. The
C API accepts a linear target above 0 and at most 1. Analysis uses the audible
native-rate source slice and the same mono downmix as playback, before fades or
mixer controls. It replaces clip gain without writing WAV data. Silence,
missing/invalid media, MIDI selections or required gain above 4 reject the
entire batch. This is sample-peak normalization, not true-peak limiting or LUFS
loudness normalization. Analysis runs synchronously on the control thread.
---
## Portable Project Collection ## Portable Project Collection
File/Collect project media... requires a new absolute folder. The original document and files remain unchanged; open project.xtc inside the new folder to use the copy. Referenced audio and track/graph sampler WAVs are deduplicated and copied byte-for-byte, with relative references. Limits are 128 distinct assets, 256 MiB per file, 1 GiB total and a canonical folder path shorter than 480 bytes. Existing destinations, missing media, nonregular files and native graph nodes reject. External plugin binaries/state are not collected; plugins must remain installed. Invalid WAV data is copied rather than repaired. The folder is visible during synchronous collection; failures remove owned outputs, and the project file is saved last. File/Collect project media... requires a new absolute folder. The original
document and files remain unchanged; open project.xtc inside the new folder to
use the copy. Referenced audio and track/graph sampler WAVs are deduplicated and
copied byte-for-byte, with relative references. Limits are 128 distinct assets,
256 MiB per file, 1 GiB total and a canonical folder path shorter than 480
bytes. Existing destinations, missing media, nonregular files and native graph
nodes reject. External plugin binaries/state are not collected; plugins must
remain installed. Invalid WAV data is copied rather than repaired. The folder is
visible during synchronous collection; failures remove owned outputs, and the
project file is saved last.

View file

@ -1,41 +1,45 @@
# Automation and expressive control # Automation and Expressive Control
Track Gain/Pan lanes in the Mixer apply to audio and scheduled instrument voices. Track Gain/Pan lanes in the Mixer apply to audio and scheduled instrument
They use absolute project ticks, linear or hold interpolation, hold-first/last outside voices. They use absolute project ticks, linear or hold interpolation,
points and the constant project tempo snapshot. A clip trim/move does not move hold-first/last outside points and the constant project tempo snapshot. A clip
track or effect automation: lanes belong to the project timeline. Instrument trim/move does not move track or effect automation: lanes belong to the project
volume is controlled by its track Gain lane; the current sine has no timbre timeline. Instrument volume is controlled by its track Gain lane; the current
parameter. Track gain ramps and boundary fades are described in AUDIO_QUALITY.md. sine has no timbre parameter. Track gain ramps and boundary fades are described
in AUDIO_QUALITY.md.
Select a LADSPA node in Modular Graph and use Edit/Selected plugin automation. Select a LADSPA node in Modular Graph and use Edit/Selected plugin automation.
Enter `port tick mode value`: mode 0 interpolates linearly, 1 holds until the next Enter `port tick mode value`: mode 0 interpolates linearly, 1 holds until the
point, and -1 removes the point at that port/tick (value ignored). Port names, next point, and -1 removes the point at that port/tick (value ignored). Port
ranges and current tick are displayed. There are at most 32 points per plugin; names, ranges and current tick are displayed. There are at most 32 points per
setting an existing port/tick replaces it. Integer/toggle controls require step plugin; setting an existing port/tick replaces it. Integer/toggle controls
mode. Invalid values, ticks, ports or duplicate persisted points reject the edit require step mode. Invalid values, ticks, ports or duplicate persisted points
or file. Edits are undoable and rebuild on the control thread while stopped. reject the edit or file. Edits are undoable and rebuild on the control thread
while stopped.
Effect lanes use the project tempo, hold first/last outside their range and Effect lanes use the project tempo, hold first/last outside their range and
apply before run at each exact sample. Distinct ticks rounding to one sample apply before run at each exact sample. Distinct ticks rounding to one sample
resolve to the later tick. Linear curves avoid stepping continuous controls; resolve to the later tick. Linear curves avoid stepping continuous controls;
intentional step curves may click. The initial implementation calls run(1) while intentional step curves may click. The initial implementation calls run(1) while
any lane exists, with connected preallocated buffers. CPU cost can be significant; any lane exists, with connected preallocated buffers. CPU cost can be
only the documented LADSPA set is qualified. No worker, allocator, loader or significant; only the documented LADSPA set is qualified. No worker, allocator,
clock is called from the host audio path. Native ABI 2 callers continue to use loader or clock is called from the host audio path. Native ABI 2 callers
sample-offset parameter events, applied before equal-offset MIDI events. continue to use sample-offset parameter events, applied before equal-offset MIDI
events.
Live MIDI input maps CC7 to per-channel volume (0..1), CC10 to pan (64 center), Live MIDI input maps CC7 to per-channel volume (0..1), CC10 to pan (64 center),
and CC64 to sustain (>=64 down). Volume/pan ramp over 5 ms. These default mappings and CC64 to sustain (>=64 down). Volume/pan ramp over 5 ms. These default
apply to live sine voices, not track faders or prerecorded clips; controller mappings apply to live sine voices, not track faders or prerecorded clips;
capture, automatic learn and plugin MIDI are not supported. CC120/123 and controller capture, automatic learn and plugin MIDI are not supported. CC120/123
Panic release all live voices and sustain and suppress already-started scheduled and Panic release all live voices and sustain and suppress already-started
notes. Note-on velocity zero decodes to note-off. Repeated live note-on on one scheduled notes. Note-on velocity zero decodes to note-off. Repeated live
channel/pitch retriggers one voice; note-off releases it or marks it pending while note-on on one channel/pitch retriggers one voice; note-off releases it or marks
sustain is held. Sustain-up releases pending voices, never newer retriggers. it pending while sustain is held. Sustain-up releases pending voices, never
Seek, stop, teardown and disconnect clear live voices; plan reset returns controller newer retriggers. Seek, stop, teardown and disconnect clear live voices; plan
values to unity/center. Equal-offset live events execute in input order before reset returns controller values to unity/center. Equal-offset live events
the sample; arrange note ends precede starts, and scheduled mixer values are execute in input order before the sample; arrange note ends precede starts, and
evaluated at that sample. Overlapping arranged notes are independent. scheduled mixer values are evaluated at that sample. Overlapping arranged notes
are independent.
Format v6 preserves v5 data plus effect lanes. A v5 plugin migrates with no lane Format v6 preserves v5 data plus effect lanes. A v5 plugin migrates with no lane
and the project tempo. Repeated render/save/reopen, odd block partition, exact and the project tempo. Repeated render/save/reopen, odd block partition, exact
@ -46,12 +50,16 @@ make test test-control test-plugins test-gui-plugins test-plugins-real
``` ```
Physical controller devices and listening remain unqualified; no tempo map, Physical controller devices and listening remain unqualified; no tempo map,
latency compensation, MIDI capture, pitch bend or plugin crash isolation is added. latency compensation, MIDI capture, pitch bend or plugin crash isolation is
added.
## Common parameter targets ---
`xtc_control.h` identifies a parameter by `(kind, stable owner ID, parameter ID)`. ## Common Parameter Targets
IDs never mean a dense list index and are not DSP pointers:
`xtc_control.h` identifies a parameter by
`(kind, stable owner ID, parameter ID)`. IDs never mean a dense list index and
are not DSP pointers:
| Kind | Owner | Parameter IDs | Metadata/reset defaults | | Kind | Owner | Parameter IDs | Metadata/reset defaults |
| --- | --- | --- | --- | | --- | --- | --- | --- |
@ -59,39 +67,44 @@ IDs never mean a dense list index and are not DSP pointers:
| PLUGIN | LADSPA graph node ID | Actual input-control port | Descriptor name, bounds, default and discrete/logarithmic hints; units unspecified | | PLUGIN | LADSPA graph node ID | Actual input-control port | Descriptor name, bounds, default and discrete/logarithmic hints; units unspecified |
| LIVE | MIDI channel 0..15 | 0 volume, 1 pan, 2 sustain | Volume 0..1 default 1; pan -1..1 default 0; sustain boolean default 0 | | LIVE | MIDI channel 0..15 | 0 volume, 1 pan, 2 sustain | Volume 0..1 default 1; pan -1..1 default 0; sustain boolean default 0 |
`xtc_project_parameter` returns copied metadata, static configuration and whether `xtc_project_parameter` returns copied metadata, static configuration and
a lane exists. LIVE values are reset defaults, not callback-owned controller whether a lane exists. LIVE values are reset defaults, not callback-owned
readbacks. `xtc_project_set_parameter` changes TRACK/PLUGIN static configuration controller readbacks. `xtc_project_set_parameter` changes TRACK/PLUGIN static
without deleting automation. `xtc_project_automate` sets/replaces/removes points configuration without deleting automation. `xtc_project_automate`
for TRACK/PLUGIN through one C contract. LIVE static setters and timeline lanes sets/replaces/removes points for TRACK/PLUGIN through one C contract. LIVE
are unsupported: these controls belong to incoming MIDI events, not the arranged static setters and timeline lanes are unsupported: these controls belong to
instrument. Plugin metadata/control edits execute trusted discovery on the incoming MIDI events, not the arranged instrument. Plugin metadata/control edits
control thread; they never run inside audio. Model-only clients must compile and execute trusted discovery on the control thread; they never run inside audio.
publish accepted snapshots before rendering; GUI edits do this transactionally. Model-only clients must compile and publish accepted snapshots before rendering;
GUI edits do this transactionally.
Unknown owners/ports fail without touching the destination model/output. Missing Unknown owners/ports fail without touching the destination model/output. Missing
plugin modules fail discovery rather than guessing ranges or installing a dummy. plugin modules fail discovery rather than guessing ranges or installing a dummy.
Track deletion removes its lanes; plugin deletion removes its embedded lanes. Track deletion removes its lanes; plugin deletion removes its embedded lanes.
Compatible plugin replacement resets controls/automation as described in Compatible plugin replacement resets controls/automation as described in
PLUGIN_HOSTING.md. Persisted dangling references reject the complete load; PLUGIN_HOSTING.md. Persisted dangling references reject the complete load; the
the current project remains intact. The GUI reports rejected edits/Open failures. current project remains intact. The GUI reports rejected edits/Open failures.
## Interpolation and precedence ---
## Interpolation and Precedence
The Mixer Interpolation chooser selects Linear or Hold (step) for the point The Mixer Interpolation chooser selects Linear or Hold (step) for the point
being added/replaced. A point owns the segment beginning there. At a hold point, being added/replaced. A point owns the segment beginning there. At a hold point,
its value remains until the next point's exact sample. Linear segments interpolate its value remains until the next point's exact sample. Linear segments
between endpoint values without an extra smoothing stage. Hold first/last before/ interpolate between endpoint values without an extra smoothing stage. Hold
after the lane. Gain/pan for tracks and buses support both; integer/toggle LADSPA first/last before/ after the lane. Gain/pan for tracks and buses support both;
controls require hold. Existing files migrate mixer points to linear. integer/toggle LADSPA controls require hold. Existing files migrate mixer points
to linear.
Automation is absolute project time, independent of clip starts, ends, trims, Automation is absolute project time, independent of clip starts, ends, trims,
splits and moves; it neither restarts nor resets at a clip boundary. Tick-to-sample splits and moves; it neither restarts nor resets at a clip boundary.
rounding uses the prepared constant tempo/rate; distinct ticks landing on the Tick-to-sample rounding uses the prepared constant tempo/rate; distinct ticks
same sample choose the greatest tick. Empty lanes restore the static value. landing on the same sample choose the greatest tick. Empty lanes restore the
Static edits while a lane exists change the fallback value, not the audible lane. static value. Static edits while a lane exists change the fallback value, not
GUI changes stop/rebuild snapshots; live mutation of faders/plugin instances is the audible lane. GUI changes stop/rebuild snapshots; live mutation of
not introduced. Plugin reset removes lanes only when explicitly applied. faders/plugin instances is not introduced. Plugin reset removes lanes only when
explicitly applied.
At each sample, arranged track/bus and plugin lanes apply to their own targets. At each sample, arranged track/bus and plugin lanes apply to their own targets.
Incoming MIDI volume/pan/sustain affect only live sine voices on that channel, Incoming MIDI volume/pan/sustain affect only live sine voices on that channel,
@ -107,41 +120,47 @@ the new position. Backward jumps evaluate the earlier timeline again; tests
exercise explicit wraps. There is no loop transport feature or promise to exercise explicit wraps. There is no loop transport feature or promise to
restore opaque LADSPA delay history across jumps (transport reset retains it). restore opaque LADSPA delay history across jumps (transport reset retains it).
## Configurable live MIDI bindings ---
## Configurable Live MIDI Bindings
Audio → MIDI controller mapping lists the current 16 channels. Enter Audio → MIDI controller mapping lists the current 16 channels. Enter
`channel volume_CC pan_CC sustain_CC`, for example `2 1 74 11`. Channels are `channel volume_CC pan_CC sustain_CC`, for example `2 1 74 11`. Channels are
zero-based; CCs are 0..127, or 128 to disable a target. Enabled CCs must be distinct zero-based; CCs are 0..127, or 128 to disable a target. Enabled CCs must be
within a channel; CC120 and CC123 cannot be rebound. All channels default to distinct within a channel; CC120 and CC123 cannot be rebound. All channels
7/10/64. Rebinding replaces the old binding; it is not an additional route. An default to 7/10/64. Rebinding replaces the old binding; it is not an additional
unmapped CC does nothing. Other channels remain independent. The same volume, route. An unmapped CC does nothing. Other channels remain independent. The same
pan and sustain transfer functions apply regardless of the chosen CC number. volume, pan and sustain transfer functions apply regardless of the chosen CC
Disabling a target leaves it at its runtime reset value until plan replacement. number. Disabling a target leaves it at its runtime reset value until plan
Changing a map stops/rebuilds playback and is one undoable edit; maps survive replacement. Changing a map stops/rebuilds playback and is one undoable edit;
save/Open and undo/redo. Cancel and invalid/duplicate/reserved bindings retain maps survive save/Open and undo/redo. Cancel and invalid/duplicate/reserved
the prior configuration. There is no automatic learn, arbitrary plugin/track CC bindings retain the prior configuration. There is no automatic learn, arbitrary
mapping, MIDI capture or write/touch/latch automation mode. plugin/track CC mapping, MIDI capture or write/touch/latch automation mode.
Plans copy only 48 binding bytes on the control thread. The callback compares Plans copy only 48 binding bytes on the control thread. The callback compares at
at most three CC numbers per control event, uses existing preallocated ramps/ most three CC numbers per control event, uses existing preallocated ramps/
voices and the existing 256-event block budget. Decode now accepts all valid voices and the existing 256-event block budget. Decode now accepts all valid
three-byte CC messages; even unmapped CCs count against that event budget. three-byte CC messages; even unmapped CCs count against that event budget. Mixer
Mixer points compile to bounded sorted arrays with binary lane lookup; processing points compile to bounded sorted arrays with binary lane lookup; processing does
does no allocation, sort, file/module I/O, locking or GUI work. LADSPA automated no allocation, sort, file/module I/O, locking or GUI work. LADSPA automated
run(1) cost and native plugin trust limits remain unchanged. run(1) cost and native plugin trust limits remain unchanged.
## Persistence and verification ---
## Persistence and Verification
Project v10 appends a mixer interpolation field and 16 controller-map records; Project v10 appends a mixer interpolation field and 16 controller-map records;
v1–v9 migrate to linear mixer lanes and default 7/10/64 maps. LADSPA point encoding v1–v9 migrate to linear mixer lanes and default 7/10/64 maps. LADSPA point
is unchanged. Public `xtc_automation_point` gains `step`: rebuild clients and encoding is unchanged. Public `xtc_automation_point` gains `step`: rebuild
initialize it to 0 (linear) or 1 (hold). See PROJECT_FORMAT.md for exact grammar. clients and initialize it to 0 (linear) or 1 (hold). See PROJECT_FORMAT.md for
exact grammar.
`test_parameters` covers metadata, missing targets, static/automation precedence, `test_parameters` covers metadata, missing targets, static/automation
exact hold/linear boundaries, save/reopen, malformed maps, low-rate sample precedence, exact hold/linear boundaries, save/reopen, malformed maps, low-rate
collisions, odd blocks, seeks and backward jumps. `test_control` compares default sample collisions, odd blocks, seeks and backward jumps. `test_control` compares
and remapped event streams, stereo pan, disabled/unmapped controls and immutable default and remapped event streams, stereo pan, disabled/unmapped controls and
plan maps across resets. Plugin tests cover common-target edits and persistence; immutable plan maps across resets. Plugin tests cover common-target edits and
GUI tests cover interpolation, map rollback, undo/redo and save/Open. Existing persistence; GUI tests cover interpolation, map rollback, undo/redo and
callback guards and sanitizers verify host ownership constraints. Physical save/Open. Existing callback guards and sanitizers verify host ownership
controller devices, listening and extended high-density endurance are pending. constraints. Physical controller devices, listening and extended high-density
endurance are pending.

View file

@ -1,42 +1,53 @@
# Creative workflow additions # Creative Workflow Additions
These features prioritize fast musical experiments and retain the existing These features prioritize fast musical experiments and retain the existing
control-thread preparation and project undo model. They introduce no dependency control-thread preparation and project undo model. They introduce no dependency
or project-format change. or project-format change.
## Selected track inserts ---
## Selected Track Inserts
**Track → Show selected track inserts** (Ctrl+5) opens the sequencer or active **Track → Show selected track inserts** (Ctrl+5) opens the sequencer or active
mixer selection directly. Bus inserts are unsupported; no selection leaves the mixer selection directly. Bus inserts are unsupported; no selection leaves the
workspace unchanged. **View → Track inserts** still opens the rack independently. workspace unchanged. **View → Track inserts** still opens the rack
Ctrl+4 remains the piano-roll command, resolving the earlier shortcut collision. independently. Ctrl+4 remains the piano-roll command, resolving the earlier
shortcut collision.
## Duplicate an effect ---
Select a slot, then **Rack actions → Duplicate effect**. The copy appears directly ## Duplicate an Effect
after it, retaining controls, automation, wet amount and bypass. It is an
independent instance with fresh processing history. Eight-slot capacity and Select a slot, then **Rack actions → Duplicate effect**. The copy appears
directly after it, retaining controls, automation, wet amount and bypass. It is
an independent instance with fresh processing history. Eight-slot capacity and
plugin preparation errors preserve the old chain; Undo removes the copy. plugin preparation errors preserve the old chain; Undo removes the copy.
## Copy a chain between tracks ---
## Copy A Chain Between Tracks
**Rack actions → Copy chain** stores an independent metadata snapshot. Select **Rack actions → Copy chain** stores an independent metadata snapshot. Select
another track and use **Paste chain (replace)** to replace its complete chain, another track and use **Paste chain (replace)** to replace its complete chain,
including output gain. Copying neither loads modules nor changes undo history; including output gain. Copying neither loads modules nor changes undo history;
pasting prepares the candidate and is undoable. The clipboard is internal to pasting prepares the candidate and is undoable. The clipboard is internal to
this session and clears on successful New/Open. Preparation errors keep both this session and clears on successful New/Open. Preparation errors keep both the
the destination and clipboard intact. destination and clipboard intact.
## Compare effect configurations ---
**Rack actions → Compare** captures or recalls A and B. Each slot is bound to the ## Compare Effect Configurations
track where it was captured; Recall is available only on that track. Capturing
copies metadata without affecting undo or loading modules. Recall prepares a
fresh chain and is undoable; histories and opaque plugin states are excluded.
The two comparison slots clear on successful New/Open. Comparisons stop
transport when recalled, following the existing safe insert-edit workflow.
## Creative starting points **Rack actions → Compare** captures or recalls A and B. Each slot is bound to
the track where it was captured; Recall is available only on that track.
Capturing copies metadata without affecting undo or loading modules. Recall
prepares a fresh chain and is undoable; histories and opaque plugin states are
excluded. The two comparison slots clear on successful New/Open. Comparisons
stop transport when recalled, following the existing safe insert-edit workflow.
---
## Creative Starting Points
**Add effect → Templates (replace)** offers Slapback (80 ms delay), Dub Echo **Add effect → Templates (replace)** offers Slapback (80 ms delay), Dub Echo
(375 ms feedback delay into a short reverb), and Ambient Space (long reverb into (375 ms feedback delay into a short reverb), and Ambient Space (long reverb into
@ -45,15 +56,19 @@ no external plugins. All controls remain editable. Times are milliseconds;
feedback and wet levels are deliberate starting points, without automatic feedback and wet levels are deliberate starting points, without automatic
loudness matching or a claim that the presets suit every source. loudness matching or a claim that the presets suit every source.
## Bulk rack actions ---
## Bulk Rack Actions
**Enable all effects**, **Bypass all effects**, and **Clear chain** each prepare **Enable all effects**, **Bypass all effects**, and **Clear chain** each prepare
one undoable edit. Enabling all intentionally enables every slot, including one undoable edit. Enabling all intentionally enables every slot, including
previously bypassed effects; Undo restores the previous mixed bypass state. previously bypassed effects; Undo restores the previous mixed bypass state.
Clear removes slots while preserving chain output gain. No plugin resources Clear removes slots while preserving chain output gain. No plugin resources are
are released in the audio callback. Empty racks return a no-result error. released in the audio callback. Empty racks return a no-result error.
## Meter while editing effects ---
## Meter While Editing Effects
The insert workspace shows the selected track's stereo sample peaks after its The insert workspace shows the selected track's stereo sample peaks after its
fader and before its bus, including the effects' output. Green bars release at fader and before its bus, including the effects' output. Green bars release at
@ -62,34 +77,41 @@ it. Stopped, offline or missing-track readings show the -90 dBFS floor. Changing
tracks clears the indicator. These are sample peaks, not true-peak or loudness tracks clears the indicator. These are sample peaks, not true-peak or loudness
measurements. The display shares the mixer's existing backend snapshot read. measurements. The display shares the mixer's existing backend snapshot read.
## Tap tempo ---
## Tap Tempo
Tap the transport **Tap** button at a quarter-note pulse. The estimator averages Tap the transport **Tap** button at a quarter-note pulse. The estimator averages
the last four valid intervals and accepts 20–300 BPM. The first tap primes it; the last four valid intervals and accepts 20–300 BPM. The first tap primes it; a
a pause longer than three seconds restarts it. Too-fast or backward timestamps pause longer than three seconds restarts it. Too-fast or backward timestamps are
are ignored. Each accepted tempo is an undoable edit and stops playback before ignored. Each accepted tempo is an undoable edit and stops playback before
preparation; tapping is unavailable while recording. New/Open resets tap history. preparation; tapping is unavailable while recording. New/Open resets tap
The estimator uses monotonic control-thread time, with no audio-callback clock history. The estimator uses monotonic control-thread time, with no
access or allocation. Tap timing is not a beat detector or MIDI clock sync. audio-callback clock access or allocation. Tap timing is not a beat detector or
MIDI clock sync.
## Audio crossfades ---
Select two partially overlapping audio clips on the same track and choose ## Audio Crossfades
**Edit → Audio tools → Crossfade two selected clips**. The earlier clip fades
out across the overlap while the later clip fades in. Gains, positions, source
files and the opposite fades are preserved. Equal boundaries, containment,
adjacency, cross-track selections and MIDI clips reject without partial edits.
This is a linear tick-based crossfade using existing clip envelopes; it does
not move clips, create overlaps or offer equal-power curves. Discrete sample
endpoints retain the engine's existing one-sample boundary convention.
## Chord strumming Select two partially overlapping audio clips on the same track and choose **Edit
→ Audio tools → Crossfade two selected clips**. The earlier clip fades out
across the overlap while the later clip fades in. Gains, positions, source files
and the opposite fades are preserved. Equal boundaries, containment, adjacency,
cross-track selections and MIDI clips reject without partial edits. This is a
linear tick-based crossfade using existing clip envelopes; it does not move
clips, create overlaps or offer equal-power curves. Discrete sample endpoints
retain the engine's existing one-sample boundary convention.
---
## Chord Strumming
Select MIDI clips and use **Edit → MIDI tools → Strum chords...**. Enter spacing Select MIDI clips and use **Edit → MIDI tools → Strum chords...**. Enter spacing
in ticks (1–960) and direction (0 ascending, 1 descending). Notes sharing a source in ticks (1–960) and direction (0 ascending, 1 descending). Notes sharing a
onset and MIDI channel within each clip form a chord. Equal pitches use stable source onset and MIDI channel within each clip form a chord. Equal pitches use
note-ID order. Each successive note starts one spacing later, while its end, stable note-ID order. Each successive note starts one spacing later, while its
pitch, velocity and channel remain unchanged. Single notes remain unchanged. end, pitch, velocity and channel remain unchanged. Single notes remain
Any resulting zero/negative duration rejects the entire edit; Undo restores the unchanged. Any resulting zero/negative duration rejects the entire edit; Undo
whole selection. This edits source notes, including notes hidden by clip trims. restores the whole selection. This edits source notes, including notes hidden by
It does not generate an arpeggiator or schedule live input. clip trims. It does not generate an arpeggiator or schedule live input.

View file

@ -1,11 +1,13 @@
# Editing commands and undo audit # Editing Commands and Undo Audit
The control thread owns project edits and history. Audio plans are immutable The control thread owns project edits and history. Audio plans are immutable
snapshots; widgets never pass editable project pointers into the callback. snapshots; widgets never pass editable project pointers into the callback. Graph
Graph commands and batch note commands use the existing public C command APIs. commands and batch note commands use the existing public C command APIs. The GUI
The GUI groups a completed gesture or accepted dialog as one history step. groups a completed gesture or accepted dialog as one history step.
## Operation audit ---
## Operation Audit
| Operation | History / grouping | Engine and external effects | | Operation | History / grouping | Engine and external effects |
| --- | --- | --- | | --- | --- | --- |
@ -30,7 +32,9 @@ plus a baseline; new accepted edits discard redo, and eviction drops the oldest
baseline. Monotonic ID high-water marks survive undo and branches. History is baseline. Monotonic ID high-water marks survive undo and branches. History is
session-only; restart/New/Open discards it. session-only; restart/New/Open discards it.
## History ownership and failure boundary ---
## History Ownership and Failure Boundary
`xtc_history_reserve` allocates one empty fixed-capacity project/graph snapshot `xtc_history_reserve` allocates one empty fixed-capacity project/graph snapshot
before an edit mutates the document or a candidate is submitted to the engine. before an edit mutates the document or a candidate is submitted to the engine.
@ -47,8 +51,8 @@ The snapshot contains metadata/graph configuration only, never sample buffers,
DSP instances, module handles or GUI pointers. An unused reservation is freed DSP instances, module handles or GUI pointers. An unused reservation is freed
with history. This adds at most one pending metadata snapshot to the 33 retained with history. This adds at most one pending metadata snapshot to the 33 retained
states. C clients that call record without reserving retain the original states. C clients that call record without reserving retain the original
clone-and-report-allocation-failure behavior; they must handle that result. clone-and-report-allocation-failure behavior; they must handle that result. All
All history work is serialized on the control thread, never the audio callback. history work is serialized on the control thread, never the audio callback.
Undo/redo checks out an owned candidate without moving the cursor, stops/resets Undo/redo checks out an owned candidate without moving the cursor, stops/resets
transport and prepares its assets/plans, then accepts the adjacent history step transport and prepares its assets/plans, then accepts the adjacent history step
@ -57,33 +61,38 @@ cursor/document, so retry is possible. Preparation failure may leave transport
stopped at zero; it does not resume playback automatically. Snapshot restoration stopped at zero; it does not resume playback automatically. Snapshot restoration
marks the document dirty conservatively, even if it matches a saved file. marks the document dirty conservatively, even if it matches a saved file.
## Selection, focus and menus ---
Ctrl+Z undoes; Ctrl+Shift+Z and Ctrl+Y redo. The main Edit menu disables unavailable ## Selection, Focus and Menus
steps and disables history while recording. Piano roll also has Edit/Undo/Redo
shortcuts. Undo preserves currently selected track/clip, mixer strip, graph node
and piano-roll clip/note when their stable IDs still exist (notes must remain
visible under the current filter). Clip selection follows its restored track. A removed selection
clears, with track fallback for a removed clip. A visible piano roll stays open
when its clip survives; missing clips hide it. Historical selections/layouts are
not saved in snapshots; undo does not resurrect a selection already cleared by
a deletion. Open/New retain their reset behavior.
Editor-local shortcuts act on focused editors. Text inputs retain Space and Ctrl+Z undoes; Ctrl+Shift+Z and Ctrl+Y redo. The main Edit menu disables
text undo/editing keys; numeric mixer inputs do not invoke fader shortcuts. unavailable steps and disables history while recording. Piano roll also has
Timeline Ctrl+D duplicates, F2 renames, Alt+Up/Down reorders tracks, S splits Edit/Undo/Redo shortcuts. Undo preserves currently selected track/clip, mixer
audio at the cursor, A opens audio properties and Delete removes the selection. strip, graph node and piano-roll clip/note when their stable IDs still exist
Graph Left/Right chooses a node, L arranges by flow, I inserts unity gain in the (notes must remain visible under the current filter). Clip selection follows its
selected wire, Delete removes the selected node/wire and Escape cancels gestures. restored track. A removed selection clears, with track fallback for a removed
Piano-roll commands are listed in its Notes menu. Escape cancels gestures; clip. A visible piano roll stays open when its clip survives; missing clips hide
ordinary deletion is reversible and does not add a confirmation dialog. it. Historical selections/layouts are not saved in snapshots; undo does not
resurrect a selection already cleared by a deletion. Open/New retain their reset
behavior.
Editor-local shortcuts act on focused editors. Text inputs retain Space and text
undo/editing keys; numeric mixer inputs do not invoke fader shortcuts. Timeline
Ctrl+D duplicates, F2 renames, Alt+Up/Down reorders tracks, S splits audio at
the cursor, A opens audio properties and Delete removes the selection. Graph
Left/Right chooses a node, L arranges by flow, I inserts unity gain in the
selected wire, Delete removes the selected node/wire and Escape cancels
gestures. Piano-roll commands are listed in its Notes menu. Escape cancels
gestures; ordinary deletion is reversible and does not add a confirmation
dialog.
Graph right-click now selects the node/wire under the pointer before showing Graph right-click now selects the node/wire under the pointer before showing
Inspect/Arrange/Insert/Delete; empty space clears a stale selection. Piano-roll Inspect/Arrange/Insert/Delete; empty space clears a stale selection. Piano-roll
right-click selects the clicked note and offers Properties/Quantize/Repeat/Delete. right-click selects the clicked note and offers
It no longer immediately deletes the note. Timeline context menus retain their Properties/Quantize/Repeat/Delete. It no longer immediately deletes the note.
selection and duplicate/rename/delete/audio commands. Dismissing a context menu Timeline context menus retain their selection and duplicate/rename/delete/audio
does not edit the document. Context menus and widgets remain C++/FLTK UI code. commands. Dismissing a context menu does not edit the document. Context menus
and widgets remain C++/FLTK UI code.
Open/import/plugin load/replacement paint a busy cursor and operation status Open/import/plugin load/replacement paint a busy cursor and operation status
before synchronous work, flushing drawing without dispatching new input or before synchronous work, flushing drawing without dispatching new input or
@ -93,13 +102,15 @@ optional-support and audio errors. Unsaved New/Open/Quit and Save As overwrite
keep their existing confirmations; undoable deletes and external-media reference keep their existing confirmations; undoable deletes and external-media reference
edits do not request extra approval. No operation here deletes source media. edits do not request extra approval. No operation here deletes source media.
## Evidence and manual procedure ---
`make all test gui test-gui-plugins test-reliability analyze test-workflow ## Evidence and Manual Procedure
test-gui-jack` covers core commands, grouped gestures/batches, backend rollback,
selection retention, take undo/redo and real software JACK state restoration. `make all test gui test-gui-plugins test-reliability analyze test-workflow test-gui-jack`
The GUI fixture injects history-reservation failure before graph/map/import covers core commands, grouped gestures/batches, backend rollback, selection
publication and verifies unchanged document, submission count and redo. The retention, take undo/redo and real software JACK state restoration. The GUI
fixture injects history-reservation failure before graph/map/import publication
and verifies unchanged document, submission count and redo. The
allocation-interposed reliability suite fails each reservation allocation, then allocation-interposed reliability suite fails each reservation allocation, then
fails all allocations while capturing a reserved edit; redo branching and fails all allocations while capturing a reserved edit; redo branching and
snapshot restoration still succeed. ASan/UBSan cover the same ownership paths. snapshot restoration still succeed. ASan/UBSan cover the same ownership paths.

View file

@ -1,44 +1,51 @@
# GUI guidelines # GUI Guidelines
Use FLTK/C++ for windows, menus, dialogs, and the event loop. Keep all audio Use FLTK/C++ for windows, menus, dialogs, and the event loop. Keep all audio
engine and project-domain logic in C. engine and project-domain logic in C.
Main views: Main views:
- Sequencer: tracks, clips, ruler, playhead, selection, zoom and scrolling. - Sequencer: tracks, clips, ruler, playhead, selection, zoom and scrolling.
- Mixer: channels, faders, pan, mute/solo and meters. - Mixer: channels, faders, pan, mute/solo and meters.
- Modular graph: nodes, ports, connections, validation feedback. - Modular graph: nodes, ports, connections, validation feedback.
- Transport: play/stop/record, tempo, meter, loop and position. - Transport: play/stop/record, tempo, meter, loop and position.
Use custom-drawn widgets where ordinary controls are not a good fit: timeline, Use custom-drawn widgets where ordinary controls are not a good fit: timeline,
waveforms, piano roll, mixer strips and graph canvas. Keep drawing separate waveforms, piano roll, mixer strips and graph canvas. Keep drawing separate from
from project mutation. Never call FLTK from the audio thread. project mutation. Never call FLTK from the audio thread.
Visual direction: compact, high-contrast, restrained dark interface. Avoid Visual direction: compact, high-contrast, restrained dark interface. Avoid
excessive animation and modal configuration. Recreate broad usability ideas, excessive animation and modal configuration. Recreate broad usability ideas, not
not energyXT's exact artwork or proprietary resources. energyXT's exact artwork or proprietary resources.
---
## Shell ## Shell
The shell opens JACK on startup through `xtc_backend.h`. It never accesses The shell opens JACK on startup through `xtc_backend.h`. It never accesses
backend structures, processes samples, or calls JACK itself. Graph domain backend structures, processes samples, or calls JACK itself. Graph domain data
data is owned by opaque C objects, manipulated only through the C API. All backend control calls run on the GUI thread; FLTK is never called is owned by opaque C objects, manipulated only through the C API. All backend
from an audio callback. No server is started and no audio ports are connected control calls run on the GUI thread; FLTK is never called from an audio
by the GUI. The initially empty graph produces silence. callback. No server is started and no audio ports are connected by the GUI. The
initially empty graph produces silence.
- **Play / F5:** start the engine. **Stop / F8:** deactivate it. **Pause / F7:** hold playback. **Space:** toggle Play/Stop. - **Play / F5:** start the engine. **Stop / F8:** deactivate it. **Pause / F7:**
**To start / Home:** stop and seek to zero. Pause/Home are disabled while recording. hold playback. **Space:** toggle Play/Stop. **To start / Home:** stop and seek
These advance/pause the sample-counted playhead; imported audio clips play through their graph source. to zero. Pause/Home are disabled while recording. These advance/pause the
- **Reconnect / F6:** close the old client and open a fresh stopped client. sample-counted playhead; imported audio clips play through their graph source.
Use this after server loss or sample-rate changes. A failed close preserves - **Reconnect / F6:** close the old client and open a fresh stopped client. Use
the handle and reports the failure instead of opening another client. this after server loss or sample-rate changes. A failed close preserves the
handle and reports the failure instead of opening another client.
- **View menu / Ctrl+1, Ctrl+2, Ctrl+3:** switch between sequencer, mixer and - **View menu / Ctrl+1, Ctrl+2, Ctrl+3:** switch between sequencer, mixer and
modular graph. Tabs provide the same view switching. Mixer, graph and sequencer editors are described below. modular graph. Tabs provide the same view switching. Mixer, graph and
sequencer editors are described below.
- **File / Quit / Ctrl+Q** or the window close button: close the backend and - **File / Quit / Ctrl+Q** or the window close button: close the backend and
synchronize audio callbacks before hiding/destroying widgets. A failed close synchronize audio callbacks before hiding/destroying widgets. A failed close
leaves the window open for a retry. New/Open/Save actions are described below. About identifies the development milestone and version. leaves the window open for a retry. New/Open/Save actions are described below.
About identifies the development milestone and version.
The indicator shows disconnected, ready, running, server lost, format changed The indicator shows disconnected, ready, running, server lost, format changed or
or process-error states. The status line shows rate, actual block size, mono process-error states. The status line shows rate, actual block size, mono
layout, processed block count, xruns and process result. Command errors remain layout, processed block count, xruns and process result. Command errors remain
visible until a successful control action; periodic refresh does not erase them. visible until a successful control action; periodic refresh does not erase them.
A GUI-owned 100 ms timer reads atomic snapshots and collects retired plans. A GUI-owned 100 ms timer reads atomic snapshots and collects retired plans.
@ -54,41 +61,44 @@ process exit. Normal close/reconnect paths permit retry and release ownership
only after successful close. The entry point catches C++ exceptions before only after successful close. The entry point catches C++ exceptions before
process exit. Backend operations return C error codes throughout. process exit. Backend operations return C error codes throughout.
Tests: `make test-gui` runs real widgets/callbacks/timers against a fake C backend Tests: `make test-gui` runs real widgets/callbacks/timers against a fake C
under Xvfb, including control errors, startup without JACK, partially initialized backend under Xvfb, including control errors, startup without JACK, partially
handles, server/format notifications, view switching, retrying close, and timer initialized handles, server/format notifications, view switching, retrying
removal. `make test-gui-jack` additionally requires a running JACK-compatible close, and timer removal. `make test-gui-jack` additionally requires a running
server, tests repeated start/stop, reconnect and active-client close, and creates JACK-compatible server, tests repeated start/stop, reconnect and active-client
no routes. Physical playback is outside this shell. See BUILD.md and the close, and creates no routes. Physical playback is outside this shell. See
integration test README for sanitizer limitations and keyboard smoke results. BUILD.md and the integration test README for sanitizer limitations and keyboard
smoke results.
## Modular editor ---
Open **Modular Graph** or press **Ctrl+3**. Add oscillator (440 Hz), gain (0.10), ## Modular Editor
pass-through meter or output nodes with the toolbar. Their parameters are fixed
presets in this milestone and are drawn on each node; parameter editing and Open **Modular Graph** or press **Ctrl+3**. Add oscillator (440 Hz), gain
live meter display are not implemented. Connect oscillator → gain → meter → (0.10), pass-through meter or output nodes with the toolbar. Their parameters
output for a simple test chain. No sound reaches hardware until ports are are fixed presets in this milestone and are drawn on each node; parameter
explicitly connected outside the GUI. Output nodes are required to render; editing and live meter display are not implemented. Connect oscillator → gain →
meter → output for a simple test chain. No sound reaches hardware until ports
are explicitly connected outside the GUI. Output nodes are required to render;
isolated sources and an empty graph produce silence. isolated sources and an empty graph produce silence.
Drag a node body/title to move it. Drag a circular output port to an input port Drag a node body/title to move it. Drag a circular output port to an input port
to connect. Click a node or wire to select it (yellow outline/wire), then press to connect. Click a node or wire to select it (yellow outline/wire), then press
Delete/Backspace or **Delete selection**. Deleting a node deletes its incident Delete/Backspace or **Delete selection**. Deleting a node deletes its incident
wires. Escape cancels a drag preview. Canvas scrollbars pan the view; positions wires. Escape cancels a drag preview. Canvas scrollbars pan the view; positions
are retained across edits/reconnect but not across application restart. There are retained across edits/reconnect but not across application restart. There is
is no graph zoom, multiselection or external hardware routing UI. Semantic no graph zoom, multiselection or external hardware routing UI. Semantic graph
graph edits participate in project undo/redo; layout does not. The default edits participate in project undo/redo; layout does not. The default project
project supports 64 nodes; auto-layout can grow the scrollable canvas. supports 64 nodes; auto-layout can grow the scrollable canvas.
Every semantic action becomes a C graph command. The window owns a C project containing the graph Every semantic action becomes a C graph command. The window owns a C project
handle and publishes the C-created candidate through the backend. Cycle, containing the graph handle and publishes the C-created candidate through the
invalid-direction, duplicate connection, missing-port, capacity and backend backend. Cycle, invalid-direction, duplicate connection, missing-port, capacity
failures are shown below the canvas; failed edits leave the graph unchanged. and backend failures are shown below the canvas; failed edits leave the graph
Capacity feedback distinguishes the possible node limit/pending edit condition unchanged. Capacity feedback distinguishes the possible node limit/pending edit
and allows retry. Editing while stopped replaces the prepared plan without condition and allows retry. Editing while stopped replaces the prepared plan
starting playback. Editing with a disconnected startup backend stays local without starting playback. Editing with a disconnected startup backend stays
until reconnect. A lost or changed live backend requires reconnect before local until reconnect. A lost or changed live backend requires reconnect before
further semantic edits. Moving/selecting nodes does not touch the engine. further semantic edits. Moving/selecting nodes does not touch the engine.
The graph state line distinguishes offline, prepared, queued and active states. The graph state line distinguishes offline, prepared, queued and active states.
@ -100,54 +110,59 @@ rejected capacity edit. Plan replacement can reset oscillator phase and is not
crossfaded. Render validation and active replacement tests are described in crossfaded. Render validation and active replacement tests are described in
tests/integration/README.md. tests/integration/README.md.
---
## Projects and sequencer ## Projects and Sequencer
File/New (Ctrl+N), Open (Ctrl+O), Save (Ctrl+S), and Save As File/New (Ctrl+N), Open (Ctrl+O), Save (Ctrl+S), and Save As (Ctrl+Shift+S) use
(Ctrl+Shift+S) use native file dialogs and the C persistence API. New/Open/Quit native file dialogs and the C persistence API. New/Open/Quit offer
offer Cancel/Discard when semantic edits are unsaved; save first to retain them. Cancel/Discard when semantic edits are unsaved; save first to retain them. Save
Save As confirms overwrites. The title marks unsaved changes. Failed loads and As confirms overwrites. The title marks unsaved changes. Failed loads and
backend submissions retain the old document. Loading parses before stopping backend submissions retain the old document. Loading parses before stopping
audio; installing a valid candidate stops and resets transport before compiling audio; installing a valid candidate stops and resets transport before compiling
and submitting. A later failure can leave the retained document stopped at zero. and submitting. A later failure can leave the retained document stopped at zero.
Opening/New resets graph layout; graph positions are not saved. See PROJECT_FORMAT.md. Opening/New resets graph layout; graph positions are not saved. See
PROJECT_FORMAT.md.
Sequencer offers Add track, Add region, Delete region/track, Zoom +/-. Select a Sequencer offers Add track, Add region, Delete region/track, Zoom +/-. Select a
track header to add a one-bar region there at the cursor. Select a region to track header to add a one-bar region there at the cursor. Select a region to
delete it; deleting a selected track cascades to its regions. Delete also works delete it; deleting a selected track cascades to its regions. Delete also works
with canvas focus. Drag a region body to move it in time or between tracks; with canvas focus. Drag a region body to move it in time or between tracks; drag
drag either edge to trim within its original source bounds. Edits commit on either edge to trim within its original source bounds. Edits commit on release
release with the selected snap grid; Escape cancels. There is no drag preview. with the selected snap grid; Escape cancels. There is no drag preview. Undo,
Undo, rename, tempo editing and additive clip selection are supported. Group rename, tempo editing and additive clip selection are supported. Group commands,
commands, clipboard and musical transforms are described in clipboard and musical transforms are described in
[MODERN_EDITING.md](MODERN_EDITING.md). Overlapping regions are permitted. [MODERN_EDITING.md](MODERN_EDITING.md). Overlapping regions are permitted.
The scrollable canvas expands to cover the arrangement plus a margin; zoom The scrollable canvas expands to cover the arrangement plus a margin; zoom
ranges from 24 to 384 pixels/quarter. Names, tempo and 4/4 meter are drawn. ranges from 24 to 384 pixels/quarter. Names, tempo and 4/4 meter are drawn. A
A ruler click seeks while stopped (or positions the cursor when disconnected). ruler click seeks while stopped (or positions the cursor when disconnected). The
The 100 ms GUI refresh converts the backend's actual sample counter to ticks; 100 ms GUI refresh converts the backend's actual sample counter to ticks; it
it never integrates wall-clock time. Play resumes and Stop pauses. Reconnect, never integrates wall-clock time. Play resumes and Stop pauses. Reconnect, New
New and Open reset position. Add region creates silent metadata; imported WAV clips play through the and Open reset position. Add region creates silent metadata; imported WAV clips
audio source node (see below). MIDI clips and notes are described below. The C model play through the audio source node (see below). MIDI clips and notes are
supports a constant tempo; a tempo map and scheduler are future work. described below. The C model supports a constant tempo; a tempo map and
scheduler are future work.
Tests cover C-model/persistence boundaries, GUI move/trim gestures and zoom, Tests cover C-model/persistence boundaries, GUI move/trim gestures and zoom,
save/reopen, failed load/submission rollback, sample-derived cursor updates, save/reopen, failed load/submission rollback, sample-derived cursor updates, and
and live JACK playhead advance/pause. live JACK playhead advance/pause.
---
## Audio workflow ## Audio Workflow
Use File/Import WAV (Ctrl+I) to add an audio clip at the playhead on the selected Use File/Import WAV (Ctrl+I) to add an audio clip at the playhead on the
track, or on a new Audio track. Record (Shift+F5) chooses a WAV destination and selected track, or on a new Audio track. Record (Shift+F5) chooses a WAV
captures JACK input until Stop finalizes and adds the take. No input routes are destination and captures JACK input until Stop finalizes and adds the take. No
created automatically. Save after Stop; the project references the external WAV. input routes are created automatically. Save after Stop; the project references
the external WAV.
Import creates an Audio clips graph source and output route if the source is Import creates an Audio clips graph source and output route if the source is
absent. Modular Graph offers Add audio source for explicit routing through gain absent. Modular Graph offers Add audio source for explicit routing through gain
or meter nodes. All tracks currently share one mono timeline mix. Arrangement or meter nodes. All tracks currently share one mono timeline mix. Arrangement
edits stop playback; graph edits remain live outside recording. During recording, edits stop playback; graph edits remain live outside recording. During
edits, file actions, reconnect and close require Stop first. recording, edits, file actions, reconnect and close require Stop first.
Open/Play reports missing, unreadable or unsupported assets, preserving their Open/Play reports missing, unreadable or unsupported assets, preserving their
regions as silence. The backend status shows the unique issue count. Select a regions as silence. The backend status shows the unique issue count. Select a
@ -158,21 +173,26 @@ can block the GUI while reading, but never the audio callback.
See AUDIO_FILES.md for supported WAV formats, memory limits, musical placement, See AUDIO_FILES.md for supported WAV formats, memory limits, musical placement,
recording overflow/partial-take recovery, external asset portability and the recording overflow/partial-take recovery, external asset portability and the
software-only acceptance tests. Waveform drawing, input monitoring, recording software-only acceptance tests. Waveform drawing, input monitoring, recording
latency compensation and per-track graph inserts are not implemented. latency compensation and per-track graph inserts are not implemented. Per-track
Per-track gain/pan/mute/solo is available in the mixer below. gain/pan/mute/solo is available in the mixer below.
## Piano roll ---
## Piano Roll
Add MIDI clip creates a one-bar clip on the selected/first existing track at the Add MIDI clip creates a one-bar clip on the selected/first existing track at the
cursor. Select it and choose Piano roll (View/Piano roll, Ctrl+4). Click to draw, cursor. Select it and choose Piano roll (View/Piano roll, Ctrl+4). Click to
drag a body to move pitch/time, drag its right edge to resize, and right-click draw, drag a body to move pitch/time, drag its right edge to resize, and
or Delete to remove. Notes use source ticks with 1/16 snapping; trimmed source right-click or Delete to remove. Notes use source ticks with 1/16 snapping;
content is retained. Edits stop transport and are blocked during audio recording. trimmed source content is retained. Edits stop transport and are blocked during
Panic/F9 silences held notes; Record continues to mean WAV audio capture. audio recording. Panic/F9 silences held notes; Record continues to mean WAV
Modular Graph offers Add instrument for explicit routing. Status includes MIDI audio capture. Modular Graph offers Add instrument for explicit routing. Status
drops. See MIDI.md for voice limits, unsupported messages and live input routing. includes MIDI drops. See MIDI.md for voice limits, unsupported messages and live
input routing.
## Mixer and editing ---
## Mixer and Editing
Mixer (Ctrl+2) now has a selectable track strip, gain/pan controls, mute/solo, Mixer (Ctrl+2) now has a selectable track strip, gain/pan controls, mute/solo,
Output L/R peaks and editable gain/pan automation lanes. Mixer edits stop Output L/R peaks and editable gain/pan automation lanes. Mixer edits stop
@ -182,35 +202,42 @@ and redo up to 32 project edits; New/Open resets history. M/S and arrow keys
provide mixer shortcuts. See MIXER.md for exact controls, history rollback, provide mixer shortcuts. See MIXER.md for exact controls, history rollback,
stereo port routing, automation semantics and prototype limits. stereo port routing, automation semantics and prototype limits.
## Minimal composition workflow ---
## Minimal Composition Workflow
[WORKFLOW.md](WORKFLOW.md) walks through creating, arranging, saving, closing, [WORKFLOW.md](WORKFLOW.md) walks through creating, arranging, saving, closing,
reopening and playing MIDI/audio with the GUI. New/Open select Sequencer; reopening and playing MIDI/audio with the GUI. New/Open select Sequencer; Ctrl+3
Ctrl+3 still opens the modular graph. Stop retains its established position-hold still opens the modular graph. Stop retains its established position-hold
behavior and finalizes recording; explicit Pause retains position without behavior and finalizes recording; explicit Pause retains position without
finalizing a take (and is refused during recording). Home stops and returns to finalizing a take (and is refused during recording). Home stops and returns to
zero. Failed candidate installation now refreshes the actual backend state zero. Failed candidate installation now refreshes the actual backend state even
even when the previous document is retained. when the previous document is retained.
`make test-workflow` uses a live JACK-compatible backend and Xvfb, with exact `make test-workflow` uses a live JACK-compatible backend and Xvfb, with exact
stereo render comparisons across destruction/recreation of the GUI and live stereo render comparisons across destruction/recreation of the GUI and live
playhead/meter/transport checks. `test-gui` covers stop/seek failure and recording playhead/meter/transport checks. `test-gui` covers stop/seek failure and
guards. No project files need manual editing for this workflow. recording guards. No project files need manual editing for this workflow.
## Timing and transitions ---
## Timing and Transitions
The GUI enables 5 ms source boundaries and master start/stop ramps. Ordinary The GUI enables 5 ms source boundaries and master start/stop ramps. Ordinary
Pause/Stop now request a fade and show “stopping” until the timer collects its Pause/Stop now request a fade and show “stopping” until the timer collects its
acknowledgement and deactivates JACK. Recording Stop, Home, fault handling and acknowledgement and deactivates JACK. Recording Stop, Home, fault handling and
project installation retain immediate semantics. See project installation retain immediate semantics. See
[AUDIO_QUALITY.md](AUDIO_QUALITY.md) for timing, overload/denormal behavior, latency [AUDIO_QUALITY.md](AUDIO_QUALITY.md) for timing, overload/denormal behavior,
limits, test commands and the pending human-listening checklist. latency limits, test commands and the pending human-listening checklist.
## Routing refinement ---
## Routing Refinement
Graph selection now reports edge counts, output reachability and external-input Graph selection now reports edge counts, output reachability and external-input
fallback. Direction arrows, role colors, incident-wire highlighting and no-output fallback. Direction arrows, role colors, incident-wire highlighting and
badges help inspect routes. View / Arrange graph by signal flow (L) is a layout no-output badges help inspect routes. View / Arrange graph by signal flow (L) is
command; Edit / Insert unity gain in selected wire (I) is one undoable graph edit. a layout command; Edit / Insert unity gain in selected wire (I) is one undoable
Left/Right selects nodes; Tab retains ordinary focus navigation. See [ROUTING.md](ROUTING.md) for summing, stereo-lane, graph edit. Left/Right selects nodes; Tab retains ordinary focus navigation. See
cycle, live publication and deferred grouping/template semantics. [ROUTING.md](ROUTING.md) for summing, stereo-lane, cycle, live publication and
deferred grouping/template semantics.

View file

@ -1,48 +1,78 @@
# Project Brief: A Modern, Lightweight, energyXT-Inspired DAW # Project Brief: A Modern, Lightweight, energyXT-Inspired DAW
**Document purpose:** This file is the authoritative project brief and implementation guide for an AI coding agent working inside an IDE. **Document purpose:** This file is the authoritative project brief and
implementation guide for an AI coding agent working inside an IDE.
**Working project name:** `xtc` (temporary; keep the project name configurable and do not hard-code it into public APIs). **Working project name:** `xtc` (temporary; keep the project name configurable
and do not hard-code it into public APIs).
**Primary goal:** Build a modern, reliable, lightweight digital audio workstation inspired by the workflow and modular philosophy of energyXT. This is a new implementation, not a clone of energyXT's proprietary code or assets. **Primary goal:** Build a modern, reliable, lightweight digital audio
workstation inspired by the workflow and modular philosophy of energyXT. This is
a new implementation, not a clone of energyXT's proprietary code or assets.
**Architecture decision:** **B — Modular engine first.** The sequencer, instruments, effects, mixers, audio inputs, and outputs are components in one unified processing graph. The default user experience should still be a simple sequencer-first workspace; users can open the modular view when they need to inspect or edit signal flow. **Architecture decision:** **B — Modular engine first.** The sequencer,
instruments, effects, mixers, audio inputs, and outputs are components in one
unified processing graph. The default user experience should still be a simple
sequencer-first workspace; users can open the modular view when they need to
inspect or edit signal flow.
**Language decision:** **Language decision:**
- **100% C for the audio engine and core domain logic.** - **100% C for the audio engine and core domain logic.**
- **FLTK with C++ for the graphical user interface only.** - **FLTK with C++ for the graphical user interface only.**
- The GUI talks to the core through a small, documented C API. - The GUI talks to the core through a small, documented C API.
- Do not introduce C++ into the audio engine or core library. - Do not introduce C++ into the audio engine or core library.
- Keep dependencies small, prefer open-source components, and avoid unnecessary frameworks. - Keep dependencies small, prefer open-source components, and avoid unnecessary
frameworks.
--- ---
## 1. Instructions to the AI coding agent ## 1. Instructions to the AI Coding Agent
Treat this document as the project contract. Follow it in every implementation task. Treat this document as the project contract. Follow it in every implementation
task.
1. Read this file and the existing project before making changes. 1. Read this file and the existing project before making changes.
2. Inspect the current build system, source tree, and tests. Do not assume the repository is empty. 2. Inspect the current build system, source tree, and tests. Do not assume the
3. Implement work in small, buildable increments. Each increment must compile and have relevant tests before proceeding. repository is empty.
4. Prefer complete, coherent file updates over partial snippets. Do not silently remove existing functionality. 3. Implement work in small, buildable increments. Each increment must compile
5. Before editing, state briefly what you intend to change and which files are affected. Then make the changes. and have relevant tests before proceeding.
6. Do not invent APIs, FLTK features, audio backend capabilities, or dependency behavior. Check installed headers and official documentation when uncertain. 4. Prefer complete, coherent file updates over partial snippets. Do not silently
7. Keep C source compatible with **C99** initially. Use C11 features only when explicitly justified and supported by the selected compiler/toolchain. remove existing functionality.
8. Use C snake_case identifiers. Use clear names; avoid cryptic one-letter names except conventional loop counters. 5. Before editing, state briefly what you intend to change and which files are
9. Validate external input, lengths, indexes, counts, enum values, and allocation sizes. Handle errors explicitly. affected. Then make the changes.
10. Do not hide failures, silently clamp invalid data, or continue with partially initialized objects. 6. Do not invent APIs, FLTK features, audio backend capabilities, or dependency
11. Avoid unnecessary dependencies, generated-code systems, global mutable state, and elaborate abstractions. behavior. Check installed headers and official documentation when uncertain.
12. Never perform audio-thread-unsafe operations from the real-time callback. See the real-time rules below. 7. Keep C source compatible with **C99** initially. Use C11 features only when
13. Keep the project usable on Linux first. Do not make Linux-specific details leak into the public core API. explicitly justified and supported by the selected compiler/toolchain.
8. Use C snake_case identifiers. Use clear names; avoid cryptic one-letter names
except conventional loop counters.
9. Validate external input, lengths, indexes, counts, enum values, and
allocation sizes. Handle errors explicitly.
10. Do not hide failures, silently clamp invalid data, or continue with
partially initialized objects.
11. Avoid unnecessary dependencies, generated-code systems, global mutable
state, and elaborate abstractions.
12. Never perform audio-thread-unsafe operations from the real-time callback.
See the real-time rules below.
13. Keep the project usable on Linux first. Do not make Linux-specific details
leak into the public core API.
14. Add or update tests and documentation with each feature. 14. Add or update tests and documentation with each feature.
15. At the end of a task, report: files changed, behavior added, build/test commands run, results, known limitations, and the next sensible step. 15. At the end of a task, report: files changed, behavior added, build/test
16. If a requirement is ambiguous, choose the smallest safe implementation consistent with this document and explain the assumption. Ask a question only if the ambiguity blocks safe progress. commands run, results, known limitations, and the next sensible step.
17. Do not claim that a feature works unless it has been implemented and tested. Clearly distinguish a prototype, stub, and finished feature. 16. If a requirement is ambiguous, choose the smallest safe implementation
18. Never copy energyXT source code, proprietary resources, UI artwork, or undocumented internal implementation. Use energyXT only as inspiration for high-level workflow ideas. consistent with this document and explain the assumption. Ask a question
only if the ambiguity blocks safe progress.
17. Do not claim that a feature works unless it has been implemented and tested.
Clearly distinguish a prototype, stub, and finished feature.
18. Never copy energyXT source code, proprietary resources, UI artwork, or
undocumented internal implementation. Use energyXT only as inspiration for
high-level workflow ideas.
### Definition of done for every task ### Definition of done for every task
A task is complete only when: A task is complete only when:
- the code builds with warnings enabled; - the code builds with warnings enabled;
- relevant tests pass; - relevant tests pass;
- error paths are considered; - error paths are considered;
@ -53,16 +83,19 @@ A task is complete only when:
--- ---
## 2. Product principles ## 2. Product Principles
The application should feel like a small, responsive music instrument rather than a general-purpose content-creation suite. The application should feel like a small, responsive music instrument rather
than a general-purpose content-creation suite.
### Core principles ### Core principles
- **Simple by default, powerful when opened up.** - **Simple by default, powerful when opened up.**
- A single unified component graph underlies the application. - A single unified component graph underlies the application.
- The sequencer, mixer, instruments, effects, audio I/O, and utility processors use a common node model. - The sequencer, mixer, instruments, effects, audio I/O, and utility processors
- Common operations should require few clicks and avoid modal dialogs when practical. use a common node model.
- Common operations should require few clicks and avoid modal dialogs when
practical.
- The modular graph should expose real signal flow, not be a decorative diagram. - The modular graph should expose real signal flow, not be a decorative diagram.
- The project must remain understandable to a human developer. - The project must remain understandable to a human developer.
- Audio reliability is more important than visual effects or feature count. - Audio reliability is more important than visual effects or feature count.
@ -75,6 +108,7 @@ The application should feel like a small, responsive music instrument rather tha
### Explicit non-goals for the first release ### Explicit non-goals for the first release
Do not initially implement: Do not initially implement:
- video, notation, collaboration, cloud accounts, or online services; - video, notation, collaboration, cloud accounts, or online services;
- an integrated marketplace; - an integrated marketplace;
- scripting as a requirement for basic operation; - scripting as a requirement for basic operation;
@ -86,11 +120,12 @@ Do not initially implement:
- automatic online update services; - automatic online update services;
- a proprietary project format that cannot be inspected or migrated. - a proprietary project format that cannot be inspected or migrated.
These may be considered later, but they must not delay the first reliable recording and playback workflow. These may be considered later, but they must not delay the first reliable
recording and playback workflow.
--- ---
## 3. High-level architecture ## 3. High-Level Architecture
Use distinct layers with narrow interfaces. Use distinct layers with narrow interfaces.
@ -125,7 +160,9 @@ Use distinct layers with narrow interfaces.
+------------------------------------------------------------+ +------------------------------------------------------------+
``` ```
The C core and audio engine may be separate libraries or clearly separated modules within one C library. Keep their interfaces explicit even if they initially compile into one library. The C core and audio engine may be separate libraries or clearly separated
modules within one C library. Keep their interfaces explicit even if they
initially compile into one library.
### Suggested repository layout ### Suggested repository layout
@ -179,13 +216,14 @@ xtc/
└── build/ # generated; do not commit └── build/ # generated; do not commit
``` ```
Keep C and C++ files clearly separated. C headers exposed to C++ must use `extern "C"` guards. Keep C and C++ files clearly separated. C headers exposed to C++ must use
`extern "C"` guards.
--- ---
## 4. Language and API boundary ## 4. Language and API Boundary
### C code owns ### C Code owns
- project and track data; - project and track data;
- graph nodes, ports, and connections; - graph nodes, ports, and connections;
@ -198,7 +236,7 @@ Keep C and C++ files clearly separated. C headers exposed to C++ must use `exter
- backend-independent engine state; - backend-independent engine state;
- error codes and diagnostic messages. - error codes and diagnostic messages.
### C++/FLTK code owns ### C++/FLTK Code owns
- windows and widgets; - windows and widgets;
- layout and drawing; - layout and drawing;
@ -211,15 +249,19 @@ Keep C and C++ files clearly separated. C headers exposed to C++ must use `exter
### Boundary rules ### Boundary rules
- Public C headers must compile from both C and C++. - Public C headers must compile from both C and C++.
- Do not expose C++ classes, STL containers, exceptions, or FLTK types through the C API. - Do not expose C++ classes, STL containers, exceptions, or FLTK types through
the C API.
- Prefer opaque handles for objects whose internal layout must remain private. - Prefer opaque handles for objects whose internal layout must remain private.
- Use explicit integer error codes and documented output parameters. - Use explicit integer error codes and documented output parameters.
- Define ownership for every pointer: borrowed, caller-owned, or transferred. - Define ownership for every pointer: borrowed, caller-owned, or transferred.
- Do not return pointers into mutable engine memory unless their lifetime is guaranteed. - Do not return pointers into mutable engine memory unless their lifetime is
guaranteed.
- Do not let GUI widgets mutate internal engine structures directly. - Do not let GUI widgets mutate internal engine structures directly.
- Avoid a public API that mirrors every internal struct field. Provide stable operations instead. - Avoid a public API that mirrors every internal struct field. Provide stable
operations instead.
Example API shape (illustrative only; refine after reviewing the actual codebase): Example API shape (illustrative only; refine after reviewing the actual
codebase):
```c ```c
#ifndef XTC_H #ifndef XTC_H
@ -254,17 +296,21 @@ xtc_result xtc_transport_stop(xtc_app *app);
#endif #endif
``` ```
Do not implement this exact API blindly if the existing design calls for a better interface. Keep it small and consistent. Do not implement this exact API blindly if the existing design calls for a
better interface. Keep it small and consistent.
--- ---
## 5. Modular processing graph — primary design decision ## 5. Modular Processing Graph — Primary Design Decision
**Decision B: Modular engine first.** The graph is the common representation of signal processing and routing. The sequencer is a source of time-based events and clips, not a separate audio system disconnected from the graph. **Decision B: Modular engine first.** The graph is the common representation of
signal processing and routing. The sequencer is a source of time-based events
and clips, not a separate audio system disconnected from the graph.
### Node categories ### Node categories
Start with a small set: Start with a small set:
- audio input; - audio input;
- audio output; - audio output;
- audio clip player; - audio clip player;
@ -277,11 +323,13 @@ Start with a small set:
- metering/analyzer; - metering/analyzer;
- utility nodes such as channel mapping. - utility nodes such as channel mapping.
Do not build every node at once. First prove the graph with a test tone, gain node, and output node. Do not build every node at once. First prove the graph with a test tone, gain
node, and output node.
### Ports and connections ### Ports and connections
Each node has a stable identifier, a node type, and declared ports. A port has: Each node has a stable identifier, a node type, and declared ports. A port has:
- a unique identifier within its node; - a unique identifier within its node;
- a direction (input or output); - a direction (input or output);
- a signal/data type (audio, MIDI/event, or control); - a signal/data type (audio, MIDI/event, or control);
@ -289,6 +337,7 @@ Each node has a stable identifier, a node type, and declared ports. A port has:
- a documented sample/block processing contract. - a documented sample/block processing contract.
Validate connections before accepting them: Validate connections before accepting them:
- source and destination exist; - source and destination exist;
- source port is an output and destination is an input; - source port is an output and destination is an input;
- signal types are compatible; - signal types are compatible;
@ -298,13 +347,18 @@ Validate connections before accepting them:
### Cycles and feedback ### Cycles and feedback
For the first working engine, reject cycles in the audio-processing graph. A directed acyclic graph is easier to schedule safely and predictably. Report a useful error to the user. Do not silently break a cycle. For the first working engine, reject cycles in the audio-processing graph. A
directed acyclic graph is easier to schedule safely and predictably. Report a
useful error to the user. Do not silently break a cycle.
Feedback support can be considered later using explicit delay/feedback nodes and a defined block-boundary model. Never introduce arbitrary graph cycles without a correct scheduling design. Feedback support can be considered later using explicit delay/feedback nodes and
a defined block-boundary model. Never introduce arbitrary graph cycles without a
correct scheduling design.
### Graph compilation ### Graph compilation
Compile the editable graph into an execution plan outside the real-time thread: Compile the editable graph into an execution plan outside the real-time thread:
1. validate nodes, ports, and edges; 1. validate nodes, ports, and edges;
2. identify source nodes and reachable output nodes; 2. identify source nodes and reachable output nodes;
3. detect cycles; 3. detect cycles;
@ -313,21 +367,31 @@ Compile the editable graph into an execution plan outside the real-time thread:
6. allocate buffers and prepare node state; 6. allocate buffers and prepare node state;
7. publish an immutable, ready-to-run plan. 7. publish an immutable, ready-to-run plan.
The audio callback processes the prepared plan. It must not perform graph discovery, sorting, dynamic allocation, file loading, or plugin creation. The audio callback processes the prepared plan. It must not perform graph
discovery, sorting, dynamic allocation, file loading, or plugin creation.
### Graph replacement during playback ### Graph replacement during playback
The editor must not modify the graph currently being traversed by the audio thread. Prepare a new plan off-thread and publish it through a bounded real-time-safe mechanism. Define how the old plan is retired only after the audio thread can no longer access it. Do not use a naive pointer swap followed by immediate free. The editor must not modify the graph currently being traversed by the audio
thread. Prepare a new plan off-thread and publish it through a bounded
real-time-safe mechanism. Define how the old plan is retired only after the
audio thread can no longer access it. Do not use a naive pointer swap followed
by immediate free.
Possible approaches include a carefully designed single-producer/single-consumer handoff, epochs, or another proven reclamation scheme. Choose one, document it, and test it under stress. Keep the initial design as simple as safety allows. Possible approaches include a carefully designed single-producer/single-consumer
handoff, epochs, or another proven reclamation scheme. Choose one, document it,
and test it under stress. Keep the initial design as simple as safety allows.
### Sequencer integration ### Sequencer integration
The sequencer stores timeline content and emits events to the graph at the correct sample positions. A clip player or instrument node consumes those events. The GUI timeline is a view/editor for project data; it is not the audio scheduler. The sequencer stores timeline content and emits events to the graph at the
correct sample positions. A clip player or instrument node consumes those
events. The GUI timeline is a view/editor for project data; it is not the audio
scheduler.
--- ---
## 6. Real-time audio requirements ## 6. Real-Time Audio Requirements
These rules are mandatory. These rules are mandatory.
@ -354,54 +418,75 @@ These rules are mandatory.
- process silence safely when a source is unavailable; - process silence safely when a source is unavailable;
- handle backend buffer sizes and channel counts within documented limits. - handle backend buffer sizes and channel counts within documented limits.
Do not assume all backends use a fixed block size unless the backend contract guarantees it. Establish maximum supported frame/event counts and define what happens when limits are exceeded. Do not assume all backends use a fixed block size unless the backend contract
guarantees it. Establish maximum supported frame/event counts and define what
happens when limits are exceeded.
### Thread ownership ### Thread ownership
Document which thread owns each mutable resource: Document which thread owns each mutable resource:
- GUI thread: widgets and GUI state; - GUI thread: widgets and GUI state;
- control/project thread: editable project state, file operations, graph compilation; - control/project thread: editable project state, file operations, graph
compilation;
- audio thread: active processing plan and real-time DSP state; - audio thread: active processing plan and real-time DSP state;
- backend callbacks: follow the backend's documented threading model. - backend callbacks: follow the backend's documented threading model.
Avoid sharing mutable structs across threads without an explicit synchronization strategy. Atomics are not automatically safe for every use: verify that the selected atomic operations are lock-free where required and use correct memory ordering. Avoid sharing mutable structs across threads without an explicit synchronization
strategy. Atomics are not automatically safe for every use: verify that the
selected atomic operations are lock-free where required and use correct memory
ordering.
### Timing ### Timing
Use audio sample position as the authoritative playback clock. Represent timeline positions with an integer or rational scheme that avoids accumulated floating-point drift. Convert musical time (bars/beats/ticks) to sample positions using tempo and meter maps. Define behavior at tempo changes before implementing advanced tempo automation. Use audio sample position as the authoritative playback clock. Represent
timeline positions with an integer or rational scheme that avoids accumulated
floating-point drift. Convert musical time (bars/beats/ticks) to sample
positions using tempo and meter maps. Define behavior at tempo changes before
implementing advanced tempo automation.
--- ---
## 7. Audio backend strategy ## 7. Audio Backend Strategy
Start on Linux and keep the backend replaceable. Start on Linux and keep the backend replaceable.
1. Define a small backend interface in C. 1. Define a small backend interface in C.
2. Implement one backend first, selected after checking the target system and installed development packages. JACK-compatible audio is a reasonable first target on Linux. 2. Implement one backend first, selected after checking the target system and
3. Test device enumeration, start/stop, sample rate, buffer size, channel count, disconnects, and shutdown. installed development packages. JACK-compatible audio is a reasonable first
target on Linux.
3. Test device enumeration, start/stop, sample rate, buffer size, channel count,
disconnects, and shutdown.
4. Keep backend-specific headers out of the public API. 4. Keep backend-specific headers out of the public API.
5. Add another backend only after the engine works and tests pass. 5. Add another backend only after the engine works and tests pass.
Do not assume PipeWire-native, JACK compatibility, ALSA PCM, and desktop audio routing are interchangeable. Document the chosen backend's setup and limitations. Do not assume PipeWire-native, JACK compatibility, ALSA PCM, and desktop audio
routing are interchangeable. Document the chosen backend's setup and
limitations.
The engine should also have an offline/test mode that processes buffers without physical audio hardware. This makes unit and integration testing repeatable. The engine should also have an offline/test mode that processes buffers without
physical audio hardware. This makes unit and integration testing repeatable.
--- ---
## 8. MIDI, parameters, automation, and DSP ## 8. MIDI, Parameters, Automation, and DSP
### MIDI/events ### MIDI/Events
- Use a compact, well-defined event representation. - Use a compact, well-defined event representation.
- Store event time relative to a project timeline and convert it to sample offsets for a processing block. - Store event time relative to a project timeline and convert it to sample
offsets for a processing block.
- Validate event sizes and event counts. - Validate event sizes and event counts.
- Handle note-on, note-off, velocity, channel, controller, pitch bend, and tempo/transport events as needed. - Handle note-on, note-off, velocity, channel, controller, pitch bend, and
- Ensure all held notes can be released on stop, panic, seek, device loss, or project teardown. tempo/transport events as needed.
- Ensure all held notes can be released on stop, panic, seek, device loss, or
project teardown.
- Avoid unbounded event queues. - Avoid unbounded event queues.
### Parameters ### Parameters
Every automatable control should have: Every automatable control should have:
- a stable parameter ID; - a stable parameter ID;
- a type and valid range; - a type and valid range;
- a default value; - a default value;
@ -409,85 +494,115 @@ Every automatable control should have:
- a safe update mechanism; - a safe update mechanism;
- optional automation and MIDI mapping. - optional automation and MIDI mapping.
Do not silently clamp invalid serialized values. Validate project data and return an error or apply a clearly documented migration rule. UI controls may constrain user input, but core validation must remain authoritative. Do not silently clamp invalid serialized values. Validate project data and
return an error or apply a clearly documented migration rule. UI controls may
constrain user input, but core validation must remain authoritative.
### Automation ### Automation
Implement automation only after transport, graph processing, and basic parameter updates work. Define interpolation types explicitly (initially step and linear are enough). Automation evaluation must be deterministic and bounded. Implement automation only after transport, graph processing, and basic parameter
updates work. Define interpolation types explicitly (initially step and linear
are enough). Automation evaluation must be deterministic and bounded.
### DSP ### DSP
Start with a few well-tested processors: Start with a few well-tested processors:
- gain; - gain;
- pan; - pan;
- simple meter/peak detector; - simple meter/peak detector;
- test oscillator; - test oscillator;
- basic delay only after buffer/lifetime handling is sound. - basic delay only after buffer/lifetime handling is sound.
Keep DSP algorithms in C and independently testable. Use appropriate numerical safeguards, including finite-value checks where needed. Avoid premature SIMD optimization until profiling shows a real bottleneck. Keep DSP algorithms in C and independently testable. Use appropriate numerical
safeguards, including finite-value checks where needed. Avoid premature SIMD
optimization until profiling shows a real bottleneck.
--- ---
## 9. FLTK/C++ GUI guidelines ## 9. FLTK/C++ GUI Guidelines
The GUI is a thin client of the C API, not the owner of the engine. The GUI is a thin client of the C API, not the owner of the engine.
### Main views ### Main views
1. **Sequencer:** tracks, audio/MIDI clips, timeline ruler, playhead, selection, zoom and horizontal scrolling. 1. **Sequencer:** tracks, audio/MIDI clips, timeline ruler, playhead, selection,
2. **Mixer:** channels, faders, pan, mute/solo, inserts/sends when supported, and meters. zoom and horizontal scrolling.
3. **Modular graph:** nodes, ports, connections, zoom/pan, selection, and connection validation feedback. 2. **Mixer:** channels, faders, pan, mute/solo, inserts/sends when supported,
4. **Transport:** play, stop, record, tempo, meter, loop range, and current position. and meters.
5. **Project/file dialogs:** create, open, save, import audio, and manage missing assets. 3. **Modular graph:** nodes, ports, connections, zoom/pan, selection, and
connection validation feedback.
4. **Transport:** play, stop, record, tempo, meter, loop range, and current
position.
5. **Project/file dialogs:** create, open, save, import audio, and manage
missing assets.
The default workspace should emphasize the sequencer and transport. The graph view is an advanced but first-class view of the same underlying project. The default workspace should emphasize the sequencer and transport. The graph
view is an advanced but first-class view of the same underlying project.
### GUI implementation ### GUI Implementation
- Use FLTK for conventional widgets and application windows. - Use FLTK for conventional widgets and application windows.
- Use custom-drawn widgets for the timeline, piano roll, mixer strips, waveform display, and graph canvas where needed. - Use custom-drawn widgets for the timeline, piano roll, mixer strips, waveform
display, and graph canvas where needed.
- Keep drawing code separate from project mutation. - Keep drawing code separate from project mutation.
- Use GUI timers or the toolkit's appropriate event mechanism for refreshing meters; never poll from the audio callback or block the GUI waiting for audio. - Use GUI timers or the toolkit's appropriate event mechanism for refreshing
meters; never poll from the audio callback or block the GUI waiting for audio.
- Avoid redrawing the entire application when only a small region changes. - Avoid redrawing the entire application when only a small region changes.
- Support keyboard shortcuts and predictable focus behavior. - Support keyboard shortcuts and predictable focus behavior.
- Make selection, dragging, snapping, zooming, and scrolling consistent across editors. - Make selection, dragging, snapping, zooming, and scrolling consistent across
editors.
- Never call FLTK APIs from the audio thread. - Never call FLTK APIs from the audio thread.
- Do not hold GUI locks while calling potentially blocking engine operations. - Do not hold GUI locks while calling potentially blocking engine operations.
- Keep C++ simple: use RAII for GUI resources, clear ownership, and small classes. Do not force the C core into C++ abstractions. - Keep C++ simple: use RAII for GUI resources, clear ownership, and small
classes. Do not force the C core into C++ abstractions.
### Visual direction ### Visual direction
Aim for a compact, dark, high-contrast interface with restrained decoration. Prioritize legible waveforms, clip boundaries, transport state, and meter levels. Avoid excessive gradients, animations, nested panels, and modal configuration windows. Aim for a compact, dark, high-contrast interface with restrained decoration.
Prioritize legible waveforms, clip boundaries, transport state, and meter
levels. Avoid excessive gradients, animations, nested panels, and modal
configuration windows.
Do not copy energyXT's artwork, exact visual assets, or proprietary UI resources. Recreate the general principles of compactness, direct manipulation, and modular routing in an original design. Do not copy energyXT's artwork, exact visual assets, or proprietary UI
resources. Recreate the general principles of compactness, direct manipulation,
and modular routing in an original design.
--- ---
## 10. Project format and persistence ## 10. Project Format and Persistence
The project format must be versioned from its first release. The project format must be versioned from its first release.
### Requirements ### Requirements
- Include a format version and application version. - Include a format version and application version.
- Store stable IDs for tracks, nodes, ports where necessary, clips, parameters, and connections. - Store stable IDs for tracks, nodes, ports where necessary, clips, parameters,
and connections.
- Save relative asset paths when possible. - Save relative asset paths when possible.
- Define behavior for missing audio files. - Define behavior for missing audio files.
- Validate every field before allocating large amounts of memory. - Validate every field before allocating large amounts of memory.
- Handle truncated, malformed, unsupported, or corrupted files gracefully. - Handle truncated, malformed, unsupported, or corrupted files gracefully.
- Never execute code while loading a project. - Never execute code while loading a project.
- Save atomically: write a temporary file, flush as appropriate, and rename it into place when safe. - Save atomically: write a temporary file, flush as appropriate, and rename it
- Preserve unknown optional fields when practical, or explicitly document when they are discarded. into place when safe.
- Preserve unknown optional fields when practical, or explicitly document when
they are discarded.
- Include migration functions when the format changes. - Include migration functions when the format changes.
- Keep project serialization outside the real-time thread. - Keep project serialization outside the real-time thread.
Choose a simple, well-supported format after considering the dependency and robustness trade-offs. A small, documented text format can help inspect projects, but large binary audio data should generally live in separate assets rather than be embedded in a text document. Do not add a serialization dependency without a clear reason. Choose a simple, well-supported format after considering the dependency and
robustness trade-offs. A small, documented text format can help inspect
projects, but large binary audio data should generally live in separate assets
rather than be embedded in a text document. Do not add a serialization
dependency without a clear reason.
--- ---
## 11. Undo/redo and editing commands ## 11. Undo/Redo and Editing Commands
Use explicit commands for user-visible edits, such as: Use explicit commands for user-visible edits, such as:
- create/delete/move/resize clip; - create/delete/move/resize clip;
- create/delete/rename track; - create/delete/rename track;
- change parameter; - change parameter;
@ -495,21 +610,28 @@ Use explicit commands for user-visible edits, such as:
- connect/disconnect ports; - connect/disconnect ports;
- change tempo or meter. - change tempo or meter.
A command should validate its inputs and have a well-defined inverse or stored previous state. Avoid designing a generic event-sourcing framework before it is needed. A command should validate its inputs and have a well-defined inverse or stored
previous state. Avoid designing a generic event-sourcing framework before it is
needed.
The initial implementation may begin without a complete undo stack, but design edit operations so undo/redo can be added without rewriting all editors. Never make an edit partially visible if it fails validation. The initial implementation may begin without a complete undo stack, but design
edit operations so undo/redo can be added without rewriting all editors. Never
make an edit partially visible if it fails validation.
--- ---
## 12. Build, coding standards, and dependencies ## 12. Build, Coding Standards, and Dependencies
### Build system ### Build system
Use a straightforward Makefile unless the existing repository has a sound reason to use another build system. Use a straightforward Makefile unless the existing repository has a sound reason
to use another build system.
The build must: The build must:
- compile C with the selected C standard and warnings enabled; - compile C with the selected C standard and warnings enabled;
- compile C++ GUI sources with the standard supported by the chosen FLTK version; - compile C++ GUI sources with the standard supported by the chosen FLTK
version;
- link the GUI with FLTK and the C core; - link the GUI with FLTK and the C core;
- link the required audio backend; - link the required audio backend;
- provide `all`, `clean`, `test`, and a debug build target; - provide `all`, `clean`, `test`, and a debug build target;
@ -517,9 +639,11 @@ The build must:
- avoid embedding machine-specific paths; - avoid embedding machine-specific paths;
- separate generated objects and binaries into a build directory. - separate generated objects and binaries into a build directory.
Use `pkg-config` where supported by the installed dependencies. Check actual package names on the target distribution rather than hard-coding unverified names. Use `pkg-config` where supported by the installed dependencies. Check actual
package names on the target distribution rather than hard-coding unverified
names.
### C style ### C Style
- Use C99 initially. - Use C99 initially.
- Use `snake_case`. - Use `snake_case`.
@ -532,7 +656,7 @@ Use `pkg-config` where supported by the installed dependencies. Check actual pac
- Use `const` where appropriate. - Use `const` where appropriate.
- Do not suppress warnings merely to get a clean build. - Do not suppress warnings merely to get a clean build.
### C++ GUI style ### C++ GUI Style
- Keep C++ confined to the GUI and any unavoidable C++-only third-party wrapper. - Keep C++ confined to the GUI and any unavoidable C++-only third-party wrapper.
- Prefer small classes with explicit ownership. - Prefer small classes with explicit ownership.
@ -544,21 +668,26 @@ Use `pkg-config` where supported by the installed dependencies. Check actual pac
### Dependencies ### Dependencies
Before adding a dependency: Before adding a dependency:
1. identify the exact feature it provides; 1. identify the exact feature it provides;
2. check license and maintenance status; 2. check license and maintenance status;
3. determine whether the existing code can provide the functionality simply and safely; 3. determine whether the existing code can provide the functionality simply and
safely;
4. document the dependency and its version constraints; 4. document the dependency and its version constraints;
5. keep it optional if the feature is optional. 5. keep it optional if the feature is optional.
Prefer open-source dependencies with clear licensing. Do not claim the whole application is license-compatible until all direct and bundled dependencies have been reviewed. Prefer open-source dependencies with clear licensing. Do not claim the whole
application is license-compatible until all direct and bundled dependencies have
been reviewed.
--- ---
## 13. Testing and diagnostics ## 13. Testing and Diagnostics
### Unit tests ### Unit tests
Test at least: Test at least:
- graph validation and topological sorting; - graph validation and topological sorting;
- invalid ports and incompatible connections; - invalid ports and incompatible connections;
- cycle detection; - cycle detection;
@ -573,6 +702,7 @@ Test at least:
### Integration tests ### Integration tests
Test: Test:
- test oscillator → gain → output processing; - test oscillator → gain → output processing;
- offline rendering with deterministic output; - offline rendering with deterministic output;
- graph replacement during active processing; - graph replacement during active processing;
@ -585,6 +715,7 @@ Test:
### Tools ### Tools
Where available, use: Where available, use:
- compiler warnings; - compiler warnings;
- AddressSanitizer and UndefinedBehaviorSanitizer in non-real-time test builds; - AddressSanitizer and UndefinedBehaviorSanitizer in non-real-time test builds;
- static analysis; - static analysis;
@ -592,25 +723,29 @@ Where available, use:
- stress tests for graph edits and shutdown; - stress tests for graph edits and shutdown;
- audio loopback or offline render comparisons. - audio loopback or offline render comparisons.
Sanitizers and diagnostic logging should not be enabled in production real-time callbacks unless their real-time implications have been specifically evaluated. Sanitizers and diagnostic logging should not be enabled in production real-time
callbacks unless their real-time implications have been specifically evaluated.
--- ---
## 14. Development roadmap and acceptance criteria ## 14. Development Roadmap and Acceptance Criteria
Implement these milestones in order. Do not jump ahead merely because a later feature is exciting. Implement these milestones in order. Do not jump ahead merely because a later
feature is exciting.
### Repository and design baseline ### Repository and design baseline
- Inspect the repository and available toolchain. - Inspect the repository and available toolchain.
- Create or update architecture documentation and the Makefile. - Create or update architecture documentation and the Makefile.
- Verify C compiler, C++ compiler, FLTK, audio development headers, and test tools. - Verify C compiler, C++ compiler, FLTK, audio development headers, and test
tools.
- Record dependency and licensing decisions. - Record dependency and licensing decisions.
- Add a minimal application that starts and exits cleanly. - Add a minimal application that starts and exits cleanly.
**Acceptance:** clean build on the target Linux environment; no audio engine assumptions hidden in GUI code. **Acceptance:** clean build on the target Linux environment; no audio engine
assumptions hidden in GUI code.
### C core and offline processing ### C Core and offline processing
- Create the C library/API. - Create the C library/API.
- Define node, port, connection, and graph data structures. - Define node, port, connection, and graph data structures.
@ -619,7 +754,8 @@ Implement these milestones in order. Do not jump ahead merely because a later fe
- Add oscillator, gain, meter, and output test nodes. - Add oscillator, gain, meter, and output test nodes.
- Implement deterministic offline block processing. - Implement deterministic offline block processing.
**Acceptance:** automated test proves oscillator → gain → meter/output produces expected samples without hardware. **Acceptance:** automated test proves oscillator → gain → meter/output produces
expected samples without hardware.
### Real-time backend ### Real-time backend
@ -629,16 +765,18 @@ Implement these milestones in order. Do not jump ahead merely because a later fe
- Add safe control-to-audio communication. - Add safe control-to-audio communication.
- Stress-test shutdown and graph replacement. - Stress-test shutdown and graph replacement.
**Acceptance:** stable playback for a sustained test; no known callback allocations or blocking calls; failures are reported clearly. **Acceptance:** stable playback for a sustained test; no known callback
allocations or blocking calls; failures are reported clearly.
### FLTK shell ### FLTK Shell
- Create main window, menus, transport, status area, and view switching. - Create main window, menus, transport, status area, and view switching.
- Call the C API through the public header. - Call the C API through the public header.
- Add an audio engine status indicator. - Add an audio engine status indicator.
- Implement clean startup and shutdown. - Implement clean startup and shutdown.
**Acceptance:** GUI can start/stop the engine, report backend errors, and close without leaks or hangs. **Acceptance:** GUI can start/stop the engine, report backend errors, and close
without leaks or hangs.
### Modular graph editor ### Modular graph editor
@ -648,16 +786,19 @@ Implement these milestones in order. Do not jump ahead merely because a later fe
- Display invalid connection feedback. - Display invalid connection feedback.
- Submit graph edits through validated C API commands. - Submit graph edits through validated C API commands.
**Acceptance:** graph shown by the GUI matches the graph used by the engine; invalid connections are rejected without corrupting active playback. **Acceptance:** graph shown by the GUI matches the graph used by the engine;
invalid connections are rejected without corrupting active playback.
### Project and sequencer foundation ### Project and sequencer foundation
- Add project persistence and stable IDs. - Add project persistence and stable IDs.
- Add tracks and clip objects. - Add tracks and clip objects.
- Implement timeline rendering, selection, moving, trimming, zoom, and scrolling. - Implement timeline rendering, selection, moving, trimming, zoom, and
scrolling.
- Add transport/sample-position synchronization. - Add transport/sample-position synchronization.
**Acceptance:** save/load a project with clips and reproduce the same arrangement. **Acceptance:** save/load a project with clips and reproduce the same
arrangement.
### Audio clips and recording ### Audio clips and recording
@ -666,16 +807,18 @@ Implement these milestones in order. Do not jump ahead merely because a later fe
- Keep file I/O outside the audio callback. - Keep file I/O outside the audio callback.
- Add missing-asset reporting. - Add missing-asset reporting.
**Acceptance:** record, save, reopen, and play a basic audio project without callback file I/O. **Acceptance:** record, save, reopen, and play a basic audio project without
callback file I/O.
### MIDI and a simple instrument ### MIDI And a simple instrument
- Add MIDI input and clip events. - Add MIDI input and clip events.
- Add a basic internal synthesizer or test instrument. - Add a basic internal synthesizer or test instrument.
- Handle note-off, panic, seeking, and stop correctly. - Handle note-off, panic, seeking, and stop correctly.
- Add a minimal piano-roll editor only after event scheduling works. - Add a minimal piano-roll editor only after event scheduling works.
**Acceptance:** record or draw MIDI notes, play them in time, save the project, and reopen it. **Acceptance:** record or draw MIDI notes, play them in time, save the project,
and reopen it.
### Mixer, automation, and editing polish ### Mixer, automation, and editing polish
@ -684,7 +827,8 @@ Implement these milestones in order. Do not jump ahead merely because a later fe
- Add a bounded undo/redo system for common edits. - Add a bounded undo/redo system for common edits.
- Improve keyboard interaction and workflow. - Improve keyboard interaction and workflow.
**Acceptance:** basic composition and mixing can be completed without editing project files manually. **Acceptance:** basic composition and mixing can be completed without editing
project files manually.
### Extensibility and release preparation ### Extensibility and release preparation
@ -693,31 +837,40 @@ Implement these milestones in order. Do not jump ahead merely because a later fe
- Review project format migration, crash behavior, performance, and licensing. - Review project format migration, crash behavior, performance, and licensing.
- Package the application and document Linux setup. - Package the application and document Linux setup.
**Acceptance:** reproducible release build, tested install/uninstall, clear limitations, and complete user/developer documentation. **Acceptance:** reproducible release build, tested install/uninstall, clear
limitations, and complete user/developer documentation.
--- ---
## 15. Initial implementation task for the agent ## 15. Initial Implementation Task for the Agent
Start with the **repository and design baseline** only. Start with the **repository and design baseline** only.
1. Inspect the current repository and report its state. 1. Inspect the current repository and report its state.
2. Identify the available C/C++ compilers, FLTK version, audio backend development packages, `pkg-config`, and test tools. 2. Identify the available C/C++ compilers, FLTK version, audio backend
development packages, `pkg-config`, and test tools.
3. Do not install packages or modify system configuration without permission. 3. Do not install packages or modify system configuration without permission.
4. Propose the smallest initial source tree compatible with this brief. 4. Propose the smallest initial source tree compatible with this brief.
5. Add or update the Makefile and documentation only as needed. 5. Add or update the Makefile and documentation only as needed.
6. Create a minimal program that builds and exits cleanly; do not yet implement a full audio engine or GUI. 6. Create a minimal program that builds and exits cleanly; do not yet implement
a full audio engine or GUI.
7. Build it and run its tests. 7. Build it and run its tests.
8. Report exact commands, results, assumptions, and any missing dependencies. 8. Report exact commands, results, assumptions, and any missing dependencies.
After the baseline is reviewed, continue with the C core and offline processing. Keep each milestone independently buildable and testable. After the baseline is reviewed, continue with the C core and offline processing.
Keep each milestone independently buildable and testable.
--- ---
## 16. Long-term quality bar ## 16. Long-Term Quality Bar
The project succeeds if a musician can open it quickly, connect components without fighting the interface, record and arrange audio/MIDI, and make music without needing to understand every detail of the engine. The project succeeds if a musician can open it quickly, connect components
without fighting the interface, record and arrange audio/MIDI, and make music
without needing to understand every detail of the engine.
The implementation succeeds if a developer can read the C code, understand ownership and thread boundaries, test components independently, and add functionality without turning the application into an unmaintainable framework. The implementation succeeds if a developer can read the C code, understand
ownership and thread boundaries, test components independently, and add
functionality without turning the application into an unmaintainable framework.
**Guiding rule:** build a small, coherent system whose components compose well. Do not build a large DAW and then attempt to simplify it afterward. **Guiding rule:** build a small, coherent system whose components compose well.
Do not build a large DAW and then attempt to simplify it afterward.

View file

@ -1,36 +1,42 @@
# Known limitations and qualification boundaries # Known Limitations and Qualification Boundaries
- Development version 0.1.0, Linux C99/C++17/FLTK, optional JACK. Physical-device - Development version 0.1.0, Linux C99/C++17/FLTK, optional JACK.
playback/capture, human transition listening, independent user acceptance and Physical-device playback/capture, human transition listening, independent user
long-duration device qualification are pending. acceptance and long-duration device qualification are pending.
- Fixed 4/4 and constant tempo snapshots; no tempo map, loop transport, plugin - Fixed 4/4 and constant tempo snapshots; no tempo map, loop transport, plugin
latency compensation, limiter, background streaming or multichannel mixer buses. latency compensation, limiter, background streaming or multichannel mixer
buses.
- Mono capture/import downmix; stereo track output. Graph ports are mono with - Mono capture/import downmix; stereo track output. Graph ports are mono with
independent paired processing. Maximum 64 project graph nodes (256 standalone), independent paired processing. Maximum 64 project graph nodes (256
64 tracks, 1024 clips/automation points, 4096 scheduled notes, 256 live MIDI standalone), 64 tracks, 1024 clips/automation points, 4096 scheduled notes,
events/block, 32 voices, 65536 frames/block. See API headers for exact bounds. 256 live MIDI events/block, 32 voices, 65536 frames/block. See API headers for
exact bounds.
- WAV samples preload with the memory/format limits in AUDIO_FILES.md. Callback - WAV samples preload with the memory/format limits in AUDIO_FILES.md. Callback
overflow reports/silences; there is no CPU preemption or expensive-plugin rescue. overflow reports/silences; there is no CPU preemption or expensive-plugin
- Live MIDI configurable per-channel volume/pan/sustain CC mapping; no controller learn, capture, SMF rescue.
import, pitch bend, custom-instrument chooser or instrument timbre controls. - Live MIDI configurable per-channel volume/pan/sustain CC mapping; no
- Native ABI 1 graph nodes are session-only. ABI 2 state is parameter-only. LADSPA controller learn, capture, SMF import, pitch bend, custom-instrument chooser
supports the documented mono amp/inverter/sine set; no opaque state, native or instrument timbre controls.
editor, MIDI, true multichannel layout, latency compensation or crash isolation. - Native ABI 1 graph nodes are session-only. ABI 2 state is parameter-only.
Automated LADSPA controls use run(1) and can be expensive. LADSPA supports the documented mono amp/inverter/sine set; no opaque state,
native editor, MIDI, true multichannel layout, latency compensation or crash
isolation. Automated LADSPA controls use run(1) and can be expensive.
- GUI loading/compilation is synchronous on the control thread. Timeline grid/ - GUI loading/compilation is synchronous on the control thread. Timeline grid/
zoom/selection and graph positions are session UI state. Undo retains 32 edits zoom/selection and graph positions are session UI state. Undo retains 32 edits
plus baseline and at most one reserved metadata snapshot; GUI edits reserve plus baseline and at most one reserved metadata snapshot; GUI edits reserve
history before mutation/publication. Media is history before mutation/publication. Media is never removed by undo/delete.
never removed by undo/delete. Broad screen-reader/accessibility testing is pending. Broad screen-reader/accessibility testing is pending.
- Format v12 reads v1–v11 and uses relative local media references. External absolute - Format v12 reads v1–v11 and uses relative local media references. External
media/module paths need manual repair after relocation. Saving does not bundle absolute media/module paths need manual repair after relocation. Saving does
media; missing media stays referenced, missing plugins reject GUI installation. not bundle media; missing media stays referenced, missing plugins reject GUI
Unknown data is rejected rather than stripped. Post-rename directory-sync errors installation. Unknown data is rejected rather than stripped. Post-rename
can report failure even though the complete new document was published. directory-sync errors can report failure even though the complete new document
- No autosave/session crash restoration, RF64, system service, automatic dependency was published.
installation or public signed release. Source/binary archives are local artifacts; - No autosave/session crash restoration, RF64, system service, automatic
binary packages require compatible system libraries. No third-party plugin or dependency installation or public signed release. Source/binary archives are
dependency binary is bundled. Power-cut durability depends on the filesystem. local artifacts; binary packages require compatible system libraries. No
third-party plugin or dependency binary is bundled. Power-cut durability
depends on the filesystem.
See ROADMAP.md, AUDIO_QUALITY.md, CONTROL.md, WORKFLOW.md, PROJECT_FORMAT.md, See ROADMAP.md, AUDIO_QUALITY.md, CONTROL.md, WORKFLOW.md, PROJECT_FORMAT.md,
PERFORMANCE.md, PLUGIN_HOSTING.md and RECOVERY.md for tested contracts/evidence. PERFORMANCE.md, PLUGIN_HOSTING.md and RECOVERY.md for tested contracts/evidence.
@ -46,43 +52,49 @@ PERFORMANCE.md, PLUGIN_HOSTING.md and RECOVERY.md for tested contracts/evidence.
- Stereo one-level mix buses share the 64-track limit. Nested buses, independent - Stereo one-level mix buses share the 64-track limit. Nested buses, independent
bus plugin inserts, per-track aux sends and sidechains remain deferred. Track bus plugin inserts, per-track aux sends and sidechains remain deferred. Track
meters are pre-bus; bus meters are pre-graph; master is post-graph. Sample peaks meters are pre-bus; bus meters are pre-graph; master is post-graph. Sample
and sticky clipping do not provide loudness, true-peak analysis or limiting. peaks and sticky clipping do not provide loudness, true-peak analysis or
limiting.
- Plugin discovery is an explicit-file descriptor browser, not a directory scan. - Plugin discovery is an explicit-file descriptor browser, not a directory scan.
Generic parameter dialogs own copied metadata; no native plugin editors or Generic parameter dialogs own copied metadata; no native plugin editors or
custom MIDI instrument chooser. LADSPA units are unspecified. Replacement custom MIDI instrument chooser. LADSPA units are unspecified. Replacement
requires the same mono audio layout and resets controls/automation. Missing requires the same mono audio layout and resets controls/automation. Missing
modules reject Open while retaining the current project; GUI module relink for modules reject Open while retaining the current project; GUI module relink for
unopened documents is deferred. In-process plugin crashes/hangs are uncontained. unopened documents is deferred. In-process plugin crashes/hangs are
uncontained.
- Common parameter targets cover track/bus gain/pan, LADSPA controls and metadata - Common parameter targets cover track/bus gain/pan, LADSPA controls and
for live sine volume/pan/sustain. Live mappings do not drive arranged lanes, metadata for live sine volume/pan/sustain. Live mappings do not drive arranged
faders or plugins. No automation capture, touch/latch/write modes or loop lanes, faders or plugins. No automation capture, touch/latch/write modes or
transport. Backward jumps are deterministic for lanes, not opaque plugin tails. loop transport. Backward jumps are deterministic for lanes, not opaque plugin
tails.
- Editing feedback remains synchronous; busy cursors are not background loading - Editing feedback remains synchronous; busy cursors are not background loading
or cancellable progress. Surviving selections are retained across undo but or cancellable progress. Surviving selections are retained across undo but
historical selection/layout is not stored. Native context-menu/focus manual historical selection/layout is not stored. Native context-menu/focus manual
qualification and broad accessibility testing remain pending. See EDITING.md. qualification and broad accessibility testing remain pending. See EDITING.md.
Project diagnostics use syntax-line locations; semantic cross-reference Project diagnostics use syntax-line locations; semantic cross-reference failures
failures identify the MODEL stage without a source line. `xtc-check --inspect` identify the MODEL stage without a source line. `xtc-check --inspect` checks
checks dependency file availability only, without loading code or WAV contents. dependency file availability only, without loading code or WAV contents. The
The project format is Linux/POSIX oriented; absolute plugin paths, architecture, project format is Linux/POSIX oriented; absolute plugin paths, architecture, ABI
ABI and JACK/device configuration must be supplied on another system. Unknown and JACK/device configuration must be supplied on another system. Unknown fields
fields are rejected, not preserved. See PROJECT_FORMAT.md for versions 1–10 and are rejected, not preserved. See PROJECT_FORMAT.md for versions 1–10 and
RECOVERY.md for save failures after rename. RECOVERY.md for save failures after rename.
CLAP/VST2/VST3 loading supports Linux binary modules, one-input mono CLAP/VST2/VST3 loading supports Linux binary modules, one-input mono float32
float32 zero-latency effects at default configuration. No modern parameter edit, zero-latency effects at default configuration. No modern parameter edit,
automation, programs, opaque state, MIDI/instruments, transport, custom editor, automation, programs, opaque state, MIDI/instruments, transport, custom editor,
workers, sidechains or true stereo/multichannel buses are implemented. Plugin workers, sidechains or true stereo/multichannel buses are implemented. Plugin
bundles must be opened at their actual Linux shared-library binary. Only original bundles must be opened at their actual Linux shared-library binary. Only
ABI fixtures are qualified; arbitrary third-party modern plugins remain untested. original ABI fixtures are qualified; arbitrary third-party modern plugins remain
See PLUGIN_HOSTING.md and decision 0010 for lifecycle and licensing boundaries. untested. See PLUGIN_HOSTING.md and decision 0010 for lifecycle and licensing
boundaries.
## Instrument support ---
## Instrument Support
MIDI tracks can bind CLAP, VST2 and VST3 instruments. In the Track menu, select MIDI tracks can bind CLAP, VST2 and VST3 instruments. In the Track menu, select
**Load instrument plugin** for the selected sequencer track; **Use built-in **Load instrument plugin** for the selected sequencer track; **Use built-in
@ -93,9 +105,9 @@ their own track's instrument. Independent instances feed track gain/pan,
mute/solo, bus routing and automation. Bindings/live target save in v12. mute/solo, bus routing and automation. Bindings/live target save in v12.
Supply the actual trusted Linux binary (including the inner .so of a VST3 Supply the actual trusted Linux binary (including the inner .so of a VST3
bundle). Supported instruments have zero audio inputs, one mono output, bundle). Supported instruments have zero audio inputs, one mono output, float32
float32 processing, zero latency and one note/event input. Default configuration processing, zero latency and one note/event input. Default configuration only:
only: no custom editor, opaque state/preset restoration, modern parameter no custom editor, opaque state/preset restoration, modern parameter
editing/automation, stereo/multichannel instruments or MIDI output. No arbitrary editing/automation, stereo/multichannel instruments or MIDI output. No arbitrary
third-party instrument is qualified by the original ABI fixture tests. third-party instrument is qualified by the original ABI fixture tests.
@ -113,19 +125,18 @@ the callback and old instances are reclaimed on the control thread. Missing
references remain inspectable without loading; failed GUI preparation retains references remain inspectable without loading; failed GUI preparation retains
the current project. Save a backup for applications limited to v11 or earlier. the current project. Save a backup for applications limited to v11 or earlier.
### Track insert chains ### Track insert chains
Track chains contain at most eight mono effects. Modern adapters require zero Track chains contain at most eight mono effects. Modern adapters require zero
reported latency, use default configuration and do not provide native plugin reported latency, use default configuration and do not provide native plugin
windows, parameter editing or opaque state persistence. A bounded VST3 output windows, parameter editing or opaque state persistence. A bounded VST3 output
parameter sink retains the final notification per parameter per block (64 parameter sink retains the final notification per parameter per block (64
parameters); these notifications are not recorded or exposed as controls. parameters); these notifications are not recorded or exposed as controls. Track
Track edits stop transport before candidate preparation. Bus inserts and tracks edits stop transport before candidate preparation. Bus inserts and tracks with
with both audio assets and MIDI reject preparation. Unassigned live sine both audio assets and MIDI reject preparation. Unassigned live sine monitoring
monitoring stays on the master input. Stereo insert layouts, latency stays on the master input. Stereo insert layouts, latency compensation,
compensation, sandboxed scanning and gapless live insert edits remain future sandboxed scanning and gapless live insert edits remain future work. Internal
work. Internal history resets on discontinuities; third-party history follows history resets on discontinuities; third-party history follows the existing host
the existing host contract. Presets retain absolute plugin references, so contract. Presets retain absolute plugin references, so compatible binaries must
compatible binaries must be installed at the same paths on another machine. be installed at the same paths on another machine. The smaller optional
The smaller optional inspector's visibility is session-local. inspector's visibility is session-local.

View file

@ -1,13 +1,14 @@
# Licensing and dependency review # Licensing and Dependency Review
The repository's original code, example and documentation are under the MIT The repository's original code, example and documentation are under the MIT
license in LICENSE (copyright Johannes Findeisen). No energyXT source, artwork, license in LICENSE (copyright Johannes Findeisen). No energyXT source, artwork,
proprietary SDK, vendored DSP or plugin binary is included. Permissively licensed proprietary SDK, vendored DSP or plugin binary is included. Permissively
ABI headers are vendored with their own notices. The source archive licensed ABI headers are vendored with their own notices. The source archive
contains repository files and an export manifest; it does not bundle system contains repository files and an export manifest; it does not bundle system
libraries. Installation includes LICENSE and these documents. libraries. Installation includes LICENSE and these documents.
The host review inspected installed package metadata and local license files. Package-level license sets can include utilities/docs that differ from the The host review inspected installed package metadata and local license files.
Package-level license sets can include utilities/docs that differ from the
specific library being linked; retain the relevant upstream notices when specific library being linked; retain the relevant upstream notices when
redistributing dependencies. The default install uses system shared libraries. redistributing dependencies. The default install uses system shared libraries.
@ -27,10 +28,10 @@ redistributing dependencies. The default install uses system shared libraries.
| D-Bus | AFL-2.1 OR GPL-2.0-or-later. | | D-Bus | AFL-2.1 OR GPL-2.0-or-later. |
| Python 3 | PSF-2.0; optional build/test/package scripts use only the standard library. Not an application runtime dependency. | | Python 3 | PSF-2.0; optional build/test/package scripts use only the standard library. Not an application runtime dependency. |
GNU Make, GCC/Clang, binutils, pkg-config and optional Xvfb/xdotool/sanitizers are GNU Make, GCC/Clang, binutils, pkg-config and optional Xvfb/xdotool/sanitizers
build/test tools, not shipped dependencies. New Python tooling needs Python 3.12+ are build/test tools, not shipped dependencies. New Python tooling needs Python
for safe tar extraction in release tests; this host has Python 3.14.8. No packages 3.12+ for safe tar extraction in release tests; this host has Python 3.14.8. No
were installed and no application dependency was added. packages were installed and no application dependency was added.
Binary redistributors must inspect their own `readelf -d`/`ldd` dependency set Binary redistributors must inspect their own `readelf -d`/`ldd` dependency set
and supply the notices/source/relinking facilities required by those exact and supply the notices/source/relinking facilities required by those exact
@ -38,7 +39,9 @@ licenses and linking choices. FLTK's exception does not remove obligations of
other linked libraries. This review covers the current unmodified system-linked other linked libraries. This review covers the current unmodified system-linked
build; static all-in-one bundles and external plugins need a separate review. build; static all-in-one bundles and external plugins need a separate review.
## LADSPA review ---
## LADSPA Review
The optional external host includes the system LADSPA 1.1 header (`ladspa` The optional external host includes the system LADSPA 1.1 header (`ladspa`
1.17-7), copyright Richard W.E. Furse, Paul Barton-Davis and Stefan Westerfeld, 1.17-7), copyright Richard W.E. Furse, Paul Barton-Davis and Stefan Westerfeld,
@ -50,12 +53,15 @@ The original fixture in tests/plugins is MIT repository code.
Acceptance dynamically loads the user's installed LADSPA sample amp/sine modules Acceptance dynamically loads the user's installed LADSPA sample amp/sine modules
(package metadata LGPL-2.1-or-later) and SWH inverter (swh-plugins 0.4.17-7, (package metadata LGPL-2.1-or-later) and SWH inverter (swh-plugins 0.4.17-7,
GPL-2.0-or-later). Testing/loading a user-installed module does not grant the right GPL-2.0-or-later). Testing/loading a user-installed module does not grant the
to bundle or relicense it. Redistributors must review each chosen module and the right to bundle or relicense it. Redistributors must review each chosen module
combined distribution's obligations separately; xtc's MIT grant covers only its and the combined distribution's obligations separately; xtc's MIT grant covers
original code. No third-party plugin is included in source archives or installs. only its original code. No third-party plugin is included in source archives or
installs.
## Pinned header dependencies ---
## Pinned Header Dependencies
CLAP headers are MIT, copyright Alexandre BIQUE, from upstream commit CLAP headers are MIT, copyright Alexandre BIQUE, from upstream commit
`a47f6badb49d948fd009998f28309cdab78979c9`. The generated VST3 C ABI header is `a47f6badb49d948fd009998f28309cdab78979c9`. The generated VST3 C ABI header is
@ -63,7 +69,8 @@ BSD-3-Clause, copyright Steinberg Media Technologies GmbH, from commit
`93a116f7c6c52a317ec3aaee9b13789068b5234e`. Exact licenses are retained under `93a116f7c6c52a317ec3aaee9b13789068b5234e`. Exact licenses are retained under
third_party and installed as CLAP-LICENSE / VST3-C-API-LICENSE. These are header third_party and installed as CLAP-LICENSE / VST3-C-API-LICENSE. These are header
specifications only: no external runtime SDK or network build step was added. specifications only: no external runtime SDK or network build step was added.
Review updates explicitly; do not assume future revisions share the same license. Review updates explicitly; do not assume future revisions share the same
license.
The VST2 compatibility declarations and all three ABI fixtures are original MIT The VST2 compatibility declarations and all three ABI fixtures are original MIT
repository code; no Steinberg VST2 SDK or third-party plugin is bundled. Hosting repository code; no Steinberg VST2 SDK or third-party plugin is bundled. Hosting

View file

@ -1,10 +1,11 @@
# Linux setup and installation # Linux Setup and Installation
Use a C99 compiler, C++17 compiler for the GUI, GNU Make, binutils and pkg-config. Use a C99 compiler, C++17 compiler for the GUI, GNU Make, binutils and
The core needs only the C/math runtime. FLTK 1.4 development files enable the GUI; pkg-config. The core needs only the C/math runtime. FLTK 1.4 development files
JACK-compatible development files enable the optional audio/MIDI backend. enable the GUI; JACK-compatible development files enable the optional audio/MIDI
Packaging/install tests additionally use Python 3.12+ (standard library only). backend. Packaging/install tests additionally use Python 3.12+ (standard library
No command starts a server, installs dependencies or changes system settings. only). No command starts a server, installs dependencies or changes system
settings.
```sh ```sh
make all test make all test
@ -15,20 +16,24 @@ make gui
Start/configure a JACK or PipeWire JACK-compatible server through your existing Start/configure a JACK or PipeWire JACK-compatible server through your existing
system setup first. xtc opens without starting one and requires reconnect after system setup first. xtc opens without starting one and requires reconnect after
server loss/rate changes. In a separate JACK routing tool connect desired MIDI server loss/rate changes. In a separate JACK routing tool connect desired MIDI
to `midi_in`, audio capture to `input`, and `output`/`output_right` to left/right to `midi_in`, audio capture to `input`, and `output`/`output_right` to
playback ports. No physical routes are automatic. Input/recording remain mono. left/right playback ports. No physical routes are automatic. Input/recording
See GUI.md, AUDIO_FILES.md, MIDI.md and MIXER.md for composing/recording/editing. remain mono. See GUI.md, AUDIO_FILES.md, MIDI.md and MIXER.md for
composing/recording/editing.
`xtc --help` and `xtc --version` work without a display/server. `xtc project.xtc` `xtc --help` and `xtc --version` work without a display/server.
opens one project; no-file startup retains FLTK options such as `-display`. `xtc project.xtc` opens one project; no-file startup retains FLTK options such
`xtc-check --validate project.xtc` validates metadata only; native graphs cannot as `-display`. `xtc-check --validate project.xtc` validates metadata only;
be serialized. See RECOVERY.md before replacing damaged files. native graphs cannot be serialized. See RECOVERY.md before replacing damaged
files.
## Staged installation ---
Core-only installation retains the original `make install` behavior and now ## Staged Installation
adds xtc-check, pkg-config metadata, license and documentation. install-gui adds
the GUI executable and desktop launcher. Defaults to PREFIX=/usr/local: Core-only installation retains the original `make install` behavior and now adds
xtc-check, pkg-config metadata, license and documentation. install-gui adds the
GUI executable and desktop launcher. Defaults to PREFIX=/usr/local:
```sh ```sh
make install-gui DESTDIR="$PWD/build/stage" PREFIX=/usr make install-gui DESTDIR="$PWD/build/stage" PREFIX=/usr
@ -57,28 +62,33 @@ file records its private dependency. Public headers contain no JACK/FLTK types.
To build without optional libraries, set PKG_CONFIG=false FLTK_CONFIG=false. To build without optional libraries, set PKG_CONFIG=false FLTK_CONFIG=false.
Python is not needed to run the installed executables or library. Python is not needed to run the installed executables or library.
## Binary/development archive ---
## Binary/Development Archive
`make package` creates a system-linked Linux archive plus SHA-256 sidecar below `make package` creates a system-linked Linux archive plus SHA-256 sidecar below
BUILD_DIR; `make test-package` verifies its contents. It includes usr/bin, BUILD_DIR; `make test-package` verifies its contents. It includes usr/bin,
headers/static library/pkg-config metadata, desktop file (when FLTK is present), headers/static library/pkg-config metadata, desktop file (when FLTK is present),
manuals, baseline and MIT license. No shared library, third-party plugin, media, manuals, baseline and MIT license. No shared library, third-party plugin, media,
service or dependency installer is bundled. `BINARY_MANIFEST.json` lists file service or dependency installer is bundled. `BINARY_MANIFEST.json` lists file
hashes/sizes, architecture/libc and ELF NEEDED libraries. The entire archive's hash hashes/sizes, architecture/libc and ELF NEEDED libraries. The entire archive's
covers the manifest too. Do not assume compatibility with another distribution. hash covers the manifest too. Do not assume compatibility with another
distribution.
Inspect/test under a temporary directory first. For a writable user prefix, Inspect/test under a temporary directory first. For a writable user prefix,
extract the archive with two leading path components stripped (`xtc-VERSION-linux- extract the archive with two leading path components stripped
ARCH/usr`), placing bin/include/lib/share directly under that prefix. Put its bin (`xtc-VERSION-linux- ARCH/usr`), placing bin/include/lib/share directly under
on PATH and lib/pkgconfig on PKG_CONFIG_PATH. The archive's pkg-config prefix is that prefix. Put its bin on PATH and lib/pkgconfig on PKG_CONFIG_PATH. The
relative to its installed location; normal `make install` retains explicit PREFIX. archive's pkg-config prefix is relative to its installed location; normal
Use the matching source's uninstall with that PREFIX, or review the manifest `make install` retains explicit PREFIX. Use the matching source's uninstall with
before removing its files. Unknown/unrelated files are retained by source uninstall. that PREFIX, or review the manifest before removing its files. Unknown/unrelated
This local tar package is not a pacman/RPM/deb registration or public distribution. files are retained by source uninstall. This local tar package is not a
pacman/RPM/deb registration or public distribution.
`xtc-check --diagnose` reports build capabilities and format/engine limits without `xtc-check --diagnose` reports build capabilities and format/engine limits
opening a server or executing a plugin. `xtc --diagnose` works headlessly as well. without opening a server or executing a plugin. `xtc --diagnose` works
Missing runtime GUI libraries are loader errors: install the distribution's headlessly as well. Missing runtime GUI libraries are loader errors: install the
compatible dependencies or rebuild the core-only configuration. Build without distribution's compatible dependencies or rebuild the core-only configuration.
optional libraries using a fresh directory, `PKG_CONFIG=false FLTK_CONFIG=false Build without optional libraries using a fresh directory,
CPPFLAGS=-DXTC_DISABLE_LADSPA`. No system setting or package is changed by tests. `PKG_CONFIG=false FLTK_CONFIG=false CPPFLAGS=-DXTC_DISABLE_LADSPA`. No system
setting or package is changed by tests.

View file

@ -1,45 +1,53 @@
# Release and maintenance policy # Release and Maintenance Policy
## Supported development target ## Supported Development Target
Initial target: x86_64 Artix/Arch-family Linux with glibc, GNU Make, GCC/Clang, Initial target: x86_64 Artix/Arch-family Linux with glibc, GNU Make, GCC/Clang,
FLTK 1.4 and a JACK-compatible client/server. The inspected host uses GCC 16.2.1, FLTK 1.4 and a JACK-compatible client/server. The inspected host uses GCC
FLTK 1.4.5 and the dependencies in LICENSES.md. Core-only/no-JACK/no-LADSPA builds 16.2.1, FLTK 1.4.5 and the dependencies in LICENSES.md.
are checked too. Build on the destination distribution for different library ABIs; Core-only/no-JACK/no-LADSPA builds are checked too. Build on the destination
there is no cross-distribution static-binary promise. Python 3.12+ and binutils distribution for different library ABIs; there is no cross-distribution
are build/package/test tools only. No dependency is fetched automatically. static-binary promise. Python 3.12+ and binutils are build/package/test tools
only. No dependency is fetched automatically.
## Release checklist ---
## Release Checklist
1. Work from a clean reviewed commit. Record `git rev-parse HEAD`, compiler, 1. Work from a clean reviewed commit. Record `git rev-parse HEAD`, compiler,
libc/FLTK/JACK versions and `xtc-check --diagnose`. No automatic version bump, libc/FLTK/JACK versions and `xtc-check --diagnose`. No automatic version
Git tag, public upload or external release is performed by local targets. bump, Git tag, public upload or external release is performed by local
2. Run warning-enabled `make all test gui test-plugins test-plugins-real targets.
test-gui-plugins test-workflow test-gui-jack test-jack-midi test-recording 2. Run warning-enabled
test-recovery test-reliability test-scale analyze` where dependencies exist. `make all test gui test-plugins test-plugins-real test-gui-plugins test-workflow test-gui-jack test-jack-midi test-recording test-recovery test-reliability test-scale analyze`
where dependencies exist.
3. Use a separate BUILD_DIR for ASan/UBSan/leak runs and another for optional- 3. Use a separate BUILD_DIR for ASan/UBSan/leak runs and another for optional-
dependency builds. GUI leak checks use only the documented fontconfig dependency builds. GUI leak checks use only the documented fontconfig
suppression; core tests use none. Run publication/recording race tests as suppression; core tests use none. Run publication/recording race tests as
described in RELIABILITY.md. Sanitizers are not latency qualification. described in RELIABILITY.md. Sanitizers are not latency qualification.
4. Run `make benchmark test-performance test-stress`; compare on matching 4. Run `make benchmark test-performance test-stress`; compare on matching
host/compiler/flags against a reviewed baseline. Investigate regressions; host/compiler/flags against a reviewed baseline. Investigate regressions; do
do not weaken thresholds or replace evidence without explanation. not weaken thresholds or replace evidence without explanation.
5. Run `make test-install test-release test-package`. Inspect archive manifests, 5. Run `make test-install test-release test-package`. Inspect archive manifests,
hashes, dynamic dependency names and licenses. Source exports rebuild twice hashes, dynamic dependency names and licenses. Source exports rebuild twice
with identical artifacts on this host. Repeat binary packaging compares bytes. with identical artifacts on this host. Repeat binary packaging compares
6. Complete physical listening, recording/device/endurance, and independent human bytes.
install/workflow checks before claiming production qualification. They remain 6. Complete physical listening, recording/device/endurance, and independent
pending for this prototype, even when all automated checks pass. human install/workflow checks before claiming production qualification. They
remain pending for this prototype, even when all automated checks pass.
7. Save checksums/test logs/conditions with the proposed release. Review known 7. Save checksums/test logs/conditions with the proposed release. Review known
limitations and format compatibility and update user/developer docs. Publishing limitations and format compatibility and update user/developer docs.
or distro repository submission is a separate action. Publishing or distro repository submission is a separate action.
## Regressions and compatibility ---
## Regressions and Compatibility
Report the exact version/commit, OS/toolchain, compiled features, reproduction, Report the exact version/commit, OS/toolchain, compiled features, reproduction,
expected/actual output, GUI errors/xruns and whether plugins are involved. Keep a expected/actual output, GUI errors/xruns and whether plugins are involved. Keep
minimal project/media fixture when redistribution is permitted; do not attach a minimal project/media fixture when redistribution is permitted; do not attach
private recordings or full memory dumps inadvertently. No telemetry/upload occurs. private recordings or full memory dumps inadvertently. No telemetry/upload
occurs.
A confirmed defect gets a focused deterministic regression or repeatable runtime A confirmed defect gets a focused deterministic regression or repeatable runtime
check before its fix. Memory/thread/file-loss defects take priority; disable the check before its fix. Memory/thread/file-loss defects take priority; disable the
@ -47,22 +55,25 @@ unsafe path rather than silently dropping media or unknown project records. Keep
candidate preparation/plan publication transactional and callbacks bounded. candidate preparation/plan publication transactional and callbacks bounded.
Format changes increment XTC_PROJECT_FORMAT_VERSION, retain explicit bounded Format changes increment XTC_PROJECT_FORMAT_VERSION, retain explicit bounded
migrations/fixtures for all supported older versions, and add malformed/roundtrip migrations/fixtures for all supported older versions, and add
checks. Unknown versions/types/records fail with actionable errors. Never rewrite malformed/roundtrip checks. Unknown versions/types/records fail with actionable
an unsupported source file during failed Open. Keep the original when diagnosing errors. Never rewrite an unsupported source file during failed Open. Keep the
post-publication sync errors. Native descriptor ABI compatibility is distinct from original when diagnosing post-publication sync errors. Native descriptor ABI
project format and is documented in NATIVE_API.md; opaque DSP state is not promised. compatibility is distinct from project format and is documented in
No public stability guarantee or support SLA is implied by development version 0.1.0. NATIVE_API.md; opaque DSP state is not promised. No public stability guarantee
or support SLA is implied by development version 0.1.0.
## Support and triage record ---
## Support and Triage Record
Verified compilers are GCC/G++ 16.2.1 and Clang/Clang++ 23.1.3 on x86_64 Linux; Verified compilers are GCC/G++ 16.2.1 and Clang/Clang++ 23.1.3 on x86_64 Linux;
these are tested versions, not a promise that arbitrary older/newer versions work. these are tested versions, not a promise that arbitrary older/newer versions
The code requires C99 plus GCC/Clang lock-free atomics, C++17 for the GUI, FLTK work. The code requires C99 plus GCC/Clang lock-free atomics, C++17 for the GUI,
1.4 and POSIX/glibc APIs. JACK (including PipeWire's JACK compatibility) is the FLTK 1.4 and POSIX/glibc APIs. JACK (including PipeWire's JACK compatibility) is
only live backend. Native Windows/macOS, other architectures and direct ALSA the only live backend. Native Windows/macOS, other architectures and direct ALSA
backends are not qualified. Core builds can disable JACK, FLTK and LADSPA. backends are not qualified. Core builds can disable JACK, FLTK and LADSPA. See
See BUILD.md and LICENSES.md for dependency roles and versions. BUILD.md and LICENSES.md for dependency roles and versions.
Release qualification adds `test-install-layout`, installed example inspection, Release qualification adds `test-install-layout`, installed example inspection,
all performance report resources, source checksum verification and the current all performance report resources, source checksum verification and the current
@ -70,11 +81,12 @@ RELEASE_NOTES.md. Retain the original graph benchmark alert in release records;
a successful compile does not clear timing qualification gaps. a successful compile does not clear timing qualification gaps.
File reproducible bugs with the repository maintainers using the source commit File reproducible bugs with the repository maintainers using the source commit
and archive SHA-256, `xtc-check --version`/`--diagnose`, distro/kernel and toolchain, and archive SHA-256, `xtc-check --version`/`--diagnose`, distro/kernel and
exact commands or GUI steps, expected versus observed behavior and frequency. toolchain, exact commands or GUI steps, expected versus observed behavior and
Reduce to a redistributable project and state whether it reproduces without frequency. Reduce to a redistributable project and state whether it reproduces
external plugins and with the offline/core tools. Include relevant stderr and without external plugins and with the offline/core tools. Include relevant
sanitizer traces; preserve original recovery files before experimenting. stderr and sanitizer traces; preserve original recovery files before
experimenting.
Maintainers should first confirm version/features and reproduce in an isolated Maintainers should first confirm version/features and reproduce in an isolated
build. Prioritize data loss/security/memory/thread defects, then crashes and build. Prioritize data loss/security/memory/thread defects, then crashes and

View file

@ -1,49 +1,57 @@
# MIDI clips and instrument # MIDI Clips and Instrument
Drawing persisted notes and live JACK MIDI input are supported. Record still Drawing persisted notes and live JACK MIDI input are supported. Record still
captures audio to WAV: MIDI capture and Standard MIDI File import are deferred. captures audio to WAV: MIDI capture and Standard MIDI File import are deferred.
---
## Workflow ## Workflow
Add/select a track in Sequencer, choose Add MIDI clip, then Piano roll (Ctrl+4). Add/select a track in Sequencer, choose Add MIDI clip, then Piano roll (Ctrl+4).
Creation adds an instrument-to-output route if no instrument exists. Existing Creation adds an instrument-to-output route if no instrument exists. Existing
instruments retain their routes; use Modular Graph to connect them as needed. instruments retain their routes; use Modular Graph to connect them as needed.
Scheduled MIDI clips now apply their track mix/automation before summing to Scheduled MIDI clips now apply their track mix/automation before summing to
paired stereo instrument buffers; live input stays centered and unassigned. No hardware ports paired stereo instrument buffers; live input stays centered and unassigned. No
are connected automatically. Connect a MIDI source to the backend's `midi_in` hardware ports are connected automatically. Connect a MIDI source to the
JACK port, and route audio output explicitly to hear it while Play is running. backend's `midi_in` JACK port, and route audio output explicitly to hear it
while Play is running.
The piano roll shows all 128 pitches and edits clip-source time. Click blank The piano roll shows all 128 pitches and edits clip-source time. Click blank
space to draw a one-beat note (velocity 100, filtered channel or channel 0); drag its body to move space to draw a one-beat note (velocity 100, filtered channel or channel 0);
it, drag the right edge to resize, or right-click/Delete to remove it. Edits drag its body to move it, drag the right edge to resize, or right-click/Delete
snap to the configured grid (default 1/16). Escape cancels a drag. Trimmed-out source notes remain editable to remove it. Edits snap to the configured grid (default 1/16). Escape cancels a
and are preserved. Arrangement/note edits stop playback and are blocked during drag. Trimmed-out source notes remain editable and are preserved.
audio recording. Save/Open preserves MIDI flags, notes and stable IDs. Arrangement/note edits stop playback and are blocked during audio recording.
Save/Open preserves MIDI flags, notes and stable IDs.
## Timing and ownership ---
The C model owns up to 4096 notes per project, with source-relative ticks at ## Timing and Ownership
960 ticks/quarter and constant tempo. Control-thread preparation intersects
notes with clip trims and converts boundaries to integer sample positions. The C model owns up to 4096 notes per project, with source-relative ticks at 960
Scheduled rendering uses absolute note age: block partitions do not change ticks/quarter and constant tempo. Control-thread preparation intersects notes
samples, and seeking into a note chases its phase. Clip ends cut notes off. with clip trims and converts boundaries to integer sample positions. Scheduled
The instrument source has its own buffer, separate from external audio input rendering uses absolute note age: block partitions do not change samples, and
and WAV timeline playback, and routes through the ordinary graph. seeking into a note chases its phase. Clip ends cut notes off. The instrument
source has its own buffer, separate from external audio input and WAV timeline
playback, and routes through the ordinary graph.
Prepared ranges are immutable; only their audio owner changes voice/panic state. Prepared ranges are immutable; only their audio owner changes voice/panic state.
Preparation allocates; rendering uses bounded preallocated storage and performs Preparation allocates; rendering uses bounded preallocated storage and performs
no allocation, freeing, I/O, locks, logging or GUI calls. Project preparation no allocation, freeing, I/O, locks, logging or GUI calls. Project preparation
builds audio and MIDI candidates before committing the stopped backend state. builds audio and MIDI candidates before committing the stopped backend state.
JACK events have sample offsets within the current block. Supported messages JACK events have sample offsets within the current block. Supported messages are
are complete note on/off (including velocity-zero note on) and CC120/123 on complete note on/off (including velocity-zero note on) and CC120/123 on channels
channels 0..15, plus CC7 volume, CC10 pan and CC64 sustain (see CONTROL.md). 0..15, plus CC7 volume, CC10 pan and CC64 sustain (see CONTROL.md). Equal-offset
Equal-offset events retain input order. Bend, program changes, running status events retain input order. Bend, program changes, running status and SysEx are
and SysEx are unsupported. unsupported. At most 256 events are accepted per block. Malformed/overflowed
At most 256 events are accepted per block. Malformed/overflowed input discards input discards that block's events and triggers panic, with a MIDI-drop status
that block's events and triggers panic, with a MIDI-drop status counter. counter.
## Recovery and limits ---
## Recovery and Limits
Panic/F9 clears live voices and suppresses scheduled notes already started; Panic/F9 clears live voices and suppresses scheduled notes already started;
future timeline notes can still start. CC120/123 does this for its channel. future timeline notes can still start. CC120/123 does this for its channel.
@ -52,15 +60,17 @@ notes to be chased again. MIDI port disconnection, xruns and callback processing
failures request panic. Control requests use lock-free generation counters; failures request panic. Control requests use lock-free generation counters;
voice state changes on the audio thread, or while stopped. voice state changes on the audio thread, or while stopped.
The sine instrument scales amplitude by 0.1 times velocity/127. It supports The sine instrument scales amplitude by 0.1 times velocity/127. It supports 32
32 simultaneous scheduled notes and a separate 32 live voices; live overflow simultaneous scheduled notes and a separate 32 live voices; live overflow steals
steals the oldest voice, and repeated channel/pitch retriggers it. Preparation the oldest voice, and repeated channel/pitch retriggers it. Preparation rejects
rejects more than 32 overlapping scheduled notes with CAPACITY. Such metadata more than 32 overlapping scheduled notes with CAPACITY. Such metadata can be
can be saved by the C model, but GUI Open/Play rejects it until overlap is saved by the C model, but GUI Open/Play rejects it until overlap is reduced.
reduced. Immediate note-off/stealing can click; there is no envelope or limiter, Immediate note-off/stealing can click; there is no envelope or limiter, and
and summed voices can exceed unity. Above-Nyquist pitches are silent. summed voices can exceed unity. Above-Nyquist pitches are silent. There is no
There is no per-track/channel instrument routing, MIDI recording, SMF import per-track/channel instrument routing, MIDI recording, SMF import or piano-roll
or piano-roll zoom yet. Note edits participate in project snapshot undo/redo. zoom yet. Note edits participate in project snapshot undo/redo.
---
## Verification ## Verification
@ -70,13 +80,15 @@ malformed files, plus callback fault recovery. `make test-gui` draws, moves and
resizes notes with FLTK events, then saves/reopens and renders them. resizes notes with FLTK events, then saves/reopens and renders them.
`make test-jack-midi` uses only software MIDI/audio ports to verify live on/off, `make test-jack-midi` uses only software MIDI/audio ports to verify live on/off,
panic, disconnection and saved/reopened scheduled playback. JACK port discovery panic, disconnection and saved/reopened scheduled playback. JACK port discovery
may lag activation under PipeWire; the test waits before connecting. may lag activation under PipeWire; the test waits before connecting. Physical
Physical MIDI devices, bridge/device loss behind an unchanged port, latency and MIDI devices, bridge/device loss behind an unchanged port, latency and
long-running performance remain unverified. Panic/Stop provides manual recovery. long-running performance remain unverified. Panic/Stop provides manual recovery.
Track mixing, automation and project undo/redo are available; see MIXER.md. Track mixing, automation and project undo/redo are available; see MIXER.md.
## Composition commands ---
## Composition Commands
The piano-roll **Notes** menu exposes these commands. Shortcuts apply while the The piano-roll **Notes** menu exposes these commands. Shortcuts apply while the
piano roll has focus; use the main window Edit menu for undo/redo. piano roll has focus; use the main window Edit menu for undo/redo.
@ -92,22 +104,22 @@ piano roll has focus; use the main window Edit menu for undo/redo.
| Select filtered | Ctrl+A | Select every note matching the current clip filter | | Select filtered | Ctrl+A | Select every note matching the current clip filter |
| Delete | Delete | Remove the single selected note or all filtered notes | | Delete | Delete | Remove the single selected note or all filtered notes |
160 ticks is a sixteenth-note triplet; 320 is an eighth-note triplet. 160 ticks is a sixteenth-note triplet; 320 is an eighth-note triplet. The grid
The grid governs drawing, drag movement and minimum resize duration. At 1-tick governs drawing, drag movement and minimum resize duration. At 1-tick snap the
snap the visual grid remains 120 ticks to stay legible at the fixed zoom. visual grid remains 120 ticks to stay legible at the fixed zoom. The header
The header shows current grid, channel/pitch filter and selection scope. shows current grid, channel/pitch filter and selection scope. Velocity also
Velocity also affects unselected note brightness. Preferences are session-only. affects unselected note brightness. Preferences are session-only.
Click selects one visible note; Ctrl+A selects the whole filtered set. A click Click selects one visible note; Ctrl+A selects the whole filtered set. A click
or filter change clears that set selection. Hidden notes cannot be mouse-edited. or filter change clears that set selection. Hidden notes cannot be mouse-edited.
A channel filter also sets the channel for newly drawn notes. No lasso or A channel filter also sets the channel for newly drawn notes. No lasso or
arbitrary disjoint multi-selection is provided. Moving/resizing uses the mouse; arbitrary disjoint multi-selection is provided. Moving/resizing uses the mouse;
V edits velocity/channel without changing timing, pitch or IDs. Quantization V edits velocity/channel without changing timing, pitch or IDs. Quantization
changes starts only, preserving durations and other properties. Repetition changes starts only, preserving durations and other properties. Repetition keeps
keeps originals and gives every copy a fresh stable ID; spacing need not equal originals and gives every copy a fresh stable ID; spacing need not equal note
note length, so both overlapping patterns and rests are possible. The existing length, so both overlapping patterns and rests are possible. The existing clip
clip source must have room for every result; extend it in Sequencer first if source must have room for every result; extend it in Sequencer first if needed.
needed. Source notes outside the visible arrangement trim remain editable. Source notes outside the visible arrangement trim remain editable.
All batch edits validate before mutation. Invalid pitch/range, copy counts, All batch edits validate before mutation. Invalid pitch/range, copy counts,
capacity overflow or a note crossing the source boundary rejects the entire capacity overflow or a note crossing the source boundary rejects the entire
@ -120,24 +132,27 @@ These edits use the existing persisted note fields; format remains v7.
Scheduled notes occupy [start,end), with ends counted before starts at an equal Scheduled notes occupy [start,end), with ends counted before starts at an equal
sample for the 32-voice limit. Overlapping same-channel/pitch scheduled notes sample for the 32-voice limit. Overlapping same-channel/pitch scheduled notes
are independent sine voices; adjacent repetitions restart phase. Live repeated are independent sine voices; adjacent repetitions restart phase. Live repeated
notes instead retrigger the matching channel/pitch voice, and a note-off releases notes instead retrigger the matching channel/pitch voice, and a note-off
that voice (or defers release under sustain). Equal-sample live on/off/control releases that voice (or defers release under sustain). Equal-sample live
messages execute in caller/JACK order, with no implicit priority reordering. on/off/control messages execute in caller/JACK order, with no implicit priority
The renderer rejects malformed/unsorted/out-of-range events before touching reordering. The renderer rejects malformed/unsorted/out-of-range events before
output or runtime; raw velocity-zero note-ons decode to note-off. touching output or runtime; raw velocity-zero note-ons decode to note-off.
`make test-composition` covers filters, property preservation, triplet rounding, `make test-composition` covers filters, property preservation, triplet rounding,
repeats/fresh IDs, rejected batch rollback, capacity, undo snapshots, equal-offset repeats/fresh IDs, rejected batch rollback, capacity, undo snapshots,
live order, adjacent/overlapping notes, exact block partitions and seek chase. equal-offset live order, adjacent/overlapping notes, exact block partitions and
`make test-gui` covers mouse edits and the new commands with undo/redo and engine seek chase. `make test-gui` covers mouse edits and the new commands with
reinstallation. Listening and physical MIDI-device validation remain pending. undo/redo and engine reinstallation. Listening and physical MIDI-device
validation remain pending.
CC7/10/64 are defaults, configurable per channel through Audio → MIDI controller CC7/10/64 are defaults, configurable per channel through Audio → MIDI controller
mapping. Disabled/unmapped CCs have no effect; CC120/123 stay panic. mapping. Disabled/unmapped CCs have no effect; CC120/123 stay panic. Decode
Decode accepts valid CC0..127 messages within the existing event budget. See accepts valid CC0..127 messages within the existing event budget. See CONTROL.md
CONTROL.md for the exact bindings, ramp, sustain and automation precedence rules. for the exact bindings, ramp, sustain and automation precedence rules.
## Instrument support ---
## Instrument Support
MIDI tracks can bind CLAP, VST2 and VST3 instruments. In the Track menu, select MIDI tracks can bind CLAP, VST2 and VST3 instruments. In the Track menu, select
**Load instrument plugin** for the selected sequencer track; **Use built-in **Load instrument plugin** for the selected sequencer track; **Use built-in
@ -148,9 +163,9 @@ their own track's instrument. Independent instances feed track gain/pan,
mute/solo, bus routing and automation. Bindings/live target save in v12. mute/solo, bus routing and automation. Bindings/live target save in v12.
Supply the actual trusted Linux binary (including the inner .so of a VST3 Supply the actual trusted Linux binary (including the inner .so of a VST3
bundle). Supported instruments have zero audio inputs, one mono output, bundle). Supported instruments have zero audio inputs, one mono output, float32
float32 processing, zero latency and one note/event input. Default configuration processing, zero latency and one note/event input. Default configuration only:
only: no custom editor, opaque state/preset restoration, modern parameter no custom editor, opaque state/preset restoration, modern parameter
editing/automation, stereo/multichannel instruments or MIDI output. No arbitrary editing/automation, stereo/multichannel instruments or MIDI output. No arbitrary
third-party instrument is qualified by the original ABI fixture tests. third-party instrument is qualified by the original ABI fixture tests.
@ -168,14 +183,17 @@ the callback and old instances are reclaimed on the control thread. Missing
references remain inspectable without loading; failed GUI preparation retains references remain inspectable without loading; failed GUI preparation retains
the current project. Save a backup for applications limited to v11 or earlier. the current project. Save a backup for applications limited to v11 or earlier.
## Note dynamics and clip editing ---
## Note Dynamics and Clip Editing
Double-click a MIDI clip to open its piano roll. The Note velocity slider and Double-click a MIDI clip to open its piano roll. The Note velocity slider and
fixed Velocity lane edit individual notes from 1 to 127, preserving timing, fixed Velocity lane edit individual notes from 1 to 127, preserving timing,
pitch and channel. Chord notes at the same start can be selected individually in pitch and channel. Chord notes at the same start can be selected individually in
the keys before editing their highlighted bar. Completed gestures are undoable the keys before editing their highlighted bar. Completed gestures are undoable
and save in the existing note velocity field. Velocity is not a separate dB and save in the existing note velocity field. Velocity is not a separate dB
volume stage: the built-in sine scales amplitude, while plugins choose their volume stage: the built-in sine scales amplitude, while plugins choose their own
own patch-dependent response. The track header instrument dropdown reuses saved patch-dependent response. The track header instrument dropdown reuses saved
project instruments or opens explicit loading; bus tracks cannot host instruments. project instruments or opens explicit loading; bus tracks cannot host
See WORKSPACE.md for grid/snap divisions, filters and control behavior. instruments. See WORKSPACE.md for grid/snap divisions, filters and control
behavior.

View file

@ -1,20 +1,23 @@
# Application configuration and MIDI workflows # Application Configuration and MIDI Workflows
`~/.xtc.conf` is the application's base configuration. It is read before creating `~/.xtc.conf` is the application's base configuration. It is read before
its first project and backend. Missing keys use compiled defaults. Preferences creating its first project and backend. Missing keys use compiled defaults.
Apply writes every tab atomically with owner-only permissions; clean exit also Preferences Apply writes every tab atomically with owner-only permissions; clean
collects workspace, snap/zoom, loop, piano filters and note defaults. A missing exit also collects workspace, snap/zoom, loop, piano filters and note defaults.
file is created on Apply or clean exit. Invalid files produce a visible startup A missing file is created on Apply or clean exit. Invalid files produce a
error and are preserved until an explicit successful Apply replaces them. A save visible startup error and are preserved until an explicit successful Apply
failure keeps the window open for retry. Project contents, plugin states and replaces them. A save failure keeps the window open for retry. Project contents,
session clipboards are project/session data, not application preferences. plugin states and session clipboards are project/session data, not application
preferences.
All persistent preference fields are listed by the shared C schema and available All persistent preference fields are listed by the shared C schema and available
in Preferences. Existing project tempo/controller maps remain project data; in Preferences. Existing project tempo/controller maps remain project data;
Preferences project defaults apply to New. No configuration I/O runs in an audio Preferences project defaults apply to New. No configuration I/O runs in an audio
callback. Tests and screenshot captures use isolated configuration paths. callback. Tests and screenshot captures use isolated configuration paths.
## MIDI backend ---
## MIDI Backend
xtc exposes `midi_in` and `midi_out` JACK ports. The output sends scheduled MIDI xtc exposes `midi_in` and `midi_out` JACK ports. The output sends scheduled MIDI
clip notes from unmuted, solo-eligible tracks, retaining source channels and clip notes from unmuted, solo-eligible tracks, retaining source channels and
@ -27,13 +30,16 @@ are outside this note-output workflow.
Input can filter a single channel, and output can preserve or force a channel. Input can filter a single channel, and output can preserve or force a channel.
Optional thru forwards supported live notes, controllers and pitch bend while Optional thru forwards supported live notes, controllers and pitch bend while
transport is active. Thru defaults off to avoid accidental feedback. Callback transport is active. Thru defaults off to avoid accidental feedback. Callback
work and event buffers are bounded; overflow requests all-notes-off and increments work and event buffers are bounded; overflow requests all-notes-off and
the MIDI-drop counter. Panic and stop send CC123 on all channels. Immediate stop increments the MIDI-drop counter. Panic and stop send CC123 on all channels.
waits up to 100 ms for callback acknowledgement before deactivation; delivery Immediate stop waits up to 100 ms for callback acknowledgement before
cannot be guaranteed after server/device loss or to devices ignoring CC123. deactivation; delivery cannot be guaranteed after server/device loss or to
No device is connected unless a user chooses it or an external patchbay routes it. devices ignoring CC123. No device is connected unless a user chooses it or an
external patchbay routes it.
## MIDI preferences ---
## MIDI Preferences
Preferences → midi has editable dropdowns for a default input source and output Preferences → midi has editable dropdowns for a default input source and output
destination, plus a Refresh MIDI devices button. Names are enumerated from the destination, plus a Refresh MIDI devices button. Names are enumerated from the
@ -42,26 +48,31 @@ field for no automatic connection. Typed offline names remain saved. Hardware
ports are visible only when the host exposes them through its MIDI bridge. ports are visible only when the host exposes them through its MIDI bridge.
Input channel `-1` accepts all channels; `0..15` selects MIDI channel 1..16. Input channel `-1` accepts all channels; `0..15` selects MIDI channel 1..16.
Output channel `-1` preserves note channels; `0..15` forces a channel. Thru defaults Output channel `-1` preserves note channels; `0..15` forces a channel. Thru
off. Apply saves these choices; the next Play/Record restores the selected routes. defaults off. Apply saves these choices; the next Play/Record restores the
A missing port rejects that start and stops the backend, preserving the saved selected routes. A missing port rejects that start and stops the backend,
choice for retry. Reconnect audio after server loss, then retry Play. Defaults preserving the saved choice for retry. Reconnect audio after server loss, then
also apply when playback resumes after a loop edit. MIDI input/thru currently retry Play. Defaults also apply when playback resumes after a loop edit. MIDI
requires active transport. Existing editing and project_defaults tabs contain input/thru currently requires active transport. Existing editing and
note length, velocity, channel, filters and new-project controller mappings. project_defaults tabs contain note length, velocity, channel, filters and
new-project controller mappings.
## Export a selected clip ---
Select exactly one MIDI clip and choose File → Export selected clip as MIDI. ## Export A Selected Clip
The save chooser confirms overwrites. Export writes an atomic Standard MIDI File
(type 0, 960 PPQN) with project tempo, clip name and audible notes relative to the
clip start, retaining channels and velocities. Notes crossing trimmed boundaries
are cropped and notes outside the trim are omitted. Trailing silence remains.
Export does not edit the project or undo history. Audio clips reject. Only note
messages are exported: instruments, opaque plugin data, controller maps,
automation and audio are excluded. Export is unavailable during recording.
## Import MIDI files Select exactly one MIDI clip and choose File → Export selected clip as MIDI. The
save chooser confirms overwrites. Export writes an atomic Standard MIDI File
(type 0, 960 PPQN) with project tempo, clip name and audible notes relative to
the clip start, retaining channels and velocities. Notes crossing trimmed
boundaries are cropped and notes outside the trim are omitted. Trailing silence
remains. Export does not edit the project or undo history. Audio clips reject.
Only note messages are exported: instruments, opaque plugin data, controller
maps, automation and audio are excluded. Export is unavailable during recording.
---
## Import MIDI Files
File → Import MIDI (also in the sequencer Create menu) adds a clip at the cursor File → Import MIDI (also in the sequencer Create menu) adds a clip at the cursor
on the selected non-bus track, or creates a track when none is selected. Import on the selected non-bus track, or creates a track when none is selected. Import
@ -71,17 +82,18 @@ backend preparation retain the old project and IDs.
The reader supports Standard MIDI File types 0 and 1 with PPQN timing, including The reader supports Standard MIDI File types 0 and 1 with PPQN timing, including
running status and velocity-zero note-offs. File tracks merge into one clip; running status and velocity-zero note-offs. File tracks merge into one clip;
channels and velocities remain. Timing rounds to 960 ticks per quarter note, channels and velocities remain. Timing rounds to 960 ticks per quarter note,
with positive sub-tick notes given one tick. Trailing silence remains. The project with positive sub-tick notes given one tick. Trailing silence remains. The
tempo is retained: tempo maps, time signatures, CC/sustain, program changes, project tempo is retained: tempo maps, time signatures, CC/sustain, program
pitch bend, SysEx and other metadata are not imported. This is a notes workflow, changes, pitch bend, SysEx and other metadata are not imported. This is a notes
not full-song or instrument reconstruction. workflow, not full-song or instrument reconstruction.
Limits are 1 MiB per file, 64 file tracks, 65,536 parsed events and 4,096 notes Limits are 1 MiB per file, 64 file tracks, 65,536 parsed events and 4,096 notes
within the project's total note capacity. SMPTE timing and type 2 reject explicitly. within the project's total note capacity. SMPTE timing and type 2 reject
Unterminated notes, zero source duration and malformed events reject rather than explicitly. Unterminated notes, zero source duration and malformed events reject
silently dropping notes. Unmatched note-offs are harmless and ignored. Overlapping rather than silently dropping notes. Unmatched note-offs are harmless and
same-pitch notes pair FIFO within each file track/channel. Project save/reload ignored. Overlapping same-pitch notes pair FIFO within each file track/channel.
uses the existing format; no format migration or extra dependency is required. Project save/reload uses the existing format; no format migration or extra
dependency is required.
Configured startup holds the transport clock and emits silence until selected Configured startup holds the transport clock and emits silence until selected
MIDI ports are connected, so notes at the song start are not consumed before MIDI ports are connected, so notes at the song start are not consumed before

View file

@ -1,42 +1,46 @@
# Studio mixer # Studio Mixer
Open **View / Mixer** (Ctrl+2) to see a channel strip for every track and bus. Open **View / Mixer** (Ctrl+2) to see a channel strip for every track and bus.
The horizontal scrollbar reveals additional channels; the master output monitor The horizontal scrollbar reveals additional channels; the master output monitor
stays pinned on the right. The bank supports the full 64-track/bus project limit. stays pinned on the right. The bank supports the full 64-track/bus project
Channel order follows the sequencer, and colors match the track headers. limit. Channel order follows the sequencer, and colors match the track headers.
Each strip offers: Each strip offers:
- A name button that selects its track for automation below the bank. - A name button that selects its track for automation below the bank.
- **Mute** and **Solo** switches using the existing inclusive track/bus solo rules. - **Mute** and **Solo** switches using the existing inclusive track/bus solo
rules.
- A flat **Pan** knob: left -1, center 0, right +1. Drag to turn it; - A flat **Pan** knob: left -1, center 0, right +1. Drag to turn it;
double-click to center. The text shows direction and percentage. double-click to center. The text shows direction and percentage.
- An output dropdown for **Master** or a project bus. Bus outputs stay at Master; - An output dropdown for **Master** or a project bus. Bus outputs stay at
nested bus routing remains unsupported. **Add bus** creates a new bus strip. Master; nested bus routing remains unsupported. **Add bus** creates a new bus
- A vertical level fader, displayed in dB. Zero dB is unity gain. The upper limit strip.
is approximately +12.04 dB (linear gain 4); the -60 dB bottom position is silence. - A vertical level fader, displayed in dB. Zero dB is unity gain. The upper
limit is approximately +12.04 dB (linear gain 4); the -60 dB bottom position
is silence.
- Independent stereo sample-peak meters, numeric dBFS readouts and sticky clip - Independent stereo sample-peak meters, numeric dBFS readouts and sticky clip
indications. **Reset peaks** clears all strip and master clip latches. indications. **Reset peaks** clears all strip and master clip latches.
Changing a fader or knob commits on release, so a completed gesture is one undo Changing a fader or knob commits on release, so a completed gesture is one undo
step. The existing preparation guard stops playback before model edits and step. The existing preparation guard stops playback before model edits and
rejects changes while recording. Failed edits restore the displayed model values. rejects changes while recording. Failed edits restore the displayed model
Selections are UI state and create no undo step. Track IDs keep controls attached values. Selections are UI state and create no undo step. Track IDs keep controls
to the correct channel after reordering, undo, deletion or project replacement. attached to the correct channel after reordering, undo, deletion or project
replacement.
The top Track selector provides another way to select automation targets; the The top Track selector provides another way to select automation targets; the
bank scrolls to reveal the selection. M/S toggle the selected track's mute/solo, bank scrolls to reveal the selection. M/S toggle the selected track's mute/solo,
Left/Right adjust its linear gain, Shift+Left/Right adjust pan, and Up/Down select Left/Right adjust its linear gain, Shift+Left/Right adjust pan, and Up/Down
adjacent tracks. The existing automation controls below the bank retain Gain/Pan, select adjacent tracks. The existing automation controls below the bank retain
First tick and Linear/Hold interpolation. Click to add a point and right-click Gain/Pan, First tick and Linear/Hold interpolation. Click to add a point and
near a point to remove it. right-click near a point to remove it.
Faders and knobs display **static** mix values. Automation replaces those values Faders and knobs display **static** mix values. Automation replaces those values
during playback; the controls are not motorized automation displays. Automation during playback; the controls are not motorized automation displays. Automation
values retain their existing linear gain and pan units. Existing project files values retain their existing linear gain and pan units. Existing project files
store mix values and automation without a format change. Very small nonzero store mix values and automation without a format change. Very small nonzero
gains display at the fader floor; pan/mute/solo edits preserve their actual value. gains display at the fader floor; pan/mute/solo edits preserve their actual
Moving the fader to its bottom explicitly sets silence. value. Moving the fader to its bottom explicitly sets silence.
Meters use the backend's consumed peak snapshot for every track and bus, Meters use the backend's consumed peak snapshot for every track and bus,
including offscreen channels. Track taps are after track controls and before the including offscreen channels. Track taps are after track controls and before the
@ -44,41 +48,49 @@ bus, bus taps follow bus controls, and the master follows the modular graph and
transport envelope. Missing taps decay to silence. Displays use instant attack, transport envelope. Missing taps decay to silence. Displays use instant attack,
20 dB/s release and sticky clipping at a linear sample peak of 1 or more. 20 dB/s release and sticky clipping at a linear sample peak of 1 or more.
Numeric displays have a -90 dBFS floor, channel bars a -60 dBFS floor; master Numeric displays have a -90 dBFS floor, channel bars a -60 dBFS floor; master
bars cover -90 to +6 dBFS. These are sample-peak meters, not loudness or true-peak bars cover -90 to +6 dBFS. These are sample-peak meters, not loudness or
meters. The master strip is an output monitor, not a new gain control. true-peak meters. The master strip is an output monitor, not a new gain control.
## Engine mixing behavior ---
Gain is a linear multiplier from 0 to 4. Pan ranges from -1 (left) to +1 (right): ## Engine Mixing Behavior
center has unity on both sides and moving away attenuates the opposite side
linearly. This preserves existing centered mono levels; it is not an equal-power
pan law. Mute wins over Solo. Solo behavior across tracks and buses is defined below. WAV clips and scheduled MIDI use their containing track's settings before
summing into the audio/instrument graph sources. Live MIDI input and independent
graph oscillators/external input are unassigned sources and bypass track mixing.
Stereo WAV files are still downmixed at import; stereo output comes from panning
those mono sources. Separate per-track graph inserts/sends are deferred.
## Automation lanes Gain is a linear multiplier from 0 to 4. Pan ranges from -1 (left) to +1
(right): center has unity on both sides and moving away attenuates the opposite
side linearly. This preserves existing centered mono levels; it is not an
equal-power pan law. Mute wins over Solo. Solo behavior across tracks and buses
is defined below. WAV clips and scheduled MIDI use their containing track's
settings before summing into the audio/instrument graph sources. Live MIDI input
and independent graph oscillators/external input are unassigned sources and
bypass track mixing. Stereo WAV files are still downmixed at import; stereo
output comes from panning those mono sources. Separate per-track graph
inserts/sends are deferred.
---
## Automation Lanes
Choose Gain or Pan under Automation. The lane shows four bars (15360 ticks); Choose Gain or Pan under Automation. The lane shows four bars (15360 ticks);
First tick moves its viewport across the project. Grid labels are absolute ticks. First tick moves its viewport across the project. Grid labels are absolute
Click to add a point or replace the point at that snapped time (1/16 note, 240 ticks. Click to add a point or replace the point at that snapped time (1/16
ticks). Vertical position maps to gain 0..4 or pan -1..1, top to bottom descending. note, 240 ticks). Vertical position maps to gain 0..4 or pan -1..1, top to
Right-click near a point's time to remove it. Removing every point restores the bottom descending. Right-click near a point's time to remove it. Removing every
static control. There are at most 1024 points globally, with one per point restores the static control. There are at most 1024 points globally, with
track/parameter/tick. Track deletion cascades to its automation. one per track/parameter/tick. Track deletion cascades to its automation.
A nonempty lane replaces that parameter's static value. Between points the C A nonempty lane replaces that parameter's static value. Between points the C
engine interpolates linearly or holds each segment (Interpolation chooser), engine interpolates linearly or holds each segment (Interpolation chooser),
holding the first/last value outside holding the first/last value outside their range. Preparation converts ticks to
their range. Preparation converts ticks to integer sample endpoints, sorts and integer sample endpoints, sorts and indexes lanes. When several ticks quantize
indexes lanes. When several ticks quantize to one sample at very low sample to one sample at very low sample rates, the last point wins at that sample.
rates, the last point wins at that sample. Rendering searches prepared bounded Rendering searches prepared bounded arrays; it never sorts, allocates, accesses
arrays; it never sorts, allocates, accesses project data, or calls the GUI. project data, or calls the GUI. Seeking and block partitioning give the same
Seeking and block partitioning give the same timeline values. Mute/solo are timeline values. Mute/solo are static; automation capture, modes, curves and
static; automation capture, modes, curves and tempo automation are deferred. tempo automation are deferred.
## Stereo output and meters ---
## Stereo Output and Meters
The GUI opens the JACK backend with two audio output ports: `output` (left) and The GUI opens the JACK backend with two audio output ports: `output` (left) and
`output_right`. Input and raw WAV recording remain mono. Connect both outputs `output_right`. Input and raw WAV recording remain mono. Connect both outputs
@ -89,30 +101,34 @@ port. Old mono audio/MIDI render functions apply gain/mute/solo and gain
automation while ignoring pan; the stereo variants apply both lanes. automation while ignoring pan; the stereo variants apply both lanes.
The graph executes the same topology separately on left and right buses; The graph executes the same topology separately on left and right buses;
oscillators advance once per frame and are centered on both sides. Existing oscillators advance once per frame and are centered on both sides. Existing port
port metadata and validation describe the mono graph topology. Stereo processing metadata and validation describe the mono graph topology. Stereo processing uses
uses explicit paired buffers, not new port IDs. Output buffers must be distinct; explicit paired buffers, not new port IDs. Output buffers must be distinct;
stereo callers must not alias them with source buffers. Graph-node meter reads stereo callers must not alias them with source buffers. Graph-node meter reads
report the maximum of both sides on the processing owner only. report the maximum of both sides on the processing owner only.
Track and bus meters measure coherent audio plus scheduled MIDI sums, after their Track and bus meters measure coherent audio plus scheduled MIDI sums, after
own gain/pan/mute/solo controls, before the shared modular graph. A track tap is their own gain/pan/mute/solo controls, before the shared modular graph. A track
before its parent bus: a muted bus can have active member-track meters. Bus taps tap is before its parent bus: a muted bus can have active member-track meters.
are after bus controls. Master L/R measure the final graph output after transport Bus taps are after bus controls. Master L/R measure the final graph output after
fades; downstream effects and graph routing can change its level independently. transport fades; downstream effects and graph routing can change its level
independently.
The backend retains interval sample-peak maxima until one serialized control The backend retains interval sample-peak maxima until one serialized control
reader consumes them with `xtc_backend_read_meters`. The GUI polls about every reader consumes them with `xtc_backend_read_meters`. The GUI polls about every
100 ms. Atomic channels are independent (not a simultaneous waveform snapshot); 100 ms. Atomic channels are independent (not a simultaneous waveform snapshot);
peaks are quantized to 0.000001 linear and capped at 1000. Legacy backend status peaks are quantized to 0.000001 linear and capped at 1000. Legacy backend status
peaks remain last-block linear values. GUI readouts display 20*log10(peak) dBFS, floor -90, with instantaneous peaks remain last-block linear values. GUI readouts display 20*log10(peak) dBFS,
attack and 20 dB/second release. Channel bars cover -60 to +6 dBFS; master floor -90, with instantaneous attack and 20 dB/second release. Channel bars
bars cover -90 to +6 dBFS. cover -60 to +6 dBFS; master bars cover -90 to +6 dBFS. Each channel latches red
Each channel latches red CLIP at linear peak >=1 (0 dBFS) until Reset peaks. CLIP at linear peak >=1 (0 dBFS) until Reset peaks. Reset is session state, not
Reset is session state, not an audio edit. Selection changes preserve every strip's meter history. These are sample-peak meters, not RMS, LUFS or true-peak measurements. an audio edit. Selection changes preserve every strip's meter history. These are
There is no limiter; summed levels can exceed unity. sample-peak meters, not RMS, LUFS or true-peak measurements. There is no
limiter; summed levels can exceed unity.
## Mix buses and gain staging ---
## Mix Buses and Gain Staging
Use Add bus in Mixer, select an ordinary track, then choose its Route: Master or Use Add bus in Mixer, select an ordinary track, then choose its Route: Master or
an existing bus. Buses share the 64-track capacity and gain/pan automation APIs. an existing bus. Buses share the 64-track capacity and gain/pan automation APIs.
@ -123,42 +139,45 @@ routing. Editing uses the existing stopped-transport preparation workflow.
Track controls apply first, then parent bus controls, then the shared graph. Track controls apply first, then parent bus controls, then the shared graph.
Gain multiplies (track 2 times bus 2 gives 4); pan uses the same center-unity Gain multiplies (track 2 times bus 2 gives 4); pan uses the same center-unity
balance law at both stages. There is no normalization or automatic attenuation. balance law at both stages. There is no normalization or automatic attenuation.
Solo is one inclusive domain: a soloed track passes through its bus, a soloed bus Solo is one inclusive domain: a soloed track passes through its bus, a soloed
admits all its members, and unrelated tracks are silent. Track mute silences bus admits all its members, and unrelated tracks are silent. Track mute silences
that member; bus mute silences its entire output even when a member is soloed. that member; bus mute silences its entire output even when a member is soloed.
Live MIDI, external audio inputs and independent graph oscillators remain Live MIDI, external audio inputs and independent graph oscillators remain
unassigned and outside this solo domain. unassigned and outside this solo domain.
Master here means the source mix feeding the shared graph, not independent Master here means the source mix feeding the shared graph, not independent
per-bus effect chains. Graph fan-out and gain branches permit parallel processing per-bus effect chains. Graph fan-out and gain branches permit parallel
of shared sources. Independent per-track aux sends, sidechains, bus plugin processing of shared sources. Independent per-track aux sends, sidechains, bus
inserts and nested buses are not represented and remain deferred. plugin inserts and nested buses are not represented and remain deferred.
Preparation allocates shared stereo monitor scratch on the control thread: Preparation allocates shared stereo monitor scratch on the control thread: 2 *
2 * track_count * max_frames * sizeof(float), at most 32 MiB at current limits. track_count * max_frames * sizeof(float), at most 32 MiB at current limits. The
The callback clears, accumulates and scans bounded buffers, with no allocation, callback clears, accumulates and scans bounded buffers, with no allocation, I/O,
I/O, locks or GUI calls. Immutable audio/MIDI snapshots borrow this scratch until locks or GUI calls. Immutable audio/MIDI snapshots borrow this scratch until
stopped replacement or backend destruction. stopped replacement or backend destruction.
## Undo and keyboard workflow ---
Ctrl+Z undoes; Ctrl+Shift+Z or Ctrl+Y redoes. The C history keeps 32 project edits ## Undo and Keyboard Workflow
plus a baseline, cloning metadata and graph, never audio samples. Common graph,
track, clip, note, mixer, import/relink and automation edits are included. One Ctrl+Z undoes; Ctrl+Shift+Z or Ctrl+Y redoes. The C history keeps 32 project
accepted gesture is one step; selection, scrolling and graph-node placement are edits plus a baseline, cloning metadata and graph, never audio samples. Common
GUI state and are not undoable. New/Open resets history; Save retains it. graph, track, clip, note, mixer, import/relink and automation edits are
Undo/redo stops and resets transport; surviving selections are preserved by included. One accepted gesture is one step; selection, scrolling and graph-node
stable ID (layout remains session state). The placement are GUI state and are not undoable. New/Open resets history; Save
candidate is installed before moving the history cursor: validation, asset-plan retains it. Undo/redo stops and resets transport; surviving selections are
or backend failure retains the current document and history position. Missing preserved by stable ID (layout remains session state). The candidate is
assets retain the existing silent/reporting behavior. A new edit after Undo installed before moving the history cursor: validation, asset-plan or backend
removes the redo branch; the oldest state is evicted at capacity. Allocation failure retains the current document and history position. Missing assets retain
ID high-water marks survive Undo/branching so abandoned IDs are not reused. History is the existing silent/reporting behavior. A new edit after Undo removes the redo
session-only and Undo marks the document dirty even if it matches a saved state. branch; the oldest state is evicted at capacity. Allocation ID high-water marks
External WAV files are never deleted or restored by Undo; recording itself cannot survive Undo/branching so abandoned IDs are not reused. History is session-only
be undone while active. Snapshot storage is reserved before GUI commands mutate/publish, so an and Undo marks the document dirty even if it matches a saved state. External WAV
allocation failure rejects the command before adoption; reserved capture itself files are never deleted or restored by Undo; recording itself cannot be undone
does not allocate. C clients using unreserved history record must handle errors. while active. Snapshot storage is reserved before GUI commands mutate/publish,
so an allocation failure rejects the command before adoption; reserved capture
itself does not allocate. C clients using unreserved history record must handle
errors.
With mixer focus, M/S toggles mute/solo, Left/Right changes gain by 0.05, With mixer focus, M/S toggles mute/solo, Left/Right changes gain by 0.05,
Shift+Left/Right changes pan, and Up/Down selects a track. Native sliders also Shift+Left/Right changes pan, and Up/Down selects a track. Native sliders also
@ -168,30 +187,34 @@ singly selected; automation-point dragging is not implemented. Track rename and
arrangement clipboard editing are available. See MODERN_EDITING.md for static arrangement clipboard editing are available. See MODERN_EDITING.md for static
mixer A/B comparison and its automation limits. mixer A/B comparison and its automation limits.
## Verification and limits ---
## Verification and Limits
`test_mixer` verifies exact stereo gain/pan, mute/solo precedence, automation `test_mixer` verifies exact stereo gain/pan, mute/solo precedence, automation
samples and block equivalence, stereo graph phase/routing, WAV mixing, v4 exact samples and block equivalence, stereo graph phase/routing, WAV mixing, v4 exact
roundtrip/truncations/malformed records, lane capacities and history eviction and roundtrip/truncations/malformed records, lane capacities and history eviction
branching. Backend fault injection verifies stereo registration failure, output and branching. Backend fault injection verifies stereo registration failure,
names, output separation and atomic meters. GUI tests edit lanes with mouse output names, output separation and atomic meters. GUI tests edit lanes with
events, use mixer keys and exercise undo/redo, failed installation rollback, mouse events, use mixer keys and exercise undo/redo, failed installation
branching, save/reopen and history reset. Live JACK MIDI acceptance verifies rollback, branching, save/reopen and history reset. Live JACK MIDI acceptance
saved/reopened hard-right playback and stereo peaks using only software clients. verifies saved/reopened hard-right playback and stereo peaks using only software
A native Xvfb mouse smoke check covers sliders and automation drawing. clients. A native Xvfb mouse smoke check covers sliders and automation drawing.
Checks cover independent strip callbacks/meters, fader gestures, pan Checks cover independent strip callbacks/meters, fader gestures, pan centering,
centering, failed-edit restoration, routing, undo/reload, duplicate names and failed-edit restoration, routing, undo/reload, duplicate names and stable IDs
stable IDs across reorder/removal. A 64-channel GUI session verifies scrolling across reorder/removal. A 64-channel GUI session verifies scrolling and
and minimum/large layouts. Screenshots include live JACK meters and a bus strip. minimum/large layouts. Screenshots include live JACK meters and a bus strip.
Physical stereo devices, listening quality, realtime endurance and high-density Physical stereo devices, listening quality, realtime endurance and high-density
project performance remain unverified; no new dependency was introduced. project performance remain unverified; no new dependency was introduced.
`test_buses` covers bus routing, exact taps, audio/MIDI cancellation, controls, `test_buses` covers bus routing, exact taps, audio/MIDI cancellation, controls,
automation, persistence, invalid routes, deletion/undo and dBFS ballistics. Backend automation, persistence, invalid routes, deletion/undo and dBFS ballistics.
tests cover consuming interval peaks; GUI tests cover routing undo and clipping. Backend tests cover consuming interval peaks; GUI tests cover routing undo and
clipping.
Linear/Hold interpolation is available for each point added or replaced. The Linear/Hold interpolation is available for each point added or replaced. The
point's mode applies to its following segment. Static controls remain fallbacks point's mode applies to its following segment. Static controls remain fallbacks
while a lane exists; moving/splitting clips does not move absolute track/bus lanes. while a lane exists; moving/splitting clips does not move absolute track/bus
See CONTROL.md for common parameter IDs and controller/automation precedence. lanes. See CONTROL.md for common parameter IDs and controller/automation
precedence.

View file

@ -1,57 +1,127 @@
# Modern editing workflows # Modern Editing Workflows
These control-thread edits were introduced without changing format 15. These control-thread edits were introduced without changing format 15. The
The current writer uses format 16 for arrangement markers and still reads older projects. Session selections, current writer uses format 16 for arrangement markers and still reads older
clipboard and comparison snapshots are not saved in projects. Audio callbacks projects. Session selections, clipboard and comparison snapshots are not saved
continue to consume prepared plans; edits use the existing stop/history workflow. in projects. Audio callbacks continue to consume prepared plans; edits use the
existing stop/history workflow.
## Arrangement Multi-selection ---
Shift-click clips to toggle selection; Ctrl+A in the sequencer or Edit/Select all clips selects every clip. Plain clicking replaces the selection. Selection is session UI state; no marquee selection yet. ## Arrangement Multi-Selection
Shift-click clips to toggle selection; Ctrl+A in the sequencer or Edit/Select
all clips selects every clip. Plain clicking replaces the selection. Selection
is session UI state; no marquee selection yet.
---
## Transactional Group Editing ## Transactional Group Editing
Use Left/Right in the sequencer (or Edit/Nudge selection) to move the selected phrase by the current snap step. Ctrl+D duplicates after its complete bounding span; Delete removes selected clips. Edits are atomic and undoable. Mouse dragging remains a single-clip gesture. Use Left/Right in the sequencer (or Edit/Nudge selection) to move the selected
phrase by the current snap step. Ctrl+D duplicates after its complete bounding
span; Delete removes selected clips. Edits are atomic and undoable. Mouse
dragging remains a single-clip gesture.
---
## Arrangement Clipboard ## Arrangement Clipboard
Ctrl+C/Ctrl+V in the sequencer, or Edit/Copy clips and Paste clips at cursor, copy/paste the selection. Earliest clip lands on the cursor. The earliest source track maps to the currently selected destination track; intervening track spacing is retained. All required tracks must already exist and must not be buses. Instruments, automation and media bytes are not copied. The clipboard survives document changes within the window but is not the OS clipboard. Ctrl+C/Ctrl+V in the sequencer, or Edit/Copy clips and Paste clips at cursor,
copy/paste the selection. Earliest clip lands on the cursor. The earliest source
track maps to the currently selected destination track; intervening track
spacing is retained. All required tracks must already exist and must not be
buses. Instruments, automation and media bytes are not copied. The clipboard
survives document changes within the window but is not the OS clipboard.
---
## Repeat Arrangement Patterns ## Repeat Arrangement Patterns
Edit/Repeat selected phrase asks for 1..1024 additional copies. The bounding span includes gaps and overlaps; copies remain on their original tracks. The entire repeat is one undo step. Clip/note capacity and timeline endpoints can limit the requested count. Edit/Repeat selected phrase asks for 1..1024 additional copies. The bounding
span includes gaps and overlaps; copies remain on their original tracks. The
entire repeat is one undo step. Clip/note capacity and timeline endpoints can
limit the requested count.
---
## Align and Slip Editing ## Align and Slip Editing
Edit/Align clip starts to cursor places every selected start at the cursor. Slip sources earlier/later changes source offsets by the snap step while retaining timeline positions and durations. Trimming must leave source material available; an invalid slip rejects the whole selection. No time stretching or destructive media edits are performed. Edit/Align clip starts to cursor places every selected start at the cursor. Slip
sources earlier/later changes source offsets by the snap step while retaining
timeline positions and durations. Trimming must leave source material available;
an invalid slip rejects the whole selection. No time stretching or destructive
media edits are performed.
---
## MIDI Swing Quantization ## MIDI Swing Quantization
Select MIDI clips and use Edit/MIDI tools/Swing quantize. Grid is in source ticks; swing delays alternate subdivisions by 0..half a grid step, while strength blends the old start toward the nearest swung point. Durations stay fixed. All source notes are affected, including notes outside visible trims. Mixed audio/MIDI selections and notes that would exceed source bounds reject the entire edit. This is destructive note editing with undo, not a live groove engine. Select MIDI clips and use Edit/MIDI tools/Swing quantize. Grid is in source
ticks; swing delays alternate subdivisions by 0..half a grid step, while
strength blends the old start toward the nearest swung point. Durations stay
fixed. All source notes are affected, including notes outside visible trims.
Mixed audio/MIDI selections and notes that would exceed source bounds reject the
entire edit. This is destructive note editing with undo, not a live groove
engine.
---
## MIDI Legato and Gate Tools ## MIDI Legato and Gate Tools
Edit/MIDI tools/Note gate scales lengths from 1 to 400 percent, rounded to at least one tick. Legato ends notes at the next strictly later onset in the same clip and channel; notes in a chord end together. The final onset extends to source end. Gate expansion beyond the source rejects the whole edit. Undo restores original durations. Edit/MIDI tools/Note gate scales lengths from 1 to 400 percent, rounded to at
least one tick. Legato ends notes at the next strictly later onset in the same
clip and channel; notes in a chord end together. The final onset extends to
source end. Gate expansion beyond the source rejects the whole edit. Undo
restores original durations.
---
## MIDI Scale Correction ## MIDI Scale Correction
Edit/MIDI tools/Correct to scale takes a root (0=C through 11=B) and chromatic, major or natural minor. Each pitch moves to the nearest permitted MIDI note; ties choose the lower pitch. Timing, channel and velocity remain unchanged. Corrections can produce unison notes; notes are not merged. This changes MIDI notes, not audio pitch or a live scale filter. Edit/MIDI tools/Correct to scale takes a root (0=C through 11=B) and chromatic,
major or natural minor. Each pitch moves to the nearest permitted MIDI note;
ties choose the lower pitch. Timing, channel and velocity remain unchanged.
Corrections can produce unison notes; notes are not merged. This changes MIDI
notes, not audio pitch or a live scale filter.
---
## MIDI Velocity Shaping ## MIDI Velocity Shaping
Edit/MIDI tools/Shape velocities scales from 0 to 400 percent, rounds, then applies an offset from -127 to 127. Results clamp to 1..127, keeping every note audible rather than introducing velocity-zero note-offs. Use 0 percent with a positive offset for a fixed velocity. No randomization is applied. Edit/MIDI tools/Shape velocities scales from 0 to 400 percent, rounds, then
applies an offset from -127 to 127. Results clamp to 1..127, keeping every note
audible rather than introducing velocity-zero note-offs. Use 0 percent with a
positive offset for a fixed velocity. No randomization is applied.
---
## Batch Audio Fade Tools ## Batch Audio Fade Tools
Edit/Audio tools/Set boundary fades sets linear fade-in and fade-out lengths in ticks; 0 or Remove boundary fades clears them. Each fade must fit within half of every selected clip duration. Gain and source offsets stay unchanged. These are boundary envelopes, not overlap-aware crossfades, equal-power curves or destructive WAV processing. Edit/Audio tools/Set boundary fades sets linear fade-in and fade-out lengths in
ticks; 0 or Remove boundary fades clears them. Each fade must fit within half of
every selected clip duration. Gain and source offsets stay unchanged. These are
boundary envelopes, not overlap-aware crossfades, equal-power curves or
destructive WAV processing.
---
## Mixer A/B Comparison ## Mixer A/B Comparison
Edit/Mixer comparison captures or recalls A and B. Capture stores static gain, pan, mute and solo for every track and bus without changing the document. Recall requires the same stable track IDs, stops playback through the normal edit guard and creates one undo step. Track order may change. Routing, plugins and automation are excluded; automation still overrides static gain and pan. Slots are session-only and clear on New/Open. This is static mix comparison, not seamless live scene morphing. Edit/Mixer comparison captures or recalls A and B. Capture stores static gain,
pan, mute and solo for every track and bus without changing the document. Recall
requires the same stable track IDs, stops playback through the normal edit guard
and creates one undo step. Track order may change. Routing, plugins and
automation are excluded; automation still overrides static gain and pan. Slots
are session-only and clear on New/Open. This is static mix comparison, not
seamless live scene morphing.
## Validation and remaining scope ---
The code is built with warnings enabled and tested. Reproduce the core, transaction and allocation-failure checks with: ## Validation and Remaining Scope
The code is built with warnings enabled and tested. Reproduce the core,
transaction and allocation-failure checks with:
```sh ```sh
make BUILD_DIR=build/roadmap-audit all gui test-reliability make BUILD_DIR=build/roadmap-audit all gui test-reliability
@ -65,7 +135,7 @@ system Fontconfig suppression. Save/reload comparisons render identical nonzero
MIDI output after musical transforms; no project format migration is required. MIDI output after musical transforms; no project format migration is required.
These workflows improve arrangement and musical editing workflows. They do not These workflows improve arrangement and musical editing workflows. They do not
provide DAW feature parity: time stretching, comping, a clip launcher, tempo maps, provide DAW feature parity: time stretching, comping, a clip launcher, tempo
track insert chains and customizable shortcuts need separate designs. No new maps, track insert chains and customizable shortcuts need separate designs. No
dependencies or audio-callback operations are introduced. Human listening, new dependencies or audio-callback operations are introduced. Human listening,
physical devices and arbitrary third-party plugins remain unqualified. physical devices and arbitrary third-party plugins remain unqualified.

View file

@ -1,12 +1,15 @@
# Native C processor API, version 1 # Native C Processor API, Version 1
`xtc_native.h` defines a small compile-time extension boundary for trusted C99 `xtc_native.h` defines a small compile-time extension boundary for trusted C99
sources, effects and instruments. Build `make build/native-gain` and run it for a sources, effects and instruments. Build `make build/native-gain` and run it for
complete graph-effect example in `examples/native_gain.c`. This is a source API a complete graph-effect example in `examples/native_gain.c`. This is a source
for this release, not a promise of binary compatibility across builds/platforms. API for this release, not a promise of binary compatibility across
There is no module scanner, dynamic loader, SDK dependency or sandbox. builds/platforms. There is no module scanner, dynamic loader, SDK dependency or
sandbox.
## Descriptor and ownership ---
## Descriptor and Ownership
Set `abi_version = XTC_NATIVE_ABI_VERSION`, `struct_size = sizeof(descriptor)`, Set `abi_version = XTC_NATIVE_ABI_VERSION`, `struct_size = sizeof(descriptor)`,
a terminated printable non-space ASCII identifier (1..63 bytes), kind and all a terminated printable non-space ASCII identifier (1..63 bytes), kind and all
@ -30,59 +33,68 @@ inside callbacks is trusted: xtc cannot enforce its realtime behavior or protect
against memory corruption. C++ processors must use C linkage and never throw against memory corruption. C++ processors must use C linkage and never throw
across this boundary; domain/DSP code in this repository remains C. across this boundary; domain/DSP code in this repository remains C.
## Block contract ---
A block contains mono input/output, frames, absolute sample position and optional ## Block Contract
MIDI events. Input NULL means silence; exact input/output aliasing is allowed and
processors must support it. Output is writable for every frame, including silent A block contains mono input/output, frames, absolute sample position and
frames; do not retain any borrowed block pointers. Other overlapping buffers are optional MIDI events. Input NULL means silence; exact input/output aliasing is
not supported. Only instruments receive events; event count <=256, offsets are allowed and processors must support it. Output is writable for every frame,
sorted and strictly less than frames, with equal offsets retaining caller order. including silent frames; do not retain any borrowed block pointers. Other
Pitch/velocity/channel/type bounds follow xtc_midi.h. Zero frames is a no-op. overlapping buffers are not supported. Only instruments receive events; event
count <=256, offsets are sorted and strictly less than frames, with equal
offsets retaining caller order. Pitch/velocity/channel/type bounds follow
xtc_midi.h. Zero frames is a no-op.
The host rejects invalid blocks before changing state/output, and reports The host rejects invalid blocks before changing state/output, and reports
CAPACITY above the prepared block limit. Position arithmetic cannot overflow. CAPACITY above the prepared block limit. Position arithmetic cannot overflow. A
A discontinuous position resets held voices/state before processing. Call reset discontinuous position resets held voices/state before processing. Call reset
for Stop/panic when hosting instruments yourself; send channel ALL_OFF for for Stop/panic when hosting instruments yourself; send channel ALL_OFF for
channel-specific panic. The native host does not implement sustain or routing. channel-specific panic. The native host does not implement sustain or routing.
On successful processing, nonfinite samples become silence. Callback failure On successful processing, nonfinite samples become silence. Callback failure
silences the block, resets that processor and returns its error. Already advanced silences the block, resets that processor and returns its error. Already
processors in a graph are not rolled back. Never rely on failure to transactionally advanced processors in a graph are not rolled back. Never rely on failure to
rewind DSP; prepare fallible resources before publication. There is no latency transactionally rewind DSP; prepare fallible resources before publication. There
compensation, tail protocol, parameter ramp or dynamic port negotiation in ABI 1. is no latency compensation, tail protocol, parameter ramp or dynamic port
negotiation in ABI 1.
## Graph and instrument integration ---
`xtc_graph_add_native` adds effects (input 0/output 1) or sources (output 1), with ## Graph and Instrument Integration
a scalar immutable parameter. Compilation creates two independent mono instances
for stereo; no mutable state is shared between sides. Graph-only edits retain `xtc_graph_add_native` adds effects (input 0/output 1) or sources (output 1),
the existing pending/retired publication contract. Reset clears native state and with a scalar immutable parameter. Compilation creates two independent mono
its graph-local frame clock; graph positions count successfully rendered frames instances for stereo; no mutable state is shared between sides. Graph-only edits
since compilation/reset, independently of backend seek/transport. Sources needing retain the existing pending/retired publication contract. Reset clears native
transport time should be hosted explicitly with a block position. state and its graph-local frame clock; graph positions count successfully
rendered frames since compilation/reset, independently of backend
seek/transport. Sources needing transport time should be hosted explicitly with
a block position.
For an instrument, use create/process directly with sample-offset events, then For an instrument, use create/process directly with sample-offset events, then
feed its output to an existing XTC_NODE_INSTRUMENT through process_sources or feed its output to an existing XTC_NODE_INSTRUMENT through process_sources or
process_stereo. The host owns event routing and panic/reset delivery. Graph-native process_stereo. The host owns event routing and panic/reset delivery.
instrument addition returns UNSUPPORTED because current graph ports carry audio, Graph-native instrument addition returns UNSUPPORTED because current graph ports
not MIDI. This separation avoids silently dropping instrument events. The built-in carry audio, not MIDI. This separation avoids silently dropping instrument
JACK MIDI sine instrument remains the GUI's instrument; custom instrument selection events. The built-in JACK MIDI sine instrument remains the GUI's instrument;
and event graph ports are future work. custom instrument selection and event graph ports are future work.
Descriptors survive graph/project cloning and transactional remove/connect edits; Descriptors survive graph/project cloning and transactional remove/connect
custom ADD_NODE commands use the dedicated constructor rather than a GUI menu. edits; custom ADD_NODE commands use the dedicated constructor rather than a GUI
Native nodes have no project-file representation: Save returns UNSUPPORTED and menu. Native nodes have no project-file representation: Save returns UNSUPPORTED
preserves the destination. Format v4 still accepts only builtin types 1..6. and preserves the destination. Format v4 still accepts only builtin types 1..6.
A durable identifier/registry, versioned processor state and missing-module policy A durable identifier/registry, versioned processor state and missing-module
must precede serialized native extensions or external plugin hosting. policy must precede serialized native extensions or external plugin hosting.
Tests cover ABI/size/field rejection, descriptor copying, aliased buffers, Tests cover ABI/size/field rejection, descriptor copying, aliased buffers,
prepare/process failures, stereo compile cleanup, retirement/destruction outside prepare/process failures, stereo compile cleanup, retirement/destruction outside
processing, event offsets, seek note reset, source ports, instrument-buffer graph processing, event offsets, seek note reset, source ports, instrument-buffer
routing and unsupported-save preservation. Run `make test`. graph routing and unsupported-save preservation. Run `make test`.
## Structured parameter processors ---
## Structured Parameter Processors
`xtc_processor.h` is the public C99 parameter-only extension interface (ABI 2). `xtc_processor.h` is the public C99 parameter-only extension interface (ABI 2).
It complements the ABI 1 graph adapter without changing its layout or behavior. It complements the ABI 1 graph adapter without changing its layout or behavior.
@ -90,27 +102,28 @@ It complements the ABI 1 graph adapter without changing its layout or behavior.
instrument using only public headers; `make test-processor` builds and exercises instrument using only public headers; `make test-processor` builds and exercises
both. The instrument has immediate note-off and no voice stealing/release tail. both. The instrument has immediate note-off and no voice stealing/release tail.
Descriptors have an exact ABI version and structure size, a persistent identifier, Descriptors have an exact ABI version and structure size, a persistent
and up to 16 parameters with unique unsigned IDs, names, units, finite ranges and identifier, and up to 16 parameters with unique unsigned IDs, names, units,
defaults. Unsupported ABI versions/sizes are rejected before initialization. finite ranges and defaults. Unsupported ABI versions/sizes are rejected before
Changing identifier, parameter order, IDs or ranges requires an explicit migration initialization. Changing identifier, parameter order, IDs or ranges requires an
by the extension author. There is no promise of binary compatibility across explicit migration by the extension author. There is no promise of binary
compiler architectures, and no C++/FLTK types in either API. compatibility across compiler architectures, and no C++/FLTK types in either
API.
Create prepares a copied descriptor on the control thread. Failed preparation Create prepares a copied descriptor on the control thread. Failed preparation
calls destroy, including with NULL: callbacks must implement partial cleanup. calls destroy, including with NULL: callbacks must implement partial cleanup.
Reset/process/parameter have one audio owner and must never allocate, free, block, Reset/process/parameter have one audio owner and must never allocate, free,
perform I/O or call GUI functions. Destroy is control-thread only after callback block, perform I/O or call GUI functions. Destroy is control-thread only after
quiescence. Reconfiguration prepares a new independent instance; publish it with callback quiescence. Reconfiguration prepares a new independent instance;
an appropriate owner handoff and retire the old instance after quiescence. The publish it with an appropriate owner handoff and retire the old instance after
callback code must remain loaded throughout that lifetime. quiescence. The callback code must remain loaded throughout that lifetime.
Each instance has one mono output and either one optional input (effect) or no Each instance has one mono output and either one optional input (effect) or no
input (source/instrument). NULL input means silence. Stereo effects use two input (source/instrument). NULL input means silence. Stereo effects use two
independent instances; multichannel interdependent effects have no adapter. independent instances; multichannel interdependent effects have no adapter.
Instruments receive at most 256 sorted sample-offset MIDI events per block. Instruments receive at most 256 sorted sample-offset MIDI events per block. The
The API intentionally has no channel-count argument: unsupported layouts cannot API intentionally has no channel-count argument: unsupported layouts cannot be
be represented. Graph ABI 1 rejects instrument descriptors; explicitly render represented. Graph ABI 1 rejects instrument descriptors; explicitly render
instrument output and feed the existing instrument source buffers. instrument output and feed the existing instrument source buffers.
Up to 256 sorted parameter events split the process block at exact sample Up to 256 sorted parameter events split the process block at exact sample
@ -122,13 +135,14 @@ reset retains current parameters. Callback errors silence the whole output and
reset voices/history; already applied parameters persist. Nonfinite/subnormal reset voices/history; already applied parameters persist. Nonfinite/subnormal
successful samples become zero. Input/output may alias exactly. successful samples become zero. Input/output may alias exactly.
`xtc_processor_save`/`restore` run only while quiescent and serialize configuration, `xtc_processor_save`/`restore` run only while quiescent and serialize
not DSP history. Format: `XTCN`, little-endian uint32 version 2, zero-padded configuration, not DSP history. Format: `XTCN`, little-endian uint32 version 2,
64-byte identifier, uint32 parameter count, zero uint32 reserved, then ordered zero-padded 64-byte identifier, uint32 parameter count, zero uint32 reserved,
pairs of uint32 ID and IEEE float32 bits. Header is 80 bytes, pairs 8 bytes. then ordered pairs of uint32 ID and IEEE float32 bits. Header is 80 bytes, pairs
Restore validates exact length, identity, IDs, ranges and defaults/descriptor 8 bytes. Restore validates exact length, identity, IDs, ranges and
compatibility before preparing a new instance; failure leaves existing handles defaults/descriptor compatibility before preparing a new instance; failure
intact. Opaque assets and hidden state are outside this ABI. ABI 1 graph nodes leaves existing handles intact. Opaque assets and hidden state are outside this
remain session-only and fail project saves explicitly; ABI 2 configuration blobs ABI. ABI 1 graph nodes remain session-only and fail project saves explicitly;
are a separate public facility, not automatically embedded in project files. ABI 2 configuration blobs are a separate public facility, not automatically
GUI graph access continues through public C APIs only. embedded in project files. GUI graph access continues through public C APIs
only.

View file

@ -1,48 +1,54 @@
# Performance and realtime qualification # Performance and Realtime Qualification
The original `test-performance` ran a hardware-independent 48 kHz, 128-frame stereo The original `test-performance` ran a hardware-independent 48 kHz, 128-frame
workload: 64 graph nodes, 4096 MIDI notes (32 simultaneous), 16 tracks and gain stereo workload: 64 graph nodes, 4096 MIDI notes (32 simultaneous), 16 tracks
automation. Preparation and block timing use CLOCK_MONOTONIC outside the engine; and gain automation. Preparation and block timing use CLOCK_MONOTONIC outside
callback code itself does not clock or log. The test checks finite output and the engine; callback code itself does not clock or log. The test checks finite
reports mean, p99, maximum and CPU-time/audio-duration ratio. A run is a workload output and reports mean, p99, maximum and CPU-time/audio-duration ratio. A run
measurement, not a universal realtime threshold. Run more blocks with: is a workload measurement, not a universal realtime threshold. Run more blocks
with:
```sh ```sh
./build/test_performance 10000 ./build/test_performance 10000
``` ```
Initial measurement on an AMD Ryzen Threadripper 1920X, GCC 16.2.1, -O2, Initial measurement on an AMD Ryzen Threadripper 1920X, GCC 16.2.1, -O2, 2000
2000 blocks: preparation 3.045 ms, mean 308.301 us, p99 422.153 us, maximum blocks: preparation 3.045 ms, mean 308.301 us, p99 422.153 us, maximum 665.096
665.096 us, versus a 2666.667 us block deadline; CPU/audio ratio 0.1156. These are us, versus a 2666.667 us block deadline; CPU/audio ratio 0.1156. These are one
one host/run's observed values and depend on scheduling/load/compiler. The test host/run's observed values and depend on scheduling/load/compiler. The test
excludes WAV disk preparation, external/native plugin complexity, GUI redraw, excludes WAV disk preparation, external/native plugin complexity, GUI redraw,
server scheduling and actual hardware latency. Native callbacks must provide server scheduling and actual hardware latency. Native callbacks must provide
their own bounded realtime implementation. their own bounded realtime implementation.
Existing software JACK loopback, stereo MIDI/save/reopen, record/reopen, Existing software JACK loopback, stereo MIDI/save/reopen, record/reopen,
restart/close and concurrent graph handoff tests remain available. Runtime restart/close and concurrent graph handoff tests remain available. Runtime fault
fault injection covers loss/rate/xrun/null-buffer and lifecycle failures without injection covers loss/rate/xrun/null-buffer and lifecycle failures without
altering the shared server. ASan/UBSan validate memory/error paths; TSan checks altering the shared server. ASan/UBSan validate memory/error paths; TSan checks
recorder/handoff races, not timing. Sanitized runs are not latency qualification. recorder/handoff races, not timing. Sanitized runs are not latency
qualification.
Current worst-case storage includes up to 256 MiB preloaded mono WAV samples, Current worst-case storage includes up to 256 MiB preloaded mono WAV samples,
paired graph buffers (128 MiB at 256 nodes/65536 frames; GUI limit 32 MiB), bounded paired graph buffers (128 MiB at 256 nodes/65536 frames; GUI limit 32 MiB),
MIDI/mix metadata and up to 33 metadata/graph history snapshots. Pending/retired bounded MIDI/mix metadata and up to 33 metadata/graph history snapshots.
plans can retain additional graph memory. Native preparation can allocate more; Pending/retired plans can retain additional graph memory. Native preparation can
the API cannot enforce an extension's memory budget. There is no CPU overload allocate more; the API cannot enforce an extension's memory budget. There is no
scheduler, disk streaming, latency compensation or callback allocator audit of CPU overload scheduler, disk streaming, latency compensation or callback
arbitrary third-party code. High-density interactive projects, physical devices, allocator audit of arbitrary third-party code. High-density interactive
long-duration stereo/recording and live server termination remain unqualified. projects, physical devices, long-duration stereo/recording and live server
termination remain unqualified.
## Matrix, capacity and contention checks ---
## Matrix, Capacity and Contention Checks
`make benchmark` records 24 warmed graph cases: 2/16/64/256 nodes, mono/stereo, `make benchmark` records 24 warmed graph cases: 2/16/64/256 nodes, mono/stereo,
44.1k/31, 48k/128 and 96k/1024. Each reports control-thread compile time separately 44.1k/31, 48k/128 and 96k/1024. Each reports control-thread compile time
from 300 process calls, mean/p99/max, block deadline, mean load, and paired graph separately from 300 process calls, mean/p99/max, block deadline, mean load, and
buffer bytes. Timing, sorting, allocation and logging happen outside processing. paired graph buffer bytes. Timing, sorting, allocation and logging happen
Metered gain chains test graph scaling; they do not represent expensive plugins. outside processing. Metered gain chains test graph scaling; they do not
The checked-in `performance-baseline.json` records CPU/compiler/flags/kernel and represent expensive plugins. The checked-in `performance-baseline.json` records
all measurements. Copy a new run only after reviewing changes and conditions. CPU/compiler/flags/kernel and all measurements. Copy a new run only after
reviewing changes and conditions.
```sh ```sh
make benchmark test-performance test-scale test-stress make benchmark test-performance test-scale test-stress
@ -60,37 +66,42 @@ shared-machine contention still affect results. No real-time scheduling/pinning
or external profiler is required. CPU-pressure runs are correctness checks, not or external profiler is required. CPU-pressure runs are correctness checks, not
comparable timing baselines. Tests/benchmark outputs live below BUILD_DIR. comparable timing baselines. Tests/benchmark outputs live below BUILD_DIR.
Initial matrix (Threadripper 1920X, GCC 16.2.1, -O2, 300 blocks): 256-node stereo Initial matrix (Threadripper 1920X, GCC 16.2.1, -O2, 300 blocks): 256-node
48k/128 mean 353.214 us, p99 396.495 us, load .132; 96k/1024 mean 2478.520 us, stereo 48k/128 mean 353.214 us, p99 396.495 us, load .132; 96k/1024 mean
p99 3092.434 us, load .232. The full JSON is the evidence. These met the local 2478.520 us, p99 3092.434 us, load .232. The full JSON is the evidence. These
investigation target of mean load <.5 for ordinary graph chains; this is a met the local investigation target of mean load <.5 for ordinary graph chains;
host-specific development target, not a supported maximum CPU or latency promise. this is a host-specific development target, not a supported maximum CPU or
latency promise.
The mixed 64-node/4096-note workload measured 340–357 us before removing unused The mixed 64-node/4096-note workload measured 340–357 us before removing unused
live-controller processing and 313 us after (mean over 2000 blocks); p99 varied live-controller processing and 313 us after (mean over 2000 blocks); p99 varied
with system load. The measured hot-path change skips live voices/controller ramps with system load. The measured hot-path change skips live voices/controller
when neither is active and avoids finished-ramp calls. Exact CC/odd-block tests ramps when neither is active and avoids finished-ramp calls. Exact CC/odd-block
continue to pass. No other graph optimization was justified by this measurement. tests continue to pass. No other graph optimization was justified by this
measurement.
`test-scale` constructs all 64 tracks, 1024 clips, 1024 automation points and `test-scale` constructs all 64 tracks, 1024 clips, 1024 automation points and
4096 notes, compiles a standalone 256-node metered graph, renders stereo odd-sized 4096 notes, compiles a standalone 256-node metered graph, renders stereo
blocks and rejects capacity excesses. Project/GUI graphs remain capped at 64 odd-sized blocks and rejects capacity excesses. Project/GUI graphs remain capped
nodes. `test-stress` runs publication, allocation/retirement, capacity and optional at 64 nodes. `test-stress` runs publication, allocation/retirement, capacity and
GUI/timer/redraw tests while two ordinary CPU workers compete; subprocesses have optional GUI/timer/redraw tests while two ordinary CPU workers compete;
timeouts and workers are always terminated. Existing callback guards verify no subprocesses have timeouts and workers are always terminated. Existing callback
host allocation/free/loading/unloading. This does not qualify arbitrary plugins. guards verify no host allocation/free/loading/unloading. This does not qualify
arbitrary plugins.
Callback work is bounded by documented node/frame/clip/note/event/point capacities. Callback work is bounded by documented node/frame/clip/note/event/point
Exceeding model/plan/mailbox capacities returns CAPACITY transactionally; invalid capacities. Exceeding model/plan/mailbox capacities returns CAPACITY
or overlarge processing buffers are silenced and reported, JACK xruns are counted. transactionally; invalid or overlarge processing buffers are silenced and
There is no CPU scheduler or preemption inside a DSP callback: an expensive trusted reported, JACK xruns are counted. There is no CPU scheduler or preemption inside
plugin can miss a deadline or hang. Automated LADSPA lanes currently use run(1) a DSP callback: an expensive trusted plugin can miss a deadline or hang.
and bounded per-sample lane searches and may be costly; they are excluded from Automated LADSPA lanes currently use run(1) and bounded per-sample lane searches
the gain-chain baseline. Large WAV memory limits remain those in AUDIO_FILES.md. and may be costly; they are excluded from the gain-chain baseline. Large WAV
Physical-device endurance, CPU-overload sound quality and a full GUI latency memory limits remain those in AUDIO_FILES.md. Physical-device endurance,
benchmark remain unqualified. CPU-overload sound quality and a full GUI latency benchmark remain unqualified.
## Qualification workloads ---
## Qualification Workloads
The current `test-performance` emits one JSON record (the old prose output is The current `test-performance` emits one JSON record (the old prose output is
retired). Default: 2,000 stereo blocks at 48 kHz/128, 64 graph nodes, 16 tracks, retired). Default: 2,000 stereo blocks at 48 kHz/128, 64 graph nodes, 16 tracks,
@ -101,10 +112,10 @@ for this revision. `benchmark-project` runs nine cases: 1/16/64 tracks,
Two long notes per first 16 tracks remain audible. Other notes are scheduled Two long notes per first 16 tracks remain audible. Other notes are scheduled
later to remain within the 32-voice plan limit. The position wraps every 96,000 later to remain within the 32-voice plan limit. The position wraps every 96,000
samples to keep long runs active. Future notes exercise storage/preparation and samples to keep long runs active. Future notes exercise storage/preparation and
bounded scans rather than additional simultaneous synthesis. Each track has bounded scans rather than additional simultaneous synthesis. Each track has 16
16 gain points; graph processing uses the same 64-node chain in every case. gain points; graph processing uses the same 64-node chain in every case. The
The graph matrix separately scales nodes and chain connections from 2 to 256. graph matrix separately scales nodes and chain connections from 2 to 256. These
These synthetic cases do not qualify every possible dense graph or plugin chain. synthetic cases do not qualify every possible dense graph or plugin chain.
```sh ```sh
make benchmark benchmark-project make benchmark benchmark-project
@ -122,22 +133,25 @@ exceedances, not actual JACK xruns: there is no server scheduling in the timing
loop. Graph compilation or project construction/preparation is timed separately. loop. Graph compilation or project construction/preparation is timed separately.
`process_cpu_ms` measures CLOCK_PROCESS_CPUTIME_ID over the complete measured `process_cpu_ms` measures CLOCK_PROCESS_CPUTIME_ID over the complete measured
loop, including timing and finite-output checks; it excludes preparation and loop, including timing and finite-output checks; it excludes preparation and
sorting. Project `cpu_audio_ratio` divides that CPU time by simulated audio time. sorting. Project `cpu_audio_ratio` divides that CPU time by simulated audio
`peak_rss_kib` is Linux getrusage process high-water RSS, including model, plans, time. `peak_rss_kib` is Linux getrusage process high-water RSS, including model,
measurement storage and executable/runtime overhead; it is not current heap size plans, measurement storage and executable/runtime overhead; it is not current
or a leak test. Graph buffer bytes remain a separate exact buffer-size estimate. heap size or a leak test. Graph buffer bytes remain a separate exact buffer-size
Timing probes, memory queries, sorting and output occur outside engine calls. estimate. Timing probes, memory queries, sorting and output occur outside engine
calls.
Checked-in `project-performance-baseline.json` provides initial project cases; Checked-in `project-performance-baseline.json` provides initial project cases;
the original graph baseline is retained. `graph-performance-baseline.json` adds the original graph baseline is retained. `graph-performance-baseline.json` adds
a current graph report with CPU/RSS/deadline fields. Gates use the existing a current graph report with CPU/RSS/deadline fields. Gates use the existing 1.5x
1.5x mean-time budget and 1 us noise allowance, with p99/max/misses and memory mean-time budget and 1 us noise allowance, with p99/max/misses and memory
reported for investigation. There is no universal hardware-independent deadline reported for investigation. There is no universal hardware-independent deadline
or RSS pass threshold. `test-benchmark-tools` verifies same-context success, or RSS pass threshold. `test-benchmark-tools` verifies same-context success,
regression rejection, missing cases and host/workload mismatch without relying regression rejection, missing cases and host/workload mismatch without relying
on machine timing. Do not replace a failing baseline without investigating. on machine timing. Do not replace a failing baseline without investigating.
## Endurance, faults and callback audit ---
## Endurance, Faults and Callback Audit
```sh ```sh
make test-soak test-stress test-stress-plugins make test-soak test-stress test-stress-plugins
@ -153,24 +167,26 @@ quiescent position/rebuild cycles. It runs faster than wall time and models
position wrap/seek and restart publication; it is not a real device start/stop position wrap/seek and restart publication; it is not a real device start/stop
or hardware endurance test. The project benchmark separately wraps active MIDI. or hardware endurance test. The project benchmark separately wraps active MIDI.
`test-stress` defaults to three rounds under two ordinary CPU workers. Each round `test-stress` defaults to three rounds under two ordinary CPU workers. Each
runs concurrent publication, allocation/lifetime guards, maximum model capacity, round runs concurrent publication, allocation/lifetime guards, maximum model
project relocation/reload and missing-media retention, save-fault diagnostics, capacity, project relocation/reload and missing-media retention, save-fault
and the soak test. With JACK headers present it also repeats the deterministic diagnostics, and the soak test. With JACK headers present it also repeats the
backend fake's start/stop/seek, rate/buffer/server-loss and failure/retry cases. deterministic backend fake's start/stop/seek, rate/buffer/server-loss and
Optional GUI tests run after those rounds under the same CPU load. failure/retry cases. Optional GUI tests run after those rounds under the same
`test-stress-plugins` adds the original LADSPA fixture's identity/layout/range, CPU load. `test-stress-plugins` adds the original LADSPA fixture's
instantiation-error and invalid-metadata cases each round; it needs LADSPA headers. identity/layout/range, instantiation-error and invalid-metadata cases each
No command changes a physical device, kills the shared server or loads unknown round; it needs LADSPA headers. No command changes a physical device, kills the
plugins. Subprocess deadlines bound each test and workers are cleaned up on exit. shared server or loads unknown plugins. Subprocess deadlines bound each test and
workers are cleaned up on exit.
The reliability executable now interposes fopen/fread/fwrite/read/write, The reliability executable now interposes fopen/fread/fwrite/read/write,
pthread_mutex_lock and pthread_cond_wait in addition to malloc/calloc/realloc/free. pthread_mutex_lock and pthread_cond_wait in addition to
Guarded host graph/realtime render calls fail immediately on these operations. malloc/calloc/realloc/free. Guarded host graph/realtime render calls fail
This complements the existing native lifetime and plugin callback guards and immediately on these operations. This complements the existing native lifetime
control/callback ownership review in RELIABILITY.md. It does not intercept every and plugin callback guards and control/callback ownership review in
libc entry point, raw syscalls, hidden dynamic-module calls, or GUI code by magic; RELIABILITY.md. It does not intercept every libc entry point, raw syscalls,
third-party DSP remains trusted. No known host callback safety violation was found. hidden dynamic-module calls, or GUI code by magic; third-party DSP remains
trusted. No known host callback safety violation was found.
`check-development` combines the ordinary build/tests, capacity/soak checks, `check-development` combines the ordinary build/tests, capacity/soak checks,
benchmark gate tests, static analysis and ASan/UBSan. The sanitizer target now benchmark gate tests, static analysis and ASan/UBSan. The sanitizer target now
@ -178,16 +194,18 @@ includes maximum capacity and virtual endurance. These are opt-in developer
checks requiring installed Clang/Python; no runtime dependency was added. checks requiring installed Clang/Python; no runtime dependency was added.
Sanitized/contended/profiled runs must not become timing baselines. Sanitized/contended/profiled runs must not become timing baselines.
## Profiling evidence and remaining costs ---
## Profiling Evidence and Remaining Costs
Before considering DSP changes, a 10,000-block, 64-track/128-frame project was Before considering DSP changes, a 10,000-block, 64-track/128-frame project was
sampled with GCC `-O2 -g -pg` and GNU gprof on the documented baseline host. sampled with GCC `-O2 -g -pg` and GNU gprof on the documented baseline host.
Flat samples attributed about 30% to mixer automation `value`, 29% to MIDI Flat samples attributed about 30% to mixer automation `value`, 29% to MIDI
`segment`, and 11% to finite-value handling. Instrumentation and inlining affect `segment`, and 11% to finite-value handling. Instrumentation and inlining affect
attribution; this identifies investigation candidates, not exact production attribution; this identifies investigation candidates, not exact production cost
cost or evidence that checks should be removed. MIDI scans bounded stored-note or evidence that checks should be removed. MIDI scans bounded stored-note arrays
arrays and evaluates per-voice, per-sample gain/pan lanes. No DSP optimization and evaluates per-voice, per-sample gain/pan lanes. No DSP optimization was
was introduced without a separate correctness/performance justification. introduced without a separate correctness/performance justification.
Reproduce in an isolated build (GNU gprof must already be installed): Reproduce in an isolated build (GNU gprof must already be installed):
@ -209,10 +227,10 @@ CPU/audio .142 and peak process RSS 19,296 KiB. The 256-node stereo/128 graph
mean was 286.398 us, p99 484.712 us, compile .300 ms. These case runs observed mean was 286.398 us, p99 484.712 us, compile .300 ms. These case runs observed
zero deadline exceedances. See JSON for all cases and host metadata. zero deadline exceedances. See JSON for all cases and host metadata.
The project comparison passed. Repeated graph comparisons flagged variable The project comparison passed. Repeated graph comparisons flagged variable small
small cases despite unchanged graph processing code; a 3,000-block run against cases despite unchanged graph processing code; a 3,000-block run against the
the retained baseline still flagged 16-node mono/31-frame mean 5.089 us retained baseline still flagged 16-node mono/31-frame mean 5.089 us above its
above its 1.5x budget. The new graph report is evidence, not a replacement chosen 1.5x budget. The new graph report is evidence, not a replacement chosen to make
to make that comparison pass. This shared-host timing alert remains an that comparison pass. This shared-host timing alert remains an investigation
investigation item; no CPU/latency guarantee is inferred from the correctness item; no CPU/latency guarantee is inferred from the correctness suite. Longer
suite. Longer comparisons can use `BENCHMARK_BLOCKS=3000`. comparisons can use `BENCHMARK_BLOCKS=3000`.

View file

@ -1,4 +1,4 @@
# External plugin hosting evaluation # External Plugin Hosting Evaluation
The original decision deferred external hosting. The native source API is useful The original decision deferred external hosting. The native source API is useful
for proving processor contracts without adding an SDK or scanning/loading code. for proving processor contracts without adding an SDK or scanning/loading code.
@ -9,27 +9,32 @@ for proving processor contracts without adding an SDK or scanning/loading code.
| CLAP | C ABI, explicit lifecycle and event model | Extension negotiation, parameter/state persistence, host callbacks, main/audio-thread scheduling and UI integration | | CLAP | C ABI, explicit lifecycle and event model | Extension negotiation, parameter/state persistence, host callbacks, main/audio-thread scheduling and UI integration |
| VST3 | Broad plugin availability | SDK/license review for the selected SDK release, C++ adapter outside the C core, component/controller and editor lifecycle, state and event translation | | VST3 | Broad plugin availability | SDK/license review for the selected SDK release, C++ adapter outside the C core, component/controller and editor lifecycle, state and event translation |
No candidate was linked, scanned or loaded. API/SDK and plugin No candidate was linked, scanned or loaded. API/SDK and plugin licenses must be
licenses must be checked at the revision actually adopted; they are separate checked at the revision actually adopted; they are separate from xtc's MIT
from xtc's MIT license. Supporting a format does not grant redistribution rights license. Supporting a format does not grant redistribution rights to individual
to individual plugins. Do not search user plugin directories from the callback. plugins. Do not search user plugin directories from the callback.
Before implementation, require versioned module identity and state, a missing- Before implementation, require versioned module identity and state, a missing-
plugin recovery policy, preparation failure/partial cleanup tests, bounded events plugin recovery policy, preparation failure/partial cleanup tests, bounded
and parameter publication, latency/tail handling, callback-safe unload after events and parameter publication, latency/tail handling, callback-safe unload
retirement, a GUI/main-thread boundary and an explicit crash-containment decision. after retirement, a GUI/main-thread boundary and an explicit crash-containment
Native ABI tests cover some ownership mechanics but do not prove third-party decision. Native ABI tests cover some ownership mechanics but do not prove
code safe. Event graph ports remain a gap; track instrument selection is described below. third-party code safe. Event graph ports remain a gap; track instrument
Choose one format with concrete user needs after those contracts are specified; selection is described below. Choose one format with concrete user needs after
do not introduce a general multi-format framework now. See decision 0008. those contracts are specified; do not introduce a general multi-format framework
now. See decision 0008.
## LADSPA implementation (supersedes the earlier deferral) ---
Decision [0009](decisions/0009-parameter-processors-and-ladspa.md) selects a bounded ## LADSPA Implementation (Supersedes the Earlier Deferral)
LADSPA 1.1 host. `xtc_plugin.h` is the C API; no C++ engine or FLTK type is needed.
1. Choose **File → Load LADSPA plugin** and a trusted absolute `.so` path. Loading Decision [0009](decisions/0009-parameter-processors-and-ladspa.md) selects a
executes native code immediately, including constructors during discovery. bounded LADSPA 1.1 host. `xtc_plugin.h` is the C API; no C++ engine or FLTK type
is needed.
1. Choose **File → Load LADSPA plugin** and a trusted absolute `.so` path.
Loading executes native code immediately, including constructors during
discovery.
2. The descriptor browser lists names, labels, makers and source/effect roles. 2. The descriptor browser lists names, labels, makers and source/effect roles.
Select a row and Choose. Counts report compatible and skipped descriptors. Select a row and Choose. Counts report compatible and skipped descriptors.
3. In the Modular Graph, connect the new source/effect output to an output or 3. In the Modular Graph, connect the new source/effect output to an output or
@ -37,18 +42,19 @@ LADSPA 1.1 host. `xtc_plugin.h` is the C API; no C++ engine or FLTK type is need
receive external input, as other graph processors do. receive external input, as other graph processors do.
4. Select its node and use **Edit → Selected plugin control**. The editor shows 4. Select its node and use **Edit → Selected plugin control**. The editor shows
name, maker, copyright, module path, descriptor index and unique ID, then name, maker, copyright, module path, descriptor index and unique ID, then
actual port IDs, editable values, rate-adjusted bounds, defaults and discrete/ actual port IDs, editable values, rate-adjusted bounds, defaults and
logarithmic hints. Apply validates all values and candidate initialization; discrete/ logarithmic hints. Apply validates all values and candidate
Cancel and window close preserve the document. Toggled controls require 0/1 initialization; Cancel and window close preserve the document. Toggled
and integer controls require integral values. controls require 0/1 and integer controls require integral values.
5. Save normally, reopen, reconnect external JACK playback if needed, and Play. 5. Save normally, reopen, reconnect external JACK playback if needed, and Play.
Plugin changes stop/rebuild playback and participate in ordinary undo/redo. Plugin changes stop/rebuild playback and participate in ordinary undo/redo.
The GUI uses only public graph/model/plugin APIs. Discovery probes explicit module The GUI uses only public graph/model/plugin APIs. Discovery probes explicit
files by descriptor index, bounded to 1024 descriptors. It never recursively scans module files by descriptor index, bounded to 1024 descriptors. It never
system directories. The C API exposes probe, parameter metadata, instance recursively scans system directories. The C API exposes probe, parameter
create/process/destroy, and add/get/set graph configuration. Missing header means metadata, instance create/process/destroy, and add/get/set graph configuration.
hosting returns UNSUPPORTED; metadata persistence continues to work. Missing header means hosting returns UNSUPPORTED; metadata persistence continues
to work.
Supported: 0/1 audio input, 1 output, <=64 total ports and <=16 input controls. Supported: 0/1 audio input, 1 output, <=64 total ports and <=16 input controls.
Stereo graph channels are two independent mono instances. Stereo/multichannel Stereo graph channels are two independent mono instances. Stereo/multichannel
@ -65,22 +71,25 @@ incompatible identity/range/layout, or instantiate returning NULL unwinds the
candidate and leaves active/pending plans intact. Only run and bounded host candidate and leaves active/pending plans intact. Only run and bounded host
buffer copies execute in audio. Destruction and unloading happen after callback buffer copies execute in audio. Destruction and unloading happen after callback
quiescence or retirement acknowledgement on the control thread. Transport reset quiescence or retirement acknowledgement on the control thread. Transport reset
retains LADSPA history; compile/reopen starts fresh. A LADSPA run callback has no retains LADSPA history; compile/reopen starts fresh. A LADSPA run callback has
error return. Nonfinite/subnormal samples become zero, but native crashes, no error return. Nonfinite/subnormal samples become zero, but native crashes,
corruption, runaway CPU, allocations or I/O inside a plugin cannot be contained corruption, runaway CPU, allocations or I/O inside a plugin cannot be contained
or prevented. Use trusted plugins; hard-realtime properties are declarations, or prevented. Use trusted plugins; hard-realtime properties are declarations,
not proof. This is an in-process host, not a crash-isolation service. not proof. This is an in-process host, not a crash-isolation service.
Project v5 stores absolute module path, index, label, LADSPA unique ID, mono input Project v5 stores absolute module path, index, label, LADSPA unique ID, mono
count and ordered input-control port/value pairs. Restore verifies identity, input count and ordered input-control port/value pairs. Restore verifies
layout, parameter ports and ranges against the loaded module. No pointers are identity, layout, parameter ports and ranges against the loaded module. No
serialized. Relocating a module requires restoring it at its saved absolute path pointers are serialized. Relocating a module requires restoring it at its saved
or deliberately modifying its metadata through a C client. GUI Open reports the absolute path or deliberately modifying its metadata through a C client. GUI
missing/incompatible identity/path and retains the current document. Metadata Open reports the missing/incompatible identity/path and retains the current
load/save and `xtc-check --validate` do not execute plugin code and do not certify document. Metadata load/save and `xtc-check --validate` do not execute plugin
module availability. LADSPA IDs are not cryptographic binary/version identities. code and do not certify module availability. LADSPA IDs are not cryptographic
binary/version identities.
## Repeatable acceptance ---
## Repeatable Acceptance
```sh ```sh
make all test gui test-plugins test-gui-plugins analyze make all test gui test-plugins test-gui-plugins analyze
@ -91,9 +100,10 @@ The first plugin target builds an original MIT shared-library fixture with gain,
source, unsupported stereo and failed-initialization descriptors. Tests exercise source, unsupported stereo and failed-initialization descriptors. Tests exercise
identity/range errors, exact in-place gain, save/reopen/exact rendering, missing identity/range errors, exact in-place gain, save/reopen/exact rendering, missing
module metadata preservation and 100 plan replacements with callback guards module metadata preservation and 100 plan replacements with callback guards
against host allocation/free/dlopen/dlclose. GUI commands cover add, control edit, against host allocation/free/dlopen/dlclose. GUI commands cover add, control
invalid-value rollback, undo/redo and save/New/reopen. ASan/UBSan/leak checks cover edit, invalid-value rollback, undo/redo and save/New/reopen. ASan/UBSan/leak
host/fixture lifetimes; plugin code from system packages is not instrumented. checks cover host/fixture lifetimes; plugin code from system packages is not
instrumented.
`test-plugins-real` requires these separately installed modules (nothing is `test-plugins-real` requires these separately installed modules (nothing is
bundled or installed by the target): bundled or installed by the target):
@ -108,32 +118,35 @@ Tested packages: ladspa 1.17-7 and swh-plugins 0.4.17-7. These zero-latency
processors define the initial compatible set; other plugins remain unqualified. processors define the initial compatible set; other plugins remain unqualified.
Physical listening and plugin CPU/endurance qualification remain pending. Physical listening and plugin CPU/endurance qualification remain pending.
## Discovery, editing and graph lifecycle ---
## Discovery, Editing and Graph Lifecycle
Discovery remains explicit-file only, bounded to 1024 descriptor indices; no Discovery remains explicit-file only, bounded to 1024 descriptor indices; no
recursive scan, plugin database or new dependency is introduced. Unsupported recursive scan, plugin database or new dependency is introduced. Unsupported
layouts are skipped with counts; source/effect roles describe audio ports, not layouts are skipped with counts; source/effect roles describe audio ports, not
MIDI instrument capabilities. Modern track instruments are described below. LADSPA sources can be identified, edited, replaced and removed in MIDI instrument capabilities. Modern track instruments are described below.
the graph but cannot receive MIDI events. LADSPA sources can be identified, edited, replaced and removed in the graph but
cannot receive MIDI events.
**Edit → Insert LADSPA on selected wire** replaces one existing connection with **Edit → Insert LADSPA on selected wire** replaces one existing connection with
`source -> effect -> destination`, as one undo step. Only one-input effects are `source -> effect -> destination`, as one undo step. Only one-input effects are
accepted; stale wires, capacity failures and initialization failures retain the accepted; stale wires, capacity failures and initialization failures retain the
original graph. **Edit → Replace selected plugin** accepts the same audio input original graph. **Edit → Replace selected plugin** accepts the same audio input
layout, keeps the node ID and all wires, and installs the new descriptor's default layout, keeps the node ID and all wires, and installs the new descriptor's
controls with no inherited automation. It can replace from another module. default controls with no inherited automation. It can replace from another
A source/effect layout mismatch is rejected. Undo restores the old identity, module. A source/effect layout mismatch is rejected. Undo restores the old
values and automation. Every candidate is compiled before adoption even when identity, values and automation. Every candidate is compiled before adoption
the backend is disconnected; no intermediate graph is published. even when the backend is disconnected; no intermediate graph is published.
Ordering is graph signal flow, not descriptor order, creation order or a flat Ordering is graph signal flow, not descriptor order, creation order or a flat
rack. Use the graph's connection/disconnection tools to place effects in series rack. Use the graph's connection/disconnection tools to place effects in series
or parallel. Insertion on a wire preserves its endpoints and changes only that or parallel. Insertion on a wire preserves its endpoints and changes only that
stage. Select a plugin and Delete to remove it and incident wires, then reconnect stage. Select a plugin and Delete to remove it and incident wires, then
as desired; deletion does not automatically bridge or sum routes. A now-unwired reconnect as desired; deletion does not automatically bridge or sum routes. A
effect receives external audio, following existing graph semantics. Undo/redo now-unwired effect receives external audio, following existing graph semantics.
restores removal and routing. Independent bus inserts are still outside the Undo/redo restores removal and routing. Independent bus inserts are still
shared-source graph model. outside the shared-source graph model.
Reset defaults in the editor previews every control's declared/default-derived Reset defaults in the editor previews every control's declared/default-derived
value. Apply also clears that plugin's automation; Cancel preserves it. The value. Apply also clears that plugin's automation; Cancel preserves it. The
@ -156,8 +169,8 @@ validated; impossible defaults fail discovery rather than reaching the engine.
The generic modal editor owns copied metadata and temporary widget values only. The generic modal editor owns copied metadata and temporary widget values only.
It never owns or retains module handles, descriptors or audio instance pointers. It never owns or retains module handles, descriptors or audio instance pointers.
Closing it cannot unload active DSP; Apply builds a new immutable plan before Closing it cannot unload active DSP; Apply builds a new immutable plan before
installation and existing retirement/quiescence rules release old instances. installation and existing retirement/quiescence rules release old instances. No
No embedded third-party editor UI is supported. embedded third-party editor UI is supported.
### Compatibility and recovery ### Compatibility and recovery
@ -174,8 +187,8 @@ Missing/incompatible modules reject GUI Open with the offending label/path and
leave the current project intact. Restore a compatible module at that path and leave the current project intact. Restore a compatible module at that path and
retry; use metadata-only validation/save to preserve an unavailable module's retry; use metadata-only validation/save to preserve an unavailable module's
records. No dummy replacement silently removes its controls. Failed discovery, records. No dummy replacement silently removes its controls. Failed discovery,
replacement or instantiate reports an error and retains the current document. replacement or instantiate reports an error and retains the current document. A
A LADSPA `run` callback has no error return; finite-sample sanitation does not LADSPA `run` callback has no error return; finite-sample sanitation does not
contain crashes, hangs, memory corruption or prohibited work inside a plugin. contain crashes, hangs, memory corruption or prohibited work inside a plugin.
Only trusted in-process plugins are supported. See RECOVERY.md. Only trusted in-process plugins are supported. See RECOVERY.md.
@ -193,71 +206,83 @@ cover process-error silence/reset. The separately installed amp/inverter/sine
modules remain the qualified LADSPA set. Human listening, arbitrary third-party modules remain the qualified LADSPA set. Human listening, arbitrary third-party
editors/plugins and extended realtime endurance remain unverified. editors/plugins and extended realtime endurance remain unverified.
## Parameter targets ---
## Parameter Targets
`xtc_control.h` supplies common stable owner/parameter IDs and control-thread `xtc_control.h` supplies common stable owner/parameter IDs and control-thread
metadata/edit APIs for track/bus gain/pan and LADSPA input ports. The GUI's plugin metadata/edit APIs for track/bus gain/pan and LADSPA input ports. The GUI's
automation command uses this contract. Linear/hold LADSPA encoding and its plugin automation command uses this contract. Linear/hold LADSPA encoding and
integer/toggle restrictions remain unchanged; v10 adds mixer interpolation and its integer/toggle restrictions remain unchanged; v10 adds mixer interpolation
live-sine CC maps without changing plugin state records. Direct static edits do and live-sine CC maps without changing plugin state records. Direct static edits
not override existing lanes. Live CCs do not map to plugins; missing targets or do not override existing lanes. Live CCs do not map to plugins; missing targets
modules fail without installing partial state. See CONTROL.md for timing, or modules fail without installing partial state. See CONTROL.md for timing,
precedence, reset and backward-jump behavior. precedence, reset and backward-jump behavior.
## CLAP, VST2 and VST3 effect loaders ---
**File → Load plugin** now detects LADSPA, CLAP, VST2 and VST3 entry points in an ## CLAP, VST2 and VST3 Effect Loaders
explicit trusted Linux module. **Insert plugin on selected wire** and **Replace
selected plugin** use the same browser and transactional graph preparation. The
identity/editor dialog names the format and stable ID. Existing LADSPA controls
and automation continue to work. Modern effects currently use their default
configuration: the editor shows identity but no editable parameters.
Choose the actual binary: `.clap` for CLAP, a VST2 `.so` exporting `VSTPluginMain`, **File → Load plugin** now detects LADSPA, CLAP, VST2 and VST3 entry points in
or a VST3 bundle's `Contents/<architecture>-linux/<module>.so` exporting an explicit trusted Linux module. **Insert plugin on selected wire** and
`ModuleEntry`, `ModuleExit` and `GetPluginFactory`. The chooser does not resolve **Replace selected plugin** use the same browser and transactional graph
bundle directories or recursively scan plugin folders. On Linux this avoids preparation. The identity/editor dialog names the format and stable ID. Existing
executing unrelated plugins just to discover one selected module. Auto-detection LADSPA controls and automation continue to work. Modern effects currently use
prefers CLAP, then VST2, then VST3, otherwise LADSPA; the C API also provides their default configuration: the editor shows identity but no editable
`xtc_plugin_probe_format` for an explicit format. parameters.
The modern subset is **one mono audio input and one mono output, float32, default Choose the actual binary: `.clap` for CLAP, a VST2 `.so` exporting
configuration, zero reported latency**. Stereo graph playback uses independent `VSTPluginMain`, or a VST3 bundle's `Contents/<architecture>-linux/<module>.so`
instances. For the modern effect subset, true stereo/multichannel/sidechain buses, instruments, MIDI/events, exporting `ModuleEntry`, `ModuleExit` and `GetPluginFactory`. The chooser does
custom editors, parameter changes/automation, programs/presets, opaque state, not resolve bundle directories or recursively scan plugin folders. On Linux this
transport/time information, workers and latency compensation are not implemented avoids executing unrelated plugins just to discover one selected module.
for these three formats. Modern metadata with controls/automation is rejected, Auto-detection prefers CLAP, then VST2, then VST3, otherwise LADSPA; the C API
not silently ignored. VST3 event buses and CLAP note ports are rejected. Plugins also provides `xtc_plugin_probe_format` for an explicit format.
The modern subset is **one mono audio input and one mono output, float32,
default configuration, zero reported latency**. Stereo graph playback uses
independent instances. For the modern effect subset, true
stereo/multichannel/sidechain buses, instruments, MIDI/events, custom editors,
parameter changes/automation, programs/presets, opaque state, transport/time
information, workers and latency compensation are not implemented for these
three formats. Modern metadata with controls/automation is rejected, not
silently ignored. VST3 event buses and CLAP note ports are rejected. Plugins
requiring unsupported host extensions may refuse initialization. This is not a requiring unsupported host extensions may refuse initialization. This is not a
claim that all CLAP/VST2/VST3 plugins will load or sound identically after updates. claim that all CLAP/VST2/VST3 plugins will load or sound identically after
updates.
Only original effect fixtures have been qualified for the new formats. Trusted Only original effect fixtures have been qualified for the new formats. Trusted
plugin code is in-process and can crash/hang or violate realtime rules. No plugin plugin code is in-process and can crash/hang or violate realtime rules. No
binary is included in packages. A VST2 ABI adapter does not provide rights to the plugin binary is included in packages. A VST2 ABI adapter does not provide
discontinued SDK or plugin redistribution. See decision 0010 and LICENSES.md. rights to the discontinued SDK or plugin redistribution. See decision 0010 and
LICENSES.md.
The control/main thread owns discovery, activation, module reference accounting, The control/main thread owns discovery, activation, module reference accounting,
retirement and unloading. CLAP's start-processing call runs on the audio owner; retirement and unloading. CLAP's start-processing call runs on the audio owner;
main-thread callbacks are coalesced and serviced via `xtc_plugin_idle` (GUI timer, main-thread callbacks are coalesced and serviced via `xtc_plugin_idle` (GUI
currently 100 ms). C hosts using CLAP must pump that API on their serialized main timer, currently 100 ms). C hosts using CLAP must pump that API on their
thread while instances live, and stop/join audio before destruction. Restart and serialized main thread while instances live, and stop/join audio before
VST2 I/O-change requests make subsequent blocks fail/silence; stop and rebuild or destruction. Restart and VST2 I/O-change requests make subsequent blocks
reload the plugin rather than reconfiguring inside the callback. CLAP process fail/silence; stop and rebuild or reload the plugin rather than reconfiguring
errors and VST3 failure results silence a block. VST2 has no process error return. inside the callback. CLAP process errors and VST3 failure results silence a
block. VST2 has no process error return.
```sh ```sh
make all test gui test-modern-plugins test-gui-modern-plugins make all test gui test-modern-plugins test-gui-modern-plugins
make test-sanitize analyze make test-sanitize analyze
``` ```
Original CLAP/VST2/VST3 shared-library fixtures exercise exact in-place and stereo Original CLAP/VST2/VST3 shared-library fixtures exercise exact in-place and
processing, entry/instance lifetimes, rejected layouts, partial initialization, stereo processing, entry/instance lifetimes, rejected layouts, partial
CLAP start/process failures, graph replacement rollback, 50 plan retirements per initialization, CLAP start/process failures, graph replacement rollback, 50 plan
format, missing modules, v11 save/reopen and GUI add/browser/undo/reopen. Host retirements per format, missing modules, v11 save/reopen and GUI
allocation/loading/unloading guards remain active while rendering. This evidence add/browser/undo/reopen. Host allocation/loading/unloading guards remain active
qualifies the subset above, not arbitrary external plugins or physical audio. while rendering. This evidence qualifies the subset above, not arbitrary
external plugins or physical audio.
## Instrument support ---
## Instrument Support
MIDI tracks can bind CLAP, VST2 and VST3 instruments. In the Track menu, select MIDI tracks can bind CLAP, VST2 and VST3 instruments. In the Track menu, select
**Load instrument plugin** for the selected sequencer track; **Use built-in **Load instrument plugin** for the selected sequencer track; **Use built-in
@ -268,9 +293,9 @@ their own track's instrument. Independent instances feed track gain/pan,
mute/solo, bus routing and automation. Bindings/live target save in v12. mute/solo, bus routing and automation. Bindings/live target save in v12.
Supply the actual trusted Linux binary (including the inner .so of a VST3 Supply the actual trusted Linux binary (including the inner .so of a VST3
bundle). Supported instruments have zero audio inputs, one mono output, bundle). Supported instruments have zero audio inputs, one mono output, float32
float32 processing, zero latency and one note/event input. Default configuration processing, zero latency and one note/event input. Default configuration only:
only: no custom editor, opaque state/preset restoration, modern parameter no custom editor, opaque state/preset restoration, modern parameter
editing/automation, stereo/multichannel instruments or MIDI output. No arbitrary editing/automation, stereo/multichannel instruments or MIDI output. No arbitrary
third-party instrument is qualified by the original ABI fixture tests. third-party instrument is qualified by the original ABI fixture tests.

View file

@ -1,19 +1,21 @@
# Application preferences and looping # Application Preferences and Looping
Open **Edit / Preferences** (Ctrl+,). Each tab edits application settings. Open **Edit / Preferences** (Ctrl+,). Each tab edits application settings.
**Apply** validates every tab, saves `~/.xtc.conf`, and applies the values. **Apply** validates every tab, saves `~/.xtc.conf`, and applies the values.
**Close** discards edits made since the last Apply. Apply stops playback; stop **Close** discards edits made since the last Apply. Apply stops playback; stop
recording before opening Preferences. Clean exit also saves collected application settings. recording before opening Preferences. Clean exit also saves collected
Startup reads `~/.xtc.conf` before project/backend creation; missing keys use defaults. application settings. Startup reads `~/.xtc.conf` before project/backend
Malformed configuration reports an error and uses defaults without overwriting creation; missing keys use defaults. Malformed configuration reports an error
it. Invalid input or failed writes preserve the previous saved configuration. and uses defaults without overwriting it. Invalid input or failed writes
preserve the previous saved configuration.
The file is INI with `[audio]`, `[plugins]`, `[midi]`, `[interface]`, `[editing]`, The file is INI with `[audio]`, `[plugins]`, `[midi]`, `[interface]`,
`[project_defaults]` and `[transport]` sections. Save uses a mode-0600 temporary `[editing]`, `[project_defaults]` and `[transport]` sections. Save uses a
file, flushes and fsyncs it, then atomically renames it. Unknown keys/sections, mode-0600 temporary file, flushes and fsyncs it, then atomically renames it.
duplicate keys, invalid ranges, embedded NULs and files larger than 64 KiB are Unknown keys/sections, duplicate keys, invalid ranges, embedded NULs and files
rejected. Blank lines and full-line `;` / `#` comments are supported. Strings larger than 64 KiB are rejected. Blank lines and full-line `;` / `#` comments
may be quoted, with `\\` and `\"` escapes. Apply rewrites comments and formatting. are supported. Strings may be quoted, with `\\` and `\"` escapes. Apply rewrites
comments and formatting.
| Section | Settings and defaults | | Section | Settings and defaults |
| --- | --- | | --- | --- |
@ -30,37 +32,41 @@ Audio client name, block capacity, output channels and fades take effect on
these remain JACK server settings. Offline rate is used when disconnected. these remain JACK server settings. Offline rate is used when disconnected.
Recording capacity applies to the next take. Tempo and CC maps apply to **New** Recording capacity applies to the next take. Tempo and CC maps apply to **New**
projects. Track mix, routing, instruments, plugin parameters, automation and projects. Track mix, routing, instruments, plugin parameters, automation and
musical content remain project data, saved through Save Project. Internal musical content remain project data, saved through Save Project. Internal safety
safety limits and plugin ABI requirements are not runtime preferences. limits and plugin ABI requirements are not runtime preferences.
Snap accepts 1 (Off), 120, 160, 240, 320, 480, 960, 1920 or 3840 ticks. Snap accepts 1 (Off), 120, 160, 240, 320, 480, 960, 1920 or 3840 ticks. Zoom
Zoom accepts 24, 48, 96, 192 or 384 pixels per beat. MIDI channels are zero-based; accepts 24, 48, 96, 192 or 384 pixels per beat. MIDI channels are zero-based;
filter channel -1 shows all channels. Window sizes, grids and filters currently filter channel -1 shows all channels. Window sizes, grids and filters currently
in use populate Preferences when it opens; Apply or a clean application exit persists them. in use populate Preferences when it opens; Apply or a clean application exit
persists them.
Plugin selection now opens a library of `.clap` and `.so` files from the search Plugin selection now opens a library of `.clap` and `.so` files from the search
paths. `~/` expands to the home directory; empty/nonabsolute roots are skipped. paths. `~/` expands to the home directory; empty/nonabsolute roots are skipped.
Traversal does not follow directory symlinks and stops at depth six, 4096 entries Traversal does not follow directory symlinks and stops at depth six, 4096
or 512 files. Linux VST3 bundle binaries are found inside bundle directories. entries or 512 files. Linux VST3 bundle binaries are found inside bundle
Listing reads filenames only. **Inspect plugin** explicitly loads the selected directories. Listing reads filenames only. **Inspect plugin** explicitly loads
trusted binary through the existing descriptor chooser; **Browse files** allows the selected trusted binary through the existing descriptor chooser; **Browse
other locations. It is not a plugin certification or isolation service. files** allows other locations. It is not a plugin certification or isolation
service.
## Loop playback ---
## Loop Playback
**Shift-drag the sequencer ruler** to select a region using the selected Snap. **Shift-drag the sequencer ruler** to select a region using the selected Snap.
Dragging in either direction works. A band shows the region; **Loop** enables Dragging in either direction works. A band shows the region; **Loop** enables or
or disables it. **Loop range...** also accepts start and end in ticks. Selection disables it. **Loop range...** also accepts start and end in ticks. Selection
and toggling take effect immediately; Apply or clean exit persists the region. and toggling take effect immediately; Apply or clean exit persists the region.
The region is global application configuration and is not stored in `.xtc` The region is global application configuration and is not stored in `.xtc`
projects. Changing a region during playback stops, reconfigures and restarts the projects. Changing a region during playback stops, reconfigures and restarts the
backend. Selecting a region does not itself enable looping. backend. Selecting a region does not itself enable looping.
Playback wraps at the exclusive end sample, including inside an audio block, Playback wraps at the exclusive end sample, including inside an audio block, and
and starts at the region beginning when the transport lies outside the region. starts at the region beginning when the transport lies outside the region. The
The region follows the current project's tempo. Enabled regions must span at region follows the current project's tempo. Enabled regions must span at least
least 64 samples at the active rate. Scheduled MIDI uses the existing seek/reset 64 samples at the active rate. Scheduled MIDI uses the existing seek/reset and
and note-chase behavior at a wrap. Effects retain tails/state. No loop-boundary note-chase behavior at a wrap. Effects retain tails/state. No loop-boundary
crossfade is added, so discontinuous audio can click. Recording requires looping crossfade is added, so discontinuous audio can click. Recording requires looping
to be disabled. Export retains its explicit linear range. to be disabled. Export retains its explicit linear range.
@ -70,24 +76,25 @@ plus JACK callback wrap positions and existing GUI/core regressions. Tests use
temporary configuration paths and do not write the user's configuration. temporary configuration paths and do not write the user's configuration.
`[transport] tail_ms=3000` (0..60000) and `stop_after_tail=0` (0/1) are `[transport] tail_ms=3000` (0..60000) and `stop_after_tail=0` (0/1) are
supported. Export always includes the configured tail; optional supported. Export always includes the configured tail; optional playback
playback stopping uses the GUI timer and is disabled during looping/recording. stopping uses the GUI timer and is disabled during looping/recording. See
See BUILTIN_DEVICES.md for endpoint, cancellation and reset semantics. BUILTIN_DEVICES.md for endpoint, cancellation and reset semantics.
`[interface]` provides `transport_detached`, `mixer_detached`, and each panel's `[interface]` provides `transport_detached`, `mixer_detached`, and each panel's
`_x`, `_y`, `_width`, `_height` rectangle. Use Preferences / Apply or close the application cleanly after `_x`, `_y`, `_width`, `_height` rectangle. Use Preferences / Apply or close the
moving panels to save the captured layout. Restoration clamps rectangles to the application cleanly after moving panels to save the captured layout. Restoration
current display; the floating transport wraps at narrow widths. clamps rectangles to the current display; the floating transport wraps at narrow
widths.
`[interface]` also includes `sequencer_detached`, `graph_detached`, `[interface]` also includes `sequencer_detached`, `graph_detached`,
`piano_detached`, editor window geometry and `track_header_width` (180..360). `piano_detached`, editor window geometry and `track_header_width` (180..360).
Piano retains `piano_width`/`piano_height` and adds `piano_x`/`piano_y`. Piano retains `piano_width`/`piano_height` and adds `piano_x`/`piano_y`. Reset
Reset workspace layout changes the session; Preferences / Apply or clean exit persists it. workspace layout changes the session; Preferences / Apply or clean exit persists
Only original-tab docking and floating windows are supported. it. Only original-tab docking and floating windows are supported.
Configuration also includes complete workspace collection and MIDI Configuration also includes complete workspace collection and MIDI
ports/channels/thru. Invalid startup files are preserved on exit; explicit Apply ports/channels/thru. Invalid startup files are preserved on exit; explicit Apply
can replace them after validation. If saving fails, the window stays open for can replace them after validation. If saving fails, the window stays open for
retry. See [MIDI_WORKFLOWS.md](MIDI_WORKFLOWS.md) for device setup and notes-only retry. See [MIDI_WORKFLOWS.md](MIDI_WORKFLOWS.md) for device setup and
Standard MIDI File import/export. JACK server hardware settings and musical notes-only Standard MIDI File import/export. JACK server hardware settings and
project contents remain owned by the server and project respectively. musical project contents remain owned by the server and project respectively.

View file

@ -1,15 +1,17 @@
# Project format v17 (reads v1–v17) # Project Format v17 (Reads v1–v17)
The project uses a bounded text format implemented in C99 without a parser library. The project uses a bounded text format implemented in C99 without a parser
The extension is `.xtc`. File operations run exclusively on the control thread. library. The extension is `.xtc`. File operations run exclusively on the control
The project owns its graph, tracks and silent/audio arrangement regions. Audio samples remain in external WAV thread. The project owns its graph, tracks and silent/audio arrangement regions.
files; MIDI notes are stored inline. Audio samples remain in external WAV files; MIDI notes are stored inline.
---
## Schema ## Schema
Records occur in this exact order, separated by LF, including the final record. Records occur in this exact order, separated by LF, including the final record.
Fields use ASCII spaces. Integers are unsigned decimal, without signs. Fields use ASCII spaces. Integers are unsigned decimal, without signs. Counts
Counts determine the number of subsequent records: determine the number of subsequent records:
```text ```text
XTC_PROJECT 11 XTC_PROJECT 11
@ -55,37 +57,38 @@ END
- `TEMPO`: constant milli-BPM (20000..300000, default 120000), fixed 4/4. - `TEMPO`: constant milli-BPM (20000..300000, default 120000), fixed 4/4.
- `IDS`: next track ID and next clip ID. IDs are uint64, monotonic and never - `IDS`: next track ID and next clip ID. IDs are uint64, monotonic and never
reused; UINT64_MAX is reserved. Track and clip ID namespaces are separate. reused; UINT64_MAX is reserved. Track and clip ID namespaces are separate.
- `GRAPH`: output-node policy (0/1), next node ID, node count, edge count. - `GRAPH`: output-node policy (0/1), next node ID, node count, edge count. Nodes
Nodes retain stable IDs (limited to the host's size_t range). No pointers or retain stable IDs (limited to the host's size_t range). No pointers or
compiled state are serialized. compiled state are serialized.
- `NODE`: ID, type (1 gain, 2 oscillator, 3 meter, 4 output, 5 audio source, 6 instrument, 8 LADSPA plugin), and exactly eight - `NODE`: ID, type (1 gain, 2 oscillator, 3 meter, 4 output, 5 audio source, 6
hexadecimal digits encoding the IEEE binary32 parameter bits, independent instrument, 8 LADSPA plugin), and exactly eight hexadecimal digits encoding
of locale and host byte order. Nonfinite values are rejected. the IEEE binary32 parameter bits, independent of locale and host byte order.
- `EDGE`: source and destination node IDs. Port directions are implied Nonfinite values are rejected.
by node type; endpoints, duplicates and cycles are validated by the graph API. - `EDGE`: source and destination node IDs. Port directions are implied by node
type; endpoints, duplicates and cycles are validated by the graph API.
- `TRACK`: ID and hex-encoded name bytes. In v4/v5 each is immediately followed - `TRACK`: ID and hex-encoded name bytes. In v4/v5 each is immediately followed
by `MIX gain_bits pan_bits mute solo`: finite binary32 gain 0..4, pan -1..1 by `MIX gain_bits pan_bits mute solo`: finite binary32 gain 0..4, pan -1..1
encoded as eight hex digits, mute and solo unsigned 0/1. encoded as eight hex digits, mute and solo unsigned 0/1.
- `ROUTE`: since v9, required after MIX: bus flag and destination bus ID; - `ROUTE`: since v9, required after MIX: bus flag and destination bus ID;
UINT64_MAX denotes master. See the mix routing constraints below. UINT64_MAX denotes master. See the mix routing constraints below.
- `CLIP`: ID, track ID, start tick, length tick, source offset tick, source - `CLIP`: ID, track ID, start tick, length tick, source offset tick, source
length tick, hex-encoded name bytes. Trimming length tick, hex-encoded name bytes. Trimming preserves source bounds, so a
preserves source bounds, so a region can re-extend only within its original region can re-extend only within its original length. Overlaps are allowed and
length. Overlaps are allowed and audio clips mix through the audio source node. audio clips mix through the audio source node.
- `ASSET`: required after each CLIP in v2/v3/v4/v5; "-" means no audio asset, otherwise - `ASSET`: required after each CLIP in v2/v3/v4/v5; "-" means no audio asset,
hex-encoded absolute path bytes, <=511 bytes after decoding, no embedded NUL otherwise hex-encoded absolute path bytes, <=511 bytes after decoding, no
or ASCII control characters. Each clip directly references a WAV; no sample embedded NUL or ASCII control characters. Each clip directly references a WAV;
data, pointers or DSP state are embedded. Paths can contain spaces. no sample data, pointers or DSP state are embedded. Paths can contain spaces.
- `MIDI`: required after each ASSET in v3/v4/v5; 0 for silent/audio regions, 1 for - `MIDI`: required after each ASSET in v3/v4/v5; 0 for silent/audio regions, 1
MIDI clips. MIDI clips must have no asset. for MIDI clips. MIDI clips must have no asset.
- `NOTES`: next stable note ID and global note count, followed by that many - `NOTES`: next stable note ID and global note count, followed by that many
`NOTE id clip_id start_tick length_tick pitch velocity channel` records. `NOTE id clip_id start_tick length_tick pitch velocity channel` records. Notes
Notes reference MIDI clips; start/length are source-relative, positive length reference MIDI clips; start/length are source-relative, positive length within
within source bounds. Pitch is 0..127, velocity 1..127, channel 0..15. source bounds. Pitch is 0..127, velocity 1..127, channel 0..15. Note IDs are
Note IDs are unique, monotonic uint64 IDs with UINT64_MAX reserved. unique, monotonic uint64 IDs with UINT64_MAX reserved.
- `AUTOMATION`: global point count, after the last NOTE and before END in v4/v5. - `AUTOMATION`: global point count, after the last NOTE and before END in v4/v5.
Follow with `POINT track_id parameter tick value_bits step` in v10 (v4–v9 Follow with `POINT track_id parameter tick value_bits step` in v10 (v4–v9 omit
omit step and migrate to linear). Step is 0 linear or 1 hold for the next segment. step and migrate to linear). Step is 0 linear or 1 hold for the next segment.
Parameter 0 is gain (0..4), 1 pan (-1..1); values use finite binary32 hex. Parameter 0 is gain (0..4), 1 pan (-1..1); values use finite binary32 hex.
Track must exist; tick <= MAX_TICK; duplicate track/parameter/tick is invalid. Track must exist; tick <= MAX_TICK; duplicate track/parameter/tick is invalid.
Record order is preserved; rendering sorts a separate prepared copy. Record order is preserved; rendering sorts a separate prepared copy.
@ -94,20 +97,24 @@ END
Ticks use 960 per quarter note, 3840 per bar. All region endpoints are bounded Ticks use 960 per quarter note, 3840 per bar. All region endpoints are bounded
by 38400000 ticks (10000 bars). Limits: 64 tracks, 1024 clips, 64 graph nodes, by 38400000 ticks (10000 bars). Limits: 64 tracks, 1024 clips, 64 graph nodes,
4096 edges, 4096 notes, 1024 automation points, 4 MiB input, 1535 bytes per record, 12 tokens per record. The reader 4096 edges, 4096 notes, 1024 automation points, 4 MiB input, 1535 bytes per
rejects invalid references, duplicate IDs, inconsistent next IDs, ranges, record, 12 tokens per record. The reader rejects invalid references, duplicate
truncated records, raw control/non-ASCII bytes, and trailing data. IDs, inconsistent next IDs, ranges, truncated records, raw control/non-ASCII
bytes, and trailing data.
Unsupported format versions return UNSUPPORTED. Other malformed data returns Unsupported format versions return UNSUPPORTED. Other malformed data returns
FORMAT. The reader explicitly accepts v1 records without ASSET lines, with FORMAT. The reader explicitly accepts v1 records without ASSET lines, with node
node types 1..4, migrating their clips to empty asset paths. Version 2 accepts types 1..4, migrating their clips to empty asset paths. Version 2 accepts node
node types 1..5 and ASSET records. Both migrate to MIDI=0 and no notes. types 1..5 and ASSET records. Both migrate to MIDI=0 and no notes. Version 3
Version 3 accepts instrument nodes and MIDI/NOTES records. Versions 1–3 migrate accepts instrument nodes and MIDI/NOTES records. Versions 1–3 migrate all tracks
all tracks to unity gain, centered pan, mute/solo off and no automation. to unity gain, centered pan, mute/solo off and no automation. Saving always
Saving always writes v15. Unknown versions are rejected without replacing the destination; older writes v15. Unknown versions are rejected without replacing the destination;
applications without v12 support cannot read current saves. Migration and maximum-path roundtrips are tested. older applications without v12 support cannot read current saves. Migration and
maximum-path roundtrips are tested.
## Ownership and failures ---
## Ownership and Failures
Load builds and validates a separate candidate, closes the input, then replaces Load builds and validates a separate candidate, closes the input, then replaces
the destination. Any failure retains the complete previous project. Saving the destination. Any failure retains the complete previous project. Saving
@ -116,35 +123,42 @@ directory, writes, flushes, fsyncs, closes and renames over the destination.
Failures before rename retain the previous destination and remove the temporary. Failures before rename retain the previous destination and remove the temporary.
After rename, the parent directory is fsynced. A directory-sync failure reports After rename, the parent directory is fsynced. A directory-sync failure reports
I/O error even though the new complete destination may already be present. I/O error even though the new complete destination may already be present.
Storage and filesystem behavior still determine power-loss durability. Save replaces symlinks themselves and does not preserve Storage and filesystem behavior still determine power-loss durability. Save
existing file permissions. POSIX I/O is private to the Linux-first persistence replaces symlinks themselves and does not preserve existing file permissions.
module; public headers contain no POSIX types. POSIX I/O is private to the Linux-first persistence module; public headers
contain no POSIX types.
Saved data reproduces the arrangement, graph, track mix and automation, including Saved data reproduces the arrangement, graph, track mix and automation,
ID allocation state. Undo history is not saved. See MIXER.md for pan/interpolation. including ID allocation state. Undo history is not saved. See MIXER.md for
Graph node positions, timeline zoom/scroll/selection, transport position and DSP pan/interpolation. Graph node positions, timeline zoom/scroll/selection,
oscillator phase are GUI/session state and are not saved. Asset references are transport position and DSP oscillator phase are GUI/session state and are not
absolute in memory; saves use relative paths for media inside the project folder, saved. Asset references are absolute in memory; saves use relative paths for
otherwise absolute paths. Media are not copied on Save As. Metadata loading preserves media inside the project folder, otherwise absolute paths. Media are not copied
missing references; audio-plan preparation reports unreadable/invalid assets on Save As. Metadata loading preserves missing references; audio-plan
and renders them silently. Relinking replaces a selected clip's path without preparation reports unreadable/invalid assets and renders them silently.
changing its IDs or arrangement. See AUDIO_FILES.md for recovery and limitations. Relinking replaces a selected clip's path without changing its IDs or
arrangement. See AUDIO_FILES.md for recovery and limitations.
## Extension and recovery policy ---
Format v4 was retained. Native processor descriptors are process-local and have no ## Extension and Recovery Policy
serialized representation; saving a native graph returns UNSUPPORTED and retains
the existing destination. Unknown file versions/types remain rejected. See
NATIVE_API.md for the extension boundary and RECOVERY.md for write-failure, killed
writer and unfinished-recording behavior. Metadata validation is available via
`xtc-check --validate PROJECT` without a GUI or server; it does not load assets.
## Version 5: plugin metadata Format v4 was retained. Native processor descriptors are process-local and have
no serialized representation; saving a native graph returns UNSUPPORTED and
retains the existing destination. Unknown file versions/types remain rejected.
See NATIVE_API.md for the extension boundary and RECOVERY.md for write-failure,
killed writer and unfinished-recording behavior. Metadata validation is
available via `xtc-check --validate PROJECT` without a GUI or server; it does
not load assets.
`XTC_PROJECT 5` was introduced; its reader also supported versions 1–4. All v4 records and ---
order remain the same. Node type 8 is a LADSPA plugin; native pointer-bearing type
7 is still unsupported in files. Immediately after each type-8 `NODE id 8 ## Version 5: Plugin Metadata
00000000` record come:
`XTC_PROJECT 5` was introduced; its reader also supported versions 1–4. All v4
records and order remain the same. Node type 8 is a LADSPA plugin; native
pointer-bearing type 7 is still unsupported in files. Immediately after each
type-8 `NODE id 8 00000000` record come:
```text ```text
PLUGIN unique_id descriptor_index mono_inputs control_count hex_label PLUGIN unique_id descriptor_index mono_inputs control_count hex_label
@ -155,130 +169,153 @@ CONTROL port_index float32_hex_bits
There are exactly `control_count` CONTROL records. Label length is 1–63 bytes, There are exactly `control_count` CONTROL records. Label length is 1–63 bytes,
module path 1–511 bytes (absolute), both hex-encoded with no embedded NUL. Index module path 1–511 bytes (absolute), both hex-encoded with no embedded NUL. Index
is <1024, mono_inputs 0/1, controls <=16, control port indices <64 strictly is <1024, mono_inputs 0/1, controls <=16, control port indices <64 strictly
increasing, and values finite. Unique ID fits uint32. The one output is implicit. increasing, and values finite. Unique ID fits uint32. The one output is
Structural validation never opens libraries. On graph compilation the host checks implicit. Structural validation never opens libraries. On graph compilation the
identity, layout, input control port IDs and rate-adjusted legal ranges against host checks identity, layout, input control port IDs and rate-adjusted legal
the module. Unknown nodes/versions fail without mutating the current project. ranges against the module. Unknown nodes/versions fail without mutating the
Missing modules remain preserved by metadata load/save; GUI installation rejects current project. Missing modules remain preserved by metadata load/save; GUI
them with a repair message. No transient DSP state, pointer or plugin binary is installation rejects them with a repair message. No transient DSP state, pointer
stored. See PLUGIN_HOSTING.md and decision 0009 for supported processors/limits. or plugin binary is stored. See PLUGIN_HOSTING.md and decision 0009 for
supported processors/limits.
---
## Version 6 ## Version 6
Version 6 was introduced and its reader accepted v1–v6. After each plugin's CONTROL records, Version 6 was introduced and its reader accepted v1–v6. After each plugin's
`PLUGIN_AUTOMATION count` is followed by exactly count (<=32) records: CONTROL records, `PLUGIN_AUTOMATION count` is followed by exactly count (<=32)
`PARAM tick port mode float32_hex_bits`. Mode is 0 linear or 1 step. Duplicate records: `PARAM tick port mode float32_hex_bits`. Mode is 0 linear or 1 step.
port/tick pairs, nonfinite values, unknown control ports and ticks beyond the Duplicate port/tick pairs, nonfinite values, unknown control ports and ticks
project limit are rejected. Loaded plugin tempo comes from TEMPO. Range and beyond the project limit are rejected. Loaded plugin tempo comes from TEMPO.
discrete-mode compatibility are checked on control-thread compilation. Versions Range and discrete-mode compatibility are checked on control-thread compilation.
1–5 migrate with empty plugin lanes. See CONTROL.md for timing/ordering. Versions 1–5 migrate with empty plugin lanes. See CONTROL.md for
timing/ordering.
## Version 7 and project folders ---
Version 7 was introduced; its readers migrated v1–v6. Record ordering and field bounds ## Version 7 and Project Folders
are otherwise unchanged from v6. v7 ASSET may contain a relative path, interpreted
against the actual containing directory of the named project file (resolved on Version 7 was introduced; its readers migrated v1–v6. Record ordering and field
the control thread), never the launch working directory. Opening a symlink to a project uses the bounds are otherwise unchanged from v6. v7 ASSET may contain a relative path,
resolved target project directory; Save replaces the named entry as before. After loading, all core interpreted against the actual containing directory of the named project file
asset references are absolute, including missing media. Media inside the save (resolved on the control thread), never the launch working directory. Opening a
folder are written relative; references outside remain absolute. Save As resolves symlink to a project uses the resolved target project directory; Save replaces
against its new folder and preserves media identity rather than rebasing it by the named entry as before. After loading, all core asset references are
accident. Copy/move a project folder together with its referenced media to retain absolute, including missing media. Media inside the save folder are written
relative; references outside remain absolute. Save As resolves against its new
folder and preserves media identity rather than rebasing it by accident.
Copy/move a project folder together with its referenced media to retain
portability. Saving does not copy assets: import/record into that folder first. portability. Saving does not copy assets: import/record into that folder first.
Plugin binaries remain trusted absolute module references, not bundled resources. Plugin binaries remain trusted absolute module references, not bundled
resources.
Empty assets still use `-`. Paths exclude embedded NUL/control bytes and must fit Empty assets still use `-`. Paths exclude embedded NUL/control bytes and must
511 bytes after resolution; overly long resolved references fail transactionally. fit 511 bytes after resolution; overly long resolved references fail
Relative references can name outside the folder using ordinary POSIX semantics; transactionally. Relative references can name outside the folder using ordinary
this is not a sandbox. Symlinks follow filesystem behavior, so portability requires POSIX semantics; this is not a sandbox. Symlinks follow filesystem behavior, so
moving their targets too. Old formats continue to require absolute media paths. portability requires moving their targets too. Old formats continue to require
No unknown version/node/record is silently discarded; unsupported versions/types absolute media paths. No unknown version/node/record is silently discarded;
or malformed/trailing data reject the candidate and retain the current project. unsupported versions/types or malformed/trailing data reject the candidate and
Parsing never opens media/plugins or constructs an audio engine. retain the current project. Parsing never opens media/plugins or constructs an
audio engine.
Save validates the complete model, writes/fsyncs a same-folder temporary, closes Save validates the complete model, writes/fsyncs a same-folder temporary, closes
it, atomically renames it, then fsyncs the containing directory. Failures before it, atomically renames it, then fsyncs the containing directory. Failures before
rename retain the old destination. A directory sync failure *after* rename reports rename retain the old destination. A directory sync failure *after* rename
I/O error but the new complete file may already be present: inspect/reopen it; reports I/O error but the new complete file may already be present:
there is no rollback across that durability boundary. Filesystem/hardware power inspect/reopen it; there is no rollback across that durability boundary.
loss guarantees still depend on the underlying storage. Recording destinations Filesystem/hardware power loss guarantees still depend on the underlying
have their separate no-clobber policy; ordinary project Save intentionally replaces storage. Recording destinations have their separate no-clobber policy; ordinary
the selected document. In-memory models and the existing destination are preserved project Save intentionally replaces the selected document. In-memory models and
on load failure. No automatic unknown-field stripping or media deletion occurs. the existing destination are preserved on load failure. No automatic
unknown-field stripping or media deletion occurs.
`make test-portability test test-plugins test-recovery test-recording` covers checked-in `make test-portability test test-plugins test-recovery test-recording` covers
v1–v10 fixtures (including missing v5/v6 plugins), current exact roundtrips, moved-folder checked-in v1–v10 fixtures (including missing v5/v6 plugins), current exact
playback from a different cwd, missing media retention, unknown records, every roundtrips, moved-folder playback from a different cwd, missing media retention,
truncation and deterministic byte mutations, malformed plugin metadata, interrupted unknown records, every truncation and deterministic byte mutations, malformed
writers and write-limit failures. ASan/UBSan/static analysis additionally audit the plugin metadata, interrupted writers and write-limit failures. ASan/UBSan/static
bounded parser. This is regression/fault-injection coverage, not exhaustive fuzzing analysis additionally audit the bounded parser. This is
or a claim of power-cut qualification. regression/fault-injection coverage, not exhaustive fuzzing or a claim of
power-cut qualification.
## Version 8: audio clip settings ---
## Version 8: Audio Clip Settings
Version 8 adds audio clip gain/fade settings and reads v1–v7 with unity clip Version 8 adds audio clip gain/fade settings and reads v1–v7 with unity clip
gain and no explicit clip fades. After **every** clip's `MIDI` record (including silent and MIDI gain and no explicit clip fades. After **every** clip's `MIDI` record (including
clips), v8 requires: silent and MIDI clips), v8 requires:
```text ```text
AUDIO gain_float32_hex_bits fade_in_ticks fade_out_ticks AUDIO gain_float32_hex_bits fade_in_ticks fade_out_ticks
``` ```
Gain is finite, linear and within 0..4. Fade durations are unsigned decimal, Gain is finite, linear and within 0..4. Fade durations are unsigned decimal,
0..source_length_tick; zero disables that explicit fade. MIDI clips require 0..source_length_tick; zero disables that explicit fade. MIDI clips require gain
gain exactly 1 and both fades zero. Invalid or missing AUDIO records reject the exactly 1 and both fades zero. Invalid or missing AUDIO records reject the whole
whole candidate. All v7 media-folder rules and atomic-save guarantees remain. candidate. All v7 media-folder rules and atomic-save guarantees remain. Older
Older applications reject v8 rather than silently stripping clip settings. applications reject v8 rather than silently stripping clip settings. Rebuild
Rebuild C/C++ consumers against the extended `xtc_clip_info` public struct; C/C++ consumers against the extended `xtc_clip_info` public struct; this
this development release does not promise an unchanged struct binary layout. development release does not promise an unchanged struct binary layout.
Fades follow the audible trimmed clip boundaries, in current-tempo ticks, with Fades follow the audible trimmed clip boundaries, in current-tempo ticks, with
sample endpoints defined in AUDIO_FILES.md. Duplicating retains settings and sample endpoints defined in AUDIO_FILES.md. Duplicating retains settings and
source references; splitting keeps the original ID on the left, gives the right source references; splitting keeps the original ID on the left, gives the right
a fresh ID/source offset, retains outer explicit fades and clears inner ones. a fresh ID/source offset, retains outer explicit fades and clears inner ones. No
No waveform cache or source audio is serialized. Checked-in v1–v7 migrations, waveform cache or source audio is serialized. Checked-in v1–v7 migrations, v8
v8 exact roundtrips, malformed new metadata and byte-mutation tests cover the exact roundtrips, malformed new metadata and byte-mutation tests cover the
format change (`make test test-portability test-audio-editing`). format change (`make test test-portability test-audio-editing`).
## Version 9: mix routing ---
Version 9 adds `ROUTE is_bus bus_id` immediately after each TRACK's MIX record. `is_bus` is 0 or 1; `bus_id` is an existing bus track ID, ## Version 9: Mix Routing
or decimal UINT64_MAX (18446744073709551615) for master. Buses must route to
master and cannot contain clips. Invalid destinations, flags and nested routing
are rejected. v1–v8 migrate all tracks to ordinary direct-to-master tracks.
Bus controls and automation use existing MIX and POINT records. Older readers
reject v9 rather than discard routing. The public `xtc_track_info` grew fields;
API consumers must rebuild. Fixtures cover v1–v8 migration and tests exercise
v9 exact roundtrips and malformed routing.
## Version 10: interpolation and controller bindings Version 9 adds `ROUTE is_bus bus_id` immediately after each TRACK's MIX record.
`is_bus` is 0 or 1; `bus_id` is an existing bus track ID, or decimal UINT64_MAX
(18446744073709551615) for master. Buses must route to master and cannot contain
clips. Invalid destinations, flags and nested routing are rejected. v1–v8
migrate all tracks to ordinary direct-to-master tracks. Bus controls and
automation use existing MIX and POINT records. Older readers reject v9 rather
than discard routing. The public `xtc_track_info` grew fields; API consumers
must rebuild. Fixtures cover v1–v8 migration and tests exercise v9 exact
roundtrips and malformed routing.
Version 10 adds interpolation modes and migration from v1–v9. Track/bus automation points are ---
`POINT track_id parameter tick value_bits step`, where step is 0 linear or 1
hold for the segment starting there. v1–v9 points migrate to step=0. LADSPA ## Version 10: Interpolation and Controller Bindings
`PARAM` encoding remains unchanged (already has interpolation mode).
Version 10 adds interpolation modes and migration from v1–v9. Track/bus
automation points are `POINT track_id parameter tick value_bits step`, where
step is 0 linear or 1 hold for the segment starting there. v1–v9 points migrate
to step=0. LADSPA `PARAM` encoding remains unchanged (already has interpolation
mode).
Immediately after all POINT records, before END, v10 requires `CONTROL_MAPS 16` Immediately after all POINT records, before END, v10 requires `CONTROL_MAPS 16`
and exactly 16 records in ascending channel order: and exactly 16 records in ascending channel order:
`MAP channel volume_CC pan_CC sustain_CC`. Channels are 0..15. Controllers are `MAP channel volume_CC pan_CC sustain_CC`. Channels are 0..15. Controllers are
0..127 or 128 (disabled); enabled numbers must be distinct within a row and may 0..127 or 128 (disabled); enabled numbers must be distinct within a row and may
not be 120 or 123. Duplicate/missing/out-of-order channels, invalid CCs, reserved not be 120 or 123. Duplicate/missing/out-of-order channels, invalid CCs,
bindings and invalid interpolation modes reject the complete candidate. v1–v9 reserved bindings and invalid interpolation modes reject the complete candidate.
supply default 7/10/64 bindings on every channel. No event history/current live v1–v9 supply default 7/10/64 bindings on every channel. No event history/current
controller values are serialized. Older readers reject v10. live controller values are serialized. Older readers reject v10.
Fixtures cover v1–v10 loading and migration. Exact roundtrips, malformed metadata, step/linear Fixtures cover v1–v10 loading and migration. Exact roundtrips, malformed
renders and remapped live events cover the new writer/reader. Public clients metadata, step/linear renders and remapped live events cover the new
must rebuild and initialize the appended `xtc_automation_point.step` field. writer/reader. Public clients must rebuild and initialize the appended
`xtc_automation_point.step` field.
## Compatibility and diagnostic contract ---
## Compatibility and Diagnostic Contract
The current reader accepts versions 1–15 and the writer always emits version 15. The current reader accepts versions 1–15 and the writer always emits version 15.
Compatibility diagnostics made no format change; plugin format identity is added in v11. Saving an older document upgrades it; Compatibility diagnostics made no format change; plugin format identity is added
retain an original backup if an older application must still open it. APP is in v11. Saving an older document upgrades it; retain an original backup if an
informational; compatibility depends on the format version, not the producer's older application must still open it. APP is informational; compatibility
application version. A future incompatible schema requires a new format number. depends on the format version, not the producer's application version. A future
Public C/C++ consumers must rebuild when public structs change. incompatible schema requires a new format number. Public C/C++ consumers must
rebuild when public structs change.
| Version | Added representation | Defaults when loading earlier versions | | Version | Added representation | Defaults when loading earlier versions |
| --- | --- | --- | | --- | --- | --- |
@ -303,41 +340,47 @@ incompatible plugins retain the current document and identify the module.
Missing media references remain available for relinking and render silently. Missing media references remain available for relinking and render silently.
`xtc_project_load_diagnostic` adds a copied result, parsed format version (zero `xtc_project_load_diagnostic` adds a copied result, parsed format version (zero
if unavailable), expected record/stage and message. Syntax locations are 1-based; if unavailable), expected record/stage and message. Syntax locations are
line zero denotes I/O, size, path resolution or whole-model validation. MODEL 1-based; line zero denotes I/O, size, path resolution or whole-model validation.
errors do not identify the original line of a cross-reference. The original MODEL errors do not identify the original line of a cross-reference. The
`xtc_project_load` remains available with identical transactional semantics. original `xtc_project_load` remains available with identical transactional
semantics.
`xtc-check --validate PROJECT` checks bounded metadata without loading dependencies. `xtc-check --validate PROJECT` checks bounded metadata without loading
`xtc-check --inspect PROJECT` additionally lists resolved media and module paths, dependencies. `xtc-check --inspect PROJECT` additionally lists resolved media
plugin labels, unique IDs and descriptor indices. It checks readable regular-file and module paths, plugin labels, unique IDs and descriptor indices. It checks
availability only. Exit status is 0 when references are available, 2 when valid readable regular-file availability only. Exit status is 0 when references are
metadata has missing/unreadable references, and 1 for invalid metadata or I/O. available, 2 when valid metadata has missing/unreadable references, and 1 for
Neither command executes plugin code, decodes WAV data, connects JACK or verifies invalid metadata or I/O. Neither command executes plugin code, decodes WAV data,
hardware. A readable file can still be an invalid WAV or incompatible plugin; connects JACK or verifies hardware. A readable file can still be an invalid WAV
actual preparation performs those checks. Paths and labels are escaped for or incompatible plugin; actual preparation performs those checks. Paths and
terminal output. There is no search-path substitution or automatic plugin relink. labels are escaped for terminal output. There is no search-path substitution or
automatic plugin relink.
Portability currently targets Linux/POSIX. The text representation is independent Portability currently targets Linux/POSIX. The text representation is
of byte order and locale and requires IEEE binary32 floats. IDs used by the graph independent of byte order and locale and requires IEEE binary32 floats. IDs used
must fit the receiving host's size_t; a 64-bit file is not guaranteed to fit a by the graph must fit the receiving host's size_t; a 64-bit file is not
32-bit build. Paths follow POSIX semantics; Windows drive letters and separators guaranteed to fit a 32-bit build. Paths follow POSIX semantics; Windows drive
are not translated. Relative media can travel with their project folder, while letters and separators are not translated. Relative media can travel with their
absolute references and symlink targets must be restored separately. LADSPA project folder, while absolute references and symlink targets must be restored
modules are absolute installed references: match architecture, ABI, descriptor separately. LADSPA modules are absolute installed references: match
identity, ports and legal controls on the destination. Plugin binaries and state architecture, ABI, descriptor identity, ports and legal controls on the
are not bundled. JACK availability, sample rate, block limits and device routing destination. Plugin binaries and state are not bundled. JACK availability,
are runtime dependencies; loading metadata does not restore device connections. sample rate, block limits and device routing are runtime dependencies; loading
metadata does not restore device connections.
`make test` includes v1–v10 migration, malformed/truncated/mutated/oversized input, `make test` includes v1–v10 migration, malformed/truncated/mutated/oversized
retained-state diagnostics, CLI dependency inspection, and deterministic save input, retained-state diagnostics, CLI dependency inspection, and deterministic
faults at flush, file sync, close, rename and directory sync. Pre-rename faults save faults at flush, file sync, close, rename and directory sync. Pre-rename
preserve the old bytes and remove the temporary. After rename, a sync failure faults preserve the old bytes and remove the temporary. After rename, a sync
reports I/O while leaving a complete new document; inspect it before retrying. failure reports I/O while leaving a complete new document; inspect it before
Fault wrappers are linked into tests only. These tests complement killed-writer retrying. Fault wrappers are linked into tests only. These tests complement
coverage in `make test-recovery`; they do not qualify power-cut behavior. killed-writer coverage in `make test-recovery`; they do not qualify power-cut
behavior.
## Version 11: plugin format identity ---
## Version 11: Plugin Format Identity
Immediately after every plugin MODULE record, v11 requires: Immediately after every plugin MODULE record, v11 requires:
@ -345,29 +388,34 @@ Immediately after every plugin MODULE record, v11 requires:
FORMAT format_number hex_identity_or_dash FORMAT format_number hex_identity_or_dash
``` ```
0 = LADSPA, 1 = CLAP, 2 = VST2, 3 = VST3. Identity is `-` for LADSPA/VST2; 0 = LADSPA, 1 = CLAP, 2 = VST2, 3 = VST3. Identity is `-` for LADSPA/VST2; CLAP
CLAP uses its stable plugin ID (1–127 non-NUL bytes); VST3 uses the 32-character lowercase uses its stable plugin ID (1–127 non-NUL bytes); VST3 uses the 32-character
hex class ID string, itself hex-encoded like other byte strings. PLUGIN's unique lowercase hex class ID string, itself hex-encoded like other byte strings.
ID remains the VST2/LADSPA integer ID (zero for CLAP/VST3), descriptor index and PLUGIN's unique ID remains the VST2/LADSPA integer ID (zero for CLAP/VST3),
label. MODULE is still the actual absolute binary path. Versions 1–10 migrate descriptor index and label. MODULE is still the actual absolute binary path.
to format 0 and empty extra identity. Older readers reject v11. The public plugin Versions 1–10 migrate to format 0 and empty extra identity. Older readers reject
struct has grown; C/C++ consumers must rebuild and initialize it to zero. v11. The public plugin struct has grown; C/C++ consumers must rebuild and
initialize it to zero.
Modern adapters accept one-input mono effects, default configuration only, with Modern adapters accept one-input mono effects, default configuration only, with
zero controls and automation points. Unsupported modern control metadata is zero controls and automation points. Unsupported modern control metadata is
rejected, not discarded. State/editor/preset changes are not exposed, so no opaque rejected, not discarded. State/editor/preset changes are not exposed, so no
state is serialized. Reloading recreates plugin defaults, which can change after opaque state is serialized. Reloading recreates plugin defaults, which can
a plugin update or with its external configuration. Matching ID/name/layout is change after a plugin update or with its external configuration. Matching
not a binary hash or sound-reproducibility guarantee. See PLUGIN_HOSTING.md. ID/name/layout is not a binary hash or sound-reproducibility guarantee. See
PLUGIN_HOSTING.md.
Metadata validation and inspection never initialize modules. Actual graph Metadata validation and inspection never initialize modules. Actual graph
preparation verifies format, descriptor, stable identity, name and layout before preparation verifies format, descriptor, stable identity, name and layout before
publishing. Missing module metadata survives C load/save; GUI preparation retains publishing. Missing module metadata survives C load/save; GUI preparation
the existing document on failure. No automatic path search or plugin substitution retains the existing document on failure. No automatic path search or plugin
occurs. Current and historical migration fixtures, malformed fields, identity substitution occurs. Current and historical migration fixtures, malformed
mismatch, missing-module and exact-render roundtrips cover this extension. fields, identity mismatch, missing-module and exact-render roundtrips cover this
extension.
## Version 12: track instruments ---
## Version 12: Track Instruments
After CONTROL_MAPS/MAP records and before END: After CONTROL_MAPS/MAP records and before END:
@ -389,25 +437,32 @@ modules; preparing playback verifies binary identity and configuration. Deleting
tracks removes bindings; reordering preserves stable track identities. Undo tracks removes bindings; reordering preserves stable track identities. Undo
snapshots include bindings. Public clients must rebuild for the new spec field. snapshots include bindings. Public clients must rebuild for the new spec field.
## Version 13 internal devices ---
## Version 13 Internal Devices
Format value 4 identifies dependency-free internal devices at `/xtc/internal`. Format value 4 identifies dependency-free internal devices at `/xtc/internal`.
Indices 0/1 bind fixed Soft Keys/Sub Bass instruments; 2/3 are Delay/Reverb Indices 0/1 bind fixed Soft Keys/Sub Bass instruments; 2/3 are Delay/Reverb
effects with three controls and existing automation records. Identity and control effects with three controls and existing automation records. Identity and
ranges are validated without loading libraries. See [BUILTIN_DEVICES.md](BUILTIN_DEVICES.md). control ranges are validated without loading libraries. See
[BUILTIN_DEVICES.md](BUILTIN_DEVICES.md).
## Version 14 instrument parameters ---
## Version 14 Instrument Parameters
Each track INSTRUMENT identity is followed by `INSTRUMENT_CONTROLS count`, Each track INSTRUMENT identity is followed by `INSTRUMENT_CONTROLS count`,
`CONTROL port value_bits` records, `INSTRUMENT_AUTOMATION count` and `CONTROL port value_bits` records, `INSTRUMENT_AUTOMATION count` and
`PARAM tick port mode value_bits` records. Bounds, duplicate points and metadata `PARAM tick port mode value_bits` records. Bounds, duplicate points and metadata
are validated before destination replacement, just as for graph effects. are validated before destination replacement, just as for graph effects. Earlier
Earlier bindings have no controls/automation; v13 internal instruments retain bindings have no controls/automation; v13 internal instruments retain fixed
fixed default sounds. Modern external instruments still require zero controls. default sounds. Modern external instruments still require zero controls.
Instrument tempo follows project tempo. Instrument preset files use this same Instrument tempo follows project tempo. Instrument preset files use this same
schema with exactly one instrument track and no clips; they are metadata only. schema with exactly one instrument track and no clips; they are metadata only.
## Version 15 sample references ---
## Version 15 Sample References
Each instrument parameter block ends with `SAMPLE hex_path_or_dash`. Only the Each instrument parameter block ends with `SAMPLE hex_path_or_dash`. Only the
internal Sample Player (index 4) may carry a nonempty sample path. Paths within internal Sample Player (index 4) may carry a nonempty sample path. Paths within
@ -415,19 +470,23 @@ the project directory are relative on disk and resolve against the project
file's directory; other paths are absolute. Metadata loading does not read WAVs. file's directory; other paths are absolute. Metadata loading does not read WAVs.
Missing/invalid samples remain references and become reported silence during Missing/invalid samples remain references and become reported silence during
preparation. Controls describe root key, whole-file loop, attack, release and preparation. Controls describe root key, whole-file loop, attack, release and
gain. Earlier projects have empty sample paths. Rebuild public API consumers gain. Earlier projects have empty sample paths. Rebuild public API consumers for
for the additional copied `sample_path` field in `xtc_plugin_spec`. the additional copied `sample_path` field in `xtc_plugin_spec`.
## Version 16: arrangement markers ---
## Version 16: Arrangement Markers
After `LIVE_INSTRUMENT` and before `END`, version 16 writes `MARKERS count` After `LIVE_INSTRUMENT` and before `END`, version 16 writes `MARKERS count`
followed by `MARKER tick hex_name` records. Count is 0..128; ticks must be strictly followed by `MARKER tick hex_name` records. Count is 0..128; ticks must be
increasing and at most MAX_TICK. Names use the existing bounded UTF-8 hex encoding. strictly increasing and at most MAX_TICK. Names use the existing bounded UTF-8
Versions 1–15 migrate to an empty marker list. Older builds reject version 16. hex encoding. Versions 1–15 migrate to an empty marker list. Older builds reject
Markers are copied by snapshots and history; the tick is their stable key. version 16. Markers are copied by snapshots and history; the tick is their
stable key.
---
## Version 17: per-track insert chains ## Version 17: per-Track Insert Chains
After MARKERS/MARKER and before END, the writer emits a chain for every track, After MARKERS/MARKER and before END, the writer emits a chain for every track,
in dense track order. Earlier versions migrate to empty chains with unity gain. in dense track order. Earlier versions migrate to empty chains with unity gain.
@ -448,16 +507,16 @@ PARAM tick port step value_bits
Each CHAIN has exactly slot_count INSERT blocks. CONTROLS/PARAMETERS determine Each CHAIN has exactly slot_count INSERT blocks. CONTROLS/PARAMETERS determine
the subsequent CONTROL/PARAM record counts, including zero. Slots are ordered, the subsequent CONTROL/PARAM record counts, including zero. Slots are ordered,
at most eight, and accept mono non-instrument effects only. Enabled is 0/1, at most eight, and accept mono non-instrument effects only. Enabled is 0/1, wet
wet is finite 0–1, output gain finite 0–4; inputs must be 1. The format values is finite 0–1, output gain finite 0–4; inputs must be 1. The format values are
are LADSPA=0, CLAP=1, VST2=2, VST3=3 and Internal=4. Existing plugin specification LADSPA=0, CLAP=1, VST2=2, VST3=3 and Internal=4. Existing plugin specification
validation applies, including descriptor/identity/port/automation bounds and validation applies, including descriptor/identity/port/automation bounds and
20,000–300,000 milli-BPM. Bus chains must be empty. No sample reference is 20,000–300,000 milli-BPM. Bus chains must be empty. No sample reference is
permitted on an effect. Duplicate, missing, reordered or excessive chain permitted on an effect. Duplicate, missing, reordered or excessive chain
records, malformed strings and nonfinite values reject the whole load. records, malformed strings and nonfinite values reject the whole load.
Loading and dependency inspection only validate metadata; no plugin module Loading and dependency inspection only validate metadata; no plugin module is
is opened. Enabled plugins are verified when a render plan is prepared. Chain opened. Enabled plugins are verified when a render plan is prepared. Chain
preset files reuse this schema with exactly one non-bus track and no clips or preset files reuse this schema with exactly one non-bus track and no clips or
instrument. Track insert APIs are declared in `xtc_plugin.h`; see instrument. Track insert APIs are declared in `xtc_plugin.h`; see
[TRACK_INSERTS.md](TRACK_INSERTS.md) for playback restrictions and workflows. [TRACK_INSERTS.md](TRACK_INSERTS.md) for playback restrictions and workflows.

View file

@ -1,44 +1,51 @@
# Persistence and failure recovery # Persistence and Failure Recovery
The project format is v12. Readers migrate v1–v11, validate bounded input The project format is v12. Readers migrate v1–v11, validate bounded input and
and atomically install a candidate only after complete success. Existing tests atomically install a candidate only after complete success. Existing tests cover
cover exact migration/roundtrip, truncations, malformed references, counts, exact migration/roundtrip, truncations, malformed references, counts, IDs,
IDs, nonfinite values and failed GUI/backend installation. Native processors nonfinite values and failed GUI/backend installation. Native processors are
are session-only; saving their graph returns UNSUPPORTED rather than writing session-only; saving their graph returns UNSUPPORTED rather than writing process
process addresses or an unreadable project. See PROJECT_FORMAT.md. addresses or an unreadable project. See PROJECT_FORMAT.md.
Project Save writes a mode-0600 same-directory `NAME.tmp.XXXXXX`, flushes and Project Save writes a mode-0600 same-directory `NAME.tmp.XXXXXX`, flushes and
fsyncs it, closes and renames it over the destination. Before rename, handled fsyncs it, closes and renames it over the destination. Before rename, handled
failures remove the temporary and retain the destination. POSIX rename gives failures remove the temporary and retain the destination. POSIX rename gives
complete old-or-new destination contents during process failure. The containing directory is fsynced after rename. If that sync fails, Save complete old-or-new destination contents during process failure. The containing
reports I/O failure but the complete new file may already be present. Preserve directory is fsynced after rename. If that sync fails, Save reports I/O failure
and validate it before retrying; filesystem/hardware durability still varies. Symlinks are but the complete new file may already be present. Preserve and validate it
before retrying; filesystem/hardware durability still varies. Symlinks are
replaced rather than followed at rename; original file permissions are not kept. replaced rather than followed at rename; original file permissions are not kept.
Recording similarly writes/finalizes a same-directory temporary, then renames Recording similarly writes/finalizes a same-directory temporary, then renames
it. Stop must synchronize callbacks before draining/finalizing. Ring overflow it. Stop must synchronize callbacks before draining/finalizing. Ring overflow
returns an explicit error and may finalize a partial take for manual recovery; returns an explicit error and may finalize a partial take for manual recovery;
I/O failure retains the previous destination. A killed recorder does not finish I/O failure retains the previous destination. A killed recorder does not finish
its WAV header. Source WAV files remain external; references inside the project folder are its WAV header. Source WAV files remain external; references inside the project
saved relative to it. Save As does not collect them. Back up the project and assets together. folder are saved relative to it. Save As does not collect them. Back up the
project and assets together.
## After a crash ---
## After A Crash
1. Preserve the destination and any `.tmp.*` files before attempting repair. 1. Preserve the destination and any `.tmp.*` files before attempting repair.
2. Run `xtc-check --validate project.xtc` for metadata validation without a GUI, 2. Run `xtc-check --validate project.xtc` for metadata validation without a GUI,
display or audio server. Run `xtc-check --inspect project.xtc` to list resolved display or audio server. Run `xtc-check --inspect project.xtc` to list
media/module paths and plugin identities: status 2 means missing/unreadable resolved media/module paths and plugin identities: status 2 means
references, 1 means invalid metadata/I/O, 0 means files are available. Neither missing/unreadable references, 1 means invalid metadata/I/O, 0 means files
command verifies file contents, plugin initialization or realtime preparation. are available. Neither command verifies file contents, plugin initialization
3. A valid destination can be opened normally. If only a temporary contains useful or realtime preparation.
data, copy it to a separate candidate and validate that copy before replacing 3. A valid destination can be opened normally. If only a temporary contains
anything. Partial project files are rejected; there is no salvage parser. useful data, copy it to a separate candidate and validate that copy before
4. An unfinished WAV temporary may have a zero/incorrect data length and needs replacing anything. Partial project files are rejected; there is no salvage
a separate recovery tool or manual header repair on a copy. xtc does not guess parser.
4. An unfinished WAV temporary may have a zero/incorrect data length and needs a
separate recovery tool or manual header repair on a copy. xtc does not guess
its intended length or automatically rename orphan temporaries. its intended length or automatically rename orphan temporaries.
5. Missing assets preserve silent clips. Use Relink selected audio, then Play to 5. Missing assets preserve silent clips. Use Relink selected audio, then Play to
revalidate. For server loss/format changes, Stop/Reconnect and explicit routing revalidate. For server loss/format changes, Stop/Reconnect and explicit
are required. Panic/F9 clears held notes. Failed close retains ownership for retry. routing are required. Panic/F9 clears held notes. Failed close retains
ownership for retry.
There is no autosave, crash journal, automatic orphan recovery or persisted Undo There is no autosave, crash journal, automatic orphan recovery or persisted Undo
history. Existing finalized files are protected against the tested process-level history. Existing finalized files are protected against the tested process-level
@ -51,18 +58,22 @@ draining but before finalization. Complete old/new destinations and explicit
orphan temporaries are verified. It does not cut power, terminate the shared orphan temporaries are verified. It does not cut power, terminate the shared
audio server, simulate a failing filesystem, or qualify disk/device endurance. audio server, simulate a failing filesystem, or qualify disk/device endurance.
## Retained recording prefixes ---
## Retained Recording Prefixes
Recording now defaults to no-clobber publication; the GUI requires an unused Recording now defaults to no-clobber publication; the GUI requires an unused
path. Failed nonempty temporary recordings survive destruction. Query the path. Failed nonempty temporary recordings survive destruction. Query the
recorder recovery path before destruction, or use the GUI's reported path. recorder recovery path before destruction, or use the GUI's reported path.
`xtc-check --recover-recording TEMP NEW.wav` reconstructs a canonical mono `xtc-check --recover-recording TEMP NEW.wav` reconstructs a canonical mono
float32 WAV from complete on-disk frames, ignoring a trailing partial frame. float32 WAV from complete on-disk frames, ignoring a trailing partial frame. It
It never changes the source or overwrites an existing destination. This never changes the source or overwrites an existing destination. This supersedes
supersedes the earlier lack of a WAV recovery tool described above. See the earlier lack of a WAV recovery tool described above. See AUDIO_FILES.md for
AUDIO_FILES.md for format/ownership/size limits and `make test-recording`. format/ownership/size limits and `make test-recording`.
## Plugin load and replacement failures ---
## Plugin Load and Replacement Failures
GUI Open reports an unavailable/incompatible plugin's label and absolute module GUI Open reports an unavailable/incompatible plugin's label and absolute module
path and retains the current document. Preserve the saved file; restore a path and retains the current document. Preserve the saved file; restore a
@ -77,27 +88,32 @@ ID only for the same audio layout. New defaults replace controls/automation;
Undo restores the original configuration. Failed discovery/compile/instantiate Undo restores the original configuration. Failed discovery/compile/instantiate
retains the original graph, controls and history position. Cancel in the generic retains the original graph, controls and history position. Cancel in the generic
parameter editor discards its temporary values. Reset defaults clears automation parameter editor discards its temporary values. Reset defaults clears automation
only when applied. No host recovery can contain a crash or hang in trusted native only when applied. No host recovery can contain a crash or hang in trusted
plugin code; LADSPA has no runtime error-return channel. native plugin code; LADSPA has no runtime error-return channel.
## Save diagnostics ---
`make test-project-diagnostics` deterministically injects flush, file-sync, close, ## Save Diagnostics
rename and directory-sync errors. Pre-rename failures retain the previous bytes
and clean temporary files. Post-rename sync errors leave a complete new file and `make test-project-diagnostics` deterministically injects flush, file-sync,
return failure; there is no rollback after replacement. Load diagnostics identify close, rename and directory-sync errors. Pre-rename failures retain the previous
the expected record and syntax line, or line zero for whole-model/I/O stages. bytes and clean temporary files. Post-rename sync errors leave a complete new
Unknown versions report the supported range (1–12); unknown records/fields are file and return failure; there is no rollback after replacement. Load
rejected without partially changing the current project. diagnostics identify the expected record and syntax line, or line zero for
whole-model/I/O stages. Unknown versions report the supported range (1–12);
unknown records/fields are rejected without partially changing the current
project.
Modern CLAP/VST2/VST3 module identity is preserved in v11 metadata even when the Modern CLAP/VST2/VST3 module identity is preserved in v11 metadata even when the
binary is unavailable. Restore the compatible saved binary path and retry Open. binary is unavailable. Restore the compatible saved binary path and retry Open.
CLAP restart and VST2 I/O-change requests fail/silence subsequent blocks; stop CLAP restart and VST2 I/O-change requests fail/silence subsequent blocks; stop
and rebuild/reload rather than attempting callback-time reconfiguration. Modern and rebuild/reload rather than attempting callback-time reconfiguration. Modern
instances restart at default configuration; no edited opaque state is exposed instances restart at default configuration; no edited opaque state is exposed or
or saved by the initial adapters. Trusted plugin crashes cannot be contained. saved by the initial adapters. Trusted plugin crashes cannot be contained.
## Instrument support ---
## Instrument Support
MIDI tracks can bind CLAP, VST2 and VST3 instruments. In the Track menu, select MIDI tracks can bind CLAP, VST2 and VST3 instruments. In the Track menu, select
**Load instrument plugin** for the selected sequencer track; **Use built-in **Load instrument plugin** for the selected sequencer track; **Use built-in
@ -108,9 +124,9 @@ their own track's instrument. Independent instances feed track gain/pan,
mute/solo, bus routing and automation. Bindings/live target save in v12. mute/solo, bus routing and automation. Bindings/live target save in v12.
Supply the actual trusted Linux binary (including the inner .so of a VST3 Supply the actual trusted Linux binary (including the inner .so of a VST3
bundle). Supported instruments have zero audio inputs, one mono output, bundle). Supported instruments have zero audio inputs, one mono output, float32
float32 processing, zero latency and one note/event input. Default configuration processing, zero latency and one note/event input. Default configuration only:
only: no custom editor, opaque state/preset restoration, modern parameter no custom editor, opaque state/preset restoration, modern parameter
editing/automation, stereo/multichannel instruments or MIDI output. No arbitrary editing/automation, stereo/multichannel instruments or MIDI output. No arbitrary
third-party instrument is qualified by the original ABI fixture tests. third-party instrument is qualified by the original ABI fixture tests.

View file

@ -1,50 +1,60 @@
# 0.1.0 development release notes # 0.1.0 Development Release Notes
This remains a development prototype, not a production-qualified DAW. The local This remains a development prototype, not a production-qualified DAW. The local
release workflow is complete without bumping the application version, creating a release workflow is complete without bumping the application version, creating a
tag or publishing artifacts. The exact source archive SHA-256 and tag or publishing artifacts. The exact source archive SHA-256 and reviewed Git
reviewed Git commit identify a build; version 0.1.0 alone does not identify all commit identify a build; version 0.1.0 alone does not identify all of the
of the development revisions that used that version. development revisions that used that version.
## Included functionality ---
The C99 engine provides bounded graphs, immutable plan publication, stereo audio, ## Included Functionality
MIDI instruments and controller bindings, track/bus mixing, automation and inline
MIDI editing. The C++17 FLTK application adds arrangement, routing, recording,
undo/redo and a generic LADSPA editor. JACK is the only live audio/MIDI backend;
core validation and offline rendering work without it. Native/LADSPA processors
are trusted in-process code. No proprietary energyXT resources are included.
Project format v13 reads versions 1–13 transactionally. Syntax diagnostics identify The C99 engine provides bounded graphs, immutable plan publication, stereo
records and lines. `xtc-check --inspect` lists dependency paths without executing audio, MIDI instruments and controller bindings, track/bus mixing, automation
plugins. Timing/CPU/RSS reports, repeatable stress runs and virtual endurance are and inline MIDI editing. The C++17 FLTK application adds arrangement, routing,
available. Configurable install directories, shared install/remove inventory, recording, undo/redo and a generic LADSPA editor. JACK is the only live
installed MIDI examples and source archive checksum sidecars are also included. audio/MIDI backend; core validation and offline rendering work without it.
Native/LADSPA processors are trusted in-process code. No proprietary energyXT
resources are included.
## Upgrading and removal Project format v13 reads versions 1–13 transactionally. Syntax diagnostics
identify records and lines. `xtc-check --inspect` lists dependency paths without
executing plugins. Timing/CPU/RSS reports, repeatable stress runs and virtual
endurance are available. Configurable install directories, shared install/remove
inventory, installed MIDI examples and source archive checksum sidecars are also
included.
---
## Upgrading and Removal
Back up documents and external media before upgrading. Opening an older project Back up documents and external media before upgrading. Opening an older project
does not rewrite it; saving upgrades it to v12, which older readers may reject. does not rewrite it; saving upgrades it to v12, which older readers may reject.
Use Save As for a separate migrated copy. Moving a project folder preserves media Use Save As for a separate migrated copy. Moving a project folder preserves
stored inside it via relative references. Absolute media and plugin module paths media stored inside it via relative references. Absolute media and plugin module
must be restored separately. There is no bundled plugin/media migration or saved paths must be restored separately. There is no bundled plugin/media migration or
JACK port configuration. See PROJECT_FORMAT.md and RECOVERY.md for exact policies. saved JACK port configuration. See PROJECT_FORMAT.md and RECOVERY.md for exact
policies.
Install to the same layout to repair owned files. Only documented owned filenames Install to the same layout to repair owned files. Only documented owned
are replaced; unrelated files with other names remain. Use the same source filenames are replaced; unrelated files with other names remain. Use the same
version and directory variables for uninstall. It removes owned files and empty source version and directory variables for uninstall. It removes owned files and
owned directories, never recordings or recursively deleting a prefix. A changed empty owned directories, never recordings or recursively deleting a prefix. A
layout or an older release with a different inventory may require removing that changed layout or an older release with a different inventory may require
old version before installing the new one. No database/package-manager registration removing that old version before installing the new one. No
or automatic cleanup of historical inventories is performed. database/package-manager registration or automatic cleanup of historical
inventories is performed.
## Known qualification limits ---
## Known Qualification Limits
Physical devices, multi-hour wall-clock endurance, power-cut behavior and other Physical devices, multi-hour wall-clock endurance, power-cut behavior and other
Linux distributions remain unqualified. There is no disk streaming, crash autosave, Linux distributions remain unqualified. There is no disk streaming, crash
plugin sandbox, latency compensation or stable shared-library ABI. A trusted autosave, plugin sandbox, latency compensation or stable shared-library ABI. A
plugin can allocate, block or crash the process. Metadata inspection checks file trusted plugin can allocate, block or crash the process. Metadata inspection
availability only. See KNOWN_LIMITATIONS.md for the complete list. checks file availability only. See KNOWN_LIMITATIONS.md for the complete list.
The retained graph performance gate has a documented small-case timing alert on The retained graph performance gate has a documented small-case timing alert on
the shared development host; the project timing comparison passed. This alert the shared development host; the project timing comparison passed. This alert
@ -53,56 +63,60 @@ measurements as latency expectations. Sanitizers and virtual endurance qualify
software paths, not hardware timing. software paths, not hardware timing.
Default-configuration CLAP/VST2/VST3 mono effect loaders, common GUI Default-configuration CLAP/VST2/VST3 mono effect loaders, common GUI
discovery/insertion/replacement and v11 format identity are supported. Pinned MIT CLAP and discovery/insertion/replacement and v11 format identity are supported. Pinned
BSD-3-Clause VST3 C headers ship with notices; VST2 uses original compatibility MIT CLAP and BSD-3-Clause VST3 C headers ship with notices; VST2 uses original
declarations. Modern instruments, parameters/state/editors and multichannel compatibility declarations. Modern instruments, parameters/state/editors and
layouts remain unsupported. No third-party modern plugin has been qualified. multichannel layouts remain unsupported. No third-party modern plugin has been
qualified.
CLAP/VST2/VST3 mono instruments are supported, bound to MIDI tracks, with CLAP/VST2/VST3 mono instruments are supported, bound to MIDI tracks, with
sample-offset notes, a saved live target, track mixing, GUI assignment/undo and sample-offset notes, a saved live target, track mixing, GUI assignment/undo and
v12 persistence. v12 persistence. See PLUGIN_HOSTING.md for compatibility and controller/state
See PLUGIN_HOSTING.md for compatibility and controller/state limits. limits.
A flat DAW workspace, shared dark theme, track-creation sidebar, read-only A flat DAW workspace, shared dark theme, track-creation sidebar, read-only
musical transport/track inspector, colored track clips and real MIDI previews are musical transport/track inspector, colored track clips and real MIDI previews
included. New MIDI/audio track commands are undoable and prepare candidates are included. New MIDI/audio track commands are undoable and prepare candidates
before adoption. The default window is 1280 × 800, minimum 1220 × 720. before adoption. The default window is 1280 × 800, minimum 1220 × 720. No
No dependency or project-format change. See WORKSPACE.md and the new dependency or project-format change. See WORKSPACE.md and the new
`modern-session.xtc` example; existing engine/plugin limitations still apply. `modern-session.xtc` example; existing engine/plugin limitations still apply.
A sequencer grid/Snap dropdown, absolute grid snapping for moves and trims, A sequencer grid/Snap dropdown, absolute grid snapping for moves and trims, MIDI
MIDI double-click editing, a piano velocity lane/slider and per-track instrument double-click editing, a piano velocity lane/slider and per-track instrument
dropdowns are included. Snap preferences are UI state; velocity and instrument dropdowns are included. Snap preferences are UI state; velocity and instrument
bindings use the existing v12 format. No new dependency or engine feature is bindings use the existing v12 format. No new dependency or engine feature is
required. See WORKSPACE.md for supported divisions, controller semantics and limits. required. See WORKSPACE.md for supported divisions, controller semantics and
limits.
Edit / Preferences provides an Apply button and `~/.xtc.conf` INI persistence, Edit / Preferences provides an Apply button and `~/.xtc.conf` INI persistence,
configurable plugin search paths and a file library, plus sequencer ruler loop configurable plugin search paths and a file library, plus sequencer ruler loop
selection and sample-boundary playback wrapping. See PREFERENCES.md selection and sample-boundary playback wrapping. See PREFERENCES.md for
for settings, reconnect behavior and looping limits. settings, reconnect behavior and looping limits.
The single-track mixer panel is replaced by a scrolling studio mixer: vertical The single-track mixer panel is replaced by a scrolling studio mixer: vertical
dB faders, flat pan knobs, mute/solo, routing and stereo meters on every track and dB faders, flat pan knobs, mute/solo, routing and stereo meters on every track
bus, plus a pinned master monitor. Selected-track automation remains and bus, plus a pinned master monitor. Selected-track automation remains below
below the strips. No project format or engine changes. Screenshots are refreshed; the strips. No project format or engine changes. Screenshots are refreshed; see
see MIXER.md and screenshots/README.md. MIXER.md and screenshots/README.md.
Searchable Commands (Ctrl+P), editable project BPM, a live track inspector, Searchable Commands (Ctrl+P), editable project BPM, a live track inspector,
session statistics, Fit song and an optional persistent Follow playhead control session statistics, Fit song and an optional persistent Follow playhead control
are included. Numeric input remains intact during display refreshes. are included. Numeric input remains intact during display refreshes. Screenshots
Screenshots are refreshed, including the command palette. See WORKSPACE.md. are refreshed, including the command palette. See WORKSPACE.md.
Dependency-free Soft Keys/Sub Bass instruments and Delay/Reverb effects are Dependency-free Soft Keys/Sub Bass instruments and Delay/Reverb effects are
included, with saved controls and automation. Choose instruments in track headers; included, with saved controls and automation. Choose instruments in track
insert effects on selected graph wires from Edit. See [BUILTIN_DEVICES.md](BUILTIN_DEVICES.md). headers; insert effects on selected graph wires from Edit. See
[BUILTIN_DEVICES.md](BUILTIN_DEVICES.md).
## Audio source editing ---
Audio clips now open a modal waveform editor with native frame selection, ## Audio Source Editing
zoom, source statistics, nondestructive timeline trims, selection export and
save-copy adoption. Local one-step undo supports reverse, polarity inversion, Audio clips now open a modal waveform editor with native frame selection, zoom,
DC removal, linear/equal-power fades and gain/normalization. Silence boundaries, source statistics, nondestructive timeline trims, selection export and save-copy
adoption. Local one-step undo supports reverse, polarity inversion, DC removal,
linear/equal-power fades and gain/normalization. Silence boundaries,
zero-crossing snapping and amplitude attack markers support transactional clip zero-crossing snapping and amplitude attack markers support transactional clip
slicing. Originals are never overwritten; exported media remains available to slicing. Originals are never overwritten; exported media remains available to
project undo/redo. Source editing uses the existing mono downmix and bounded project undo/redo. Source editing uses the existing mono downmix and bounded

View file

@ -1,10 +1,13 @@
# Release preparation (0.1.0 development prototype) # Release Preparation (0.1.0 Development Prototype)
Source packaging, staged install/uninstall and repeatable builds are testable. It does not turn the prototype into a production-qualified DAW. The Source packaging, staged install/uninstall and repeatable builds are testable.
version remains 0.1.0; no tag, external release or binary publication is created It does not turn the prototype into a production-qualified DAW. The version
by `make dist` or `make release`. remains 0.1.0; no tag, external release or binary publication is created by
`make dist` or `make release`.
## Build and package ---
## Build and Package
```sh ```sh
make release make release
@ -24,63 +27,75 @@ a source manifest; stage intended files first and check `git diff --cached`.
Untracked files, ignored build output and .git are excluded. Exported archives Untracked files, ignored build output and .git are excluded. Exported archives
can reproduce themselves without Git using SOURCE_MANIFEST.json. can reproduce themselves without Git using SOURCE_MANIFEST.json.
Archive order, permissions, ownership, gzip filename and timestamps are normalized. Archive order, permissions, ownership, gzip filename and timestamps are
SOURCE_DATE_EPOCH defaults to 0; set an unsigned 32-bit epoch explicitly if desired. normalized. SOURCE_DATE_EPOCH defaults to 0; set an unsigned 32-bit epoch
The archive does not claim to identify a commit: release from a clean checkout explicitly if desired. The archive does not claim to identify a commit: release
and record its commit/hash externally. Do not treat a dirty-tree archive as an from a clean checkout and record its commit/hash externally. Do not treat a
attested release. No artifact is uploaded by these targets. dirty-tree archive as an attested release. No artifact is uploaded by these
targets.
test-release unpacks the source into two different temporary paths, runs release test-release unpacks the source into two different temporary paths, runs release
in each, compares libxtc.a/xtc-check/optional xtc SHA-256 values, and repacks each in each, compares libxtc.a/xtc-check/optional xtc SHA-256 values, and repacks
export to compare archive bytes. This verifies reproducibility for the same host, each export to compare archive bytes. This verifies reproducibility for the same
compiler, flags and dependency versions. It does not promise bit-identical output host, compiler, flags and dependency versions. It does not promise bit-identical
across distributions, compilers or library updates. Compiler/debug/sanitizer output across distributions, compilers or library updates.
build directories are intentionally separate; changing toolchain/flags in place Compiler/debug/sanitizer build directories are intentionally separate; changing
requires a clean directory. Python release tests require version 3.12+. toolchain/flags in place requires a clean directory. Python release tests
require version 3.12+.
## Qualification record ---
## Qualification Record
Run normal core/GUI and live software JACK tests, no-JACK builds and ASan/UBSan. Run normal core/GUI and live software JACK tests, no-JACK builds and ASan/UBSan.
GUI leak tests retain the narrow fontconfig suppression; core tests do not. GUI leak tests retain the narrow fontconfig suppression; core tests do not. TSan
TSan recorder/publication tests complement lifecycle tests. Native API tests recorder/publication tests complement lifecycle tests. Native API tests verify
verify callback failures, cleanup and retirement; they cannot certify arbitrary callback failures, cleanup and retirement; they cannot certify arbitrary
extensions. See BUILD.md and tests/integration/README.md for commands. extensions. See BUILD.md and tests/integration/README.md for commands.
Record versions, archive hashes, installation/consumer checks and measured timing. Record versions, archive hashes, installation/consumer checks and measured
Use LICENSES.md for dependency/license review and PLUGIN_HOSTING.md for the timing. Use LICENSES.md for dependency/license review and PLUGIN_HOSTING.md for
external-hosting decision. Review RECOVERY.md for migration and process-crash the external-hosting decision. Review RECOVERY.md for migration and
semantics. Physical devices, power loss, sustained recording/performance, user process-crash semantics. Physical devices, power loss, sustained
acceptance on other distributions and binary redistribution are still separate recording/performance, user acceptance on other distributions and binary
qualification work. There is no DEB/RPM/AppImage, bundled runtime, autosave, redistribution are still separate qualification work. There is no
external plugin host or stable shared-library extension ABI in this release. DEB/RPM/AppImage, bundled runtime, autosave, external plugin host or stable
shared-library extension ABI in this release.
## Local binary packaging and installed workflow ---
## Local Binary Packaging and Installed Workflow
`make test-package` builds the release, stages installation only in a temporary `make test-package` builds the release, stages installation only in a temporary
DESTDIR, writes a normalized Linux binary/development archive, checks its SHA-256 DESTDIR, writes a normalized Linux binary/development archive, checks its
sidecar and per-file manifest, relocatable pkg-config paths and headless commands. SHA-256 sidecar and per-file manifest, relocatable pkg-config paths and headless
Same-host repeated packaging is byte-identical; dependency versions and toolchain commands. Same-host repeated packaging is byte-identical; dependency versions
still determine binary compatibility. `make test-install` now repeats installation and toolchain still determine binary compatibility. `make test-install` now
to verify repair/upgrade of owned files while preserving an unrelated sentinel, repeats installation to verify repair/upgrade of owned files while preserving an
then compiles both C99 and C++17 installed consumers that create MIDI, save/reopen unrelated sentinel, then compiles both C99 and C++17 installed consumers that
and compare nonzero exact rendering. Removal remains exact and idempotent. create MIDI, save/reopen and compare nonzero exact rendering. Removal remains
exact and idempotent.
USER_MANUAL.md consolidates operation; KNOWN_LIMITATIONS.md lists qualification USER_MANUAL.md consolidates operation; KNOWN_LIMITATIONS.md lists qualification
gaps; MAINTENANCE.md defines the release checklist, regression and compatibility gaps; MAINTENANCE.md defines the release checklist, regression and compatibility
policy. Build diagnostics make no server/plugin calls. There is no asynchronous policy. Build diagnostics make no server/plugin calls. There is no asynchronous
crash uploader or signal-handler cleanup: inspect OS core/debugger evidence on crash uploader or signal-handler cleanup: inspect OS core/debugger evidence on
the control side, preserving project/media recovery paths. No release is publicly the control side, preserving project/media recovery paths. No release is
published by these commands, and human/device/listening checks remain pending. publicly published by these commands, and human/device/listening checks remain
pending.
## Release workflow ---
Installation now shares one explicit file inventory with uninstallation. GNU-style ## Release Workflow
`exec_prefix`, `bindir`, `libdir`, `includedir`, `datarootdir`, `datadir`, `docdir`,
`pkgconfigdir` and the xtc-specific `licensedir`, `examplesdir`, `desktopdir` can be Installation now shares one explicit file inventory with uninstallation.
overridden alongside `PREFIX`/`DESTDIR`. Paths must be absolute POSIX paths; GNU-style `exec_prefix`, `bindir`, `libdir`, `includedir`, `datarootdir`,
spaces are supported. Do not include DESTDIR in a directory variable: staging is `datadir`, `docdir`, `pkgconfigdir` and the xtc-specific `licensedir`,
applied separately and never embedded in pkg-config metadata. Staging refuses `examplesdir`, `desktopdir` can be overridden alongside `PREFIX`/`DESTDIR`.
parent symlinks that escape DESTDIR. Use exactly the same variables for removal. Paths must be absolute POSIX paths; spaces are supported. Do not include DESTDIR
in a directory variable: staging is applied separately and never embedded in
pkg-config metadata. Staging refuses parent symlinks that escape DESTDIR. Use
exactly the same variables for removal.
```sh ```sh
make BUILD_DIR=build/release-check all test gui make BUILD_DIR=build/release-check all test gui
@ -90,18 +105,20 @@ make PREFIX=/usr DESTDIR=/tmp/xtc-stage libdir=/usr/lib64 uninstall
make test-release test-package make test-release test-package
``` ```
Core install includes the static library, public headers, `xtc-check`, pkg-config Core install includes the static library, public headers, `xtc-check`,
metadata, Markdown documentation, all checked-in performance reports, license and pkg-config metadata, Markdown documentation, all checked-in performance reports,
`share/xtc/examples` (a media/plugin-free MIDI demo). GUI install adds the desktop license and `share/xtc/examples` (a media/plugin-free MIDI demo). GUI install
entry and `xtc`. No desktop MIME association is claimed. Package archives keep adds the desktop entry and `xtc`. No desktop MIME association is claimed.
their fixed `/usr` layout and relocatable pkg-config metadata; custom live layouts Package archives keep their fixed `/usr` layout and relocatable pkg-config
are tested separately. Existing owned file symlinks are replaced, not followed; metadata; custom live layouts are tested separately. Existing owned file
installation is atomic per file, not a transaction over the entire prefix. symlinks are replaced, not followed; installation is atomic per file, not a
transaction over the entire prefix.
`make dist` now writes a `.sha256` sidecar as well as the normalized source archive. `make dist` now writes a `.sha256` sidecar as well as the normalized source
Record that hash, the reviewed commit, version and build/dependency conditions archive. Record that hash, the reviewed commit, version and build/dependency
with release logs. `test-release` verifies the hash, builds/tests two fresh source conditions with release logs. `test-release` verifies the hash, builds/tests two
exports and compares artifacts and repacks. It validates exported tracked content, fresh source exports and compares artifacts and repacks. It validates exported
including staged work; it does not attest that the caller's Git tree is clean. tracked content, including staged work; it does not attest that the caller's Git
For public release use a clean reviewed checkout and inspect `git status` first. tree is clean. For public release use a clean reviewed checkout and inspect
RELEASE_NOTES.md supplies upgrade guidance and remaining qualification limits. `git status` first. RELEASE_NOTES.md supplies upgrade guidance and remaining
qualification limits.

View file

@ -1,10 +1,12 @@
# Engine correctness and reliability # Engine Correctness and Reliability
The existing engine was audited rather than extended with musical features. The The existing engine was audited rather than extended with musical features. The
review covers graph edits/compilation, offline DSP, plan publication, JACK review covers graph edits/compilation, offline DSP, plan publication, JACK
callbacks/lifecycle, audio/MIDI source plans and the recorder handoff. callbacks/lifecycle, audio/MIDI source plans and the recorder handoff.
## Ownership and synchronization ---
## Ownership and Synchronization
| Resource | Owner and lifetime | | Resource | Owner and lifetime |
| --- | --- | | --- | --- |
@ -24,7 +26,9 @@ The public realtime API is SPSC: multiple producers, simultaneous process calls,
collection from arbitrary threads, and destruction during processing are not collection from arbitrary threads, and destruction during processing are not
supported. Atomics do not remove those ownership requirements. supported. Atomics do not remove those ownership requirements.
## Bounded callback work ---
## Bounded Callback Work
Compilation, topological sorting, asset I/O, allocation and native preparation Compilation, topological sorting, asset I/O, allocation and native preparation
are control-thread operations. Graph processing traverses at most 256 nodes, are control-thread operations. Graph processing traverses at most 256 nodes,
@ -50,13 +54,15 @@ callbacks are trusted: the host cannot prevent their allocation, blocking,
crashes or excessive runtime. Maximum supported capacities are safety bounds, crashes or excessive runtime. Maximum supported capacities are safety bounds,
not a promise that every workload meets its audio deadline. not a promise that every workload meets its audio deadline.
## Findings and regression coverage ---
## Findings and Regression Coverage
Graph rendering previously mixed the left output before reading right-channel Graph rendering previously mixed the left output before reading right-channel
sources. With external input equal to the left output, the right pass could sources. With external input equal to the left output, the right pass could read
read already processed samples (gain applied twice). Both passes now finish already processed samples (gain applied twice). Both passes now finish source
source reads before either output is written. Tests check mono input aliasing reads before either output is written. Tests check mono input aliasing and
and cross-channel source aliasing against exact expected samples. Output buffers cross-channel source aliasing against exact expected samples. Output buffers
must not overlap each other. A redundant assignment in missing-asset preparation must not overlap each other. A redundant assignment in missing-asset preparation
was also removed after Clang's dead-store diagnostic; behavior is unchanged. was also removed after Clang's dead-store diagnostic; behavior is unchanged.
@ -67,20 +73,23 @@ that failed replacement preserves output and revisions, and counts both stereo
native instances through retirement, collection and destruction of a pending native instances through retirement, collection and destruction of a pending
plan. It repeats this lifecycle 100 times. A maximum 256-node reverse-insertion plan. It repeats this lifecycle 100 times. A maximum 256-node reverse-insertion
chain checks dependency scheduling, long-cycle rejection/rollback and compiled chain checks dependency scheduling, long-cycle rejection/rollback and compiled
snapshot independence. The pthread handoff test submits 2000 native graphs snapshot independence. The pthread handoff test submits 2000 native graphs while
while processing, checks whole-block output/revision consistency, asserts that processing, checks whole-block output/revision consistency, asserts that
prepare/destruction run on the control thread, and balances all native lifetimes prepare/destruction run on the control thread, and balances all native lifetimes
after joining the consumer. after joining the consumer.
Existing backend fault tests cover repeated start/stop, partial registration, Existing backend fault tests cover repeated start/stop, partial registration,
callback setup, open/activate/deactivate/close failures and retries, server loss, callback setup, open/activate/deactivate/close failures and retries, server
rate/buffer changes, missing buffers, MIDI disconnect/panic, and recorder detach. loss, rate/buffer changes, missing buffers, MIDI disconnect/panic, and recorder
Other suites cover deterministic variable-block DSP, finite-output handling, detach. Other suites cover deterministic variable-block DSP, finite-output
project errors, record-ring saturation and concurrent draining, and crash/write handling, project errors, record-ring saturation and concurrent draining, and
failure preservation. No critical realtime safety defect remains known within crash/write failure preservation. No critical realtime safety defect remains
these contracts; these tests are evidence for tested paths, not a formal proof. known within these contracts; these tests are evidence for tested paths, not a
formal proof.
## Reproducing the audit ---
## Reproducing the Audit
From the repository root: From the repository root:
@ -92,22 +101,22 @@ make BUILD_DIR=build/audit test-gui test-gui-jack test-jack test-jack-audio test
make BUILD_DIR=build/nojack PKG_CONFIG=false all test-reliability gui make BUILD_DIR=build/nojack PKG_CONFIG=false all test-reliability gui
``` ```
`test-sanitize` needs installed Clang and runs core, allocation-failure and recovery `test-sanitize` needs installed Clang and runs core, allocation-failure and
tests with ASan/UBSan and Linux's default ASan leak checking. `analyze` uses recovery tests with ASan/UBSan and Linux's default ASan leak checking. `analyze`
Clang's path-sensitive analyzer on every core translation unit, including JACK uses Clang's path-sensitive analyzer on every core translation unit, including
when its development files are available, and fails on diagnostics. Python and JACK when its development files are available, and fails on diagnostics. Python
Clang are development tools, not new runtime dependencies. The allocation guard and Clang are development tools, not new runtime dependencies. The allocation
requires a GNU-compatible linker. Live tests require an existing JACK-compatible guard requires a GNU-compatible linker. Live tests require an existing
server; GUI tests use installed FLTK and Xvfb. No target installs dependencies or JACK-compatible server; GUI tests use installed FLTK and Xvfb. No target
changes server/system configuration. installs dependencies or changes server/system configuration.
Verification on GCC 16.2.1 / Clang 23.1.3 passed normal and no-JACK builds, Verification on GCC 16.2.1 / Clang 23.1.3 passed normal and no-JACK builds, all
all core tests, ASan/UBSan/leak checks, Clang static analysis, GUI/mock and live core tests, ASan/UBSan/leak checks, Clang static analysis, GUI/mock and live
JACK integrations, plus GCC TSan on the pthread native-publication and recorder JACK integrations, plus GCC TSan on the pthread native-publication and recorder
concurrency tests. The GUI also passed ASan/UBSan with the existing concurrency tests. The GUI also passed ASan/UBSan with the existing
`tests/integration/lsan.supp` fontconfig-only suppression; core tests use no leak `tests/integration/lsan.supp` fontconfig-only suppression; core tests use no
suppression. The live graph test observed both replacement gains, ten restart leak suppression. The live graph test observed both replacement gains, ten
cycles and zero xruns during its five-second software run. restart cycles and zero xruns during its five-second software run.
Physical audio devices, hard realtime scheduling, multi-hour endurance and Physical audio devices, hard realtime scheduling, multi-hour endurance and
arbitrary third-party native callbacks remain unqualified. Sanitizers change arbitrary third-party native callbacks remain unqualified. Sanitizers change
@ -115,7 +124,9 @@ timing and should not be used to measure production realtime performance. See
PERFORMANCE.md for workload measurements, RECOVERY.md for process-crash limits, PERFORMANCE.md for workload measurements, RECOVERY.md for process-crash limits,
and NATIVE_API.md for the trusted extension boundary. and NATIVE_API.md for the trusted extension boundary.
## Transition ownership ---
## Transition Ownership
Source fade configuration is written before publication and immutable during Source fade configuration is written before publication and immutable during
rendering. The master ramp and stop-observed flag belong to the audio consumer rendering. The master ramp and stop-observed flag belong to the audio consumer
@ -126,12 +137,15 @@ voices/flags. A completed callback continues silence/holds position until that
deactivation. No callback waits or calls JACK control functions. Immediate deactivation. No callback waits or calls JACK control functions. Immediate
stop/close handles faults or missing callbacks. See AUDIO_QUALITY.md. stop/close handles faults or missing callbacks. See AUDIO_QUALITY.md.
## Repeatable development checks ---
`make check-development` combines normal tests, capacity/endurance, deterministic ## Repeatable Development Checks
benchmark-gate tests, static analysis and ASan/UBSan (including scale and soak).
`make test-stress` repeats project/backend/publication faults under CPU contention; `make check-development` combines normal tests, capacity/endurance,
`make test-stress-plugins` adds the original LADSPA failure fixture. The reliability deterministic benchmark-gate tests, static analysis and ASan/UBSan (including
callback guard now also rejects common host file I/O and blocking mutex/condition scale and soak). `make test-stress` repeats project/backend/publication faults
calls. It audits directly linked host code, not arbitrary dynamic-plugin internals. under CPU contention; `make test-stress-plugins` adds the original LADSPA
See PERFORMANCE.md for exact workloads, metrics, profiles and qualification limits. failure fixture. The reliability callback guard now also rejects common host
file I/O and blocking mutex/condition calls. It audits directly linked host
code, not arbitrary dynamic-plugin internals. See PERFORMANCE.md for exact
workloads, metrics, profiles and qualification limits.

View file

@ -1,15 +1,15 @@
# Modular routing workflow # Modular Routing Workflow
Open Modular Graph (Ctrl+3). Select a node or wire to inspect it in the feedback Open Modular Graph (Ctrl+3). Select a node or wire to inspect it in the feedback
line. Nodes have stable IDs, role labels and distinct colors: teal sources, line. Nodes have stable IDs, role labels and distinct colors: teal sources,
purple gain/bus nodes, blue processors/meters and red output sums. LADSPA nodes purple gain/bus nodes, blue processors/meters and red output sums. LADSPA nodes
also show the saved plugin label. Track names remain in Sequencer/Mixer; the also show the saved plugin label. Track names remain in Sequencer/Mixer; the
Audio mix and MIDI mix graph sources represent their shared mix buffers, not Audio mix and MIDI mix graph sources represent their shared mix buffers, not
individual tracks. MIDI mix is the internal sine-instrument mix; its graph individual tracks. MIDI mix is the internal sine-instrument mix; its graph ports
ports carry audio, not MIDI events. Native nodes are trusted extensions. carry audio, not MIDI events. Native nodes are trusted extensions.
Wires show arrows from right-hand audio outputs to left-hand inputs. Selecting Wires show arrows from right-hand audio outputs to left-hand inputs. Selecting a
a node highlights all its incoming/outgoing wires; selecting a wire shows its node highlights all its incoming/outgoing wires; selecting a wire shows its
endpoint IDs. Inputs sum all connected signals without normalization; fan-out endpoint IDs. Inputs sum all connected signals without normalization; fan-out
sends the same signal to each destination. Multiple output nodes sum into the sends the same signal to each destination. Multiple output nodes sum into the
engine output. Gain/bus nodes are ordinary summing gain processors, not a new engine output. Gain/bus nodes are ordinary summing gain processors, not a new
@ -31,6 +31,8 @@ path is not a substitute for stopping playback or muting a track. The graph
models one audio lane per port; stereo compilation processes paired independent models one audio lane per port; stereo compilation processes paired independent
lanes. It does not expose separate left/right, MIDI or control wires. lanes. It does not expose separate left/right, MIDI or control wires.
---
## Commands ## Commands
| Action | Operation | | Action | Operation |
@ -58,11 +60,13 @@ allocates a fresh stable node ID and is **one** semantic edit/undo step. It
preserves all other edges. Failure (including capacity, a missing/stale wire or preserves all other edges. Failure (including capacity, a missing/stale wire or
backend refusal) keeps the previous graph and plan. It introduces no new node backend refusal) keeps the previous graph and plan. It introduces no new node
type or project record: v8 persistence already represents the resulting graph. type or project record: v8 persistence already represents the resulting graph.
One-level stereo mix buses are added before shared graph sources (see One-level stereo mix buses are added before shared graph sources (see MIXER.md).
MIXER.md). Track templates, folders and independent per-track/bus graph inserts Track templates, folders and independent per-track/bus graph inserts remain
remain deferred because graph sources still expose shared mixes. deferred because graph sources still expose shared mixes.
## Transactions, cycles and realtime ownership ---
## Transactions, Cycles and Realtime Ownership
The public C `xtc_graph_apply_edit` command constructs a private candidate. The public C `xtc_graph_apply_edit` command constructs a private candidate.
`XTC_EDIT_INSERT_GAIN` uses `data.connection` and returns the fresh ID through `XTC_EDIT_INSERT_GAIN` uses `data.connection` and returns the fresh ID through
@ -77,34 +81,37 @@ The GUI clones its project and compiles/publishes the exact candidate through
the existing backend before replacing the displayed model/history. When running, the existing backend before replacing the displayed model/history. When running,
a complete immutable plan is adopted at a block boundary; the control thread a complete immutable plan is adopted at a block boundary; the control thread
collects acknowledged retired plans. The feedback line distinguishes prepared, collects acknowledged retired plans. The feedback line distinguishes prepared,
queued and active revisions. A queued graph can appear before it becomes audible. queued and active revisions. A queued graph can appear before it becomes
The single pending slot can reject a busy edit with CAPACITY: wait for adoption audible. The single pending slot can reject a busy edit with CAPACITY: wait for
and retry. There is no hidden unbounded edit queue or callback mutation. Normal adoption and retry. There is no hidden unbounded edit queue or callback
graph edits can run during playback; undo/redo uses the existing stopped-plan mutation. Normal graph edits can run during playback; undo/redo uses the
installation contract. Recording still blocks semantic graph edits. Plan swaps existing stopped-plan installation contract. Recording still blocks semantic
may reset oscillator/plugin state and are not crossfaded. graph edits. Plan swaps may reset oscillator/plugin state and are not
crossfaded.
`xtc_graph_analyze` computes input/output edge counts, longest-path level, `xtc_graph_analyze` computes input/output edge counts, longest-path level,
rendered-output reachability and external-input fallback in dense-node order. rendered-output reachability and external-input fallback in dense-node order. It
It is a bounded, allocation-free **control-thread** API, not a callback service. is a bounded, allocation-free **control-thread** API, not a callback service. It
It never loads plugins. It honors explicit outputs; legacy terminal fallback never loads plugins. It honors explicit outputs; legacy terminal fallback
applies only when there are no explicit output nodes and the caller has not applies only when there are no explicit output nodes and the caller has not
required them. Errors leave result storage/count unchanged. The GUI caches this required them. Errors leave result storage/count unchanged. The GUI caches this
analysis when binding a graph; drawing reads cached metadata only. It uses analysis when binding a graph; drawing reads cached metadata only. It uses
public C snapshots/commands and never engine internals or callback storage. public C snapshots/commands and never engine internals or callback storage.
## Verification and limits ---
## Verification and Limits
`make test-routing` checks branch reachability, external input, levels, capacity `make test-routing` checks branch reachability, external input, levels, capacity
rollback, exact unity-insertion rendering and complete plan handoff. `make test` rollback, exact unity-insertion rendering and complete plan handoff. `make test`
also retains concurrent publication/lifecycle tests. also retains concurrent publication/lifecycle tests. `make test-reliability`
`make test-reliability` retains the callback-allocation guards. retains the callback-allocation guards. `make test-gui-plugins` checks selection
`make test-gui-plugins` checks selection diagnostics, unique placement/layout, diagnostics, unique placement/layout, failed backend publication, atomic
failed backend publication, atomic insertion undo/redo and keyboard focus. insertion undo/redo and keyboard focus. `make test-gui-jack`
`make test-gui-jack` inserts/disconnects/restores real routes during live software inserts/disconnects/restores real routes during live software playback and
playback and checks engine revision acknowledgements and cycle rejection. checks engine revision acknowledgements and cycle rejection.
Physical routing/listening and hardware endurance remain unqualified. This is Physical routing/listening and hardware endurance remain unqualified. This is an
an audio-lane DAG editor with a default 64-node project capacity (standalone audio-lane DAG editor with a default 64-node project capacity (standalone C
C graphs up to 256), no zoom/lasso or persisted layout, no latency compensation graphs up to 256), no zoom/lasso or persisted layout, no latency compensation
and no plugin crash isolation. Unconnected branches are diagnosed, not pruned. and no plugin crash isolation. Unconnected branches are diagnosed, not pruned.

View file

@ -1,8 +1,8 @@
# Track inserts # Track Inserts
The sequencer's **Create** dropdown groups track creation, WAV import and The sequencer's **Create** dropdown groups track creation, WAV import and
instrument loading. **View → Track inspector** opens the optional inspector; instrument loading. **View → Track inspector** opens the optional inspector; it
it is hidden when the application starts to leave more room for editing. is hidden when the application starts to leave more room for editing.
Project format 17 stores up to eight effect slots per track, their order, Project format 17 stores up to eight effect slots per track, their order,
bypass, wet amount and chain output gain. Formats 1–16 read with empty chains. bypass, wet amount and chain output gain. Formats 1–16 read with empty chains.
@ -17,39 +17,41 @@ chain; plugin identity and layout are checked when preparing playback.
Playback prepares an independent serial graph for each affected track. Audio Playback prepares an independent serial graph for each affected track. Audio
clips on the same track sum before its effects; instrument and scheduled MIDI clips on the same track sum before its effects; instrument and scheduled MIDI
contributions also pass through their track graph. Effects run before track contributions also pass through their track graph. Effects run before track and
and bus gain/pan, and meters observe their output. Wet/dry uses parallel gain bus gain/pan, and meters observe their output. Wet/dry uses parallel gain
branches. Internal effect history resets at timeline discontinuities. branches. Internal effect history resets at timeline discontinuities.
Each renderer owns its plans and preallocated raw buffers (up to 256 KiB per Each renderer owns its plans and preallocated raw buffers (up to 256 KiB per
affected track); processing uses blocks of at most 256 frames, with no host affected track); processing uses blocks of at most 256 frames, with no host
allocation or I/O. Mono effect adapters require zero reported latency. External allocation or I/O. Mono effect adapters require zero reported latency. External
plugin state on seeks follows the existing host contract. Bus inserts and plugin state on seeks follows the existing host contract. Bus inserts and tracks
tracks containing both audio assets and MIDI reject preparation explicitly. containing both audio assets and MIDI reject preparation explicitly. Live
Live unassigned sine monitoring remains a separate master input. unassigned sine monitoring remains a separate master input.
Open **View → Track inserts** (or Ctrl+5 for the selected track) and choose a track. **Add effect** offers Open **View → Track inserts** (or Ctrl+5 for the selected track) and choose a
internal Delay/Reverb and the plugin library. The left rack shows signal order; track. **Add effect** offers internal Delay/Reverb and the plugin library. The
the graph displays the actual generated graph compiled by playback. Wiring in left rack shows signal order; the graph displays the actual generated graph
this view is read-only: edit the rack to preserve the serial chain. The global compiled by playback. Wiring in this view is read-only: edit the rack to
Modular Graph remains independent. A failed plugin probe or playback preparation preserve the serial chain. The global Modular Graph remains independent. A
preserves the current project; successful additions are undoable. failed plugin probe or playback preparation preserves the current project;
successful additions are undoable.
The library searches configured Preferences paths recursively (including VST3 The library searches configured Preferences paths recursively (including VST3
bundle contents), with case-insensitive search and format filters. LADSPA and bundle contents), with case-insensitive search and format filters. LADSPA and
VST2 both use `.so`, so those filters share candidates until inspection identifies VST2 both use `.so`, so those filters share candidates until inspection
the ABI. Listing files executes no plugin code. Only inspect trusted binaries. identifies the ABI. Listing files executes no plugin code. Only inspect trusted
binaries.
Select a slot in the rack, then use **Up**, **Down**, **Bypass** or **Remove**. Select a slot in the rack, then use **Up**, **Down**, **Bypass** or **Remove**.
The selection follows reordered effects. Bypassed slots retain their settings The selection follows reordered effects. Bypassed slots retain their settings
and appear marked in the rack; their nodes are omitted from the compiled graph. and appear marked in the rack; their nodes are omitted from the compiled graph.
Each successful operation creates one project undo step. Each successful operation creates one project undo step.
**Inspect / edit** verifies the selected plugin and opens its identity and generic **Inspect / edit** verifies the selected plugin and opens its identity and
controls. Reset defaults clears parameter automation on Apply. Cancel leaves the generic controls. Reset defaults clears parameter automation on Apply. Cancel
project unchanged. Internal effects and LADSPA expose controls; modern adapters leaves the project unchanged. Internal effects and LADSPA expose controls;
currently expose none and run default configurations. Native plugin editor modern adapters currently expose none and run default configurations. Native
windows and opaque plugin-state chunks are not implemented. plugin editor windows and opaque plugin-state chunks are not implemented.
**Preset → Save chain / Load chain** reuses the atomic versioned project file **Preset → Save chain / Load chain** reuses the atomic versioned project file
container with one track. Order, bypass, wet amounts, output gain and exposed container with one track. Order, bypass, wet amounts, output gain and exposed
@ -67,19 +69,19 @@ insert layouts, latency compensation and gapless live insert edits are future
work. Editing stops transport before preparing the replacement plan. work. Editing stops transport before preparing the replacement plan.
The CLI `xtc-check --inspect project.xtc` lists insert format, slot order, The CLI `xtc-check --inspect project.xtc` lists insert format, slot order,
bypass/wet settings and missing binary references without loading plugins. bypass/wet settings and missing binary references without loading plugins. VST3
VST3 processing supplies a fixed output-parameter sink (64 IDs, final point per processing supplies a fixed output-parameter sink (64 IDs, final point per
ID/block) for plugins that emit meter/parameter notifications; notifications are ID/block) for plugins that emit meter/parameter notifications; notifications are
not recorded or exposed as controls. not recorded or exposed as controls.
Host qualification on 2026-11-06 used the installed Linux x86_64 ZamEQ2, Host qualification on 2026-11-06 used the installed Linux x86_64 ZamEQ2,
ZamAutoSat and ZamDelay VST3 binaries under `/usr/lib/vst3`. All three were ZamAutoSat and ZamDelay VST3 binaries under `/usr/lib/vst3`. All three were
prepared together, rendered with nonzero finite output and exported to WAV. prepared together, rendered with nonzero finite output and exported to WAV. This
This verifies those default mono configurations, not arbitrary plugins or verifies those default mono configurations, not arbitrary plugins or physical
physical hardware. The repository fixtures cover LADSPA, CLAP, VST2 and VST3 hardware. The repository fixtures cover LADSPA, CLAP, VST2 and VST3 insert
insert adoption and VST3 output notification bounds. Internal-plan allocation adoption and VST3 output notification bounds. Internal-plan allocation failure
failure and callback guards, sanitizers, GUI/JACK lifecycle and install checks and callback guards, sanitizers, GUI/JACK lifecycle and install checks cover
cover host ownership and cleanup. host ownership and cleanup.
Reproduce focused verification with: Reproduce focused verification with:

View file

@ -1,36 +1,44 @@
# xtc user manual (0.1.0 development build) # xtc User Manual (0.1.0 Development Build)
xtc is a bounded Linux music-making prototype. Start with a copy of an important xtc is a bounded Linux music-making prototype. Start with a copy of an important
project and keep backups of its WAV media. Supported limits and unfinished areas project and keep backups of its WAV media. Supported limits and unfinished areas
are listed in KNOWN_LIMITATIONS.md; no production/device qualification is implied. are listed in KNOWN_LIMITATIONS.md; no production/device qualification is
implied.
## Build or install ---
## Build or Install
Follow BUILD.md and LINUX.md. Core: `make all test`; GUI: `make gui`, then Follow BUILD.md and LINUX.md. Core: `make all test`; GUI: `make gui`, then
`./build/xtc`. Use an existing JACK-compatible server and reconnect with F6. `./build/xtc`. Use an existing JACK-compatible server and reconnect with F6.
Connect `midi_in` to a controller, `input` to mono capture and `output` / Connect `midi_in` to a controller, `input` to mono capture and `output` /
`output_right` to left/right playback using your JACK routing application. `output_right` to left/right playback using your JACK routing application. No
No server, physical routing or system configuration is changed automatically. server, physical routing or system configuration is changed automatically.
`xtc --version`, `xtc --help` and `xtc --diagnose` need no display/server. `xtc --version`, `xtc --help` and `xtc --diagnose` need no display/server.
For a source install stage with `make install-gui DESTDIR="$PWD/build/stage" For a source install stage with
PREFIX=/usr`, inspect the stage, and use your normal deployment process. A user `make install-gui DESTDIR="$PWD/build/stage" PREFIX=/usr`, inspect the stage,
prefix is supported (for example an absolute path to your `.local` folder). and use your normal deployment process. A user prefix is supported (for example
`make uninstall` with the same DESTDIR/PREFIX removes only that version's known an absolute path to your `.local` folder). `make uninstall` with the same
files. See LINUX.md for package extraction, dependencies and removal. Never use DESTDIR/PREFIX removes only that version's known files. See LINUX.md for package
a release built for a different system ABI without rebuilding/testing it. extraction, dependencies and removal. Never use a release built for a different
system ABI without rebuilding/testing it.
## Make a short project ---
## Make A Short Project
1. New/Open starts in Sequencer. Add track; use F2 to name it. 1. New/Open starts in Sequencer. Add track; use F2 to name it.
2. Add MIDI clip, select it and open Piano roll (Ctrl+4). Left-click to draw 2. Add MIDI clip, select it and open Piano roll (Ctrl+4). Left-click to draw
notes, drag to move, drag a right edge to resize, Delete/right-click to remove. notes, drag to move, drag a right edge to resize, Delete/right-click to
remove.
3. Alternatively Import WAV (Ctrl+I). It creates/routes an Audio clips source; 3. Alternatively Import WAV (Ctrl+I). It creates/routes an Audio clips source;
supported WAV formats and preload limits are in AUDIO_FILES.md. supported WAV formats and preload limits are in AUDIO_FILES.md.
4. Drag clips to arrange them and edges to trim. View/Snap sets the timeline 4. Drag clips to arrange them and edges to trim. View/Snap sets the timeline
grid; +/- zoom buttons and scrollbars navigate. Ctrl+D duplicates a clip after grid; +/- zoom buttons and scrollbars navigate. Ctrl+D duplicates a clip
its end, retaining its complete source. F2 names clips; right-click offers after its end, retaining its complete source. F2 names clips; right-click
duplicate/rename/delete. Alt+Up/Down with timeline focus reorders tracks. offers duplicate/rename/delete. Alt+Up/Down with timeline focus reorders
tracks.
5. Play/F5 starts; Pause/F7 or Stop/F8 leaves the position for resuming. Home 5. Play/F5 starts; Pause/F7 or Stop/F8 leaves the position for resuming. Home
returns to start while stopped (or stops first); click the ruler to seek. returns to start while stopped (or stops first); click the ruler to seek.
Recording uses Stop, not Pause. F9 Panic releases the instrument. Recording uses Stop, not Pause. F9 Panic releases the instrument.
@ -38,22 +46,27 @@ a release built for a different system ABI without rebuilding/testing it.
Click to add points, right-click to remove. CONTROL.md explains timing and Click to add points, right-click to remove. CONTROL.md explains timing and
live CC7 volume, CC10 pan and CC64 sustain. live CC7 volume, CC10 pan and CC64 sustain.
7. Save (Ctrl+S), close, reopen and continue editing. Ctrl+Z undoes and 7. Save (Ctrl+S), close, reopen and continue editing. Ctrl+Z undoes and
Ctrl+Shift+Z/Ctrl+Y redoes. New/Open resets history after unsaved-change handling. Ctrl+Shift+Z/Ctrl+Y redoes. New/Open resets history after unsaved-change
handling.
Modular Graph (Ctrl+3) exposes routing. Drag output to input to connect, select a Modular Graph (Ctrl+3) exposes routing. Drag output to input to connect, select
wire/node and Delete to remove; cycles and invalid ports are rejected. Keep an a wire/node and Delete to remove; cycles and invalid ports are rejected. Keep an
Output route for playback. GUI changes show queued/active graph status. Graph Output route for playback. GUI changes show queued/active graph status. Graph
positions are session-only; music/routing/automation are saved. Edits that prepare positions are session-only; music/routing/automation are saved. Edits that
new plans stop playback; disk/compilation may pause the GUI, never run in audio. prepare new plans stop playback; disk/compilation may pause the GUI, never run
in audio.
## Record and retain media ---
Choose a project folder and save the project there first. Record/Shift+F5 asks for ## Record and Retain Media
an unused WAV path. Stop finalizes the take and inserts it. Existing destinations
are protected, including a file created while recording. Undo/removing a take Choose a project folder and save the project there first. Record/Shift+F5 asks
removes its reference, never the recorded file. Imported external files are also for an unused WAV path. Stop finalizes the take and inserts it. Existing
never deleted. WAVs inside the save folder become relative references; move the destinations are protected, including a file created while recording.
folder and media together. Save As preserves the same source media, not a copy. Undo/removing a take removes its reference, never the recorded file. Imported
external files are also never deleted. WAVs inside the save folder become
relative references; move the folder and media together. Save As preserves the
same source media, not a copy.
On write failure the GUI reports a retained path. Keep it and recover complete On write failure the GUI reports a retained path. Keep it and recover complete
frames to a different, unused destination: frames to a different, unused destination:
@ -62,62 +75,74 @@ frames to a different, unused destination:
xtc-check --recover-recording take.wav.tmp.XXXXXX recovered.wav xtc-check --recover-recording take.wav.tmp.XXXXXX recovered.wav
``` ```
Only xtc's canonical float32 mono temporary format is salvaged. Incomplete trailing Only xtc's canonical float32 mono temporary format is salvaged. Incomplete
frames are omitted. The source is retained. Import/relink the recovered WAV. trailing frames are omitted. The source is retained. Import/relink the recovered
Missing/unreadable media is silent and reported; select the clip and use File/ WAV. Missing/unreadable media is silent and reported; select the clip and use
Relink selected audio. See RECOVERY.md for interruption, disk-full and power-loss File/ Relink selected audio. See RECOVERY.md for interruption, disk-full and
limits. Close/project operations are refused while recording; finish the take first. power-loss limits. Close/project operations are refused while recording; finish
the take first.
## Trusted effects and sources ---
File/Load LADSPA plugin lists compatible descriptor indices from the chosen `.so`. ## Trusted Effects and Sources
Loading executes native code. Select a documented compatible mono plugin, connect
its graph ports, then use Edit/Selected plugin control. Edit/Selected plugin
automation adds/removes tick-based linear or step points. Control names/ranges
appear in the dialogs. Module identity/control values/lanes save with the project;
module binaries and DSP history do not. The initial amp/inverter/sine set and
unsupported layouts are listed in PLUGIN_HOSTING.md. Missing plugins reject GUI
Open with identity/path information and preserve the saved/current documents.
Restore the compatible module at that path and retry. Plugins are not sandboxed.
## Diagnose a problem File/Load LADSPA plugin lists compatible descriptor indices from the chosen
`.so`. Loading executes native code. Select a documented compatible mono plugin,
connect its graph ports, then use Edit/Selected plugin control. Edit/Selected
plugin automation adds/removes tick-based linear or step points. Control
names/ranges appear in the dialogs. Module identity/control values/lanes save
with the project; module binaries and DSP history do not. The initial
amp/inverter/sine set and unsupported layouts are listed in PLUGIN_HOSTING.md.
Missing plugins reject GUI Open with identity/path information and preserve the
saved/current documents. Restore the compatible module at that path and retry.
Plugins are not sandboxed.
Run `xtc-check --diagnose` and capture the version, architecture, compiled features, ---
GUI error text and steps to reproduce. `xtc-check --validate song.xtc` checks
metadata without opening plugins/media; it does not certify playback resources.
Check JACK routing, sample rate/format and xrun/process status in the GUI. After
server loss/rate change stop/reconnect and retry. Keep the original project,
media and retained temporary files before recovery attempts.
A native plugin can crash/hang the process. There is no crash-isolation guarantee. ## Diagnose A Problem
Use the operating system's core-dump/debugger tools if available; xtc does not
install signal handlers, change core limits or upload crash reports. Debugger/
core files may contain media paths and audio memory; share only what you intend.
Report bugs following MAINTENANCE.md, including whether the issue reproduces
without plugins. Physical listening and independent human install/usability
acceptance remain pending.
## MIDI composition Run `xtc-check --diagnose` and capture the version, architecture, compiled
features, GUI error text and steps to reproduce. `xtc-check --validate song.xtc`
checks metadata without opening plugins/media; it does not certify playback
resources. Check JACK routing, sample rate/format and xrun/process status in the
GUI. After server loss/rate change stop/reconnect and retry. Keep the original
project, media and retained temporary files before recovery attempts.
A native plugin can crash/hang the process. There is no crash-isolation
guarantee. Use the operating system's core-dump/debugger tools if available; xtc
does not install signal handlers, change core limits or upload crash reports.
Debugger/ core files may contain media paths and audio memory; share only what
you intend. Report bugs following MAINTENANCE.md, including whether the issue
reproduces without plugins. Physical listening and independent human
install/usability acceptance remain pending.
---
## MIDI Composition
In Piano roll, use the Notes menu for grid (including triplets), channel/pitch In Piano roll, use the Notes menu for grid (including triplets), channel/pitch
filters, velocity/channel, quantization, transposition and pattern repetition. filters, velocity/channel, quantization, transposition and pattern repetition.
Click selects a single note; Ctrl+A selects all filtered notes. Ctrl+D repeats Click selects a single note; Ctrl+A selects all filtered notes. Ctrl+D repeats
that selection at a chosen spacing within the existing clip source. Up/Down that selection at a chosen spacing within the existing clip source. Up/Down
transposes by a semitone. Commands are undoable through the main Edit menu. transposes by a semitone. Commands are undoable through the main Edit menu. See
See [MIDI.md](MIDI.md#composition-commands) for shortcuts, timing and limits. [MIDI.md](MIDI.md#composition-commands) for shortcuts, timing and limits.
## Audio clip tools ---
## Audio Clip Tools
Select an audio clip, click the ruler at the desired cut while stopped, then Select an audio clip, click the ruler at the desired cut while stopped, then
choose Edit / Split clip at cursor (S with timeline focus). Ctrl+D duplicates; choose Edit / Split clip at cursor (S with timeline focus). Ctrl+D duplicates;
Delete removes the clip reference. Drag the body to move and edges to trim. Delete removes the clip reference. Drag the body to move and edges to trim. Edit
Edit / Audio clip gain and fades (A) sets linear gain and fade durations in ticks. / Audio clip gain and fades (A) sets linear gain and fade durations in ticks.
All are undoable and preserve the WAV file. Waveforms show cached raw source All are undoable and preserve the WAV file. Waveforms show cached raw source
peaks, before gain/fades; large media can take time to load on Open/import. peaks, before gain/fades; large media can take time to load on Open/import. See
See [AUDIO_FILES.md](AUDIO_FILES.md#non-destructive-clip-editing) for [AUDIO_FILES.md](AUDIO_FILES.md#non-destructive-clip-editing) for boundary
boundary behavior, preview limits and the deferred stretch/pitch tools. behavior, preview limits and the deferred stretch/pitch tools.
## Inspecting and organizing routing ---
## Inspecting and Organizing Routing
In Modular Graph, select a node or wire to inspect its routing in the feedback In Modular Graph, select a node or wire to inspect its routing in the feedback
line. Arrows show direction; yellow no-output badges identify disconnected line. Arrows show direction; yellow no-output badges identify disconnected
@ -126,48 +151,54 @@ unity gain/bus node as one undoable edit. These commands are also in View/Edit
and the graph context menu. Unwired effect inputs use external audio, as shown and the graph context menu. Unwired effect inputs use external audio, as shown
by “ext in”; they do not imply silence. See [ROUTING.md](ROUTING.md). by “ext in”; they do not imply silence. See [ROUTING.md](ROUTING.md).
The Mixer also offers Add bus and a Route choice for ordinary tracks. The Mixer also offers Add bus and a Route choice for ordinary tracks. Select
Select Track/Bus strips to edit their controls and automation. Master and selected Track/Bus strips to edit their controls and automation. Master and selected
strip meters show dBFS with 20 dB/s release; Reset peaks clears latched clipping. strip meters show dBFS with 20 dB/s release; Reset peaks clears latched
See MIXER.md for solo rules, gain staging and meter taps. clipping. See MIXER.md for solo rules, gain staging and meter taps.
File → Load LADSPA plugin offers a descriptor browser. Select a graph File → Load LADSPA plugin offers a descriptor browser. Select a graph plugin and
plugin and Edit → Selected plugin control for identity, values, ranges and Edit → Selected plugin control for identity, values, ranges and Reset defaults;
Reset defaults; Apply commits, Cancel discards. Edit also offers replacement, Apply commits, Cancel discards. Edit also offers replacement, reset and
reset and insertion of an effect on a selected wire. Graph wires determine insertion of an effect on a selected wire. Graph wires determine processing
processing order; Delete removes a selected plugin and its wires. Changes are order; Delete removes a selected plugin and its wires. Changes are undoable.
undoable. Replacement keeps compatible audio wiring but resets plugin controls Replacement keeps compatible audio wiring but resets plugin controls and
and automation. See PLUGIN_HOSTING.md for supported formats and failure behavior. automation. See PLUGIN_HOSTING.md for supported formats and failure behavior.
Mixer Interpolation selects Linear or Hold for each point added or replaced. Audio → MIDI controller mapping lists all channel bindings; enter Mixer Interpolation selects Linear or Hold for each point added or replaced.
Audio → MIDI controller mapping lists all channel bindings; enter
`channel volume_CC pan_CC sustain_CC` (channels 0..15, 128 disables, enabled CCs `channel volume_CC pan_CC sustain_CC` (channels 0..15, 128 disables, enabled CCs
must be distinct, 120/123 reserved). Maps are undoable and saved. Controllers must be distinct, 120/123 reserved). Maps are undoable and saved. Controllers
modify live sine voices; arranged track/bus/plugin automation takes precedence modify live sine voices; arranged track/bus/plugin automation takes precedence
over its static controls and is unaffected by those MIDI bindings. See CONTROL.md. over its static controls and is unaffected by those MIDI bindings. See
CONTROL.md.
Unavailable main-menu undo/redo actions are disabled, including while recording. Piano roll has Edit/Undo/Redo shortcuts and a right-click note menu; Unavailable main-menu undo/redo actions are disabled, including while recording.
right-click no longer immediately deletes notes. Graph right-click targets the Piano roll has Edit/Undo/Redo shortcuts and a right-click note menu; right-click
node/wire under the pointer. Undo preserves surviving selections. Completed no longer immediately deletes notes. Graph right-click targets the node/wire
takes add one undo step; undo removes references and keeps WAV files. See under the pointer. Undo preserves surviving selections. Completed takes add one
EDITING.md for grouped commands, failure handling and keyboard/focus behavior. undo step; undo removes references and keeps WAV files. See EDITING.md for
grouped commands, failure handling and keyboard/focus behavior.
Project troubleshooting: `xtc-check --validate PROJECT.xtc` reports metadata Project troubleshooting: `xtc-check --validate PROJECT.xtc` reports metadata
errors with the expected record and syntax line. `xtc-check --inspect PROJECT.xtc` errors with the expected record and syntax line.
lists resolved media/module paths and plugin identities without executing plugins `xtc-check --inspect PROJECT.xtc` lists resolved media/module paths and plugin
or starting JACK. Exit codes: 0 = valid metadata and available files, 2 = missing identities without executing plugins or starting JACK. Exit codes: 0 = valid
or unreadable references, 1 = metadata/I/O error. File availability does not metadata and available files, 2 = missing or unreadable references, 1 =
certify WAV contents or plugin compatibility. See [PROJECT_FORMAT.md](PROJECT_FORMAT.md) metadata/I/O error. File availability does not certify WAV contents or plugin
for the compatibility and relocation policy. compatibility. See [PROJECT_FORMAT.md](PROJECT_FORMAT.md) for the compatibility
and relocation policy.
**File → Load plugin** supports LADSPA plus an initial Linux **File → Load plugin** supports LADSPA plus an initial Linux CLAP/VST2/VST3
CLAP/VST2/VST3 effect subset. Select the actual module binary (`.clap` or `.so`, effect subset. Select the actual module binary (`.clap` or `.so`, including a
including a VST3 bundle's inner binary). Modern effects require one mono input VST3 bundle's inner binary). Modern effects require one mono input and output,
and output, float32 and zero latency; they use default configuration. Instruments, float32 and zero latency; they use default configuration. Instruments, modern
modern parameter editing/automation, opaque state and custom editors are not parameter editing/automation, opaque state and custom editors are not
implemented. Projects now save v12 and read v1–v12. See PLUGIN_HOSTING.md for the implemented. Projects now save v12 and read v1–v12. See PLUGIN_HOSTING.md for
full contract and test/qualification limits. the full contract and test/qualification limits.
## Instrument support ---
## Instrument Support
MIDI tracks can bind CLAP, VST2 and VST3 instruments. In the Track menu, select MIDI tracks can bind CLAP, VST2 and VST3 instruments. In the Track menu, select
**Load instrument plugin** for the selected sequencer track; **Use built-in **Load instrument plugin** for the selected sequencer track; **Use built-in
@ -178,9 +209,9 @@ their own track's instrument. Independent instances feed track gain/pan,
mute/solo, bus routing and automation. Bindings/live target save in v12. mute/solo, bus routing and automation. Bindings/live target save in v12.
Supply the actual trusted Linux binary (including the inner .so of a VST3 Supply the actual trusted Linux binary (including the inner .so of a VST3
bundle). Supported instruments have zero audio inputs, one mono output, bundle). Supported instruments have zero audio inputs, one mono output, float32
float32 processing, zero latency and one note/event input. Default configuration processing, zero latency and one note/event input. Default configuration only:
only: no custom editor, opaque state/preset restoration, modern parameter no custom editor, opaque state/preset restoration, modern parameter
editing/automation, stereo/multichannel instruments or MIDI output. No arbitrary editing/automation, stereo/multichannel instruments or MIDI output. No arbitrary
third-party instrument is qualified by the original ABI fixture tests. third-party instrument is qualified by the original ABI fixture tests.
@ -198,27 +229,32 @@ the callback and old instances are reclaimed on the control thread. Missing
references remain inspectable without loading; failed GUI preparation retains references remain inspectable without loading; failed GUI preparation retains
the current project. Save a backup for applications limited to v11 or earlier. the current project. Save a backup for applications limited to v11 or earlier.
---
## Workspace ## Workspace
The flat workspace puts New MIDI/audio track, WAV import and instrument loading The flat workspace puts New MIDI/audio track, WAV import and instrument loading
in a persistent sidebar, with a musical transport display and selected-track in a persistent sidebar, with a musical transport display and selected-track
overview. See [WORKSPACE.md](WORKSPACE.md) for shortcuts and workflow. Open overview. See [WORKSPACE.md](WORKSPACE.md) for shortcuts and workflow. Open
`modern-session.xtc` in the installed examples for an original four-track arrangement. `modern-session.xtc` in the installed examples for an original four-track
arrangement.
Use the sequencer Snap dropdown for straight/triplet/bar grids or Off. Use the sequencer Snap dropdown for straight/triplet/bar grids or Off.
Double-click MIDI clips to edit notes, then use the Velocity lane or selected-note Double-click MIDI clips to edit notes, then use the Velocity lane or
slider for dynamics. Each non-bus track header has an instrument dropdown offering selected-note slider for dynamics. Each non-bus track header has an instrument
built-in sine, project instruments and explicit plugin loading. See WORKSPACE.md. dropdown offering built-in sine, project instruments and explicit plugin
loading. See WORKSPACE.md.
Additive clip selection, atomic group commands, phrase clipboard and repeat, Additive clip selection, atomic group commands, phrase clipboard and repeat,
alignment/slip tools, MIDI swing/gate/legato/scale/velocity transforms, batch alignment/slip tools, MIDI swing/gate/legato/scale/velocity transforms, batch
audio fades and static mixer A/B recall are also available. See audio fades and static mixer A/B recall are also available. See
[MODERN_EDITING.md](MODERN_EDITING.md) for commands, scope and limitations. [MODERN_EDITING.md](MODERN_EDITING.md) for commands, scope and limitations.
See [arrangement and composition workflows](COMPOSITION_WORKFLOWS.md) for markers, MIDI composition, normalization and portable media collection. See [arrangement and composition workflows](COMPOSITION_WORKFLOWS.md) for
markers, MIDI composition, normalization and portable media collection.
Select an audio clip and open View / Sample editor (or double-click it) to inspect Select an audio clip and open View / Sample editor (or double-click it) to
source samples, choose native frame ranges, trim the clip or export a new WAV. inspect source samples, choose native frame ranges, trim the clip or export a
Processing and one-level local undo remain in the editor until Save full copy and new WAV. Processing and one-level local undo remain in the editor until Save
relink. See [AUDIO_EDITOR.md](AUDIO_EDITOR.md) for all sample tools, transient full copy and relink. See [AUDIO_EDITOR.md](AUDIO_EDITOR.md) for all sample
slicing, musical-tick rounding and source preservation limits. tools, transient slicing, musical-tick rounding and source preservation limits.

View file

@ -1,14 +1,17 @@
# Make and reopen a small project # Make and Reopen A Small Project
The default workspace is Sequencer. New and Open return to this workspace; The default workspace is Sequencer. New and Open return to this workspace; Mixer
Mixer and Modular Graph remain available with Ctrl+2 and Ctrl+3. Start with an and Modular Graph remain available with Ctrl+2 and Ctrl+3. Start with an empty
empty project at 120 BPM, 4/4. Clips default to one bar and piano-roll notes use project at 120 BPM, 4/4. Clips default to one bar and piano-roll notes use
velocity 100; no notes or test oscillators are inserted automatically. velocity 100; no notes or test oscillators are inserted automatically.
## Create music ---
1. Start your existing JACK-compatible server, launch `xtc`, and check that audio ## Create Music
says ready/stopped. If disconnected, start the server and use Reconnect/F6.
1. Start your existing JACK-compatible server, launch `xtc`, and check that
audio says ready/stopped. If disconnected, start the server and use
Reconnect/F6.
2. Click Add track, then Add MIDI clip. Click Piano roll (Ctrl+4) and click the 2. Click Add track, then Add MIDI clip. Click Piano roll (Ctrl+4) and click the
grid to draw notes. Drag notes to move them or their right edges to resize. grid to draw notes. Drag notes to move them or their right edges to resize.
Close the piano-roll window to return to the arrangement. Close the piano-roll window to return to the arrangement.
@ -21,14 +24,16 @@ velocity 100; no notes or test oscillators are inserted automatically.
5. MIDI creation supplies an instrument-to-output route, and audio import 5. MIDI creation supplies an instrument-to-output route, and audio import
supplies an audio-to-output route for a new project. No graph editing or supplies an audio-to-output route for a new project. No graph editing or
project-file editing is required. Ctrl+3 shows these routes if you want to project-file editing is required. Ctrl+3 shows these routes if you want to
inspect or change them. Existing custom routes are respected; see MIDI.md inspect or change them. Existing custom routes are respected; see MIDI.md and
and AUDIO_FILES.md for routing limits. AUDIO_FILES.md for routing limits.
6. In your existing JACK routing tool, connect xtc's `output` and `output_right` 6. In your existing JACK routing tool, connect xtc's `output` and `output_right`
ports to left/right playback inputs. Press Play/F5. Mixer/Ctrl+2 shows output ports to left/right playback inputs. Press Play/F5. Mixer/Ctrl+2 shows output
meters and track gain/pan/mute/solo. If meters move but nothing is audible, meters and track gain/pan/mute/solo. If meters move but nothing is audible,
check external playback routing and device levels. xtc does not connect check external playback routing and device levels. xtc does not connect
physical ports automatically or start/configure the server. physical ports automatically or start/configure the server.
---
## Transport ## Transport
| Command | Behavior | | Command | Behavior |
@ -41,27 +46,30 @@ velocity 100; no notes or test oscillators are inserted automatically.
| Click timeline ruler | Seek on the sixteenth-note grid while stopped; during playback the seek is refused. | | Click timeline ruler | Seek on the sixteenth-note grid while stopped; during playback the seek is refused. |
| New / Open / Reconnect | Stop and reset the position. New/Open show Sequencer; Reconnect leaves the document intact. | | New / Open / Reconnect | Stop and reset the position. New/Open show Sequencer; Reconnect leaves the document intact. |
Pause/Stop completion is asynchronous: the GUI briefly shows “stopping”. Recording Pause/Stop completion is asynchronous: the GUI briefly shows “stopping”.
Stop and Home retain immediate deactivation. See [AUDIO_QUALITY.md](AUDIO_QUALITY.md) Recording Stop and Home retain immediate deactivation. See
for the 5 ms fade policy and timing bounds. [AUDIO_QUALITY.md](AUDIO_QUALITY.md) for the 5 ms fade policy and timing bounds.
The backend sample count drives the playhead; its display refreshes every 100 ms. The backend sample count drives the playhead; its display refreshes every 100
Pause/Stop retain the exact sample position even though the ruler displays ticks. ms. Pause/Stop retain the exact sample position even though the ruler displays
Arrangement, note and mixer edits stop playback; graph edits can be adopted live. ticks. Arrangement, note and mixer edits stop playback; graph edits can be
A failed stop leaves playback running and the controls reflect that. A failed adopted live. A failed stop leaves playback running and the controls reflect
return-to-start seek does not set the cursor to zero. If candidate project that. A failed return-to-start seek does not set the cursor to zero. If
preparation fails after stopping/seeking, the old document stays loaded and the candidate project preparation fails after stopping/seeking, the old document
GUI refreshes to its actual stopped position (which may be zero). A parse failure stays loaded and the GUI refreshes to its actual stopped position (which may be
occurs before installation and leaves playback alone. zero). A parse failure occurs before installation and leaves playback alone.
## Save, close and continue ---
## Save, Close and Continue
Use File/Save (Ctrl+S), choose a `.xtc` path and retain external WAV files. The Use File/Save (Ctrl+S), choose a `.xtc` path and retain external WAV files. The
unsaved title marker clears on success. File/Save As confirms overwrites. unsaved title marker clears on success. File/Save As confirms overwrites. Close
Close the window, launch xtc again and use File/Open (Ctrl+O), or run the window, launch xtc again and use File/Open (Ctrl+O), or run
`xtc /path/to/song.xtc`. The arrangement, stable IDs, routing, notes, mixing and `xtc /path/to/song.xtc`. The arrangement, stable IDs, routing, notes, mixing and
automation are restored; the transport starts stopped at zero. Continue drawing, automation are restored; the transport starts stopped at zero. Continue drawing,
importing or moving clips, use Ctrl+Z/Ctrl+Shift+Z for undo/redo, and save again. importing or moving clips, use Ctrl+Z/Ctrl+Shift+Z for undo/redo, and save
again.
New/Open/Quit ask before discarding unsaved edits. They are refused while New/Open/Quit ask before discarding unsaved edits. They are refused while
recording: use Stop to finalize the take, then save. Missing media stays silent recording: use Stop to finalize the take, then save. Missing media stays silent
@ -70,7 +78,9 @@ selected audio. Failed file operations retain the document and show an error;
check the path, format and permissions. Server loss or rate changes require check the path, format and permissions. Server loss or rate changes require
Reconnect; this resets position, so seek again before resuming. Reconnect; this resets position, so seek again before resuming.
## Repeatable acceptance test ---
## Repeatable Acceptance Test
With installed FLTK/JACK development files, Xvfb and an already running With installed FLTK/JACK development files, Xvfb and an already running
JACK-compatible server: JACK-compatible server:
@ -80,14 +90,15 @@ make all test gui test-gui test-workflow test-gui-jack test-jack-midi
``` ```
`test-workflow` drives real GUI commands and timeline gestures under Xvfb. It `test-workflow` drives real GUI commands and timeline gestures under Xvfb. It
creates a track, draws two notes, arranges the MIDI clip two beats later, imports creates a track, draws two notes, arranges the MIDI clip two beats later,
a generated WAV and verifies both automatic source routes. It saves and closes, imports a generated WAV and verifies both automatic source routes. It saves and
destroys the first window, reopens in a fresh window and compares the complete closes, destroys the first window, reopens in a fresh window and compares the
stereo offline render exactly (including silence, imported audio and notes). complete stereo offline render exactly (including silence, imported audio and
It checks stable clip IDs/positions and graph counts, seeks through the ruler, notes). It checks stable clip IDs/positions and graph counts, seeks through the
plays on the real backend, observes nonzero stereo output meters and advancing ruler, plays on the real backend, observes nonzero stereo output meters and
playhead, pauses/resumes, stops, returns to start, edits/undoes/redoes, saves and advancing playhead, pauses/resumes, stops, returns to start,
reopens again. It uses temporary files only and cleans them on success. edits/undoes/redoes, saves and reopens again. It uses temporary files only and
cleans them on success.
`test-gui` injects stop/seek/prepare failures to verify controls, position and `test-gui` injects stop/seek/prepare failures to verify controls, position and
retained document state, plus recording guards. `test-gui-jack` exercises live retained document state, plus recording guards. `test-gui-jack` exercises live
@ -98,34 +109,40 @@ JACK support. Core tests use no GUI leak suppression.
These checks cover a minimal composition workflow, not physical listening or These checks cover a minimal composition workflow, not physical listening or
endurance qualification. No new dependency, project-format change, plugin host, endurance qualification. No new dependency, project-format change, plugin host,
MIDI capture, tempo editor, loop playback, device-routing GUI, or persisted editor MIDI capture, tempo editor, loop playback, device-routing GUI, or persisted
layout was introduced. See GUI.md, MIDI.md, MIXER.md and LINUX.md for details. editor layout was introduced. See GUI.md, MIDI.md, MIXER.md and LINUX.md for
details.
## Editing workflow ---
Select a clip and press Ctrl+D (or Edit/Duplicate clip) to duplicate it immediately ## Editing Workflow
after its end. Complete source notes, trim offsets and media references are
retained with fresh stable IDs. No audio file is copied or overwritten. F2 or
Edit/Rename clip or track names the selection. Clip labels show both kind and
user name. Alt+Up/Down with timeline focus reorders its selected track without
changing IDs, clip membership or routing. Right-click a timeline clip/track for
Duplicate, Rename and Delete. Invalid/capacity edits retain the existing model.
View/Snap selects 1, 120, 240 (default), 480 or 960 ticks. It affects ruler seeks, Select a clip and press Ctrl+D (or Edit/Duplicate clip) to duplicate it
clip moves and trims; 1 tick is the finest grid. Existing +/- zoom and scrollbars immediately after its end. Complete source notes, trim offsets and media
retain their behavior. Piano roll has its own configurable grid (see MIDI.md); mixer lanes retain their 240-tick references are retained with fresh stable IDs. No audio file is copied or
grid. Grid and zoom are application preferences saved on Apply/clean exit; overwritten. F2 or Edit/Rename clip or track names the selection. Clip labels
selection is session state. These controls are not undoable music edits. show both kind and user name. Alt+Up/Down with timeline focus reorders its
Escape cancels a drag; Delete affects the focused editor; text fields keep their selected track without changing IDs, clip membership or routing. Right-click a
own editing keys. Commands are named in menus/tooltips and timeline focus accepts timeline clip/track for Duplicate, Rename and Delete. Invalid/capacity edits
keyboard editing; screen-reader and broad accessibility qualification are pending. retain the existing model.
View/Snap selects 1, 120, 240 (default), 480 or 960 ticks. It affects ruler
seeks, clip moves and trims; 1 tick is the finest grid. Existing +/- zoom and
scrollbars retain their behavior. Piano roll has its own configurable grid (see
MIDI.md); mixer lanes retain their 240-tick grid. Grid and zoom are application
preferences saved on Apply/clean exit; selection is session state. These
controls are not undoable music edits. Escape cancels a drag; Delete affects the
focused editor; text fields keep their own editing keys. Commands are named in
menus/tooltips and timeline focus accepts keyboard editing; screen-reader and
broad accessibility qualification are pending.
Create/delete/move/trim/duplicate, names/order, graph connects/deletes, notes, Create/delete/move/trim/duplicate, names/order, graph connects/deletes, notes,
mixer/automation and plugin edits use the 32-edit snapshot history. Ctrl+Z undoes; mixer/automation and plugin edits use the 32-edit snapshot history. Ctrl+Z
Ctrl+Shift+Z or Ctrl+Y redoes. Backend preparation precedes accepting an undo/redo undoes; Ctrl+Shift+Z or Ctrl+Y redoes. Backend preparation precedes accepting an
snapshot; failure retains its cursor/document. Model edits stop playback before undo/redo snapshot; failure retains its cursor/document. Model edits stop
mutation; compilation/file work occurs on the control thread and may pause the playback before mutation; compilation/file work occurs on the control thread and
GUI, never block the audio callback. This is not a background-loading editor. may pause the GUI, never block the audio callback. This is not a
background-loading editor.
A successful recorded take adds a reference as one undoable edit. Undo/removing A successful recorded take adds a reference as one undoable edit. Undo/removing
its region never deletes the WAV; recording failures retain their recovery path. its region never deletes the WAV; recording failures retain their recovery path.
@ -133,14 +150,16 @@ There is no destructive media-delete operation. New/Open reset history after
unsaved-change handling. The capacity limit and allocation-error reporting in unsaved-change handling. The capacity limit and allocation-error reporting in
MIXER.md still apply. Tests: `make test-editing test-gui-plugins test-workflow`. MIXER.md still apply. Tests: `make test-editing test-gui-plugins test-workflow`.
## Editing audit ---
## Editing Audit
See [EDITING.md](EDITING.md) for the operation-by-operation undo matrix, command See [EDITING.md](EDITING.md) for the operation-by-operation undo matrix, command
ownership, allocation-failure boundary, external-media behavior and manual ownership, allocation-failure boundary, external-media behavior and manual
context-menu/focus procedure. History now reserves storage before edits and context-menu/focus procedure. History now reserves storage before edits and
publication, preserving redo when commands fail. Recording start has no empty publication, preserving redo when commands fail. Recording start has no empty
undo entry; a completed take is one step and undo never deletes its WAV. undo entry; a completed take is one step and undo never deletes its WAV. Undo
Undo preserves surviving selections and an open piano roll; the piano window preserves surviving selections and an open piano roll; the piano window also
also offers Edit/Undo/Redo. Graph context menus select under the pointer and offers Edit/Undo/Redo. Graph context menus select under the pointer and
piano-roll right-click now opens a menu instead of immediately deleting a note. piano-roll right-click now opens a menu instead of immediately deleting a note.
Load/import/plugin work paints busy feedback while remaining synchronous. Load/import/plugin work paints busy feedback while remaining synchronous.

View file

@ -1,21 +1,24 @@
# Flat DAW workspace # Flat DAW Workspace
The workspace presents the existing xtc editors in a consistent flat layout. It uses The workspace presents the existing xtc editors in a consistent flat layout. It
FLTK vector drawing and system fonts: no images, shadows, bevels, 3D rendering, uses FLTK vector drawing and system fonts: no images, shadows, bevels, 3D
external theme packages or new runtime dependencies. rendering, external theme packages or new runtime dependencies.
The default window is 1280 × 800; the supported minimum is 1220 × 720. The main The default window is 1280 × 800; the supported minimum is 1220 × 720. The main
editor grows with the window. Use the scrollbars for longer arrangements and editor grows with the window. Use the scrollbars for longer arrangements and
larger track counts. The sidebar stays available across Sequencer, Mixer and larger track counts. The sidebar stays available across Sequencer, Mixer and
Modular Graph views. Ctrl+1/2/3 switches those views; Ctrl+4 opens the piano roll. Modular Graph views. Ctrl+1/2/3 switches those views; Ctrl+4 opens the piano
roll.
## Create and arrange ---
- **New MIDI track** (Ctrl+Shift+M) creates a named track and a one-bar MIDI clip ## Create and Arrange
at the cursor. The new clip and mixer track are selected together. **Edit MIDI
notes** opens its piano roll. Creation is one undo step. - **New MIDI track** (Ctrl+Shift+M) creates a named track and a one-bar MIDI
- **New audio track** (Ctrl+Shift+A) creates an empty named track. **Import WAV** clip at the cursor. The new clip and mixer track are selected together. **Edit
imports into the selected track, or creates one when none is selected. MIDI notes** opens its piano roll. Creation is one undo step.
- **New audio track** (Ctrl+Shift+A) creates an empty named track. **Import
WAV** imports into the selected track, or creates one when none is selected.
- **Load instrument plugin** opens the instrument browser for the selected - **Load instrument plugin** opens the instrument browser for the selected
sequencer track. Unassigned MIDI tracks retain the built-in sine instrument. sequencer track. Unassigned MIDI tracks retain the built-in sine instrument.
- Existing Add track/region/MIDI clip, duplication, trimming, split, rename and - Existing Add track/region/MIDI clip, duplication, trimming, split, rename and
@ -36,16 +39,18 @@ time visible. The transport display reads **bar : beat : tick** (4/4, 960 ticks
per beat); BPM follows the project tempo. These displays are read-only. per beat); BPM follows the project tempo. These displays are read-only.
The sidebar inspector follows the sequencer selection, showing track name, The sidebar inspector follows the sequencer selection, showing track name,
gain/pan, mute/solo and assigned instrument. Use the Mixer for controls, routing, gain/pan, mute/solo and assigned instrument. Use the Mixer for controls,
meters and automation; the sidebar is an overview, not another mixer. Status and routing, meters and automation; the sidebar is an overview, not another mixer.
errors remain visible below the editor. Existing engine connection, transport, Status and errors remain visible below the editor. Existing engine connection,
recording and keyboard safety behavior is retained. transport, recording and keyboard safety behavior is retained.
## Example and verification ---
## Example and Verification
Open `examples/projects/modern-session.xtc` for an original four-track, four-bar Open `examples/projects/modern-session.xtc` for an original four-track, four-bar
MIDI arrangement with 16 clips and 128 notes. It needs no media or plugins. It is MIDI arrangement with 16 clips and 128 notes. It needs no media or plugins. It
also installed with the project examples. Connect JACK playback ports before is also installed with the project examples. Connect JACK playback ports before
listening. Work on a copy if the installed examples are read-only. listening. Work on a copy if the installed examples are read-only.
GUI tests exercise sidebar creation, selected clip/editor access, undo/redo, GUI tests exercise sidebar creation, selected clip/editor access, undo/redo,
@ -56,22 +61,24 @@ add commercial-DAW features: detachable/resizable docks, mixer channel strips,
automatic plugin scanning, custom plugin editors and new audio/MIDI features automatic plugin scanning, custom plugin editors and new audio/MIDI features
remain outside this workspace. Existing plugin and engine limits still apply. remain outside this workspace. Existing plugin and engine limits still apply.
## Editing controls ---
The **Snap** dropdown above the sequencer selects Off, 1/32, 1/16 triplet, ## Editing Controls
1/16, 1/8 triplet, 1/8, 1/4, 1/2 or one bar. Existing View/Snap commands update
the same preference. Bar lines are strongest, beats medium, subdivisions subtle. The **Snap** dropdown above the sequencer selects Off, 1/32, 1/16 triplet, 1/16,
At low zoom closely spaced lines are coarsened to at least six pixels; snapping 1/8 triplet, 1/8, 1/4, 1/2 or one bar. Existing View/Snap commands update the
same preference. Bar lines are strongest, beats medium, subdivisions subtle. At
low zoom closely spaced lines are coarsened to at least six pixels; snapping
still uses the selected division. Clip moves and trim boundaries snap to the still uses the selected division. Clip moves and trim boundaries snap to the
nearest absolute grid position (halfway rounds forward), including clips that nearest absolute grid position (halfway rounds forward), including clips that
start off the grid. Off removes musical quantization; pointer resolution and start off the grid. Off removes musical quantization; pointer resolution and
integer model ticks still apply. Invalid trims retain the original clip. integer model ticks still apply. Invalid trims retain the original clip. Snap is
Snap is a UI preference, not a project edit or a saved project setting. a UI preference, not a project edit or a saved project setting.
Double-click a MIDI clip with the left mouse button to open its piano roll. Double-click a MIDI clip with the left mouse button to open its piano roll. The
The clip is selected and the click does not move/trim it. Audio/silent clips clip is selected and the click does not move/trim it. Audio/silent clips retain
retain their existing selection/drag behavior. The piano roll also has a Snap their existing selection/drag behavior. The piano roll also has a Snap dropdown
dropdown for its existing note grid; sequencer and piano snap remain independent. for its existing note grid; sequencer and piano snap remain independent.
The piano roll has a fixed **Velocity** lane beneath the keys and a **Note The piano roll has a fixed **Velocity** lane beneath the keys and a **Note
velocity** slider. Select a note, then drag its bar vertically or release the velocity** slider. Select a note, then drag its bar vertically or release the
@ -82,25 +89,29 @@ the desired note in the keys first; its highlighted velocity bar and slider
remain individually editable. Horizontal scrolling aligns the lane with note remain individually editable. Horizontal scrolling aligns the lane with note
starts; vertical scrolling moves the keys while the lane stays available. starts; vertical scrolling moves the keys while the lane stays available.
Filters apply to lane visibility and selection. Values save with existing note Filters apply to lane visibility and selection. Values save with existing note
metadata and affect built-in synthesis; plugin patches determine velocity response. metadata and affect built-in synthesis; plugin patches determine velocity
This is MIDI velocity, not a per-note audio gain or loudness meter. response. This is MIDI velocity, not a per-note audio gain or loudness meter.
Every ordinary track header has an instrument dropdown. Choose **Built-in sine**, Every ordinary track header has an instrument dropdown. Choose **Built-in
an instrument already assigned elsewhere in the project, or **Load plugin...** sine**, an instrument already assigned elsewhere in the project, or **Load
for a trusted CLAP/VST2/VST3 binary. Entries show the format and plugin label. plugin...** for a trusted CLAP/VST2/VST3 binary. Entries show the format and
Each assignment creates an independent track instance, selects that track for plugin label. Each assignment creates an independent track instance, selects
live MIDI and remains undoable; clear/reorder/reopen use stable track IDs. Bus that track for live MIDI and remains undoable; clear/reorder/reopen use stable
tracks show their bus label instead. Cancel or failed preparation restores the track IDs. Bus tracks show their bus label instead. Cancel or failed preparation
model's selection in the dropdown. The list is project-local, not a scanner or restores the model's selection in the dropdown. The list is project-local, not a
installed-plugin database. Existing mono/default-configuration host limits apply. scanner or installed-plugin database. Existing mono/default-configuration host
Instrument changes stop/reprepare playback and are refused while recording. limits apply. Instrument changes stop/reprepare playback and are refused while
recording.
Verification includes off-grid move/trim, snap Off, dropdown labels, double-click, Verification includes off-grid move/trim, snap Off, dropdown labels,
coincident notes, per-note slider/lane edits, invalid velocities, undo/reload and double-click, coincident notes, per-note slider/lane edits, invalid velocities,
instrument reuse/clear/reorder for all three fixture formats. Manual checks use undo/reload and instrument reuse/clear/reorder for all three fixture formats.
the original modern-session example and the flat sequencer/piano windows. Manual checks use the original modern-session example and the flat
sequencer/piano windows.
## Studio mixer ---
## Studio Mixer
The Mixer tab now presents all tracks and buses as channel strips with vertical The Mixer tab now presents all tracks and buses as channel strips with vertical
dB faders, flat pan knobs, mute/solo, output routing and stereo meters. Scroll dB faders, flat pan knobs, mute/solo, output routing and stereo meters. Scroll
@ -109,63 +120,68 @@ Select a channel name to edit its gain/pan automation below the bank. Existing
undo, keyboard controls and project persistence remain available. See undo, keyboard controls and project persistence remain available. See
[MIXER.md](MIXER.md) for fader units, metering, automation and limits. [MIXER.md](MIXER.md) for fader units, metering, automation and limits.
---
## Direct editing and navigation ## Direct Editing and Navigation
**Commands** (Ctrl+P) searches active menu actions by case-insensitive substring. **Commands** (Ctrl+P) searches active menu actions by case-insensitive
Type part of an action path such as `mixer`, `import` or `preferences`, use substring. Type part of an action path such as `mixer`, `import` or
Up/Down to select, then Enter or **Run command**. Double-click also runs an `preferences`, use Up/Down to select, then Enter or **Run command**.
entry. Escape or Cancel leaves the project unchanged. The palette closes before Double-click also runs an entry. Escape or Cancel leaves the project unchanged.
executing the existing menu callback; context and recording guards still apply. The palette closes before executing the existing menu callback; context and
recording guards still apply.
The transport **BPM** field edits project tempo from 20 to 300 BPM, stored in The transport **BPM** field edits project tempo from 20 to 300 BPM, stored in
milli-BPM. Enter or release commits one undoable change. It stops and rewinds milli-BPM. Enter or release commits one undoable change. It stops and rewinds
transport and prepares the project, including converting loop ticks to samples transport and prepares the project, including converting loop ticks to samples
at the new tempo. Failed validation/preparation retains the previous project; at the new tempo. Failed validation/preparation retains the previous project;
the field returns to its actual value. Existing track/clip and piano-roll the field returns to its actual value. Existing track/clip and piano-roll
selection survives. This is project tempo, separate from Preferences' new-project selection survives. This is project tempo, separate from Preferences'
default. Tempo automation is not added. new-project default. Tempo automation is not added.
The **Track inspector** now edits the selected track's linear Gain (0..4), Pan The **Track inspector** now edits the selected track's linear Gain (0..4), Pan
(-1..1), Mute and Solo. In Mixer it follows the selected channel; in Sequencer (-1..1), Mute and Solo. In Mixer it follows the selected channel; in Sequencer
and Graph it follows arrangement selection. Controls synchronize with model and Graph it follows arrangement selection. Controls synchronize with model
changes and undo, while typing is protected from the display refresh timer. changes and undo, while typing is protected from the display refresh timer. Only
Only the edited field is committed. Unchanged values create no history entry. the edited field is committed. Unchanged values create no history entry. These
These are static values; automation still overrides gain and pan. Recording are static values; automation still overrides gain and pan. Recording prevents
prevents edits. Track instruments and routing retain their existing selectors. edits. Track instruments and routing retain their existing selectors.
The session row shows track/bus count, clip count and the arrangement's bar span. The session row shows track/bus count, clip count and the arrangement's bar
**Fit song** (Ctrl+0) returns to the start and selects the largest supported zoom span. **Fit song** (Ctrl+0) returns to the start and selects the largest
that fits the arrangement plus a beat of padding. Supported zoom remains supported zoom that fits the arrangement plus a beat of padding. Supported zoom
24, 48, 96, 192 or 384 pixels per beat; a sufficiently long arrangement requires remains 24, 48, 96, 192 or 384 pixels per beat; a sufficiently long arrangement
scrolling even at the minimum. Empty projects fit one bar. requires scrolling even at the minimum. Empty projects fit one bar.
**Follow playhead** pages the arrangement horizontally during playback, retaining **Follow playhead** pages the arrangement horizontally during playback,
vertical scroll. It operates only in Sequencer and waits while a mouse drag is retaining vertical scroll. It operates only in Sequencer and waits while a mouse
active. It is off by default; Preferences / Apply persists it in drag is active. It is off by default; Preferences / Apply persists it in
`[transport] follow_playhead=0` or `1`. Manual scrolling remains available and `[transport] follow_playhead=0` or `1`. Manual scrolling remains available and
Fit song resets the viewport without editing the project. Fit song resets the viewport without editing the project.
Verification covers real FLTK palette filtering, empty searches and cancellation, Verification covers real FLTK palette filtering, empty searches and
tempo callbacks/undo/failed adoption, inspector edits and in-progress numeric cancellation, tempo callbacks/undo/failed adoption, inspector edits and
input, fit and follow navigation, and existing core/GUI/live JACK workflows. in-progress numeric input, fit and follow navigation, and existing core/GUI/live
JACK workflows.
---
## Detachable transport and mixer ## Detachable Transport and Mixer
Use **View → Detach transport** or **View → Detach mixer** to move either panel Use **View → Detach transport** or **View → Detach mixer** to move either panel
into a separate window. Toggle the same menu action, use the floating panel's into a separate window. Toggle the same menu action, use the floating panel's
**Dock** button, or close its window to return it to the main workspace. **Dock** button, or close its window to return it to the main workspace. **View
**View → Dock all panels** restores both. **View → Mixer** (Ctrl+2) also raises → Dock all panels** restores both. **View → Mixer** (Ctrl+2) also raises the
the floating mixer. The main arrangement expands when the transport is detached. floating mixer. The main arrangement expands when the transport is detached. The
The mixer remains the same live view, with its channel selection, faders, routing mixer remains the same live view, with its channel selection, faders, routing
and meters; the main Mixer tab offers Show and Dock actions while it is floating. and meters; the main Mixer tab offers Show and Dock actions while it is
floating.
The transport owns the playback buttons, position, **BPM**, **Loop** toggle and The transport owns the playback buttons, position, **BPM**, **Loop** toggle and
**Loop range...** settings in both layouts. Shift-drag on the sequencer ruler **Loop range...** settings in both layouts. Shift-drag on the sequencer ruler
still updates the same loop region. BPM remains an undoable project edit; still updates the same loop region. BPM remains an undoable project edit; loop
loop preferences persist through Preferences / Apply or clean application exit. preferences persist through Preferences / Apply or clean application exit. Space
Space and menu playback shortcuts work from either floating panel, with normal and menu playback shortcuts work from either floating panel, with normal
numeric/text editing protection. Closing the main window closes all panels only numeric/text editing protection. Closing the main window closes all panels only
after the existing unsaved/recording/backend shutdown checks succeed. after the existing unsaved/recording/backend shutdown checks succeed.
@ -174,12 +190,15 @@ The initial layout used session-only positions and a transport minimum width of
described below. The floating mixer resizes down to 600 × 420 and retains described below. The floating mixer resizes down to 600 × 420 and retains
horizontal track scrolling. horizontal track scrolling.
Verified with `make BUILD_DIR=build/workspace-detach gui test-gui test-gui-instruments Verified with
test-gui-jack` (one line), plus the GUI suite under AddressSanitizer/UBSan. `make BUILD_DIR=build/workspace-detach gui test-gui test-gui-instruments test-gui-jack`
Tests cover repeated reparenting, close-to-dock, resize, shared BPM/loop state, (one line), plus the GUI suite under AddressSanitizer/UBSan. Tests cover
playback shortcuts, failed shutdown and cleanup. repeated reparenting, close-to-dock, resize, shared BPM/loop state, playback
shortcuts, failed shutdown and cleanup.
## Saved panel layouts ---
## Saved Panel Layouts
Preferences / Apply now saves transport/mixer dock state and window rectangles. Preferences / Apply now saves transport/mixer dock state and window rectangles.
Clean application exit also saves their current positions. Startup restores the Clean application exit also saves their current positions. Startup restores the
@ -194,41 +213,46 @@ Existing close-to-dock, Dock all and shortcuts retain their shared widget state.
Tests cover narrow control bounds, save/restart, off-screen restoration, invalid Tests cover narrow control bounds, save/restart, off-screen restoration, invalid
geometry, repeated docking/resizing and shared transport state. geometry, repeated docking/resizing and shared transport state.
## Editor panels and pinned track headers ---
## Editor Panels and Pinned Track Headers
**View → Detach sequencer** and **Detach modular graph** float each complete **View → Detach sequencer** and **Detach modular graph** float each complete
editor, including its toolbar and scroll area. Its main tab offers Show and Dock editor, including its toolbar and scroll area. Its main tab offers Show and Dock
buttons. Closing the floating window docks it; View shortcuts also raise floating buttons. Closing the floating window docks it; View shortcuts also raise
editors. Supported dock locations are their original main-window tabs; arbitrary floating editors. Supported dock locations are their original main-window tabs;
split regions, tab reordering and drag-to-dock are not implemented. Sequencer arbitrary split regions, tab reordering and drag-to-dock are not implemented.
minimum floating size is 1100 × 420; Graph is 1000 × 420. Sequencer minimum floating size is 1100 × 420; Graph is 1000 × 420.
The Piano roll now also has a main-window tab. Select a MIDI clip and open it as The Piano roll now also has a main-window tab. Select a MIDI clip and open it as
before. **View → Float piano roll** switches its existing widgets between the tab before. **View → Float piano roll** switches its existing widgets between the
and a floating window; closing the floating window docks it. The tab's Open tab and a floating window; closing the floating window docks it. The tab's Open
selected MIDI clip button opens the current selection. Default floating behavior selected MIDI clip button opens the current selection. Default floating behavior
is retained for older preferences. A missing/invalid MIDI selection is reported. is retained for older preferences. A missing/invalid MIDI selection is reported.
All panels share project/history, preserve editor selections and scroll offsets, All panels share project/history, preserve editor selections and scroll offsets,
and retain playback shortcuts and text-input protection. and retain playback shortcuts and text-input protection.
**View → Dock all panels** includes all editors. **Reset workspace layout** docks **View → Dock all panels** includes all editors. **Reset workspace layout**
all panels, restores default floating rectangles and resets header width. Use docks all panels, restores default floating rectangles and resets header width.
Preferences / Apply to persist either result. Editor dock state, floating window Use Preferences / Apply to persist either result. Editor dock state, floating
rectangles and header width use the same validated settings/restore path as the window rectangles and header width use the same validated settings/restore path
transport/mixer. Piano content opens lazily for the selected MIDI clip; startup as the transport/mixer. Piano content opens lazily for the selected MIDI clip;
does not invent a clip or save a project-specific selection in application settings. startup does not invent a clip or save a project-specific selection in
application settings.
Arrangement track headers and instrument selectors now stay pinned during Arrangement track headers and instrument selectors now stay pinned during
horizontal scrolling. Drag the divider within the header's final five pixels to horizontal scrolling. Drag the divider within the header's final five pixels to
resize from 180 to 360 pixels; Escape cancels the drag. Clip timing coordinates, resize from 180 to 360 pixels; Escape cancels the drag. Clip timing coordinates,
snap and horizontal scrolling use the resized origin. Header layout is a UI snap and horizontal scrolling use the resized origin. Header layout is a UI
preference, not an undoable project edit. Vertical scrolling remains shared with preference, not an undoable project edit. Vertical scrolling remains shared with
the track rows. Clips covered by the pinned header cannot be selected through it. the track rows. Clips covered by the pinned header cannot be selected through
it.
GUI regressions cover repeated detach/dock and close-to-dock, toolbar ownership, GUI regressions cover repeated detach/dock and close-to-dock, toolbar ownership,
minimum/large resize, shared model/note/clip selection, scrolling, header dragging, minimum/large resize, shared model/note/clip selection, scrolling, header
short-clip hit tests, Apply/restart, reset layout and failed/successful shutdown. dragging, short-clip hit tests, Apply/restart, reset layout and
ASan/UBSan checks retain only the existing system Fontconfig leak suppression. failed/successful shutdown. ASan/UBSan checks retain only the existing system
Fontconfig leak suppression.
Additive clip selection, atomic group commands, phrase clipboard and repeat, Additive clip selection, atomic group commands, phrase clipboard and repeat,
alignment/slip tools, MIDI swing/gate/legato/scale/velocity transforms, batch alignment/slip tools, MIDI swing/gate/legato/scale/velocity transforms, batch
@ -237,8 +261,10 @@ audio fades and static mixer A/B recall are also available. See
Arrangement markers and sections, ripple edits, complete track duplication, MIDI Arrangement markers and sections, ripple edits, complete track duplication, MIDI
splitting/consolidation and composition tools, peak normalization and portable splitting/consolidation and composition tools, peak normalization and portable
media collection are also available. See [COMPOSITION_WORKFLOWS.md](COMPOSITION_WORKFLOWS.md). media collection are also available. See
[COMPOSITION_WORKFLOWS.md](COMPOSITION_WORKFLOWS.md).
A modal audio source editor provides frame selection, reversible processing, A modal audio source editor provides frame selection, reversible processing,
selection export, copy adoption and transient slicing. See selection export, copy adoption and transient slicing. See
[AUDIO_EDITOR.md](AUDIO_EDITOR.md) for commands, undo behavior and mono/size limits. [AUDIO_EDITOR.md](AUDIO_EDITOR.md) for commands, undo behavior and mono/size
limits.

View file

@ -1,18 +1,24 @@
# Decision 0001: Modular engine first # Decision 0001: Modular Engine First
**Status:** Accepted **Status:** Accepted
---
## Context ## Context
The project aims to preserve energyXT's combination of a simple workflow and a The project aims to preserve energyXT's combination of a simple workflow and a
flexible modular routing model. flexible modular routing model.
---
## Decision ## Decision
Use one unified processing graph for sources, instruments, effects, mixers, Use one unified processing graph for sources, instruments, effects, mixers, and
and outputs. The sequencer is a timeline/event source integrated with that outputs. The sequencer is a timeline/event source integrated with that graph.
graph. The default GUI is simple and sequencer-oriented, while a graph editor The default GUI is simple and sequencer-oriented, while a graph editor exposes
exposes routing when needed. routing when needed.
---
## Consequences ## Consequences

View file

@ -1,11 +1,15 @@
# Decision 0002: C core with FLTK/C++ GUI # Decision 0002: C Core with FLTK/C++ GUI
**Status:** Accepted **Status:** Accepted
---
## Context ## Context
The project prioritizes a readable, small C implementation while using FLTK The project prioritizes a readable, small C implementation while using FLTK for
for a lightweight desktop GUI. a lightweight desktop GUI.
---
## Decision ## Decision
@ -13,6 +17,8 @@ Keep the engine, DSP, project model, graph, and public core API in C (C99
baseline). Use C++ only for FLTK windows and widgets. Communicate through baseline). Use C++ only for FLTK windows and widgets. Communicate through
documented C headers with `extern "C"` guards. documented C headers with `extern "C"` guards.
---
## Consequences ## Consequences
- No C++ classes, STL containers, exceptions, or FLTK types cross the C API. - No C++ classes, STL containers, exceptions, or FLTK types cross the C API.

View file

@ -1,19 +1,25 @@
# Decision 0003: Real-time thread rules # Decision 0003: Real-Time Thread Rules
**Status:** Accepted **Status:** Accepted
---
## Context ## Context
Audio glitches and unsafe graph changes can undermine the whole application. Audio glitches and unsafe graph changes can undermine the whole application.
---
## Decision ## Decision
The audio callback performs bounded processing using preallocated resources. The audio callback performs bounded processing using preallocated resources. It
It performs no allocation/free, blocking locks, file I/O, logging, GUI calls, performs no allocation/free, blocking locks, file I/O, logging, GUI calls,
plugin loading, or waits. Graphs are prepared outside the callback and active plugin loading, or waits. Graphs are prepared outside the callback and active
graph resources are retired only after safe reclamation. graph resources are retired only after safe reclamation.
---
## Consequences ## Consequences
Thread ownership and publication mechanisms must be documented and tested. Thread ownership and publication mechanisms must be documented and tested. Any
Any exception to these rules requires explicit evidence and review. exception to these rules requires explicit evidence and review.

View file

@ -1,7 +1,9 @@
# Decision 0004: JACK backend and bounded plan handoff # Decision 0004: JACK Backend and Bounded Plan Handoff
**Status:** Accepted **Status:** Accepted
---
## Backend ## Backend
Use the JACK C client API. The inspected system provides PipeWire's maintained Use the JACK C client API. The inspected system provides PipeWire's maintained
@ -22,10 +24,12 @@ framework or second driver implementation.
The optional dependency provides actual audio callbacks and port routing. The optional dependency provides actual audio callbacks and port routing.
PipeWire's installed JACK library is MIT licensed (`pipewire-jack/COPYING`). PipeWire's installed JACK library is MIT licensed (`pipewire-jack/COPYING`).
Traditional JACK client libraries use LGPL licensing; review the actual linked Traditional JACK client libraries use LGPL licensing; review the actual linked
implementation, transitive libraries and notices when distributing. Nothing implementation, transitive libraries and notices when distributing. Nothing is
is bundled or installed here. ALSA is still a future MIDI candidate. bundled or installed here. ALSA is still a future MIDI candidate.
## C99 and atomic operations ---
## C99 and Atomic Operations
Retain C99 and use GCC/Clang `__atomic` builtins. This is a documented compiler Retain C99 and use GCC/Clang `__atomic` builtins. This is a documented compiler
extension, avoiding a language-standard change and keeping the existing core extension, avoiding a language-standard change and keeping the existing core
@ -34,12 +38,15 @@ unsigned-int atomics to be always lock-free, or returns unsupported before
callbacks are installed. Naturally aligned fields reside in calloc-allocated callbacks are installed. Naturally aligned fields reside in calloc-allocated
objects. There is no mutex or libatomic fallback in the callback path. objects. There is no mutex or libatomic fallback in the callback path.
## Ownership and publication ---
## Ownership and Publication
One serialized control producer and one audio consumer share two pointer slots: One serialized control producer and one audio consumer share two pointer slots:
- `pending`: producer publishes a fully compiled plan with release; consumer - `pending`: producer publishes a fully compiled plan with release; consumer
exchanges it for NULL with acquire/release at the next eligible block boundary. exchanges it for NULL with acquire/release at the next eligible block
boundary.
- `retired`: consumer releases its previous active plan into this slot only - `retired`: consumer releases its previous active plan into this slot only
after its final access. Producer exchanges with NULL using acquire/release, after its final access. Producer exchanges with NULL using acquire/release,
then destroys that acknowledged plan on the control thread. then destroys that acknowledged plan on the control thread.
@ -50,40 +57,47 @@ callers never receive plan pointers through the realtime/backend API.
A full pending slot returns capacity without replacing anything. A full retired A full pending slot returns capacity without replacing anything. A full retired
slot defers adoption while the current plan continues processing. The consumer 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. performs at most one adoption per block and never loops waiting for the
Producer collection can overlap processing because the retired plan is no producer. Producer collection can overlap processing because the retired plan is
longer reachable from the consumer. Collection clears the slot before freeing; no longer reachable from the consumer. Collection clears the slot before
consumer may then retire another, distinct plan. Publication never immediately freeing; consumer may then retire another, distinct plan. Publication never
frees the previously active plan. immediately frees the previously active plan.
Submitting collects any already retired plan, so a producer repeatedly submitting Submitting collects any already retired plan, so a producer repeatedly
also reclaims memory. If it becomes idle, one pending, one active, and one retired submitting also reclaims memory. If it becomes idle, one pending, one active,
plan bound retained resources (at most three plans, each with up to 64 MiB of and one retired plan bound retained resources (at most three plans, each with up
sample buffers). Periodic collect is recommended. Shutdown closes the JACK client to 64 MiB of sample buffers). Periodic collect is recommended. Shutdown closes
and synchronizes its callbacks before destroying any active/pending/retired plan. the JACK client and synchronizes its callbacks before destroying any
Standalone realtime-engine callers must stop and join their consumer themselves. 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, ## Status, Errors and Recovery
not one transactional state. Counters wrap naturally; no string formatting,
logging, allocation, GUI operation or blocking synchronization occurs in project Unsigned-int status fields use lock-free atomics. Values are individual
callbacks. JACK's shutdown notification only stores a flag and leaves cleanup to snapshots, not one transactional state. Counters wrap naturally; no string
the control thread. Other notifications store the current format and xrun count. 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 Buffer changes within the configured maximum require no allocation. Larger
blocks are fully silenced and reported as capacity errors; smaller blocks can blocks are fully silenced and reported as capacity errors; smaller blocks can
resume processing. Sample-rate changes latch a flag and silence processing resume processing. Sample-rate changes latch a flag and silence processing
rather than replaying an oscillator compiled for a different rate. Close/reopen 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. and resubmit after a rate change or server loss. There is no automatic
JACK-deactivate pauses the retained plan; restart continues its oscillator phase. reconnect. JACK-deactivate pauses the retained plan; restart continues its
Plan replacement starts the newly compiled oscillator at phase zero, without oscillator phase. Plan replacement starts the newly compiled oscillator at phase
crossfade. Smooth parameter ramps and click-free graph transitions are future work. 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 No stereo, recording, MIDI, native PipeWire API, GUI engine controls or hardware
configuration dialogs are included. configuration dialogs are included.
## Adoption acknowledgement and stopped editing ---
## Adoption Acknowledgement and Stopped Editing
The pending pointer has an associated atomic revision. Producer stores the The pending pointer has an associated atomic revision. Producer stores the
revision before release-publishing the pointer. Consumer acquire-loads a revision before release-publishing the pointer. Consumer acquire-loads a
@ -97,10 +111,10 @@ snapshots, not plan pointers or a multi-field transactional snapshot.
While JACK is stopped, a backend submission builds a separate realtime engine, While JACK is stopped, a backend submission builds a separate realtime engine,
submits/compiles the graph there, and replaces the retained engine only on submits/compiles the graph there, and replaces the retained engine only on
success. Process callbacks cannot run on an inactive client or after successful success. Process callbacks cannot run on an inactive client or after successful
deactivation, and notification callbacks do not access the engine pointer. deactivation, and notification callbacks do not access the engine pointer. The
The control thread can therefore reclaim all old state safely. Counters and control thread can therefore reclaim all old state safely. Counters and
oscillator phases restart for that new engine epoch. The standalone realtime oscillator phases restart for that new engine epoch. The standalone realtime API
API keeps its one-pending-slot contract regardless of external stop state. keeps its one-pending-slot contract regardless of external stop state.
The callback still adopts at most one plan and never allocates/frees. Revision The callback still adopts at most one plan and never allocates/frees. Revision
ordering is tested alongside the plan's rendered gain under 2,000 concurrent ordering is tested alongside the plan's rendered gain under 2,000 concurrent

View file

@ -1,4 +1,4 @@
# 0005: Preloaded WAV clips and control-thread recording consumer # 0005: Preloaded WAV Clips and Control-Thread Recording Consumer
Status: accepted. Status: accepted.
@ -11,16 +11,19 @@ codec library needs its own dependency and licensing review.
Compile musical clip ranges and decoded, shared-per-path mono buffers while Compile musical clip ranges and decoded, shared-per-path mono buffers while
stopped. The callback renders the timeline into a preallocated buffer consumed stopped. The callback renders the timeline into a preallocated buffer consumed
by an explicit graph source node, preserving graph routing rather than mixing by an explicit graph source node, preserving graph routing rather than mixing
outside the graph. The old snapshot remains until candidate preparation succeeds. outside the graph. The old snapshot remains until candidate preparation
All track clips currently share one source; per-track routing/mixing is deferred. succeeds. All track clips currently share one source; per-track routing/mixing
is deferred.
Use a bounded SPSC ring for callback input capture, with release/acquire index Use a bounded SPSC ring for callback input capture, with release/acquire index
publication and a GUI/control-thread consumer. This adds no worker-thread publication and a GUI/control-thread consumer. This adds no worker-thread
dependency; a two-second ring tolerates normal 100 ms polling but exposes dependency; a two-second ring tolerates normal 100 ms polling but exposes
overflow if the GUI or disk stalls. Sticky errors prevent silent time compression. overflow if the GUI or disk stalls. Sticky errors prevent silent time
Finalization requires callback quiescence. Future long-session/background I/O compression. Finalization requires callback quiescence. Future
work can reuse the ring contract with a dedicated consumer. long-session/background I/O work can reuse the ring contract with a dedicated
consumer.
Store external absolute paths in version 2 metadata and retain version 1 loads. Store external absolute paths in version 2 metadata and retain version 1 loads.
Missing/unreadable assets are reported silence, with explicit relinking; portable Missing/unreadable assets are reported silence, with explicit relinking;
bundles and content-addressed assets are deferred. See AUDIO_FILES.md for limits. portable bundles and content-addressed assets are deferred. See AUDIO_FILES.md
for limits.

View file

@ -1,16 +1,16 @@
# 0006 — Bounded MIDI notes and internal instrument # 0006 — Bounded MIDI Notes and Internal Instrument
Status: accepted. Status: accepted.
Use the existing JACK backend for timestamped MIDI input, avoiding another Use the existing JACK backend for timestamped MIDI input, avoiding another
backend/dependency. Store source-relative notes in the C project and v3 format; backend/dependency. Store source-relative notes in the C project and v3 format;
read v1/v2 without MIDI. Prepare sample spans on the control thread and render read v1/v2 without MIDI. Prepare sample spans on the control thread and render a
a small sine instrument into its own graph source buffer. Use a piano roll to small sine instrument into its own graph source buffer. Use a piano roll to meet
meet draw/play/save/reopen acceptance before adding MIDI capture or SMF import. draw/play/save/reopen acceptance before adding MIDI capture or SMF import.
Keep explicit bounds: 4096 project notes, 32 overlapping scheduled notes, 32 live Keep explicit bounds: 4096 project notes, 32 overlapping scheduled notes, 32
voices and 256 events/block. Reject scheduled overcapacity at preparation; steal live voices and 256 events/block. Reject scheduled overcapacity at preparation;
oldest live voices. Panic handles event loss, disconnects and callback failures; steal oldest live voices. Panic handles event loss, disconnects and callback
Stop/seek resets live state. Immediate note-off and a fixed sine voice keep this failures; Stop/seek resets live state. Immediate note-off and a fixed sine voice
prototype small, at the cost of possible clicks and no expressive controllers. keep this prototype small, at the cost of possible clicks and no expressive
See ../MIDI.md for ownership, detailed limits and validation. controllers. See ../MIDI.md for ownership, detailed limits and validation.

View file

@ -1,4 +1,4 @@
# 0007 — Track mixing, paired stereo buses and snapshot history # 0007 — Track Mixing, Paired Stereo Buses and Snapshot History
Status: accepted. Status: accepted.

View file

@ -1,19 +1,19 @@
# 0008 — Native processors and source release preparation # 0008 — Native Processors and Source Release Preparation
Status: accepted. Status: accepted.
Add a versioned copied C descriptor with control-thread prepare/destroy and Add a versioned copied C descriptor with control-thread prepare/destroy and
single-owner realtime reset/process. Prove effects/sources in the existing graph, single-owner realtime reset/process. Prove effects/sources in the existing
with two independent mono instances for stereo, and event instruments rendered graph, with two independent mono instances for stereo, and event instruments
through the public instrument-source buffer boundary. Keep trusted code statically rendered through the public instrument-source buffer boundary. Keep trusted code
linked. Native project serialization and graph event ports need more contracts; statically linked. Native project serialization and graph event ports need more
reject unsupported saves without replacing files. Defer LV2/CLAP/VST3 after a contracts; reject unsupported saves without replacing files. Defer LV2/CLAP/VST3
concrete evaluation rather than linking SDKs speculatively. after a concrete evaluation rather than linking SDKs speculatively.
Keep format v4 and test migration plus process-crash/write-failure behavior. Keep format v4 and test migration plus process-crash/write-failure behavior.
Provide deterministic source exports and optimized release builds, staged Provide deterministic source exports and optimized release builds, staged
core/GUI installation, exact uninstall and installed C/C++ consumer tests. Use core/GUI installation, exact uninstall and installed C/C++ consumer tests. Use
Python standard-library tools only for packaging/testing; no application runtime Python standard-library tools only for packaging/testing; no application runtime
dependency or system changes. Document dependency licenses, Linux routing and dependency or system changes. Document dependency licenses, Linux routing and
measured offline performance separately from unverified hardware/endurance. measured offline performance separately from unverified hardware/endurance. See
See NATIVE_API.md, PLUGIN_HOSTING.md, RELEASING.md and RECOVERY.md. NATIVE_API.md, PLUGIN_HOSTING.md, RELEASING.md and RECOVERY.md.

View file

@ -1,52 +1,55 @@
# 0009 — Parameter processors and bounded LADSPA hosting # 0009 — Parameter Processors and Bounded LADSPA Hosting
Status: accepted (2026-10-27). Status: accepted (2026-10-27).
Native ABI 1 remains compatible. ABI 2 adds explicitly parameter-only configuration, Native ABI 1 remains compatible. ABI 2 adds explicitly parameter-only
metadata, validation and sample-offset parameter/MIDI ordering. It is a public C configuration, metadata, validation and sample-offset parameter/MIDI ordering.
facility; opaque state and automatic native descriptor registration in project It is a public C facility; opaque state and automatic native descriptor
files remain unsupported. Native instrument and gain examples exercise the registration in project files remain unsupported. Native instrument and gain
interface before adding a dynamically loaded module lifetime. examples exercise the interface before adding a dynamically loaded module
lifetime.
Select LADSPA 1.1 for the first external host. Reconsidering earlier candidates, Select LADSPA 1.1 for the first external host. Reconsidering earlier candidates,
LV2 needs RDF/features/URIDs/workers, CLAP needs host scheduling/extension LV2 needs RDF/features/URIDs/workers, CLAP needs host scheduling/extension
negotiation, and VST3 needs a separate C++ SDK adapter and release-specific license negotiation, and VST3 needs a separate C++ SDK adapter and release-specific
review. LADSPA adds a mature Linux C ABI, installed real plugins, straightforward license review. LADSPA adds a mature Linux C ABI, installed real plugins,
port negotiation and no RDF/SDK runtime library. Its specification is old and straightforward port negotiation and no RDF/SDK runtime library. Its
stable, not an actively expanding instrument/event ecosystem. This trades MIDI, specification is old and stable, not an actively expanding instrument/event
editors, opaque state and modern automation for a small testable first host. ecosystem. This trades MIDI, editors, opaque state and modern automation for a
No general multi-format abstraction is introduced. small testable first host. No general multi-format abstraction is introduced.
Use the installed LGPL-2.1-or-later header, system dynamic loader (`-ldl`), and Use the installed LGPL-2.1-or-later header, system dynamic loader (`-ldl`), and
user-selected, trusted module files. Neither the header nor plugin binaries are user-selected, trusted module files. Neither the header nor plugin binaries are
vendored. Header absence or `XTC_DISABLE_LADSPA` yields unsupported hosting stubs; vendored. Header absence or `XTC_DISABLE_LADSPA` yields unsupported hosting
metadata can still be loaded/saved. Plugin licenses are independent of the host stubs; metadata can still be loaded/saved. Plugin licenses are independent of
and require review before distribution. See LICENSES.md. the host and require review before distribution. See LICENSES.md.
Support zero/one mono audio input and exactly one mono output, at most 64 ports Support zero/one mono audio input and exactly one mono output, at most 64 ports
and 16 input controls. Graph stereo consists of independent instances, not a and 16 input controls. Graph stereo consists of independent instances, not a
stereo LADSPA layout. Reject incompatible layouts and identities explicitly. stereo LADSPA layout. Reject incompatible layouts and identities explicitly.
Control outputs are connected to private storage, not exposed as graph events. Control outputs are connected to private storage, not exposed as graph events.
LADSPA has no MIDI or opaque state API: save control values only. Parameter LADSPA has no MIDI or opaque state API: save control values only. Parameter
changes rebuild plans on the control thread; sample automation is provided through changes rebuild plans on the control thread; sample automation is provided
parameter processors. Rate-scaled bounds are recalculated at compile. Instance history/tails through parameter processors. Rate-scaled bounds are recalculated at compile.
reset on recompile, not every transport seek (LADSPA has no realtime reset rule). Instance history/tails reset on recompile, not every transport seek (LADSPA has
Latency compensation is unsupported: the supported set is zero-latency amp, no realtime reset rule). Latency compensation is unsupported: the supported set
inverter and sine source, not arbitrary lookahead/delay/routing plugins. is zero-latency amp, inverter and sine source, not arbitrary
lookahead/delay/routing plugins.
Discovery, dlopen, descriptor calls, instantiate, connect, activate, deactivate, Discovery, dlopen, descriptor calls, instantiate, connect, activate, deactivate,
cleanup and dlclose are control-thread operations. Only preallocated copies and cleanup and dlclose are control-thread operations. Only preallocated copies and
run execute in audio; the usual pending/retired plan handoff owns module handles. run execute in audio; the usual pending/retired plan handoff owns module
Partial initialization unwinds on the control thread. The host cannot enforce handles. Partial initialization unwinds on the control thread. The host cannot
third-party callback behavior, bound CPU time, catch native crashes/hangs, or enforce third-party callback behavior, bound CPU time, catch native
undo memory corruption. There is no sandbox or crash recovery inside the audio crashes/hangs, or undo memory corruption. There is no sandbox or crash recovery
process. A fault may kill xtc; previously saved documents/recording recovery files inside the audio process. A fault may kill xtc; previously saved
remain subject to their ordinary durability guarantees. Test only trusted modules. documents/recording recovery files remain subject to their ordinary durability
guarantees. Test only trusted modules.
Project v5 stores plain identity/layout/control metadata. Parsing never opens Project v5 stores plain identity/layout/control metadata. Parsing never opens
modules. Compile failure preserves the source document and active plan. GUI Open modules. Compile failure preserves the source document and active plan. GUI Open
rejects missing/incompatible modules, retains its current document, and reports rejects missing/incompatible modules, retains its current document, and reports
saved module identity/path. Restore the compatible module at that path and retry; saved module identity/path. Restore the compatible module at that path and
a metadata-only C client can preserve/save the document without the module. retry; a metadata-only C client can preserve/save the document without the
No silent replacement or deletion occurs. Same-ID/label/ports do not authenticate module. No silent replacement or deletion occurs. Same-ID/label/ports do not
binaries or guarantee unchanged sound across plugin upgrades. authenticate binaries or guarantee unchanged sound across plugin upgrades.

View file

@ -1,13 +1,14 @@
# 0010 — Bounded CLAP, VST2 and VST3 effect loading # 0010 — Bounded CLAP, VST2 and VST3 Effect Loading
Accepted. The user's request to support these three formats Accepted. The user's request to support these three formats supersedes the
supersedes the earlier recommendation to choose only one external format. earlier recommendation to choose only one external format.
Keep the engine/model/host adapters in C99. CLAP uses pinned MIT headers and VST3 Keep the engine/model/host adapters in C99. CLAP uses pinned MIT headers and
uses the upstream BSD-3-Clause generated C ABI. No SDK runtime library is needed; VST3 uses the upstream BSD-3-Clause generated C ABI. No SDK runtime library is
Linux dynamic loading and POSIX thread identity are existing platform facilities. needed; Linux dynamic loading and POSIX thread identity are existing platform
VST2 uses original minimal compatibility declarations, not the discontinued SDK. facilities. VST2 uses original minimal compatibility declarations, not the
See third_party/README.md and LICENSES.md for exact revisions and notices. discontinued SDK. See third_party/README.md and LICENSES.md for exact revisions
and notices.
Reuse the existing plugin graph node, paired mono instances and immutable plan Reuse the existing plugin graph node, paired mono instances and immutable plan
ownership. The common facade routes LADSPA to its existing implementation and ownership. The common facade routes LADSPA to its existing implementation and
@ -18,25 +19,27 @@ configuration is used; no parameter/controller/editor or opaque-state access is
exposed. These are usable effect loaders, not complete implementations of every exposed. These are usable effect loaders, not complete implementations of every
extension in the formats. Unsupported layouts fail explicitly. extension in the formats. Unsupported layouts fail explicitly.
Project v11 adds format plus CLAP stable ID or VST3 class ID to each saved plugin. Project v11 adds format plus CLAP stable ID or VST3 class ID to each saved
Older plugins migrate to LADSPA with an empty extra ID. VST2 retains its integer plugin. Older plugins migrate to LADSPA with an empty extra ID. VST2 retains its
unique ID. Reopening checks format/index/identity/name/layout; metadata operations integer unique ID. Reopening checks format/index/identity/name/layout; metadata
never open modules. This does not authenticate a binary or freeze defaults across operations never open modules. This does not authenticate a binary or freeze
plugin releases. No modified opaque state exists in this initial API to persist. defaults across plugin releases. No modified opaque state exists in this initial
API to persist.
Discovery and instance creation execute trusted module code on one serialized Discovery and instance creation execute trusted module code on one serialized
main/control thread. A module cache balances CLAP entry init/deinit and Linux main/control thread. A module cache balances CLAP entry init/deinit and Linux
VST3 ModuleEntry/ModuleExit across overlapping graph instances and retired plans. VST3 ModuleEntry/ModuleExit across overlapping graph instances and retired
CLAP start_processing runs on the processing owner; stop_processing runs after plans. CLAP start_processing runs on the processing owner; stop_processing runs
quiescence with the symbolic audio-thread context. CLAP callback requests use after quiescence with the symbolic audio-thread context. CLAP callback requests
lock-free flags and are serviced by xtc_plugin_idle on main/control (the GUI timer use lock-free flags and are serviced by xtc_plugin_idle on main/control (the GUI
calls it). Restart/I/O-change requests latch an error for re-preparation; the audio timer calls it). Restart/I/O-change requests latch an error for re-preparation;
callback does not reconfigure or wait for GUI work. CLAP process wakeups need no the audio callback does not reconfigure or wait for GUI work. CLAP process
queue because playback processes every block, including silence. wakeups need no queue because playback processes every block, including silence.
Only preallocated buffers and plugin processing occur in audio. Error status Only preallocated buffers and plugin processing occur in audio. Error status
silences the failed block; nonfinite/subnormal samples are sanitized. Plugin code silences the failed block; nonfinite/subnormal samples are sanitized. Plugin
can still allocate, block, corrupt memory or crash; there is no sandbox, scanner code can still allocate, block, corrupt memory or crash; there is no sandbox,
subprocess or recoverable crash boundary. Modules unload only after destruction scanner subprocess or recoverable crash boundary. Modules unload only after
on a quiescent/control owner. Real third-party plugins and hardware timing remain destruction on a quiescent/control owner. Real third-party plugins and hardware
unqualified; original ABI fixtures demonstrate the bounded contracts. timing remain unqualified; original ABI fixtures demonstrate the bounded
contracts.

View file

@ -1,27 +1,31 @@
# 0011 — Prepared MIDI track instruments # 0011 — Prepared MIDI Track Instruments
Accepted. Extend the modern C99 adapters with zero-audio-input, Accepted. Extend the modern C99 adapters with zero-audio-input, mono-output
mono-output instruments and one event input. Keep graph effects separate from instruments and one event input. Keep graph effects separate from track
track bindings. Each assigned track owns one prepared instance; the existing bindings. Each assigned track owns one prepared instance; the existing
instrument source sums their mixed output and the sine output of unassigned instrument source sums their mixed output and the sine output of unassigned
tracks. A stable saved live target prevents accidental broadcast/double playback. tracks. A stable saved live target prevents accidental broadcast/double
playback.
Bindings are copied metadata in the project/history model, serialized in v12. Bindings are copied metadata in the project/history model, serialized in v12.
Loading metadata never executes modules. GUI edits prepare a candidate before Loading metadata never executes modules. GUI edits prepare a candidate before
publication, preserving the previous project on missing/invalid binaries. Existing publication, preserving the previous project on missing/invalid binaries.
quiescent plan retirement owns destruction; no module operation belongs in audio. Existing quiescent plan retirement owns destruction; no module operation belongs
in audio.
Preallocate 256-frame audio scratch and 256 incoming events per chunk. Stable Preallocate 256-frame audio scratch and 256 incoming events per chunk. Stable
insertion orders reset, scheduled off/on and live events without callback sort or insertion orders reset, scheduled off/on and live events without callback sort
allocation. VST3 translates into a bounded 2304-event IEventList (2048 possible or allocation. VST3 translates into a bounded 2304-event IEventList (2048
held keys plus 256 incoming events); panic expands held keys to note-offs. CLAP possible held keys plus 256 incoming events); panic expands held keys to
prefers MIDI where supported, otherwise native note events; VST2 uses MIDI ABI note-offs. CLAP prefers MIDI where supported, otherwise native note events; VST2
messages. Native CLAP and VST3 currently omit controller mapping. Existing uses MIDI ABI messages. Native CLAP and VST3 currently omit controller mapping.
32 simultaneous scheduled notes and 4096 stored notes remain validation bounds. Existing 32 simultaneous scheduled notes and 4096 stored notes remain validation
bounds.
Discontinuities send all-off and chase spanning notes with fresh note-ons. Discontinuities send all-off and chase spanning notes with fresh note-ons. Panic
Panic suppresses already-started scheduled notes. Neither operation promises suppresses already-started scheduled notes. Neither operation promises opaque
opaque DSP reconstruction or removal of release tails. Overflow/process errors DSP reconstruction or removal of release tails. Overflow/process errors silence
silence the block and arm reset for the following render. Hosts cannot guarantee the block and arm reset for the following render. Hosts cannot guarantee that
that third-party processing is realtime safe. Qualification covers original ABI third-party processing is realtime safe. Qualification covers original ABI
fixtures; arbitrary third-party plugins, physical audio and MIDI remain unverified. fixtures; arbitrary third-party plugins, physical audio and MIDI remain
unverified.