# 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` / `UMP128` shape types — each carries a `Uint32Array` of words. - `midi1ToUmp` lifts any MIDI 1.0 `MidiMessage` to its UMP equivalent. - `umpToMidi1` reverses 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_TYPE` constant exposed for downstream dispatchers. ## Usage: Basic ```ts 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 ```ts 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; `words` is `Uint32Array` of 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` — defaults `group` to 0. - `umpToMidi1(packet: UmpPacket): MidiMessage` — throws `MidiError("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.