Phase 24: add mix buses, coherent peak meters and master clipping indicators

This commit is contained in:
Johannes Findeisen 2026-10-27 16:15:34 +01:00
commit 2fd7724065
32 changed files with 632 additions and 61 deletions

View file

@ -21,7 +21,7 @@
zoom/selection and graph positions are session UI state. Undo retains 32 edits
plus baseline; allocation failures are reported, not silently hidden. Media is
never removed by undo/delete. Broad screen-reader/accessibility testing is pending.
- Format v8 reads v1–v7 and uses relative local media references. External absolute
- Format v9 reads v1–v8 and uses relative local media references. External absolute
media/module paths need manual repair after relocation. Saving does not bundle
media; missing media stays referenced, missing plugins reject GUI installation.
Unknown data is rejected rather than stripped. Post-rename directory-sync errors
@ -42,3 +42,8 @@ PERFORMANCE.md, PLUGIN_HOSTING.md and RECOVERY.md for tested contracts/evidence.
- Graph folders, per-track graph routing and track templates remain deferred:
timeline and MIDI sources expose shared mixes. Layout is session-only; no
feedback cycles, plugin isolation or latency compensation are supplied.
- 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
meters are pre-bus; bus meters are pre-graph; master is post-graph. Sample peaks
and sticky clipping do not provide loudness, true-peak analysis or limiting.

View file

@ -9,8 +9,7 @@ recording. Graph edits retain their existing live publication behavior.
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. If any track is soloed, only soloed, unmuted tracks
play. WAV clips and scheduled MIDI use their containing track's settings before
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
@ -52,12 +51,50 @@ uses 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
report the maximum of both sides on the processing owner only.
Mixer Output L/R bars show last-block post-graph absolute peaks, polled at 100 ms.
Peaks above unity turn red. Independent lock-free integer snapshots avoid sharing
DSP pointers or float state between callback and GUI; stopped meters show zero.
They are output meters, without per-track RMS, peak hold or loudness analysis.
There is no limiter: finite summed output can exceed unity. GUI polling may miss
brief peaks between refreshes; these bars are not overload history.
Track and bus meters measure coherent audio plus scheduled MIDI sums, after their
own gain/pan/mute/solo controls, before the shared modular graph. A track tap is
before its parent bus: a muted bus can have active member-track meters. Bus taps
are after bus controls. Master L/R measure the final graph output after transport
fades; downstream effects and graph routing can change its level independently.
The backend retains interval sample-peak maxima until one serialized control
reader consumes them with `xtc_backend_read_meters`. The GUI polls about every
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 remain last-block linear values. GUI bars display 20*log10(peak) dBFS, floor
-90, instantaneous attack and 20 dB/second release, with a +6 display ceiling.
Each channel latches red CLIP at linear peak >=1 (0 dBFS) until Reset peaks.
Reset is session state, not an audio edit. Selection changes reset selected-strip
history. These are sample-peak meters, not RMS, LUFS or true-peak measurements.
There is no limiter; summed levels can exceed unity.
## Mix buses and gain staging (Phase 24)
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.
They contain no clips and feed master directly; nesting is rejected. Deleting a
bus reroutes its members to master without deleting their clips; undo restores
routing. Editing uses the existing stopped-transport preparation workflow.
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
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
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.
Live MIDI, external audio inputs and independent graph oscillators remain
unassigned and outside this solo domain.
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
of shared sources. Independent per-track aux sends, sidechains, bus plugin
inserts and nested buses are not represented and remain deferred.
Preparation allocates shared stereo monitor scratch on the control thread:
2 * track_count * max_frames * sizeof(float), at most 32 MiB at current limits.
The callback clears, accumulates and scans bounded buffers, with no allocation,
I/O, locks or GUI calls. Immutable audio/MIDI snapshots borrow this scratch until
stopped replacement or backend destruction.
## Undo and keyboard workflow
@ -96,3 +133,7 @@ saved/reopened hard-right playback and stereo peaks using only software clients.
A native Xvfb mouse smoke check covers sliders and automation drawing.
Physical stereo devices, listening quality, realtime endurance and high-density
project performance remain unverified; no new dependency was introduced.
`test_buses` covers bus routing, exact taps, audio/MIDI cancellation, controls,
automation, persistence, invalid routes, deletion/undo and dBFS ballistics. Backend
tests cover consuming interval peaks; GUI tests cover routing undo and clipping.

View file

@ -1,4 +1,4 @@
# Project format v8 (reads v1–v7)
# Project format v9 (reads v1–v8)
Phases 5–15 use a bounded text format implemented in C99 without a parser library.
The extension is `.xtc`. File operations run exclusively on the control thread.
@ -192,7 +192,7 @@ or a claim of power-cut qualification.
## Phase 22: version 8 audio clip settings
Current writer emits v8 and reads v1–v7 with unity clip gain and no explicit
Phase 22 introduced v8 and reads v1–v7 with unity clip gain and no explicit
clip fades. After **every** clip's `MIDI` record (including silent and MIDI
clips), v8 requires:
@ -215,3 +215,15 @@ a fresh ID/source offset, retains outer explicit fades and clears inner ones.
No waveform cache or source audio is serialized. Checked-in v1–v7 migrations,
v8 exact roundtrips, malformed new metadata and byte-mutation tests cover the
format change (`make test test-portability test-audio-editing`).
## Phase 24: version 9 mix routing
The writer emits v9. Immediately after each TRACK's MIX record it writes
`ROUTE is_bus bus_id`. `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.

View file

@ -587,10 +587,26 @@ Provide the essential mixing controls needed to finish a small or medium-sized p
- Test mute, solo, pan, gain, and routing interactions.
### Acceptance criteria
- Mixer controls behave consistently across tracks, buses, and master output.
- Meter readings correspond to documented signal points and units.
- Solo and mute behavior is covered by automated tests.
- No mixer operation introduces allocations, locks, or other prohibited work in the audio callback.
- [x] Mixer controls behave consistently across tracks, buses, and master output.
- [x] Meter readings correspond to documented signal points and units.
- [x] Solo and mute behavior is covered by automated tests.
- [x] No mixer operation introduces allocations, locks, or other prohibited work in the audio callback.
Implemented: one-level stereo mix buses with routing, shared gain/pan automation,
inclusive solo and mute precedence; coherent audio/MIDI track and bus peaks;
interval master peaks, dBFS ballistics and sticky clipping; v9 persistence reading
v1–v8. See MIXER.md for exact taps, gain staging and callback scratch bounds.
Independent per-track aux sends/sidechains and per-bus graph effects remain
conditional/deferred because graph sources still expose shared mixes.
Validation: warning-enabled core/GUI builds and unit/integration tests, bus and
backend fault tests, GUI plugin/workflow tests, software JACK GUI/audio/MIDI,
allocation-failure/callback-allocation guard, static analysis, installation and
package relocation, optional-dependency-free build, and core/GUI ASan+UBSan.
The live MIDI connection test required one retry after a JACK connection failure;
its full playback assertions passed on retry. GUI leak checks use the existing
fontconfig suppression; core checks are unsuppressed. Physical hardware,
human listening and extended high-density endurance remain unverified.
## Phase 25 — Plugin and Instrument Usability

View file

@ -58,9 +58,9 @@ 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
backend refusal) keeps the previous graph and plan. It introduces no new node
type or project record: v8 persistence already represents the resulting graph.
There are no track templates, folders or per-track graph buses yet. The flat
track model and shared source buffers cannot represent those relationships
faithfully; these conditional roadmap tasks remain deferred.
Phase 24 adds one-level stereo mix buses before shared graph sources (see
MIXER.md). Track templates, folders and independent per-track/bus graph inserts
remain deferred because graph sources still expose shared mixes.
## Transactions, cycles and realtime ownership

View file

@ -125,3 +125,8 @@ branches. L arranges nodes by signal flow. Select a wire and press I to insert a
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
by “ext in”; they do not imply silence. See [ROUTING.md](ROUTING.md).
Phase 24: Mixer also offers Add bus and a Route choice for ordinary tracks.
Select 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.
See MIXER.md for solo rules, gain staging and meter taps.