Expression & MIDI 2.0
MIDI 2.0 & the Universal MIDI Packet
MIDI 2.0 is not a new cable or a new byte stream. It is a new packet format, the Universal MIDI Packet (UMP), plus a higher-resolution set of messages and ways for devices to discover each other. MIDI 1.0 messages still exist inside UMP, so the two protocols live side by side.
What changes, and why
| Limit in MIDI 1.0 | MIDI 2.0 answer |
|---|---|
| 7-bit velocity and controllers (128 steps). 14-bit pairs are awkward to send. | 16-bit velocity; 32-bit controllers, pressure and pitch bend |
| Pitch bend and controllers apply to the whole channel | Per-note pitch bend, per-note controllers, per-note management |
| 16 channels per cable | 16 groups × 16 channels = 256 channels per UMP Endpoint |
| Bank select, program change and RPN/NRPN take several messages each | Single atomic messages: Program Change carries its bank, and Registered/Assignable Controllers replace RPN/NRPN |
| Byte stream with no framing; parsing depends on running status | Fixed-size 32-bit-word packets whose size can be read from the first 4 bits |
| One-way: a sender can't tell what a receiver supports | Endpoint and Function Block discovery (UMP Stream) and MIDI-CI |
| Timing depends on when bytes arrive | Jitter Reduction (JR) timestamps |
UMP basics
A UMP is made of 1 to 4 32-bit words, so packets are 32, 64, 96 or 128 bits long. The top four bits of the first word are the Message Type (MT), which also sets the packet size. A receiver can always tell how long a packet is, even when it doesn't know the message.
For most message types, the next four bits are the group (0–15). Each group works like a separate MIDI 1.0 cable with its own 16 channels. Utility (0x0) and UMP Stream (0xF) messages have no group; they address the whole Endpoint.
Message types
| MT | Bits | Type | Contents |
|---|---|---|---|
0x0 |
32 | Utility | NOOP, JR Clock, JR Timestamp, Delta Clockstamp Ticks Per Quarter Note (DCTPQ), Delta Clockstamp. No group. |
0x1 |
32 | System Real Time & System Common | MIDI 1.0 F1–FF messages (clock, start/stop, MTC quarter frame, SPP, song select, tune request) |
0x2 |
32 | MIDI 1.0 Channel Voice | The classic 8n–En messages with 7- and 14-bit values, carried in UMP |
0x3 |
64 | Data (SysEx7) | MIDI 1.0 System Exclusive, split into packets of up to 6 data bytes |
0x4 |
64 | MIDI 2.0 Channel Voice | High-resolution and per-note messages of the MIDI 2.0 Protocol |
0x5 |
128 | Data 128 | System Exclusive 8 (8-bit SysEx) and Mixed Data Set |
0x6–0x7 |
32 | Reserved | |
0x8–0xA |
64 | Reserved | |
0xB–0xC |
96 | Reserved | |
0xD |
128 | Flex Data | Tempo, time and key signature, metronome, chord names, lyrics and other text |
0xE |
128 | Reserved | |
0xF |
128 | UMP Stream | Endpoint and Function Block discovery, stream configuration, clip start/end. No group. |
Worked example: one Note On, two ways
Middle C (note 60) on channel 1, group 1 (written as 0 in the packet), at full velocity.
MIDI 2.0 Protocol Note On (MT 0x4, 64 bits)
0x40903C00 0xFFFF0000
- MT = 4: MIDI 2.0 Channel Voice, so the packet is 64 bits.
- Group = 0, status = 9 (Note On), channel = 0 (channel 1).
- Note = 0x3C (60). The note number is still 7 bits; the top bit of that byte is 0.
- Attribute type = 0 (none). Other defined types are 1 = manufacturer specific, 2 = profile specific, and 3 = pitch 7.9, a fixed-point pitch in semitones for microtonal notes.
- Velocity = 0xFFFF, the maximum of a 16-bit range.
The same note as MIDI 1.0 in UMP (MT 0x2, 32 bits)
0x20903C64, which is the classic 90 3C 64 (velocity 100) with a one-byte MT and group prefix.
MIDI 2.0 Channel Voice messages
Every MT 0x4 message has the same first word: MT | group | status | channel | index/note | ....
The status nibble picks the message:
| Status | Message | Notes |
|---|---|---|
0x0 | Registered Per-Note Controller | Per-note controller, 256 defined indexes, 32-bit value |
0x1 | Assignable Per-Note Controller | Per-note controller, 256 free indexes, 32-bit value |
0x2 | Registered Controller | RPN: 128 banks × 128 indexes, 32-bit value, one message (no Data Entry sequence) |
0x3 | Assignable Controller | NRPN: 128 banks × 128 indexes, 32-bit value |
0x4 | Relative Registered Controller | Signed 32-bit increment/decrement of an RPN |
0x5 | Relative Assignable Controller | Signed 32-bit increment/decrement of an NRPN |
0x6 | Per-Note Pitch Bend | 32-bit unsigned, center 0x80000000 |
0x8 | Note Off | 16-bit velocity + attribute |
0x9 | Note On | 16-bit velocity + attribute. Velocity 0 is a real Note On, not a Note Off. |
0xA | Poly Pressure | 32-bit |
0xB | Control Change | 7-bit index, 32-bit value |
0xC | Program Change | Program number plus optional bank MSB/LSB in the same message |
0xD | Channel Pressure | 32-bit |
0xE | Pitch Bend | 32-bit unsigned, center 0x80000000 |
0xF | Per-Note Management | Detach (D) and/or reset (S) the per-note controllers of a note |
Field layouts at a glance
| Message | Word 1 (after MT/group/status/channel) | Word 2 |
|---|---|---|
| Note On / Off | note (7) · attribute type (8) | velocity (16) · attribute data (16) |
| Poly Pressure | note (7) · reserved | pressure (32) |
| Control Change | index (7) · reserved | value (32) |
| Registered / Assignable Controller | bank (7) · index (7) | value (32) |
| Per-Note Controller (registered / assignable) | note (7) · index (8) | value (32) |
| Program Change | reserved (8) · option flags (8); bit 0 = Bank Valid | program (7) · reserved (8) · bank MSB (7) · bank LSB (7) |
| Channel Pressure | reserved | pressure (32) |
| Pitch Bend / Per-Note Pitch Bend | reserved / note (7) | bend (32), center 0x80000000 |
| Per-Note Management | note (7) · flags: D (detach), S (reset) | reserved |
Velocity zero
In MIDI 1.0, a Note On with velocity 0 means Note Off. In the MIDI 2.0 Protocol it doesn't, so senders must use Note Off (0x8). When translating MIDI 2.0 to MIDI 1.0, a velocity that scales down to 0 is sent as 1.
Value scaling: Min-Center-Max
Converting a 7-bit value to 16 or 32 bits is not just a left shift. A plain shift turns 127 into 0xFE00, so the maximum would no longer be the maximum. The spec defines a Min-Center-Max upscaling rule that keeps three points exact:
- Minimum: 0 always maps to 0.
- Center: the source midpoint (64 for 7-bit, 8192 for 14-bit) maps to the exact destination midpoint (0x8000, 0x80000000).
- Maximum: the source maximum maps to all ones.
Values at or below the center are shifted left. For values above the center, the bits below the source's top bit are repeated into the empty low-order bits of the result. Values above the center are spread evenly up to the maximum, and values below it keep the plain shift.
// Reference algorithm from the UMP spec (abridged)
function scaleUp(srcVal, srcBits, dstBits) {
const scaleBits = dstBits - srcBits;
let out = srcVal << scaleBits; // plain shift
const center = 1 << (srcBits - 1);
if (srcVal <= center) return out; // min .. center: done
const repeatBits = srcBits - 1;
let repeat = srcVal & ((1 << repeatBits) - 1); // bits below the top bit
repeat = scaleBits > repeatBits
? repeat << (scaleBits - repeatBits)
: repeat >> (repeatBits - scaleBits);
while (repeat) { out |= repeat; repeat >>= repeatBits; }
return out;
}
| 7-bit | → 16-bit | → 32-bit |
|---|---|---|
| 0 | 0x0000 | 0x00000000 |
| 1 | 0x0200 | 0x02000000 |
| 64 | 0x8000 | 0x80000000 |
| 65 | 0x8208 | 0x82082082 |
| 100 | 0xC924 | 0xC9249249 |
| 127 | 0xFFFF | 0xFFFFFFFF |
Downscaling is a simple right shift. For example, a 16-bit velocity becomes 7 bits with v >> 9.
Endpoints, Function Blocks and the UMP Stream
A UMP Endpoint is one end of a bidirectional UMP connection, such as a USB MIDI 2.0 device or a virtual port. It may contain up to 32 Function Blocks, each a logical unit (a synth engine, a keyboard, a DIN port) that spans one or more groups. Hosts learn this layout from UMP Stream messages (MT 0xF), which identify themselves with a 2-bit format and a 10-bit status:
| Status | Message | Purpose |
|---|---|---|
0x000 | Endpoint Discovery | Ask an Endpoint to describe itself |
0x001 | Endpoint Info Notification | UMP version, number of Function Blocks, supported protocols and JR timestamps |
0x002 | Device Identity Notification | Manufacturer, family, model and version (same fields as a SysEx Identity Reply) |
0x003 | Endpoint Name Notification | UTF-8 name, possibly across several packets |
0x004 | Product Instance Id Notification | Unique ID, such as a serial number |
0x005 | Stream Configuration Request | Ask to switch protocol (MIDI 1.0 / MIDI 2.0) and JR timestamp use |
0x006 | Stream Configuration Notification | Report the current protocol and JR settings |
0x010 | Function Block Discovery | Ask for one or all Function Blocks |
0x011 | Function Block Info Notification | Active flag, direction, first group and number of groups spanned, MIDI-CI version |
0x012 | Function Block Name Notification | UTF-8 name of a block |
0x020 | Start of Clip | Marks the start of a sequence (e.g. in a MIDI Clip File) |
0x021 | End of Clip | Marks the end of a sequence |
Choosing a protocol
An Endpoint may support the MIDI 1.0 Protocol, the MIDI 2.0 Protocol, or both. Since UMP 1.1 (2023), the host picks one with Stream Configuration Request/Notification, and the same exchange turns JR timestamps on or off. MIDI-CI 1.1 negotiated the protocol with its own SysEx messages; MIDI-CI 1.2 deprecated that mechanism, so new implementations should use UMP Stream messages.
Jitter Reduction timestamps
A sender can put a JR Timestamp utility message (MT 0x0) in front of other messages. It records when the event happened, as a 16-bit count in units of 1/31,250 second (32 µs). The receiver tracks the sender's clock from periodic JR Clock messages and uses the timestamps to undo transport jitter. This helps most on buffered links such as USB and networks. Musical timing, as opposed to transport timing, uses Delta Clockstamps in ticks (with DCTPQ) in MIDI Clip Files.
SysEx, Flex Data and files
- SysEx7 (MT 0x3) carries MIDI 1.0 SysEx, up to six bytes per 64-bit packet, with a status of complete, start, continue or end. The F0 and F7 framing bytes are not sent.
- SysEx8 (MT 0x5) carries full 8-bit data with a stream ID, so binary payloads don't need 7-bit encoding.
- Flex Data (MT 0xD) holds tempo, time signature, key signature, metronome and chord-name messages, plus text such as lyrics and metadata. It replaces many SMF meta events for live streams.
- MIDI Clip File and MIDI Container File are the UMP-based successors to the Standard MIDI File.
Platform support
| Platform | Status |
|---|---|
| macOS / iOS | CoreMIDI has UMP APIs (MIDIEventList) since macOS 11 / iOS 14, and supports USB MIDI 2.0 devices. |
| Windows | Windows MIDI Services, a new UMP-native MIDI stack for Windows 11 with MIDI 2.0, multi-client ports and a compatibility layer for older APIs. |
| Linux | ALSA gained UMP and USB MIDI 2.0 support in kernel 6.5, with matching userspace support in alsa-lib 1.2.10 and later. |
| Android | USB MIDI 2.0 host support since Android 13. |
| Web | The Web MIDI API is still MIDI 1.0 only. |
Libraries with UMP support include AM_MIDI2.0Lib, ni-midi2, ktmidi, cmidi2 and libremidi. For moving UMP over USB, BLE and networks, see Transports.
Spotted an error? Edit this page on GitHub.