Format based on Keep a Changelog. This project follows Semantic Versioning.
midi2_msg_stream_fb_info()and theon_fb_infodispatch callback drop theirprotocolparameter. Word 1 of a Function Block Info Notification carries four byte-wide fields (M2-104-UM 7.1.8) and no protocol field. Callers pass one argument less.- Software Revision Level is carried as four manufacturer defined bytes,
replacing the 28-bit packing used since 0.8.0. Both M2-104-UM 7.1.3 and
M2-101-UM 5.5 describe the field as four bytes whose format is device
specific, and the M2-105 DeviceInfo schema types it as a four item byte
array. The change covers the UMP Stream Device Identity Notification and
MIDI-CI Discovery, so a device declaring
0x00010000now puts00 01 00 00on the wire in both.
- Function Block Info encodes and decodes the layout the spec defines: Max Number of SysEx8 Streams occupies the low byte and covers its documented 0-255 range, MIDI-CI Message Version is a byte, and Number of Groups Spanned fits the maximum of 16.
- The
atmega32u4-device-baremetalexample packs its manufacturer id the way the CI builder expects, and theteensy-device-midi2example declares the same identity across Property Exchange, UMP Stream and MIDI-CI.
midi2_conv_next(): drains the additional message a single MIDI 1.0 byte can produce. Per M2-104-UM 7.7.1, System Real-Time may interleave inside a SysEx and any other status terminates it, so one byte can yield two UMP messages; the canonical consumption loop is nowif (midi2_conv_feed(...)) do { ... } while (midi2_conv_next(...));. For ordinary bytes the loop body runs once. Debug builds assert if a queued message is left undrained; release builds drop it.
midi2_conv: a completed System Common message (F1-F3) no longer leaves its status as Running Status. Data bytes arriving after Song Position, Song Select or MTC Quarter Frame are orphans per MIDI 1.0 -- previously every further data-byte pair fabricated another copy of the message.
midi2_convnow applies the M2-104-UM 7.7.1 interspersing rules:- A Real-Time byte inside a SysEx is emitted after the partial packet holding the bytes that preceded it on the wire, preserving its timing position (previously it was emitted first, reordering it ahead of data it followed).
- A status byte inside a SysEx closes the message with a COMPLETE or END packet carrying the bytes received (previously the buffered bytes were dropped and an already-emitted START was left unterminated).
- A SysEx Start while another SysEx is open closes the previous message the same way before starting the new one. Verified against the byte vectors from AM_MIDI2.0Lib issues #16, #23 and #24; the first packets for the #24 vector now match that issue's expected words exactly.
- Example
atmega32u4-device-arduino: USB MIDI 2.0 device on the Arduino Leonardo over the midi2duino transport, with UMP Stream and MIDI-CI responders. MIDI-CI replies are queued atomically and inbound Data128 is echoed directly, so a full TX ring cannot corrupt USB output. - Example
atmega32u4-device-baremetal: USB MIDI 2.0 device on the Arduino Pro Micro, bare metal C99 over the midi2lufa transport, with the 58-entry M2-104 catalog, UMP Stream and MIDI-CI responders, and a MIDI 1.0 fallback riff. Validated on hardware (Linux ALSA UMP, echo, per-boot MUID).
- Device identity field byte order in the Stream and MIDI-CI builders now
follows M2-104 Figure 14 and the MIDI-CI Discovery tables: the packed
manufacturer_idisid1<<16 | id2<<8 | id3(the educational prefix 0x7D packs as 0x7D0000), family and model are 14-bit values sent as 7-bit LSB/MSB pairs, and the version is 28 bits sent as four 7-bit bytes LSB first. Callers that passed the manufacturer id in the previous packed form must update; the wire layout is cross-checked against the MIDI 2.0 Workbench, the Linux kernel UMP driver and Windows MIDI Services.
- MIDI-CI responder re-announces via Discovery after receiving an Invalidate MUID or detecting a MUID collision (Workbench ci1.2).
- Multi-packet senders (
midi2_proc_send_sysex7,midi2_proc_send_sysex8,midi2_proc_send_fb_name, endpoint name and product id) stop at the first short write instead of continuing past it, so a full transport sink can no longer leave a gap in the middle of a message. The receiver resynchronizes on the next Start packet.
Validated end to end against the official MIDI 2.0 Workbench on hardware (Teensy 4.1, RP2350, Raspberry Pi Pico): Discovery, Profile Configuration, and Property Exchange with zero errors and zero warnings.
- MIDI-CI responder handles Set Profile On/Off: a listed always-on profile answers with a Profile Enabled Report, an unlisted profile is NAKed.
- MIDI-CI responder handles Process Inquiry MIDI Message Report: Reply and End messages for Function Block and in-use channel requests, NAK for channels not in use.
- Property Exchange Get replies for list resources (ResourceList, ChannelList,
ProgramList) now carry
totalCountin the header, as M2-105 requires for paginable resources. Object resources such as DeviceInfo are unchanged.
- Example set reduced to three real USB MIDI 2.0 devices sharing the C99 core:
teensy-device-midi2(Arduino loop),rp2350-device-freertos(FreeRTOS + TinyUSB), andrpi-pico-device-zephyr(nativeusbd_midi2). The two synthetic sketches (basic-usage,ci-discovery) were removed. The Teensy sketch announces the UMP Stream identity in-band so hosts running active Endpoint Discovery register it.
- MIDI-CI responder declares Message Version 0x02 across all replies.
- Property Exchange Get/Set replies carry a status header, match the requested resource, and return 404 when it does not exist.
- Added
midi2_ci_set_capabilitiesand a built-inResourceListresource.
midi2_dispatch_feedandmidi2_proc_feeddrop a typed message shorter than its type requires instead of over-reading.midi2_cireturnsMIDI2_CI_ERR_NULLfor a NULL state or name in the property and subscribe entry points.- Added an overridable
MIDI2_ASSERTand a debug-only reentrancy guard (both compiled out underNDEBUG). Reproducible amalgam (no build-date stamp).
- Zephyr module support and a Raspberry Pi Pico example.
- NULL arguments at public entry points are no-ops or zero returns;
midi2_msg_word_countcovers all 16 message types.
Breaking signature changes so the builders emit spec fields they previously dropped. Default-safe values reproduce the prior wire behaviour; migration snippets are in git history.
- UMP Stream: Config Request and Notify gain JR enable bits; FB Info gains
ui_hintandmax_sysex8_streams. - Utility:
midi2_msg_jr_clockand_jr_timestampdrop the now-reserved Group parameter; addedmidi2_msg_noop. - MIDI 2.0 Channel Voice: Note On/Off split the attribute into
attr_typeplus a 16-bitattr_data; Program Change Bank Valid bit moved to the spec lane. - Flex Data:
midi2_msg_time_siggainsnum_32nd_notes.
- ESP-IDF gate consumes the modular
src/*.cset the Component Manager delivers (it filtersdist/out of the dependency tarball).
- ESP-IDF Component Manager support via a root
idf_component.ymland anESP_PLATFORMgate ahead ofproject(), so native CMake consumers are unaffected.
- CMake build alongside the Makefile:
midi2::midi2target, install and export ruleset forfind_package(midi2 CONFIG), FetchContent support, and a downstream consumer test.
- Arduino Library Manager compliance:
library.properties, thesrc/midi2.humbrella, and reference sketches. Registered on the Arduino Library Manager.
- Property Exchange Subscribe/Notify state machine (M2-101-UM 8.11 to 8.13) via
midi2_ci_init_ex; legacymidi2_ci_initstays source-compatible. - UMP Stream and SysEx8 fragmenters in
midi2_proc; MT 0x4 to MT 0x2 downgrade; USB MIDI 1.0 cable-event helper; System message wrappers. - Breaking:
midi2_ci_propertygains a trailingsubscribablebool (designated-initialiser call sites compile unchanged).
- MIDI-CI responder completeness (M2-101-UM Appendix E): MUID regeneration and collision detection, NAK-on-unknown, and an automatic PE Capability reply. Additive, no breaking change.
dist/midi2.ccompanion to the single header;tools/amalgamate.shemits both files underdist/. Spec references added across the module headers.
- Single-header amalgam (
midi2.h, stb-style). midi2_msg_mt2_to_mt4protocol translation with value scaling and a dispatchupscale_mt2flag; streaming SysEx7 inmidi2_conv.
midi2_dispatch(42 typed UMP callbacks),midi2_ci_msgandmidi2_ci_dispatch(full MIDI-CI surface), complete UMP construction.- Fixed the Utility status nibble shift and the SysEx dispatch status values.
Initial release: midi2_msg, midi2_proc, midi2_ci, midi2_conv. Caller
provided storage, zero allocation, C99.