Commit 4d9f1059 by PLN (Algolia)

fix(rig): the two reconcilers that lived only in $HOME — five orbits reached no Ardour track

tidal-ardour-autoroute.service was disabled+inactive after launch: orbits 5-9
(SuperCollider:out_9..18) had NO link to their "Tidal 05..09" tracks, while
1-4 and 10-12 survived as leftovers from an earlier session. Enabled it; all
24 outs reconcile every 2s again.

Root cause of the regression is that both reconcilers — this one and
midi-autoconnect — existed ONLY as plain files in ~/.local/bin and
~/.config/systemd/user, unlike every other unit here which symlinks into
tools/. Nothing was version-controlled, so a `disable` (or a fresh systemd
user config) was unrecoverable and invisible to git. Now in the repo, with
$HOME pointing back at it. Also started parvagues-sc-watchdog, which was
linked but dead.
parent 49e1b78e
[Unit]
Description=Enforce fixed ALSA-seq MIDI wiring (Launch Control XL <-> Midi Through -> aseqdump)
# Run alongside the sound/PipeWire session.
After=pipewire.service
Wants=pipewire.service
[Service]
Type=simple
ExecStart=%h/.local/bin/midi-autoconnect.sh
Restart=always
RestartSec=2
# Tiny footprint; it's mostly sleeping.
Nice=10
[Install]
WantedBy=default.target
#!/usr/bin/env bash
# midi-autoconnect — enforce a fixed ALSA-seq MIDI wiring.
#
# Rules (re-applied every $INTERVAL seconds, idempotent):
# 1. Launch Control XL :0 -> Midi Through :0 (controls out)
# 2. ACTIVELY CUT Midi Through :0 -> Launch Control XL :0
#
# aconnect matches clients by name, so this survives replug / renumbering.
# "Connection is already subscribed" errors are expected and ignored.
#
# ---------------------------------------------------------------------------
# 2026-07-27 — why rule 2 INVERTED from "connect" to "disconnect".
#
# The old rule 2 connected Midi Through BACK to the LCXL, labelled "LED/visual
# feedback". It is in fact a MIDI FEEDBACK LOOP and the direct cause of the
# long-standing "my button colours snap to yellow the moment I press them" bug:
#
# press button -> LCXL emits Note On velocity 127
# -> LCXL:0 -> Midi Through:0 -> (rule 2) -> LCXL:0
# -> the LCXL reads an INBOUND note velocity as an LED COLOUR
# -> 127 = 0x7F = full red + full green = YELLOW
#
# So every press repainted itself yellow, wiping whatever the HUD had set.
# `aconnect -d 14:0 24:0` fixed it by hand — and then this service silently
# recreated the loop within 2 seconds, which is why the fix "never stuck"
# across a Tidal reboot. The reconcile loop was fighting the operator.
#
# The LCXL's LEDs do NOT need this link: the HUD drives them directly with
# SysEx via `aseqsend` to the LCXL's own port (lib/lcxl-leds.js). Nothing was
# ever gained by the return leg. LCXL -> Midi Through -> Ardour still works, so
# Ardour's click-to-map fader learn is unaffected; Ardour simply stops talking
# back to the surface, which is what we want when the HUD owns the LEDs.
#
# We DISCONNECT rather than merely "not connect", because Ardour's control
# surface and SuperCollider's `MIDIIn.connectAll` can both recreate the return
# leg on their own. Same shape as every other binding on this rig: a
# connection that must be re-asserted, never assumed.
#
# The old rule 3 (Midi Through -> every aseqdump) is gone too: aseqdump
# clients already subscribe to the LCXL directly, so the extra hop delivered
# every CC TWICE to the HUD — pure duplicated work on a renderer we are
# actively trying to keep off the audio thread's back.
# ---------------------------------------------------------------------------
set -u
LCXL="Launch Control XL"
THRU="Midi Through"
LCXL_PORT=0 # "first channel"
THRU_PORT=0
INTERVAL="${MIDI_AUTOCONNECT_INTERVAL:-2}"
connect() { # $1 = source, $2 = dest ; silent unless it's a real failure
aconnect "$1" "$2" 2>/dev/null
}
disconnect() { # $1 = source, $2 = dest ; silent when there was nothing to cut
aconnect -d "$1" "$2" 2>/dev/null
}
reconcile() {
connect "$LCXL:$LCXL_PORT" "$THRU:$THRU_PORT" # rule 1: controls out
disconnect "$THRU:$THRU_PORT" "$LCXL:$LCXL_PORT" # rule 2: kill the loop
}
echo "midi-autoconnect: reconciling every ${INTERVAL}s"
while :; do
reconcile
sleep "$INTERVAL"
done
[Unit]
Description=Auto-route SuperDirt orbits to Ardour Tidal tracks; keep SC off hardware
After=pipewire.service
Wants=pipewire.service
[Service]
Type=simple
ExecStart=%h/.local/bin/tidal-ardour-autoroute.sh
Restart=always
RestartSec=2
Nice=10
[Install]
WantedBy=default.target
#!/usr/bin/env bash
# tidal-ardour-autoroute — keep SuperDirt orbits wired to their Ardour tracks.
#
# SuperDirt emits stereo orbit pairs on SuperCollider:out_N. Orbit k (1-based)
# is the pair (out_{2k-1}, out_{2k}). Ardour has stereo tracks "Tidal NN".
#
# Rules (re-applied every $INTERVAL s, idempotent):
# ADD: SuperCollider:out_{2k-1} -> ardour:Tidal {NN}/audio_in 1
# SuperCollider:out_{2k} -> ardour:Tidal {NN}/audio_in 2
# PRUNE: drop every SuperCollider:out_* -> <hardware>:playback_* link.
# In this multichannel setup SC never feeds hardware directly —
# monitoring is via Ardour Master. sclang/SuperDirt re-adds the
# orbit-0 monitor (out_1/out_2 -> Headphones) on (re)boot; this kills it.
#
# Track numbers are auto-detected from the live graph, so adding/removing
# "Tidal NN" tracks in Ardour needs no edit here. pw-link "File exists" /
# missing-port errors are expected and ignored.
set -u
SC="SuperCollider"
INTERVAL="${TIDAL_AUTOROUTE_INTERVAL:-2}"
link() { # $1 = source port, $2 = dest port ; silent (already-linked is fine)
pw-link "$1" "$2" 2>/dev/null
}
# Print the set of Ardour "Tidal NN" track numbers currently present.
tidal_track_numbers() {
pw-link -i 2>/dev/null \
| grep -oE "ardour:Tidal [0-9]+/audio_in 1" \
| grep -oE "[0-9]+" | sort -un
}
# Print "src<TAB>dest" for every SuperCollider:out_* -> hardware playback link.
sc_hw_playback_links() {
pw-link -lo 2>/dev/null | awk -v sc="$SC" '
/^[^ \t]/ { src = $0; next } # non-indented = source port
index(src, sc ":out_") == 1 && /playback_/ { # indented hw-playback dest
dst = $0; sub(/^[ \t]*\|?->? */, "", dst);
print src "\t" dst
}'
}
reconcile() {
# ADD: orbit pair -> matching Tidal track
while read -r nn; do
[ -n "${nn:-}" ] || continue
k=$((10#$nn)) # strip leading zero (NN -> k)
l=$((2 * k - 1)); r=$((2 * k))
link "$SC:out_$l" "ardour:Tidal $nn/audio_in 1"
link "$SC:out_$r" "ardour:Tidal $nn/audio_in 2"
done < <(tidal_track_numbers)
# PRUNE: SuperCollider must never reach hardware playback directly
while IFS=$'\t' read -r src dst; do
[ -n "${src:-}" ] && [ -n "${dst:-}" ] && pw-link -d "$src" "$dst" 2>/dev/null
done < <(sc_hw_playback_links)
}
echo "tidal-ardour-autoroute: reconciling orbit->track every ${INTERVAL}s"
while :; do
reconcile
sleep "$INTERVAL"
done
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment