Search guides and the APICtrl K
  1. API reference
  2. MIDI and OSC

ump128.h

The 128-bit Universal MIDI Packets ump.h left out: Flex Data (message type 0xD), UMP Stream (0xF), and SysEx8 with its Mixed Data Set (0x5).

#include <sonore/ump128.h>
On this page

Design notes

Why these three, and why together

ump.h stopped at what a host bridging a MIDI 2.0 stream hands a plugin: channel voice and SysEx7. That is still the common case. But a plugin that SPEAKS UMP -- an endpoint in its own right, which is where MIDI 2.0 goes once hosts pass packets through instead of flattening them -- meets all three of these the moment it is connected:

  • UMP Stream is how an endpoint introduces itself. Before anything else crosses, the other side asks "who are you, which protocol, how many function blocks", and a plugin that cannot answer is not discovered.
  • Flex Data carries what MIDI 1.0 put in a Standard MIDI File's meta events: tempo, time and key signature, chord names, lyrics, the song's name. A clip launched from a MIDI 2.0 sequencer brings them inline.
  • SysEx8 is SysEx without the seven-bit tax, and the Mixed Data Set is its bulk-transfer sibling. A device that has both will use them.

They are one file because they share one shape: four words, a two-bit or four-bit "form" saying whether this packet is a whole message, its start, its middle or its end, and bytes packed most-significant-first into the words. The reassembly problem is the same for all three, so it is written once (detail::Reassembly) and each message type supplies only its key and where its bytes sit.

Bit positions

Every layout here is the field table of the UMP specification (M2-104-UM, version 1.1), and each encoder states the word it builds in that table's own terms, MSB first. Packets are arrays of host-order 32-bit words, as in ump.h: byte order is the transport's business, not the message's.

Reassembly, and what it refuses

The rule from ump.h's SysEx7 assembler holds: a partial message is never handed on. A start inside an open message abandons the old one, a continue or end with nothing open is an orphan from a stream joined half way, and a message longer than its buffer is DROPPED rather than truncated -- a truncated SysEx is not a smaller message, it is a different one. Every refusal is counted, because "it silently did nothing" is the failure an assembler is otherwise indistinguishable from.

Interleaving is legal and handled: SysEx8 carries a stream ID precisely so several messages can be in flight on one group, Flex Data text on different channels or of different kinds is independent, and a function block's name can arrive between two halves of the endpoint's. Each assembler keys an open message by everything that distinguishes it, in a fixed pool of slots, so nothing here allocates except the Mixed Data Set -- see that class for why it is the exception.

Types 14

Word 0 of a Flex Data packet, taken apart.

A Flex Data time signature, as setTimeSignature() writes it and decodeSetTimeSignature() fills it. The denominator is held as its power of two, the way the packet carries it; denominator() gives the number a score shows. Defaults to 4/4.

A Flex Data metronome setting, field for field as the packet carries it: the primary click's spacing in MIDI clocks (24 a quarter note), the three bar-accent bytes and the two subdivision click counts, each carried as it is.

A Flex Data key signature: sharps (positive) or flats (negative), and the tonic as one of the kNote letters -- kNoteUnknown when the sender does not say. Both travel in four-bit fields, so a value outside them is cut to four bits on the way out.

One alteration of a chord -- add, subtract, raise or lower a scale degree -- as the spec's two nibbles. Type 0 is an empty slot.

A Flex Data chord name, for a lead sheet or a chord track: the chord's tonic and type with up to four alterations, and a bass chord with up to two. Codes are the spec's tables, carried as numbers (see above); notes are kNote letters with a signed sharps/flats count. setChordName() addresses a channel by default, unlike the other setup messages.

Reassembles Flex Data text -- metadata and lyrics -- from its packets.

Keyed by group, address, channel, bank and status: a lyric line on channel 1 and the clip's name on the group are two messages, and may arrive packet by packet in alternation. Setup messages (tempo and the rest) are single packets with nothing to assemble; push() ignores them, so a caller can hand it every Flex Data packet without sorting first.

The zero padding is stripped per packet: a packet's text ends at its first zero byte, which UTF-8 never contains as part of a character.

What an endpoint says about itself in an Endpoint Info Notification -- the answer to Endpoint Discovery: the UMP version it speaks, how many function blocks it has and whether they can change, which protocols it can be switched to, and its jitter-reduction timestamp support.

The protocol a stream runs (kProtocolMidi2 or the MIDI 1.0 one) and whether jitter-reduction timestamps go each way. The same fields serve the request that asks for a configuration and the notification that confirms one.

One function block, as a Function Block Info Notification describes it: its number, whether it is active, the groups it spans (firstGroup and numGroups, groups counted from 0), its direction, UI hint and MIDI 1.0 role (the kBlock constants), and its MIDI-CI and SysEx8 capacity. Fields narrower than a byte are cut to their width on the way out.

Reassembles the three name notifications.

Keyed by status and, for a function block's name, by the block: an endpoint answering a discovery sends its own name and every block's, and a sender that interleaves them is within its rights.

Reassembles SysEx8 from its packets, several streams at once.

A packet whose byte count is 0 (not even the stream ID) or above 14 is malformed. If it claimed to continue or end a message that is open, that message is now missing bytes, so it is abandoned rather than finished with a hole in it.

A Mixed Data Set chunk header, field for field: which group and data set (mdsId) it belongs to, how many payload bytes the chunk carries, its place among the set's chunks, and the manufacturer, device and sub IDs that say what the data is. Written by mixedDataSetHeader(), and filled for each completed chunk by MixedDataSetAssembler.

Reassembles Mixed Data Set chunks, one per group and MDS ID at a time.

The one assembler here that allocates. A chunk may be 65535 bytes and 256 may be open at once; a fixed pool for that is sixteen megabytes held against data that is almost never sent. So each open chunk reserves what its header declares -- once, on the header, never while payloads arrive -- and a Mixed Data Set is not something to assemble on the audio thread. It is a bulk transfer: a sample, a firmware image, a file.

Delivered per CHUNK. Putting the chunks of a set back together is the caller's, because only the caller knows whether chunk numbers it never saw mean a gap or a sender that numbers from somewhere else.

Functions 44

Aliases 1

↑↓ to move↵ to openEvery public name in the headers, and every guide