2.9 KiB
ump/ump
(no submodules)
Purpose
MIDI 2.0 introduces the Universal MIDI Packet (UMP) format — fixed-width 32, 64, 96, or 128 bit packets that carry MIDI messages alongside high-resolution per-note controllers, per-note pitch, and 32-bit velocity. This module exposes the four packet types, the message-type nibble constants, and a lossless bridge between MIDI 1.0 messages and their UMP equivalents.
The bridge is deliberately one-way for any MIDI 2.0 feature that exceeds MIDI 1.0's expressivity: a MIDI 2.0 channel-voice packet with 32-bit velocity cannot be downgraded to MIDI 1.0 without losing precision, so umpToMidi1 rejects it with MidiError("MIDI.UNSUPPORTED_UMP_VARIANT") rather than silently truncating. Engines that want to "best-effort downgrade" wrap this bridge with their own approximation policy.
Features
UMP32/UMP64/UMP96/UMP128shape types — each carries aUint32Arrayof words.midi1ToUmplifts any MIDI 1.0MidiMessageto its UMP equivalent.umpToMidi1reverses the lift when lossless; throws for MIDI 2.0-only features.- Short SysEx (≤ 6 bytes) bridges through a single UMP64 data-64 packet.
UMP_MESSAGE_TYPEconstant exposed for downstream dispatchers.
Usage: Basic
import { midi1ToUmp, umpToMidi1 } from "@suaveplan/midi";
const packet = midi1ToUmp({
type: "noteOn",
channel: 0,
note: 60,
velocity: 100,
});
const back = umpToMidi1(packet);
// back is identical to the input.
Usage: Advanced
import { midi1ToUmp, type UmpPacket } from "@suaveplan/midi";
// Lift an entire stream onto group 3.
function liftToGroup(stream: MidiMessage[], group: number): UmpPacket[] {
return stream.map((m) => midi1ToUmp(m, group));
}
// Filter packets by message-type nibble.
function isChannelVoice(packet: UmpPacket): boolean {
const mt = (packet.words[0]! >>> 28) & 0xf;
return mt === 0x2 || mt === 0x4;
}
API Reference
UMP32 / UMP64 / UMP96 / UMP128— discriminated shape types;wordsisUint32Arrayof length 1/2/3/4.UmpPacket = UMP32 | UMP64 | UMP96 | UMP128.UMP_MESSAGE_TYPE— frozen object exposing the MT nibble constants.midi1ToUmp(msg: MidiMessage, group?: number): UmpPacket— defaultsgroupto 0.umpToMidi1(packet: UmpPacket): MidiMessage— throwsMidiError("MIDI.UNSUPPORTED_UMP_VARIANT")for MIDI 2.0-only packets.
Implementation Notes
Channel-voice packets pack the full status byte (statusHigh | channel) into bits 16..23 of word 0; the encoder builds the byte first and shifts it as a unit rather than ORing the two nibbles separately at different shift counts (which was an earlier source of mis-encoding on channel != 0). The SysEx bridge supports only the single-packet "complete" form (status nibble 0) because multi-packet sysex-7 / sysex-8 streams require an explicit codec with start/continue/end framing — that codec lives in a dedicated downstream package.