midi: drop the dead time argument, and inject_midi with it - #1040
Conversation
The time parameter threaded through amy_midi was already dead: in amy_midi.c it is declared AMY_UNSET_VALUE(time) and never reassigned, so every real MIDI path -- USB, gadget, UART, mac, sysex -- has always passed it unset. amy_received_control_change() didn't even read it. Nothing outside src/ called these at all. The single caller that ever supplied a real value was pyamy's inject_midi(), i.e. the test suite. That reflects the truth of the thing: a live MIDI message has no time of its own, it plays when it arrives. So remove the argument from amy_event_midi_message_received() and the five amy_received_* handlers. inject_midi() goes too. It was the weaker of the two Python entry points -- it hardcoded len=3, so it could not send a 2-byte message at all (test.py already used inject_midi_bytes for program change), and it handed a pre-formed message straight past the byte-stream parser. inject_midi_bytes() runs the real parser, so tests now exercise running status and real-time interleaving the way actual MIDI input does. The ~16 test call sites that passed an absolute millisecond time move to a new amy_inject_midi_at(), the MIDI twin of amy_send_at(): render up to the moment, then inject. All 122 tests pass and all 116 waveform comparisons are bit-exact, so no reference audio needed regenerating. midi_message_handler_to_queue() KEEPS its time parameter -- it lives in midi_mappings.c and patches.c drives it with a real e->time for the wave=AMY_MIDI osc, which is how a tick-scheduled event emits MIDI on the beat. amy_event_midi_message_received() now passes it an explicit unset. Also fixes three bare `return Py_None` in pyamy.c (missing INCREF, so each call over-decremented None's refcount) and updates the Daisy port, whose midi_polyphony() demo had been staggering notes with the argument that never did anything. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
🎛️ AMY HW CI (AMYboard bench)Flashed this PR's AMY (LoadTestChord: 6-voice Juno ✅ PASS — the bench ran the test to completion.
Full chord settled render μs: 2765 (was 2767, Δ -0.1%) (peak 2771, 39 samples) ⬇️ Artifacts: serial log · load trace · report Self-hosted bench (amyboardci). FAIL means only that the test could not run — the load values are informational, with no threshold and no audio compare. See |
⛓️ tulipcc integration PR openedThis merge was pinned into tulipcc for full-system CI: shorepine/tulipcc#1273 Test it there and merge that PR to move tulipcc onto this AMY. |
The
timeargument was already deadIn
amy_midi.c,timeis declaredAMY_UNSET_VALUE(time)(lines 321, 395) and never reassigned — so every real MIDI path (USB, gadget, UART, mac, sysex) has always passed it unset.amy_received_control_change()didn't even read its copy. Nothing outsidesrc/called these at all. The one caller that ever supplied a real value was pyamy'sinject_midi(), i.e. the test suite.That reflects the truth of the thing: a live MIDI message has no time of its own — it plays when it arrives. So the argument is gone from
amy_event_midi_message_received()and the fiveamy_received_*handlers.inject_midigoes with itIt was the weaker of the two Python entry points:
len=3, so it could not send a 2-byte message at all —test.pyalready usedinject_midi_bytes([0xC0, 5])for program change.inject_midi_bytes()runs the realconvert_midi_bytes_to_messages(), so the tests now exercise running status and real-time interleaving the way actual MIDI input does. Net gain in coverage.The ~16 test call sites that passed an absolute millisecond time move to a new
amy_inject_midi_at(), the MIDI twin of the existingamy_send_at(): render up to the moment, then inject.What deliberately did NOT change
midi_message_handler_to_queue()keeps itstimeparameter. It lives inmidi_mappings.c, andpatches.c:1081drives it with a reale->timefor thewave=AMY_MIDIosc — that's how a tick-scheduled event emits MIDI on the beat.amy_event_midi_message_received()now passes it an explicit unset instead of a laundered one.Drive-by fixes
pyamy.chad three barereturn Py_None;(missing INCREF — each call over-decrementedNone's refcount). NowPy_RETURN_NONE.midi_polyphony()demo was staggering notes 1000ms apart with the argument that never did anything; it now says so instead of pretending.Testing
make test: 122 tests pass, and all 116 waveform comparisons are bit-exact (err=-100.0 dB) — no reference audio needed regenerating, despite the tests now routing through the byte-stream parser and the render-forward clock.make check-c-api: no generated-API drift. Cleanmakewith no new warnings.🤖 Generated with Claude Code