#Any idea what MIDI API would help make timing easier
1 messages · Page 1 of 1 (latest)
I've been experimenting on this a bit, hacking little C experiments into CircuitPython. Not getting very far. I've been approaching the question by asking: "How to deal with MIDI clock?"
Short version: API that implements an input "reader" and output "player" . The "reader" reads & filters incoming MIDI data, parsing time-critical events. The "player" provides a mechanism to play a MIDI event list (a "sequencer") and sends steady MIDI clocks.
MIDI clock is a single-byte MIDI message that's used to sync up devices. It's transmitted at 24 pulses-per-quarternote (PPQ). Thus the PPQ time in milliseconds is 60_000 / (24 * bpm), so for 120 BPM, ppq_millis = 20.83. At 180 BPM, ppq_millis = 13.88. Transmitting MIDI clock in a manner that seems "tight" rhythmically should have no more than a few percent jitter, let's say 5%, or 0.7 milliseconds per clock at 180 BPM. (iIf we only have millis resolution and keep track of the delta we're off by, the jitter error can be smeared across the clocks, making it less noticeable)
When receiving MIDI data, a flurry of incoming messages causes a significant pause in program flow as the Python-based MIDI library parses out the MIDI messages. Some C-based mechanism could really help here, especially if it did some filtering of the real-time messages like MIDI clock or thinning of CC messages (which can be transmitted at MIDI clock rates). Since this reader is doing that, providing a BPM calculation for the user should be pretty easy too.
When sending MIDI data, the ~7 millisecond cost (on RP2040) of the GC is devastating, same for the similar amount of time when updating a display. Having a timer-based way to send MIDI events is crucial. This is why I was harping on Timer interrupts for so long back in the day. I was hoping that would be a solution. Instead, maybe we have a "player" , where the user submits a list of events to be played. I don't think DMA is required for this but something in supervisor_tick() would suffice. But how to modify that list while it's being played, letting the user know all events have been sent, allowing for immediate playing, I've not yet thought too hard on yet.
Any idea what MIDI API would help make timing better
Any idea what MIDI API would help make timing easier
Can I ask why you went with supervisor_tick instead of monotonic_ns?
a couple of reasons, some of which may be wrong:
time.monotonic_ns()still only has millisecond resolution in CircuitPython- belief that large int math is slower than small int math
I’m new to python and using asyncio to do cooperative multitasking but from the reading I’ve done it seems like there’s not a way for the clock/sequencer task to take back execution after a defined amount of time - I just have to give it up when it’s not yet time to tick and hope that whatever other task(s) (ui read write) hand back execution in time for the next tick.
That is correct. await asyncio.sleep(n) will pause your function for "n + some_amount" seconds and you nave no ability to know how much "some_amount" is. So you can't just do sometihng like await asyncio.sleep(ppq_secs) because your function will quickly slide out of sync
I like the idea of "faster than human" things being handled under the hood. There isn't really a way to get around the GC time. That's the cost of dynamic memory
Are you doing MIDI over UART or USB?
You may want to schedule messages on a per pulse basis and then offload that.
you can do interrupts during a GC
both
So we'd need a clock stripper and scheduler