diff --git a/scripts/led-matrix-viz.py b/scripts/led-matrix-viz.py index 2134d4e..3512cb2 100644 --- a/scripts/led-matrix-viz.py +++ b/scripts/led-matrix-viz.py @@ -7,7 +7,8 @@ import socket, msgpack, time, os, select, collections VIZ_PORT = int(os.environ.get("VIZ_PORT", "8082")) ROUTER_SOCK = os.environ.get("ROUTER_SOCK", "/var/run/arduino-router.sock") -DECAY_FRAMES = int(os.environ.get("DECAY_FRAMES", "8")) +DECAY_FRAMES = int(os.environ.get("DECAY_FRAMES", "3")) +MAX_NOTE_AGE = int(os.environ.get("MAX_NOTE_AGE", "15")) COLS = 13 ROWS = 8 @@ -23,8 +24,6 @@ POLLOUT = select.POLLOUT SOCK_BUFFER = 4096 ROUTER_RECONNECT_DELAY = 0.1 -_frame_hooks = [] # for metrics - def pack_frame(pixels): bits = 0 @@ -87,7 +86,6 @@ def main(): next_frame_time = time.monotonic() pending_frames = FrameQueue(maxsize=5) - router_registered = False router_fd = router.fileno() _poll.register(router, POLLIN | POLLOUT) @@ -95,6 +93,8 @@ def main(): frames_dropped = 0 router_errors = 0 stats_last_write = time.monotonic() + fps_sample_start = time.monotonic() + fps_sample_frames = 0 while True: now = time.monotonic() @@ -127,7 +127,7 @@ def main(): vel = int(parts[3]) if len(parts) >= 4 else 0 msg_type = status & 0xF0 if msg_type == 0x90 and vel > 0: - active_notes[note] = DECAY_FRAMES + active_notes[note] = {"decay": DECAY_FRAMES, "age": 0} elif msg_type == 0x80 or (msg_type == 0x90 and vel == 0): active_notes.pop(note, None) @@ -153,26 +153,32 @@ def main(): router_errors += 1 pending_frames.push(frame) time.sleep(ROUTER_RECONNECT_DELAY) + old_fd = router_fd try: router.close() except OSError: pass router = connect_router() router_fd = router.fileno() - _poll.unregister(router) + try: + _poll.unregister(old_fd) + except (KeyError, OSError): + pass _poll.register(router, POLLIN | POLLOUT) # Render frame when due if now >= next_frame_time: next_frame_time = now + frame_interval + fps_sample_frames += 1 pixels = [[0] * COLS for _ in range(ROWS)] expired = [] - for note, decay in list(active_notes.items()): - if decay <= 0: + for note, state in list(active_notes.items()): + state["age"] += 1 + state["decay"] -= 1 + if state["decay"] <= 0 or state["age"] > MAX_NOTE_AGE: expired.append(note) continue - active_notes[note] = decay - 1 note_min, note_max = 36, 96 row = 7 - int((note - note_min) / (note_max - note_min) * 7) row = max(0, min(7, row)) @@ -195,14 +201,14 @@ def main(): # Write stats periodically (every 2s) if now - stats_last_write >= 2.0: - _write_stats(frames_sent, frames_dropped, pending_frames) + fps = fps_sample_frames / (now - fps_sample_start) if (now - fps_sample_start) > 0 else 0 + _write_stats(frames_sent, frames_dropped, pending_frames, fps, len(active_notes)) stats_last_write = now + fps_sample_start = now + fps_sample_frames = 0 -_stats_last_write = 0.0 - - -def _write_stats(sent, dropped, pending): +def _write_stats(sent, dropped, pending, fps, active_count): import json try: with open(STATS_FILE, "w") as f: @@ -211,6 +217,8 @@ def _write_stats(sent, dropped, pending): "frames_dropped": dropped, "pending_queue": len(pending), "queue_full": pending.full(), + "fps": round(fps, 1), + "active_notes": active_count, "ts": time.time() }, f) except OSError: