pnpm add @audio-ui/engine
import { createMidiInput, DEFAULT_PITCH_BEND_RANGE, parseMidiMessage, pitchBendToSemitones } from "@audio-ui/engine/midi";| Used by | use-midi-input |
|---|
| Export | Kind | What it is |
|---|---|---|
createMidiInput | function | The MIDI inputs of the page through the Web MIDI API, as one source: every listener hears the messages of every connected device, and devices plugged or unplugged later are picked up, and a device switched off with setInputEnabled is not heard. |
MidiInput | interface | The page's MIDI inputs as one source: connect, listen to messages, switch inputs off, and follow its state. |
MidiInputState | interface | What createMidiInput knows of the page's MIDI: whether it is supported and connected, the inputs, the muted ones, and why it is not connected. |
DEFAULT_PITCH_BEND_RANGE | const | The bend range most synths start with, in semitones. |
MidiMessage | type | Live MIDI 1.0 channel messages, as a Web MIDI input delivers them (one message per event, a status byte then its data bytes). |
parseMidiMessage | function | Parses one channel voice message: note on, note off, control change or pitch bend. |
pitchBendToSemitones | function | A pitch bend value in semitones, for a receiver whose bend range is range semitones each way. |
Usage
createMidiInput listens to every MIDI input of the page as one source. Make one and share it, so the browser asks for access once, and connect it from a user gesture, since the browser may prompt:
import { createMidiInput } from "@audio-ui/engine/midi";
const midi = createMidiInput();
button.addEventListener("click", () => midi.connect());
const stop = midi.listen((message) => {
if (message.type === "noteon") {
synth.noteOn(message.note, message.velocity);
} else if (message.type === "noteoff") {
synth.noteOff(message.note);
}
});Devices plugged in later are heard as they come, and setInputEnabled(name, false) silences one by name. The state, with subscribe and getSnapshot for useSyncExternalStore or any store, says whether the browser has Web MIDI (it needs a secure context), whether a device is connected, the names of the inputs, and why it is not connected when it is not. System exclusive access is not asked for.
Messages
parseMidiMessage(data) reads the bytes of one message into a MidiMessage: a note on or off, a control change or a pitch bend, on its channel, or null for anything else. Values are normalized the way the rest of audio/ui reads them: a velocity or a controller from 0 to 1, a pitch bend from -1 to 1. A note on with a velocity of 0 is read as the note off it stands for.
pitchBendToSemitones(value, range) turns a bend into semitones for a receiver that bends range semitones each way, DEFAULT_PITCH_BEND_RANGE (2) unless told otherwise.
The registry's useMidiInput wraps this for a React page.