[sdiy] Tools for testing MIDI
brianw
brianw at audiobanshee.com
Thu May 21 00:25:32 CEST 2026
For MIDI, make sure you know the implementation of the UART code and how it deals with timing and queuing. This is true for both the sender and receiver.
Tom and others have a good point: If your device only sends MIDI Clock, then there aren't as many details to worry about. If your device sends other MIDI messages, then the order and timing of individual bytes can result in better or worse latency, depending upon your implementation. Understanding this will inform the receiving side as well, for testing.
MIDI uses asynchronous serial, which means that if the line is idle, then a new MIDI Clock can be instantaneously loaded into the parallel register and it will start the conversion to serial right away. Most async serial ports for MIDI are clocked at 1 MHz or greater, so the latency between your timer interrupt and the start of serial data output is 0.5 microsecond on average. That's much faster than USB-MIDI with 500 milliseconds latency (a million times less latency).
If there are other MIDI messages being sent, then the code much use a queue, because the serial port register might still be holding the bits from a previous message. The way you manage the queue will affect timing, so you have to pay attention. Fortunately, the MIDI protocol was designed to maximize the performance based on the type of message.
The naive approach is to have a single queue and interrupts, so the main code just adds all MIDI data to the end of the queue. At the other end, the async serial port interrupts whenever it goes idle, and that interrupt handler can check the queue and feed the next byte if there's anything available. That's simple, but it's not the lowest latency for MIDI Clock.
The beauty of the MIDI protocol is that System Real Time messages like MIDI Clock can be sent any time, between any two other bytes, even if that would break a multibyte MIDI message into parts. Say you start a MIDI Pitch Bend message and get one byte in, but then MIDI Clock is scheduled. If you just add the clock to the queue, then the clock gets delayed until after the remaining two bytes of the pitch bend. That's a huge latency. Sometimes the queue can be backlogged, so the clock gets delayed even further.
What I recommend is to have two queues: One for System Real Time messages, and another queue for non-real time messages. You can't write the MIDI Clock immediately, unless the serial port is idle, so you need a dedicated queue to hold those timing-critical messages. With this design, your async serial output interrupt can first check the system real time output queue for anything, and prioritize any bytes there. If there's nothing in the real time queue, then the async serial interrupt handler can move on to the non-real time queue for lower priority data.
I have not determined how big the System Real Time queue needs to be, but obviously you can get by with only a few bytes. I don't see how you could get hundreds of bytes behind for system real time unless there's something horribly wrong. Nothing will stay in the real time queue for long anyway, since it has top priority. Since many 8-bit MCU chips have limited RAM, saving on queue size is important.
Now for testing: You can also implement a MIDI receiver that treats System Real Time specially. There's no need for a full MIDI parser in the async serial receiver input interrupt handler: You can just test the top 5 bits for real time, and if they match then time stamp the data and handle it. Non-real time messages can be queued, and time-stamped if you care. By prioritizing real time on the receiver, you get a more accurate time stamp. You'll need some sort of hardware timer that gives accurate timing, and you'll need to grab that timestamp at the exact beginning of the async serial interrupt handler to make sure your measurements do not include software parsing jitter. The full MIDI parser can be relegated to the software that reads the input queue. The MIDI parser in the interrupt handler can be a single branch statement!
As suggested, you could instead use a logic analyzer or oscilloscope for timing measurements, but the challenge is that all MIDI messages kinda look the same on the screen. They're all 10 bit cells wide, with a start bit, 8 data bits, and a stop bit. If your analyzer/scope can parse MIDI, then maybe it would be easy enough to measure timing and jitter for just the MIDI Clock messages, ignoring all other MIDI messages, but without parsing that can be difficult. I've certainly seen tools that can handle this parsing, but sometimes it's almost as difficult to set up as writing a MIDI receiver in assembly.
Stay away from USB-MIDI devices as a test tool, because there are no timing guarantees, and no timestamps on the incoming data. If you are implementing your own USB test device with MIDI ports, then you might be able to provide timestamps through some custom protocol other than USB-MIDI. That's fairly advanced, though.
Brian Willoughby
p.s. If your device is running a full operating system, a real-time operating system, or bare metal (no operating system), then this choice can have a drastic effect on latency. Even within the specific RTOS brand from a singe vendor, there can be huge latency differences between one setup and another. For example, the RTOS used for STM32 micros can be configured so that interrupts go through the thread scheduler, which adds a lot of latency. The latency is significant compared to the MIDI master asynchronous clock with its 1 microsecond period. You can configure specific hardware interrupts to be handled by a dedicated routine, but this option is not available in the lite RTOS - only the upgrade. Also, some processors are not supported by the better configuration (at least this was true in 2017 when I was working with that processor). Personally, I prefer bare metal implementations because I have control over every CPU clock cycle, and can get latency down to the minimum. If you're not doing bare metal, then at least be aware of the configuration options that affect latency.
p.p.s. Timing of serial protocols can be difficult to deal with, because the parallel to serial conversion happens in hardware, and the main body of the code has no direct knowledge of when that conversion is actually completed. There are ways to check, and of course there are interrupts so the serial port can wake up the main software after each serial word has completed. I've been bitten several times in early design implementations by not allowing time for a serial word to be sent before the next word. Once you're aware of the challenge, it's usually not too difficult to solve.
On May 20, 2026, at 1:46 PM, Tom Wiltshire wrote:
> Is it *only* sending MIDI clock?
>
> If so, it's a simple job and I'd be surprised if it's even breaking a sweat. As others have said, it's the receiving side that more commonly has the problems, since it's having to parse everything coming in in a timely manner. With Clock in particular, that's crucial, although the MIDI designers had already thought of this and made it easy.
>
> If it's *not* only sending MIDI clock, get it sending *everything* else it can possibly send all at once, and then make it pass through a massive Sysex data dump from somewhere else too. If it survives that, it's better than 95% of the MIDI stuff that's out there!
>
> MIDIOX and similar tools are great fro making sure that the bytes you're getting are the bytes you think you should be getting. I've had to use it for debugging and that level of detail is vital.
>
> On 20 May 2026, at 21:20, Chromatest J. Pantsmaker wrote:
>> Well, that's a great question. What *should* I be looking for? I assumed jitter, but anything else?
>>
>> On 5/20/2026 9:27 AM, Chromatest J. Pantsmaker wrote:
>>> Hey all,
>>>
>>> I'm finally getting a new project completed. Basically, it's a MIDI clock generator running on an ESP32-C6. It appears to be functioning properly, but I want to stress test the MIDI clock output.
>>>
>>> What tools are people using to test MIDI?
More information about the Synth-diy
mailing list