40 lines
1.6 KiB
TypeScript
40 lines
1.6 KiB
TypeScript
import { useMemo } from "react";
|
|
import type { MidiMessage } from "../../midi-contract/midi-message.types.js";
|
|
import { adaptOutput } from "../../port/web-midi-port.js";
|
|
import { useMidiContextOrThrow } from "../use-midi-context.js";
|
|
import type { PortSelector } from "../use-midi-input/use-midi-input.js";
|
|
|
|
export interface MidiOutputHandle {
|
|
readonly id: string;
|
|
readonly name: string;
|
|
readonly manufacturer: string | undefined;
|
|
readonly state: "connected" | "disconnected";
|
|
send(bytes: Uint8Array): void;
|
|
sendMessage(msg: MidiMessage): void;
|
|
}
|
|
|
|
/**
|
|
* Returns a typed output handle for the selected Web MIDI output, or
|
|
* `null` while access is loading / the selector misses. The handle's
|
|
* `sendMessage` encodes a `MidiMessage` via `encodeMessage` and
|
|
* forwards the resulting bytes to the underlying `MIDIOutput.send`;
|
|
* `send(bytes)` is the raw escape-hatch (typically used for SysEx
|
|
* pass-through).
|
|
*/
|
|
export function useMidiOutput(selector: PortSelector): MidiOutputHandle | null {
|
|
const ctx = useMidiContextOrThrow();
|
|
const stateVersion = ctx.stateVersion;
|
|
return useMemo<MidiOutputHandle | null>(() => {
|
|
void stateVersion;
|
|
if (ctx.access === null) {
|
|
return null;
|
|
}
|
|
if (selector === "first") {
|
|
const next = ctx.access.outputs.values().next();
|
|
return next.done === true ? null : adaptOutput(next.value);
|
|
}
|
|
const target = ctx.access.outputs.get(selector);
|
|
return target === undefined ? null : adaptOutput(target);
|
|
}, [ctx.access, stateVersion, selector]);
|
|
}
|