USB MIDI 2.0 transport: UMP device and host support#703
Conversation
Device side exposes MIDI 2.0 descriptors (Alt 0 MIDI 1.0 compat + Alt 1 UMP, group terminal block served via GET_DESCRIPTOR). Host side detects Alt 1 on attached devices, drives SET_INTERFACE to completion before opening pipes, and hands complete UMP messages to a registered callback. MidiUsbTransport keeps the existing MIDI 1.0 byte path on Alt 0; UMP framing applies only when the active interface is Alt 1. USBH_MAX_NUM_INTERFACES goes from 2 to 4 so Itf_Desc holds the three entries a MIDI 2.0 device exposes (AudioControl + MIDIStreaming Alt 0 + Alt 1). USBH_SelectInterface receives the Alt 0 index, which satisfies its bNumInterfaces bounds check; the Alt 1 index drives the endpoint scan and SET_INTERFACE.
Paired stress-test examples for the USB MIDI 2.0 transport. The device emits a deterministic UMP stream (4001 packets per burst, sequence number embedded in NoteOn velocity, CC 119 marker per burst). The host validates reception with cumulative counters and reports a once-per- second tally. Validated on hardware: 1.47M messages between two Daisy Seeds over 5 minutes with zero loss, and 72 consecutive bursts of exactly 4001 packets captured via Linux ALSA on the device side.
|
Woah! Very cool. Can't wait to check this out. I've got a few of the older PRs, and things outside of libDaisy to get through today/next week before I get to reviewing this, but I should have some time to check it out in the next few weeks! |
|
Thanks, Stephen! Appreciate the reply. Taking it slow makes total sense. The idea was to start with the transport layer, since that is the foundation: raw UMP words in and out, with no opinion about what to do with them. With that solid, everything else builds on top. I've been experimenting quite a bit with the fork and put together a few devices on top of it that work really well. If you want to play with them, they live in midi2cpp, a C++ MIDI 2.0 library I maintain: the When it makes sense to think about the layers above transport, MIDI 2.0 has a few specifics compared to 1.0 (group terminal blocks, function blocks, protocol negotiation over MIDI-CI), and I'm happy to help think those decisions through if it's ever useful. |
Summary
This PR adds USB MIDI 2.0 (Universal MIDI Packet) transport support to
MidiUsbTransport, on both the device and the host side. The MIDI 1.0byte path is unchanged: MIDI 2.0 traffic flows only when the active
MIDIStreaming interface is Alternate Setting 1.
What was added
Device side
The peripheral now enumerates with the USB MIDI 2.0 descriptor layout:

Alternate Setting 0 keeps the existing MIDI 1.0 interface for backward
compatibility, Alternate Setting 1 exposes the UMP endpoints (bcdMSC
0x0200). A Group Terminal Block descriptor (1 bidirectional block,
group 0, protocol MIDI 2.0) is served via the class GET_DESCRIPTOR
request. Hosts that do not know MIDI 2.0 stay on Alt 0 and see a
regular MIDI 1.0 device.
Host side
USBH_MIDInow checks attached devices for a MIDIStreaming Alt 1. Whenpresent, it drives SET_INTERFACE to completion before opening the IN/OUT
pipes, so the device has switched to UMP framing when the host starts
reading. Devices without Alt 1 keep working exactly as before (MIDI 1.0
CIN packets).
USBH_MAX_NUM_INTERFACESgoes from 2 to 4 soItf_Desc[]can hold thethree entries a MIDI 2.0 device exposes (AudioControl, MIDIStreaming
Alt 0, MIDIStreaming Alt 1).
API
One new callback on
MidiUsbTransport, delivering complete UMPmessages (1 to 4 words, split by message type per the UMP spec):
The MIDI 1.0 parse callback registered through
StartRx()keepsworking; both can be registered at the same time.
Examples
Two paired examples provide a reproducible stress test:
examples/MIDI2_USB_Device: deterministic UMP emitter. Per burst,2000 NoteOn + 2000 NoteOff (sequence number embedded in the NoteOn
velocity) + 1 marker CC, then 400 ms of silence.
examples/MIDI2_Host: validator. Counts every received word incumulative counters inside the RX callback (no work in the hot path)
and prints a once-per-second tally with an OK/FAIL verdict: the span
between the first and last marker must hold an exact multiple of
4001 packets, with zero sequence gaps.
Hardware validation
/dev/snd/umpC*D0)Descriptors verified against the USB Device Class Definition for MIDI


Devices v2.0 (Alt 1 endpoint descriptors are the 7-byte form per Table
B-17, Group Terminal Block per Table B-21/B-22) and enumeration checked
with
lsusb -von Linux 6.17 (ALSA creates the UMP endpoint andselects Alt 1 automatically).
How to test
Device side needs nothing beyond a USB cable: flash
examples/MIDI2_USB_Deviceand watch the UMP stream on any MIDI 2.0capable host (on Linux,
/dev/snd/umpC*D0).Host side needs a USB-A receptacle wired to the Seed:
Flash
examples/MIDI2_Host, open the serial console, plug the emitterinto the USB-A jack and watch the HEARTBEAT lines.
Backward compatibility
examples/MIDI_USBH_Inputrebuilt and retested against a MIDI 1.0device, behavior unchanged.