# dispatch/running-status ## Purpose `RunningStatusDecoder` is the streaming MIDI 1.0 decoder that honours running status — the wire convention where successive same-status messages omit redundant status bytes. The MIDI 1.0 spec mandates that consumers retain the last channel-voice status byte and apply it implicitly to following data-only frames; serial-MIDI hardware almost universally exploits this to save bandwidth. This module also handles the orthogonal real-time interleaving rule: any byte in the range `0xF8..0xFF` is a one-byte real-time message that can appear at any moment, even mid-frame, without disturbing the running status state. Implementing both rules correctly is the difference between a stable port adapter and one that drops one note in twenty. ## Features - Stateful streaming decoder — feed bytes incrementally via `push`, drain complete messages via `drain`. - Honours running status across multiple `push` calls; partial data accumulates until a complete frame arrives. - Splits real-time messages out without affecting the running status pointer. - Clears running status after every system-common message, matching the MIDI 1.0 spec. - `decodeStream` convenience wrapper for one-shot decoding of a finished buffer. ## Usage: Basic ```ts import { decodeStream } from "@suaveplan/midi"; const messages = decodeStream([0x91, 0x40, 0x64, 0x42, 0x55]); // two noteOn events on channel 1: notes 64+100 and 66+85 ``` ## Usage: Advanced ```ts import { RunningStatusDecoder } from "@suaveplan/midi"; const decoder = new RunningStatusDecoder(); inputPort.onRawBytes((chunk) => { decoder.push(chunk); for (const msg of decoder.drain()) { handleMessage(msg); } }); // When the port disconnects and reconnects, reset the implicit state. inputPort.onDisconnect(() => decoder.reset()); ``` ## API Reference - `class RunningStatusDecoder` - `push(bytes: Uint8Array | readonly number[]): void` — append bytes to the internal queue. - `drain(): MidiMessage[]` — return every complete message available; partial frames remain queued for the next call. - `reset(): void` — clear the buffer and running-status pointer (use on port reconnect). - `decodeStream(bytes: Uint8Array | readonly number[]): MidiMessage[]` — convenience for one-shot decoding when the entire stream is known up-front. ## Implementation Notes The decoder uses a `number[]` buffer rather than a `Uint8Array` because it needs `shift` / `splice` semantics that `Uint8Array` does not provide; the alternative is a ring buffer with explicit head/tail pointers, but for typical port chunk sizes (≤ 64 bytes) the array form is faster and far less code. SysEx accumulates until the `F7` terminator is found; if the terminator never arrives the messages stay queued and `drain` returns the messages decoded so far, with the partial SysEx still pending — callers can `reset` to drop it on a port reset.