# midi

> MIDI input from @audio-ui/engine: every connected keyboard and controller as one source through the Web MIDI API, raw messages read into notes, controllers and pitch bend, and pitch bend in semitones.

```bash
npm install @audio-ui/engine
```

```ts
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:

```ts
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.

**In React**

The registry's [`useMidiInput`](/docs/hooks/use-midi-input) wraps this for a React page.

