#!/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
