- env~ is a cyclone external not available on board's Pd 0.55
- Replace with line~ + *~ 0.0055 for velocity-scaled AR envelope
- Fix stripnote outlet 1 (gate trigger) connected to envelope instead of outlet 0 (pitch)
- Change dac~ to dac~ 1 for mono output matching -outchannels 1
- Envelope: attack ~50ms, release ~50ms, sustain scaled by velocity
hsl GUI objects don't initialize in -nogui mode, causing
cutoff at 0 Hz and volume at 0 → no audio output.
Use loadbang + float for headless-safe initialization.
- scripts/diagnostics/test-pipeline.py: full end-to-end test
- just diag-full: run pipeline diagnostic
- just viz-log/midi-log: watch service logs live
- just viz-stats/midi-stats: check counters
- just reset-mcu: reset MCU + reopen serial via router RPC
- just test-chord: send test MIDI chord locally
- just fix-perms: restore +x bits on all scripts
RouterBridge on MCU ignores NOTIFICATION (type 2) messages.
Must use REQUEST (type 0) for draw_frame, then drain the
response to prevent router buffer buildup.
Viz was sending REQUEST (type 0) but never reading responses.
Router response buffer filled up, blocking all subsequent RPCs.
NOTIFICATION (type 2) expects no response — no buffer buildup.
Rewrite led-matrix-viz.py to eliminate latency:
- Render and pack frame immediately on each MIDI event
- Send on next poll cycle (no POLLOUT dependency, no frame loop)
- Notes on/off purely from MIDI data — zero decay
- Background 5 FPS refresh for router drop recovery
- Clean stats: FPS, active_notes, midi_events, bytes_received, raw_packets
- DECAY_FRAMES default 8→3 (100ms visual trail)
- MAX_NOTE_AGE safety cap: force-expire notes after 15 frames
- Track note age separately from decay to prevent stale notes
- Add FPS and active_notes to viz stats for diagnostics
- Fix router reconnect: unregister old fd, not new one
- Create skills: rego-pd-validator, usb-gadget-configfs, led-matrix-msgpack-rpc
- Update pd-audio-synthesis skill with Uno Q specifics
- Add Pd version check (min 0.54.0) to start-synth.sh
- Add DO NOT EDIT headers to generated pd patches
- Add Gitea Actions CI workflow for patch validation
- Add ConditionPathExists guards to legacy systemd services
- Add 'just ci' recipe for CI
- dashboard.html: standalone HTML, fetches :9090/metrics, auto-refresh
- Exporter serves dashboard at /dashboard and /dashboard.html
- Cards: Pipeline, Audio, Mixer, MIDI Bridge, LED Viz
- Live gauges, counters, service status dots, last-activity timestamps
midi-bridge: removed HTTP server on :9091, now writes /tmp/midi-stats.json
prometheus-exporter: reads /tmp/midi-stats.json, exposes all metrics on :9090/metrics
Single scrape endpoint for all pipeline components
The O_NONBLOCK flag set by setblocking(False) was not cleared by
settimeout(n) on this CPython/kernel combo, causing recvfrom to never
return data. Switched to explicit select.poll() with frame-interval
based timeout — simpler, more portable, and avoids the O_NONBLOCK trap.
Also fixes midi-bridge: add time.sleep(0.01) in no-data paths to
prevent 100% CPU spin from spurious POLLIN on f_midi rawmidi.
midi-bridge.py:
- Auto-detect rawmidi device (gadget f_midi > env override > fallback)
- Reconnect on ENODEV (device hotplug)
- No longer needs MIDI_BRIDGE_DEV env var in common cases
start-synth.sh:
- Remove all -alsamidi/-midiadddev flags (kernel lacks CONFIG_SND_SEQUENCER)
- MIDI handled entirely by midi-bridge.service → UDP → netreceive
- Host mode: same approach, no ALSA sequencer needed
mpk-mini-sim.pd:
- New MPK Mini Mk II simulator (25 keys, 8 pads, 8 CC knobs)
- Proper Pd #X obj format for all objects (bng, hsl, t, del, netsend, loadbang)
- UDP output to board:8081, matching midi-bridge format
system: enable usb-gadget-audio.service at boot, load dwc3 module at boot