first commit
This commit is contained in:
72
docs/ui/react/react-midi/src/port/web-midi-port.md
Normal file
72
docs/ui/react/react-midi/src/port/web-midi-port.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# `port/web-midi-port`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## 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
|
||||
|
||||
```ts
|
||||
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
|
||||
|
||||
```ts
|
||||
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.
|
||||
Reference in New Issue
Block a user