# @suaveplan/midi - Universal MIDI primitives: 18-variant MidiMessage discriminated union, encode/decode with running-status, NRPN/RPN/CC14 reducers, SysEx framing with manufacturer registry, MidiPortLike contract, MIDI Clock helpers, General MIDI tables (Level 1, Level 2, GS, XG), and MIDI 2.0 UMP framing with lossless MIDI1<->UMP bridge. Zero browser/Node coupling. Universal MIDI primitives for the Suaveplan platform. Owns the canonical types, byte parsers, message constructors, control-surface dispatchers (NRPN, RPN, 14-bit CC), SysEx framing with a manufacturer-id registry, MIDI Clock helpers, the General MIDI Level 1 / Level 2 / GS / XG patch tables, MIDI 2.0 UMP framing with a lossless MIDI1 ↔ UMP bridge, and an OpenTelemetry-shaped (structural) instrumentation surface. Zero browser/Node coupling — Web MIDI access lives in `@suaveplan/react-midi`, node-midi access in a future `@suaveplan/midi-node`. ## Documentation tree - [clock](./src/clock/clock.md) — Helpers for converting between MIDI Clock ticks, beats, and tempo. MIDI 1.0 defines 24 pulses per quarter note (`PPQN =… - [clock](./src/clock/clock.md) — Helpers for converting between MIDI Clock ticks, beats, and tempo. MIDI 1.0 defines 24 pulses per quarter note (`PPQN =… - [cc14](./src/dispatch/cc14.md) — `CC14Reducer` accumulates paired MIDI 1.0 control-change messages into 14-bit values. The wire convention is: CCs 0..31… - [nrpn](./src/dispatch/nrpn.md) — `NRPNReducer` accumulates the four-CC "dance" that MIDI 1.0 uses to express Non-Registered Parameter Number changes int… - [rpn](./src/dispatch/rpn.md) — `RPNReducer` is the sister of `NRPNReducer` for Registered Parameter Numbers — the parameter MSB and LSB CC numbers mov… - [running-status](./src/dispatch/running-status.md) — `RunningStatusDecoder` is the streaming MIDI 1.0 decoder that honours running status — the wire convention where succes… - [error-codes](./src/error-codes/error-codes.md) — Defines the `MidiError` class and the `MIDI_CODES` namespaced registry used by every `@suaveplan/midi` module that rais… - [error-codes](./src/error-codes/error-codes.md) — Defines the `MidiError` class and the `MIDI_CODES` namespaced registry used by every `@suaveplan/midi` module that rais… - [gs](./src/gm/gs.md) — Roland's GS dialect predates GM2 and uses Bank-Select LSB (CC 32) to select instrument variations on top of the GM1 bas… - [level1](./src/gm/level1.md) — The General MIDI Level 1 patch and percussion tables — the canonical instrument-name lookups every GM-compliant playbac… - [level2](./src/gm/level2.md) — General MIDI Level 2 added a bank-select-driven variation layer on top of GM1. Each GM1 program (0..127) gains optional… - [xg](./src/gm/xg.md) — Yamaha's XG dialect uses Bank-Select MSB (CC 0) to select instrument variations on top of the GM1 base programs — the i… - [decode](./src/message/decode.md) — `decodeMessage` parses one MIDI 1.0 message starting at a given offset in a `Uint8Array` and returns both the typed mes… - [encode](./src/message/encode.md) — `encodeMessage` converts a strongly-typed `MidiMessage` value into the byte sequence a MIDI 1.0 wire format expects. It… - [sysex](./src/sysex/sysex.md) — `parseSysEx` and `buildSysEx` are the canonical helpers for working with MIDI System Exclusive payloads — the manufactu… - [manufacturers](./src/sysex/manufacturers.md) — The manufacturer-id registry maps the bytes that follow `F0` in a SysEx frame to a human-readable name. The MIDI Manufa… - [sysex](./src/sysex/sysex.md) — `parseSysEx` and `buildSysEx` are the canonical helpers for working with MIDI System Exclusive payloads — the manufactu… - [telemetry](./src/telemetry/telemetry.md) — OpenTelemetry-shaped instrumentation for `@suaveplan/midi` without taking a runtime dependency on `@opentelemetry/api`.… - [telemetry](./src/telemetry/telemetry.md) — OpenTelemetry-shaped instrumentation for `@suaveplan/midi` without taking a runtime dependency on `@opentelemetry/api`.… - [ump](./src/ump/ump.md) — MIDI 2.0 introduces the Universal MIDI Packet (UMP) format — fixed-width 32, 64, 96, or 128 bit packets that carry MIDI… - [ump](./src/ump/ump.md) — MIDI 2.0 introduces the Universal MIDI Packet (UMP) format — fixed-width 32, 64, 96, or 128 bit packets that carry MIDI… ## Install ```bash bun add @suaveplan/midi ``` ## Quick start ```ts import { decodeMessage, encodeMessage, RunningStatusDecoder, } from "@suaveplan/midi"; // Encode a NoteOn. const bytes = encodeMessage({ type: "noteOn", channel: 0, note: 60, velocity: 100, }); // Decode a buffer with running-status enabled. const decoder = new RunningStatusDecoder(); decoder.push(bytes); const messages = decoder.drain(); // messages = [{ type: "noteOn", channel: 0, note: 60, velocity: 100 }] ``` ## Modules - [error-codes](./src/error-codes.md) — `MidiError` and stable `MIDI.*` code registry. - [message/encode](./src/message/encode.md) — `encodeMessage`, `STATUS` byte constants. - [message/decode](./src/message/decode.md) — `decodeMessage` with optional running-status. - [dispatch/running-status](./src/dispatch/running-status.md) — streaming decoder honouring MIDI 1.0 running status + real-time interleaving. - [dispatch/nrpn](./src/dispatch/nrpn.md) — Non-Registered Parameter Number reducer. - [dispatch/rpn](./src/dispatch/rpn.md) — Registered Parameter Number reducer. - [dispatch/cc14](./src/dispatch/cc14.md) — paired-CC 14-bit reducer. - [sysex/sysex](./src/sysex/sysex.md) — `parseSysEx`, `buildSysEx`, `SysExFrame`. - [sysex/manufacturers](./src/sysex/manufacturers.md) — manufacturer-id registry + `lookupManufacturer`. - [clock/clock](./src/clock/clock.md) — `bpmToClock`, `clockToBpm`, `PPQN`. - [gm/level1](./src/gm/level1.md) — General MIDI Level 1 patch + percussion tables. - [gm/level2](./src/gm/level2.md) — GM2 variation bank lookup. - [gm/gs](./src/gm/gs.md) — Roland GS variation lookup. - [gm/xg](./src/gm/xg.md) — Yamaha XG variation lookup. - [ump/ump](./src/ump/ump.md) — MIDI 2.0 UMP framing + MIDI1 ↔ UMP bridge. - [telemetry/telemetry](./src/telemetry/telemetry.md) — structural OTel-compatible counter + histogram. ## Tier and dependencies - Tier: **universal** — no `window`, no `navigator`, no `node:*`. - Runtime deps: `@suaveplan/error`, `@suaveplan/types`, `tslib`, `zod`. - Test deps: `@suaveplan/testing`, `fast-check`. - No `@opentelemetry/api` runtime dep — OTel is consumed structurally per `MidiTelemetry`. ## Coverage gate `bunx turbo test:coverage --filter=@suaveplan/midi` enforces 100/100/100/100 (statements / branches / functions / lines).