pm-grid: fix dropped notes in chords (queue USB-MIDI packets)

The bulk MIDI endpoint holds one 4-byte packet until the host reads it (~once
per USB frame), so calling send_bytes twice for simultaneous lane hits dropped
the 2nd note (WouldBlock, silently ignored). Queue note-ons in a VecDeque and
drain one-per-poll, keeping the rest for the next iteration — chords now play in
full (staggered ~1ms, imperceptible) instead of all-but-one.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Me Here 2026-06-03 15:31:07 -05:00
parent 56bff7e599
commit 47ffb46aa2

View file

@ -19,6 +19,7 @@
#![no_main]
extern crate alloc;
use alloc::collections::VecDeque;
use alloc::string::String;
use alloc::vec::Vec;
use embedded_alloc::LlffHeap as Heap;
@ -781,6 +782,9 @@ fn main() -> ! {
let (mut nextrep_x, mut nextrep_y) = (0i64, 0i64);
let mut last_frame_us = 0i64;
let mut hb_us = 0i64;
// pending USB-MIDI packets: the bulk endpoint holds one 4-byte packet at a time, so simultaneous
// notes are queued here and drained one-per-poll (otherwise the 2nd note of a chord is dropped).
let mut note_q: VecDeque<[u8; 4]> = VecDeque::new();
loop {
let us = now_us();
@ -842,10 +846,21 @@ fn main() -> ! {
px = x;
py = y;
// ---- scheduler: advance clocks, send a USB-MIDI note-on per lane hit (ch10) ----
// ---- scheduler: advance clocks, queue a USB-MIDI note-on per lane hit (ch10) ----
app.tick(now_ns, |note, vel| {
let _ = midi.send_bytes([0x09, 0x99, note, vel]); // cable 0, note-on, channel 10
if note_q.len() < 64 {
note_q.push_back([0x09, 0x99, note, vel]); // cable 0, note-on, channel 10
}
});
// drain the queue to the endpoint: send until it's busy (WouldBlock), keep the rest for the
// next poll. This is why chords play in full instead of dropping all but the first note.
while let Some(&pkt) = note_q.front() {
if midi.send_bytes(pkt).is_ok() {
note_q.pop_front();
} else {
break;
}
}
// ---- ticker scroll advance (~120ms) ----
// (uses the frame clock implicitly; scroll_off wraps mod scroll_total)