v0.9.0
Added
-
Audio trigger input: Piezoelectric drum triggers (or any transient audio signal) can now
trigger sample playback via a standard audio interface input. Configure per-channel threshold,
scan window, retrigger lockout, gain, and velocity curve (linear, logarithmic, or fixed).
Trigger inputs can fire samples or release voice groups (e.g. cymbal choke). No MIDI hardware
required — plug piezos directly into any cpal-supported audio interface. Advanced options
include a high-pass filter (highpass_freq) to reject stage rumble and bass bleed, dynamic
threshold decay (dynamic_threshold_decay_ms) that raises the threshold after a hit to reject
ringing, and crosstalk suppression (crosstalk_window_ms/crosstalk_threshold) to prevent
a single hit from firing multiple channels. -
Unified trigger config with
kinddiscriminator: Audio and MIDI trigger inputs now coexist
in a singletrigger.inputslist using akindfield (audioormidi). MIDI triggers
defined this way replace the top-levelsample_triggerssection. Thedevicefield is now
optional (only required when audio inputs are present). Legacysample_triggersare
automatically converted tokind: miditrigger inputs at startup. -
Trigger config in profiles: The
triggersection can now be placed inside a hardware
profile, alongsideaudio,midi, anddmx. This allows different hosts to use different
trigger configurations. Top-leveltriggeris still supported and automatically normalized
into the profile. -
calibrate-triggersCLI command: A newmtrack calibrate-triggers <device>command
measures the noise floor and hit characteristics of a connected audio input device, then
generates a ready-to-paste YAML trigger configuration with calibrated thresholds, scan windows,
retrigger lockout times, and optional high-pass filter settings. -
output_trackfor samples: Sample definitions now support anoutput_trackfield that
references a track mapping name from the active hardware profile'strack_mappings. This allows
a single sample definition to work across different hardware profiles with different channel
assignments. The existingoutput_channelsfield continues to work unchanged. If both are set,
output_tracktakes precedence. -
Lighting simulator: A new web-based UI for visualizing lighting effects in real-time without
physical DMX hardware. Start withmtrack start --simulator(optionally--simulator-port).
The simulator shows per-fixture color/strobe state, processes layer commands, and supports
hot-reload of.lightDSL files during playback. When the OLA daemon is unavailable, the DMX
engine falls back to a null client so the effects engine can still run. -
Legacy MIDI-to-DMX integration with effect engine: Legacy MIDI light shows now feed their
interpolated DMX values into the DSL effect engine via a lockless atomic store
(LegacyDmxStore). This allows MIDI-driven fixtures to appear in the lighting simulator and
coexist with DSL effects on the same universes. DSL effects take priority over legacy values. -
Fixture strobe frequency range: Fixture type definitions now support
min_strobe_frequency
andstrobe_dmx_offsetfields, allowing accurate strobe DMX calculation for fixtures whose
strobe channel starts above DMX value 0 (e.g. Astera PixelBrick: offset 7, range 0.4–25 Hz). -
Configurable FFT resampling: A new
resampleroption in the audio configuration allows
choosing betweensinc(default, high-quality sinc interpolation) andfft(FFT-based
resampling, considerably faster for fixed-ratio resampling). The FFT mode can significantly
reduce CPU usage on low-power hardware like the Raspberry Pi when source and output sample
rates differ. Setresampler: fftin the audio config to enable it. -
Terminal UI (TUI): When
mtrack startis run from an interactive terminal, it now
launches a full-screen terminal UI built with ratatui/crossterm. The TUI shows the playlist,
now-playing status with a progress bar, real-time fixture colors from the lighting engine,
active effects, and an inline log panel. Keyboard shortcuts provide play/stop, prev/next,
playlist switching, and quit. When stdin is not a TTY (e.g. systemd, pipes), mtrack runs in
headless mode as before. Use--no-tuito force headless mode from an interactive terminal.
All configured controllers (gRPC, OSC, MIDI) continue to run alongside the TUI.
Changed
-
Custom MIDI playback engine replaces nodi: The
nodidependency has been removed and replaced
with a bespoke MIDI playback engine built onmidly. MIDI events are pre-computed into absolute-
timestamped event streams in a single pass, with wall-clock timing viaspin_sleepfor precise
hardware MIDI output. Legacy MIDI-to-DMX light shows are now driven from the effects loop using
cursor-based playback rather than spawning separate threads with nodi players, reducing thread
count and improving timing consistency. -
Strobe DMX normalization uses period-linear interpolation: Strobe frequency-to-DMX mapping
now interpolates in period-space (1/frequency) rather than frequency-space, matching how most
LED fixtures actually scale their strobe channel. This produces significantly higher DMX values
for high strobe frequencies (e.g. 10 Hz on a PixelBrick now sends DMX 248 instead of 103). -
Sequence tempo rescaling: Sequences referenced from shows with different tempos now have
their cue timing correctly rescaled from the sequence's internal BPM to the expansion-point
tempo. Previously, sequences always played at their own BPM regardless of the calling context. -
note_offrenamed torelease_behavior: The sample configuration fieldnote_offhas been
renamed torelease_behaviorto better reflect its source-agnostic purpose. The oldnote_off
key is still accepted for backwards compatibility. -
Legacy field warnings: When
profilesis present in the player config, any top-level
audio,midi,dmx,trigger,track_mappings, orsample_triggersfields now produce
a warning that they are being ignored, making misconfiguration easier to diagnose. -
Shared state sampler: Lighting fixture state and active effect information are now
broadcast via awatchchannel from a shared 20 Hz sampler (state.rs). Both the TUI and
the lighting simulator subscribe to the same channel, replacing the simulator's previous
dedicated polling. This reduces lock contention on the effect engine.
Fixed
-
Looped sequence effects compounding: Fixed a bug where effects in looped lighting sequences
would accumulate across iterations (e.g. multiply chases compounding). Each loop iteration now
stops the previous iteration's effects before starting its own. -
Lighting simulator layer commands: The lighting simulator now processes layer commands
(clear, release, freeze, unfreeze, master) and sequence stop commands, matching the behavior of
the player's DMX engine. -
Seeking past layer clears: When using play-from to seek into a song, layer
clear()
commands in the timeline history now correctly purge effects that were started before the clear.
Previously, seeking would accumulate all perpetual effects from the entire history, ignoring
intermediate clears. -
OLA connection resilience: The OLA client now supports automatic reconnection after
connection failures, with backoff retry logic. A single failed DMX send no longer drops the
entire connection. The DMX engine also avoids inadvertently starting the OLA daemon. -
Stale lighting state between songs: Fixed a bug where DSL songs without tempo blocks would
inherit stale tempo maps from previous songs. Tempo maps, timelines, and legacy MIDI values are
now properly cleared on song transitions. -
DMX timeline completion race: Fixed a race condition where the effects loop could mark the
timeline as finished before the first song had set up its lighting state. Thetimeline_finished
flag now initializes totrueand is only reset when a song begins, preventing premature
completion signals.