Files
skills/docs/ui/react/react-midi/src/port/web-midi-port.md
2026-05-29 08:20:56 +01:00

2.4 KiB

port/web-midi-port

(no submodules)

Purpose

Bridges the native Web MIDI WebMIDIInput / WebMIDIOutput shapes into the structural MidiPortLike consumed by the rest of the package. The adapter handles two responsibilities the underlying DOM types do not: it parses MIDIMessageEvent.data into a typed MidiMessage (so consumers never touch raw bytes), and it owns the fan-out from the single onmidimessage slot to multiple subscribers.

Two factories ship: adaptInput for inputs (yielding parsed messages via onMessage) and adaptOutput for outputs (encoding MidiMessage to bytes via encodeMessage before forwarding to port.send). Both record traffic via the telemetry module so dashboards see one counter per direction.

Features

  • Multiple subscribers on a single input — the adapter fans the single onmidimessage slot out to a Set of handlers.
  • Lazy bind/unbind — the onmidimessage slot is occupied only while at least one subscriber is active; the last unsubscribe restores null.
  • Input-side decode swallows malformed frames silently (the runtime already filters; defensive null check keeps subscribers safe).
  • Output-side sendMessage encodes a typed MidiMessage; raw send(bytes) remains available for SysEx pass-through.

Usage: Basic

import { adaptInput, adaptOutput } from "@suaveplan/react-midi/port/web-midi-port";

const port = adaptInput(midiAccess.inputs.get("midi-in-0")!);
const unsubscribe = port.onMessage((msg) => {
    if (msg.type === "noteOn") console.log("note", msg.note);
});

Usage: Advanced

const out = adaptOutput(midiAccess.outputs.get("midi-out-0")!);
// Typed send (encoded):
out.sendMessage({
    type: "noteOn",
    channel: 0,
    note: 60,
    velocity: 100,
});
// Raw send (for SysEx pass-through):
out.send(new Uint8Array([0xf0, 0x7e, 0x00, 0xf7]));

API Reference

  • adaptInput(input: WebMIDIInput): MidiPortLike
  • adaptOutput(output: WebMIDIOutput): MidiPortLike & { sendMessage(msg: MidiMessage): void }

Implementation Notes

The input adapter uses Set (not array) for subscribers so unsubscribing during fan-out doesn't reorder iteration over the remaining subscribers. The output adapter's onMessage returns a no-op unsubscriber rather than throwing, which lets generic code that walks both directions on a port handle either shape without a runtime type check.