-
feat(lcxl): drive the LEDs over SysEx — the Note-On path was never going to work · 1459ef02
The LED work (#11) had been stuck for two days on "some nonzero value lights it", with no reliable palette and buttons that refused to light at all. Today's live session found out why, and it was not a bug in our code. THE ROOT CAUSE. The Launchpad Note-On protocol only lights a control if the CURRENTLY SELECTED template maps that exact note/CC on that exact MIDI channel. On User 1 the LCXL's buttons are CC-mapped, so every Note-On we sent to notes 73/74/91/92 had no matching target and was silently discarded. The knobs are note-mapped, which is why they lit and the buttons never did — a split that looked like flaky hardware and was actually the protocol working as documented. THE FIX. The SysEx "Set LEDs" message updates any control regardless of its note/CC value or MIDI channel: F0 00 20 29 02 11 78 <template> <index> <value> [<index> <value> ...] F7 Verified on hardware: top button row went red and bottom row green on the first try, after Note-On had failed on those same buttons all morning. Indices are contiguous (00-07/08-0F/10-17 knob rows, 18-1F and 20-27 button rows, 28-2B side, 2C-2F arrows), which is far easier to generate role-family colours over than the scattered note numbers. PALETTE, MEASURED NOT GUESSED. colour = (16 * green) + red + flags, each channel 0-3, flags 12 normal / 8 flashing. Confirmed by lighting the top knob row with the full ramp and having PLN read the colours back left to right: off, red low, red full, green low, green full, amber low, amber full, yellow — matching the Programmer's Reference table exactly. Also ran the built-in brightness tests (B0 00 7F / 7D): all-orange then all-dim-yellow, which proves the unit is NOT in low-power mode and that dim-vs-full is simply hard to read by eye. Conclusion for the design: use FLASH, not brightness, when a state must be unmissable. STALE-BINDING FIX. ~lcxlOut was resolved once at boot. A replug moves the ALSA port (we watched it go 20:0 -> 24:0) and the cached MIDIOut then points at a device that no longer exists — LEDs die silently while input keeps working. It now re-resolves lazily on every write, and drops the cache on error so the next write retries. Added ~lcxlReconnect for explicit post-replug recovery, kept manual on purpose: MIDIClient.init re-scans the whole MIDI world and is not something to fire mid-set on a guess. This is the fourth instance today of one failure shape: a device binding resolved once at startup, invalidated by a device event, failing with NO error. The others were Ardour's master port (twice) and the HUD's aseqdump sitting alive on a dead port. Tracked in #41. Validation: delimiter balance checked; full compile is proven at the next SC boot, not before — deliberately not restarting SuperCollider mid-rehearsal.
PLN (Algolia) authored1459ef02
×