Skip to content

v0.9.0

Choose a tag to compare

@mdwn mdwn released this 26 Feb 18:04
· 145 commits to main since this release
9e89ce9

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 kind discriminator: Audio and MIDI trigger inputs now coexist
    in a single trigger.inputs list using a kind field (audio or midi). MIDI triggers
    defined this way replace the top-level sample_triggers section. The device field is now
    optional (only required when audio inputs are present). Legacy sample_triggers are
    automatically converted to kind: midi trigger inputs at startup.

  • Trigger config in profiles: The trigger section can now be placed inside a hardware
    profile, alongside audio, midi, and dmx. This allows different hosts to use different
    trigger configurations. Top-level trigger is still supported and automatically normalized
    into the profile.

  • calibrate-triggers CLI command: A new mtrack 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_track for samples: Sample definitions now support an output_track field that
    references a track mapping name from the active hardware profile's track_mappings. This allows
    a single sample definition to work across different hardware profiles with different channel
    assignments. The existing output_channels field continues to work unchanged. If both are set,
    output_track takes precedence.

  • Lighting simulator: A new web-based UI for visualizing lighting effects in real-time without
    physical DMX hardware. Start with mtrack start --simulator (optionally --simulator-port).
    The simulator shows per-fixture color/strobe state, processes layer commands, and supports
    hot-reload of .light DSL 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
    and strobe_dmx_offset fields, 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 resampler option in the audio configuration allows
    choosing between sinc (default, high-quality sinc interpolation) and fft (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. Set resampler: fft in the audio config to enable it.

  • Terminal UI (TUI): When mtrack start is 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-tui to 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 nodi dependency has been removed and replaced
    with a bespoke MIDI playback engine built on midly. MIDI events are pre-computed into absolute-
    timestamped event streams in a single pass, with wall-clock timing via spin_sleep for 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_off renamed to release_behavior: The sample configuration field note_off has been
    renamed to release_behavior to better reflect its source-agnostic purpose. The old note_off
    key is still accepted for backwards compatibility.

  • Legacy field warnings: When profiles is present in the player config, any top-level
    audio, midi, dmx, trigger, track_mappings, or sample_triggers fields 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 a watch channel 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. The timeline_finished
    flag now initializes to true and is only reset when a song begins, preventing premature
    completion signals.