Commit 35fb6edb by PLN (Algolia)

fix(rig): the LCXL checks knew only the v2 board, and Ardour launched off-graph

The Bridge's RIG UP ran tools/gig-up.sh --converge, whose converge never
starts lcxl3-driver (boot policy manual), and whose LCXL checks grep the
v2 name 'Launch Control XL' — so a v3 board plugged in read as absent,
painted by nobody, and the gate still said GO. Meanwhile the desktop
launcher runs the ROOT gig-up.sh, a second launcher with the leds() the
gate lacks — two launchers, each owning half the surface story.

- launchers: Ardour launches under pw-jack like gig-up does (bare ardour
  starts its own jackd and splits the graph from scsynth)
- gig-up: 'LCXL present' matches LCXL3 too; 'LCXL LEDs' is
  generation-aware (LCXL3 -> lcxl3-driver, original -> lcxl-leds-watch)
- lcxl-path: follows the v3 path LCXL3 -> driver's 'ParVagues LCXL3'
  virtual port -> SC, falling back to the Midi Through road for v2
parent fa548f0f
......@@ -70,6 +70,7 @@ LAUNCHERS = [
{"key": "ardour", "name": "Ardour", "blurb": "DAW: the Tidal Live session (records the stems)",
"candidates": ["ardour9", "ardour8", "ardour7", "ardour6", "ardour"],
"args": ([str(ARDOUR_SESSION)] if ARDOUR_SESSION.exists() else []),
"wrap": ["pw-jack"],
"exe": r"[Aa]rdour[-\d.]*$"},
{"key": "qjackctl", "name": "QjackCtl", "blurb": "JACK / audio graph control",
"candidates": ["qjackctl"], "args": [], "exe": r"qjackctl$"},
......@@ -391,7 +392,11 @@ def is_running(spec, lines=None):
def _argv(spec):
inner = [binary(spec), *spec.get("args", [])]
# `wrap` (e.g. Ardour's pw-jack) sits OUTSIDE the binary: ldconfig resolves
# libjack.so.0 to jackd2's, so a bare `ardour` starts its own jackd, grabs a
# card and splits the graph from scsynth's — the two-servers trap gig-up's
# launch note documents. Same mechanism, same reason, one line.
inner = [*spec.get("wrap", []), binary(spec), *spec.get("args", [])]
return _term_argv(inner) if spec.get("terminal") else inner
......
......@@ -423,7 +423,7 @@ soft "transition ghosts" \
# venue, a warn here means the set has no hands.
soft "LCXL present" \
"replug the LCXL (USB OUT endpoint stalls — LEDs dark but input alive), then tools/lcxl-init.py" \
bash -c 'aseqdump -l 2>/dev/null | grep -qi "Launch Control XL"'
bash -c 'aseqdump -l 2>/dev/null | grep -qiE "Launch Control XL|LCXL3"'
# --- 7b. the live chain, opt-in (--live) -------------------------------------
# Everything above proves the SET is correct. None of it proves a RIG EXISTS.
......@@ -488,9 +488,19 @@ if (( LIVE )); then
# The surface being WIRED is not the surface being LIT. Separate daemon, separate
# failure, and until tonight nothing checked it: this is the exact gap that let
# PLN arrive at "i dont see midi feedback visuel anymore" the day before a gig.
# Generation-aware, same rule as the root launcher's leds(): an LCXL3 is
# painted by lcxl3-driver (and Conflicts= the v2 watcher), the original LCXL
# by lcxl-leds-watch. Name-match both generations — the v3 board enumerates
# as "LCXL3 1", and a v2-only grep made this check pass by accident.
soft "LCXL LEDs" \
"systemctl --user start lcxl-leds-watch — board is plugged in but nothing is painting it" \
bash -c '! aseqdump -l 2>/dev/null | grep -qi "Launch Control XL" || systemctl --user is-active --quiet lcxl-leds-watch.service'
"start the painter: LCXL3 → systemctl --user start lcxl3-driver; original LCXL → systemctl --user start lcxl-leds-watch" \
bash -c 'if aseqdump -l 2>/dev/null | grep -qi "LCXL3"; then
systemctl --user is-active --quiet lcxl3-driver.service
elif aseqdump -l 2>/dev/null | grep -qi "Launch Control XL"; then
systemctl --user is-active --quiet lcxl-leds-watch.service
else
exit 0
fi'
# Asks whether THIS instance warmed, by keying to the unit's own start time —
# not "was there ever a PRELOAD line", which a previous boot would satisfy forever.
......
......@@ -22,16 +22,17 @@ else:
except (OSError, subprocess.SubprocessError):
sys.exit(2)
# client id -> name, and (client:port) -> set of destination clients
names, edges, client, port = {}, {}, None, None
# client id -> name, port name by (client:port), and (client:port) -> destinations
names, pnames, edges, client, port = {}, {}, {}, None, None
for line in out.splitlines():
m = re.match(r"client (\d+): '([^']*)'", line)
if m:
client, names[int(m.group(1))] = int(m.group(1)), m.group(2)
continue
m = re.match(r"\s+(\d+) '", line)
m = re.match(r"\s+(\d+) '([^']*)'", line)
if m and client is not None:
port = (client, int(m.group(1)))
pnames[port] = m.group(2)
continue
m = re.match(r"\s+Connecting To: (.*)", line)
if m and port is not None:
......@@ -41,7 +42,7 @@ for line in out.splitlines():
def clients(pred):
return {c for c, n in names.items() if pred(n)}
lcxl = clients(lambda n: "Launch Control XL" in n)
lcxl = clients(lambda n: "Launch Control XL" in n or "LCXL3" in n)
sc = clients(lambda n: n == "SuperCollider")
thru = clients(lambda n: "Midi Through" in n)
if not lcxl:
......@@ -49,6 +50,20 @@ if not lcxl:
if not sc:
print("SuperCollider is not on the MIDI bus at all"); sys.exit(1)
# The v3 board routes through the LCXL3 driver: the board reaches SC via the
# driver's "ParVagues LCXL3" virtual port, not via Midi Through — and chasing
# the PipeWire relay mesh instead would make this check unable to fail again
# (everything reaches everything through 143). So: the driver's virtual port
# existing AND holding an edge into SC IS the v3 path; Midi Through stays the
# v2 board's road. NB the virtual port is a PORT name ("ParVagues LCXL3" under
# client 'RtMidiOut Client'), so it is matched per-port, not per-client.
drv = {p for p, n in pnames.items() if "ParVagues LCXL3" in n}
if drv:
reach = {d for p in drv for d in edges.get(p, set())}
if reach & sc:
print("LCXL3 -> lcxl3-driver -> SC"); sys.exit(0)
print("LCXL3 driver is up but its virtual port does NOT reach SC"); sys.exit(1)
# one hop, then two hops via Midi Through
reach = {d for (c, _), ds in edges.items() if c in lcxl for d in ds}
if reach & sc:
......
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