From 3725df926e64542bcd9837a40a0178b749a641c0 Mon Sep 17 00:00:00 2001 From: David Gwilliam Date: Wed, 24 Jun 2026 19:10:13 -0700 Subject: [PATCH] Fix MIDI bridge: remove semicolons (corrupts Pd velocity parsing), retry on device busy --- scripts/led-matrix-viz.py | 82 ++++++++++++++++++--------------------- scripts/midi-bridge.py | 14 +++++-- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/scripts/led-matrix-viz.py b/scripts/led-matrix-viz.py index 42c8d95..6ed7470 100755 --- a/scripts/led-matrix-viz.py +++ b/scripts/led-matrix-viz.py @@ -17,12 +17,6 @@ SERVICES = ["midi-bridge", "pd-synth-onboard", "led-matrix-viz", "prometheus-exp STATUS_CACHE = {} LAST_STATUS_CHECK = 0.0 -STATUS_ROW = 0 -SERVICE_COLS = {0: 0, 1: 1, 2: 2, 3: 3} -AUDIO_COL = 4 -NOTE_ROWS = 7 -NOTE_ROW_OFFSET = 1 - def _check_services(): global STATUS_CACHE, LAST_STATUS_CHECK now = time.monotonic() @@ -55,31 +49,28 @@ def render(active_notes): pixels = [[0] * COLS for _ in range(ROWS)] note_min, note_max = 36, 96 for note in active_notes: - row = NOTE_ROWS - 1 - int((note - note_min) / (note_max - note_min) * (NOTE_ROWS - 1)) - row = max(0, min(NOTE_ROWS - 1, row)) + NOTE_ROW_OFFSET + row = 7 - int((note - note_min) / (note_max - note_min) * 7) + row = max(0, min(6, row)) col = note % 12 pixels[row][col] = 1 status = _check_services() - for idx, svc in enumerate(SERVICES): - col = SERVICE_COLS[idx] - if status.get(svc): - pixels[STATUS_ROW][col] = 1 + ok = sum(1 for s in SERVICES if status.get(s)) if len(active_notes) > 0: - pixels[STATUS_ROW][AUDIO_COL] = 1 + ok += 1 + for c in range(ok): + pixels[7][c] = 1 return pack_frame(pixels) def _diag_frame(active_notes): pixels = [[0] * COLS for _ in range(ROWS)] status = _check_services() - for idx, svc in enumerate(SERVICES): - col = SERVICE_COLS[idx] - if status.get(svc): - for r in range(ROWS): - pixels[r][col] = 1 + ok = sum(1 for s in SERVICES if status.get(s)) if len(active_notes) > 0: - for r in range(ROWS): - pixels[r][AUDIO_COL] = 1 + ok += 1 + for r in range(ROWS): + for c in range(ok): + pixels[r][c] = 1 return pack_frame(pixels) @@ -193,34 +184,35 @@ def main(): except BlockingIOError: pass - while b";" in buf: - raw_msg, buf = buf.split(b";", 1) - msg = raw_msg.decode().strip() - parts = msg.split() - if len(parts) >= 3 and parts[0] == "midi": + msg = data.decode().strip() + parts = msg.split() + if len(parts) >= 3 and parts[0] == "midi": + try: status = int(parts[1]) note = int(parts[2]) vel = int(parts[3]) if len(parts) >= 4 else 0 - msg_type = status & 0xF0 - changed = False - if msg_type == 0x90 and vel > 0: - if note not in active_notes: - active_notes.add(note) - changed = True - elif msg_type == 0x80 or (msg_type == 0x90 and vel == 0): - if note in active_notes: - active_notes.discard(note) - changed = True - if changed: - if diag_active: - diag_active = False - last_diag = now - events_processed += 1 - params = render(active_notes) - if params != last_sent_params: - msg_id = (msg_id + 1) % 65535 - pending_bytes = packer.pack([0, msg_id, "draw_frame", params]) - last_sent_params = params + except ValueError: + continue + msg_type = status & 0xF0 + changed = False + if msg_type == 0x90 and vel > 0: + if note not in active_notes: + active_notes.add(note) + changed = True + elif msg_type == 0x80 or (msg_type == 0x90 and vel == 0): + if note in active_notes: + active_notes.discard(note) + changed = True + if changed: + if diag_active: + diag_active = False + last_diag = now + events_processed += 1 + params = render(active_notes) + if params != last_sent_params: + msg_id = (msg_id + 1) % 65535 + pending_bytes = packer.pack([0, msg_id, "draw_frame", params]) + last_sent_params = params if pending_bytes is not None: router, ok = try_send(router, pending_bytes) diff --git a/scripts/midi-bridge.py b/scripts/midi-bridge.py index e1fdfd9..a8da567 100644 --- a/scripts/midi-bridge.py +++ b/scripts/midi-bridge.py @@ -46,8 +46,8 @@ def _find_midi_dev(): def midi_msg(status, data1, data2=None): if data2 is not None: - return f"midi {status} {data1} {data2};" - return f"midi {status} {data1};" + return f"midi {status} {data1} {data2}" + return f"midi {status} {data1}" def _open_midi(): @@ -55,7 +55,15 @@ def _open_midi(): while not os.path.exists(midi_dev): time.sleep(0.5) midi_dev = _find_midi_dev() - fd = os.open(midi_dev, os.O_RDWR | os.O_NONBLOCK) + while True: + try: + fd = os.open(midi_dev, os.O_RDWR | os.O_NONBLOCK) + break + except OSError as e: + if e.errno == 16: + time.sleep(0.5) + continue + raise os.set_blocking(fd, False) return fd