xtc/docs/CONTROL.md
2026-11-06 19:05:14 +01:00

9.3 KiB
Raw Blame History

Automation and Expressive Control

Track Gain/Pan lanes in the Mixer apply to audio and scheduled instrument voices. They use absolute project ticks, linear or hold interpolation, hold-first/last outside points and the constant project tempo snapshot. A clip trim/move does not move track or effect automation: lanes belong to the project timeline. Instrument volume is controlled by its track Gain lane; the current 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. Enter port tick mode value: mode 0 interpolates linearly, 1 holds until the next point, and -1 removes the point at that port/tick (value ignored). Port names, ranges and current tick are displayed. There are at most 32 points per plugin; setting an existing port/tick replaces it. Integer/toggle controls require step mode. Invalid values, ticks, ports or duplicate persisted points 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 apply before run at each exact sample. Distinct ticks rounding to one sample resolve to the later tick. Linear curves avoid stepping continuous controls; intentional step curves may click. The initial implementation calls run(1) while any lane exists, with connected preallocated buffers. CPU cost can be significant; only the documented LADSPA set is qualified. No worker, allocator, loader or clock is called from the host audio path. Native ABI 2 callers 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), and CC64 to sustain (>=64 down). Volume/pan ramp over 5 ms. These default mappings apply to live sine voices, not track faders or prerecorded clips; controller capture, automatic learn and plugin MIDI are not supported. CC120/123 and Panic release all live voices and sustain and suppress already-started scheduled notes. Note-on velocity zero decodes to note-off. Repeated live note-on on one channel/pitch retriggers one voice; note-off releases it or marks it pending while sustain is held. Sustain-up releases pending voices, never newer retriggers. Seek, stop, teardown and disconnect clear live voices; plan reset returns controller values to unity/center. Equal-offset live events execute in input order before the sample; arrange note ends precede starts, and 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 and the project tempo. Repeated render/save/reopen, odd block partition, exact endpoints, seek/plan replacement, sustain/retrigger and controller ramp tests:

cmake --build build --target check test-control test-plugins test-gui-plugins test-plugins-real

Physical controller devices and listening remain unqualified; no tempo map, 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). IDs never mean a dense list index and are not DSP pointers:

Kind Owner Parameter IDs Metadata/reset defaults
TRACK Track or mix-bus stable uint64 ID 0 gain, 1 pan Gain 0..4 linear, default 1; pan -1..1 balance, default 0
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

xtc_project_parameter returns copied metadata, static configuration and whether a lane exists. LIVE values are reset defaults, not callback-owned controller readbacks. xtc_project_set_parameter changes TRACK/PLUGIN static configuration without deleting automation. xtc_project_automate sets/replaces/removes points for TRACK/PLUGIN through one C contract. LIVE static setters and timeline lanes are unsupported: these controls belong to incoming MIDI events, not the arranged instrument. Plugin metadata/control edits execute trusted discovery on the control thread; they never run inside audio. 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 plugin modules fail discovery rather than guessing ranges or installing a dummy. Track deletion removes its lanes; plugin deletion removes its embedded lanes. Compatible plugin replacement resets controls/automation as described in PLUGIN_HOSTING.md. Persisted dangling references reject the complete load; the current project remains intact. The GUI reports rejected edits/Open failures.


Interpolation and Precedence

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, its value remains until the next point's exact sample. Linear segments interpolate between endpoint values without an extra smoothing stage. Hold first/last before/ after the lane. Gain/pan for tracks and buses support both; integer/toggle LADSPA controls require hold. Existing files migrate mixer points to linear.

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 rounding uses the prepared constant tempo/rate; distinct ticks landing on the same sample choose the greatest tick. Empty lanes restore the static value. Static edits while a lane exists change the fallback value, not the audible lane. GUI changes stop/rebuild snapshots; live mutation of 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. Incoming MIDI volume/pan/sustain affect only live sine voices on that channel, never those lanes or the static project controls. Equal-offset MIDI events run in input order before synthesis. A controller event then affects that sample's ramp; gain/pan reach the target over max(1, rate/200) frames. Sustain changes at the event sample. Panic CC120/123 remains reserved and also suppresses already started arranged notes according to the existing panic contract.

Seek/discontinuous positions clear live voices and runtime controllers to their reset values but retain prepared bindings. Arranged lanes evaluate directly at the new position. Backward jumps evaluate the earlier timeline again; tests exercise explicit wraps. There is no loop transport feature or promise to restore opaque LADSPA delay history across jumps (transport reset retains it).


Configurable Live MIDI Bindings

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 zero-based; CCs are 0..127, or 128 to disable a target. Enabled CCs must be distinct within a channel; CC120 and CC123 cannot be rebound. All channels default to 7/10/64. Rebinding replaces the old binding; it is not an additional route. An unmapped CC does nothing. Other channels remain independent. The same volume, pan and sustain transfer functions apply regardless of the chosen CC number. Disabling a target leaves it at its runtime reset value until plan replacement. Changing a map stops/rebuilds playback and is one undoable edit; maps survive save/Open and undo/redo. Cancel and invalid/duplicate/reserved bindings retain the prior configuration. There is no automatic learn, arbitrary 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 at most three CC numbers per control event, uses existing preallocated ramps/ 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. Mixer points compile to bounded sorted arrays with binary lane lookup; processing does no allocation, sort, file/module I/O, locking or GUI work. LADSPA automated run(1) cost and native plugin trust limits remain unchanged.


Persistence and Verification

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 is unchanged. Public xtc_automation_point gains step: rebuild 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, exact hold/linear boundaries, save/reopen, malformed maps, low-rate sample collisions, odd blocks, seeks and backward jumps. test_control compares default and remapped event streams, stereo pan, disabled/unmapped controls and immutable plan maps across resets. Plugin tests cover common-target edits and persistence; GUI tests cover interpolation, map rollback, undo/redo and save/Open. Existing callback guards and sanitizers verify host ownership constraints. Physical controller devices, listening and extended high-density endurance are pending.