Signatur
Beschreibung
Das Ereignis midimessage ist der zentrale Weg, um Daten von externen MIDI-Geräten wie Keyboards, Pads, Controllern oder DAW-Oberflächen im Browser zu empfangen. Jede vollständig empfangene MIDI-Nachricht (z. B. Note On, Note Off, Control Change, Pitch Bend oder SysEx) löst ein MIDIMessageEvent aus. Die Rohdaten liegen in der Eigenschaft event.data als Uint8Array vor.
Einen Handler registriert man entweder mit input.addEventListener("midimessage", handler) oder über die Eigenschaft input.onmidimessage = handler. Wichtig: Wird onmidimessage gesetzt, öffnet der Browser den Port implizit (input.open()). Bei addEventListener sollte man den Port je nach Implementierung ausdrücklich mit open() öffnen.
Das erste Byte (data[0]) ist das Statusbyte: Die oberen 4 Bits geben den Nachrichtentyp an (0x90 = Note On, 0x80 = Note Off, 0xB0 = Control Change, 0xE0 = Pitch Bend), die unteren 4 Bits den MIDI-Kanal (0–15). Die folgenden Datenbytes (Wertebereich 0–127) hängen vom Typ ab, bei Noten z. B. Notennummer und Anschlagstärke (Velocity).
Den Zugriff auf die Eingänge erhält man über navigator.requestMIDIAccess(), das ein MIDIAccess-Objekt mit der Map inputs liefert. Der Zeitstempel event.timeStamp ist ein DOMHighResTimeStamp relativ zu performance.now() und eignet sich für präzises Timing.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | Der Ereignisname "midimessage". |
|
| $handler Pflicht | (event: MIDIMessageEvent) => void | Callback, der ein MIDIMessageEvent erhält. Wichtigste Eigenschaft: event.data (Uint8Array mit Status- und Datenbytes); außerdem event.timeStamp und event.target (der MIDIInput). |
|
| $options | boolean | AddEventListenerOptions | false | Optionale Listener-Optionen wie once oder signal (zum Entfernen per AbortController). |
Rückgabewert
addEventListener gibt nichts zurück. Der Handler erhält ein MIDIMessageEvent.Beispiele
Alle Eingänge abhören und Noten auswerten (addEventListener)
const onMessage = (event) => {
const [status, data1, data2] = event.data;
const command = status & 0xf0;
const channel = (status & 0x0f) + 1;
// Note On mit Velocity 0 gilt als Note Off
if (command === 0x90 && data2 > 0) {
console.log(`Note On | Kanal ${channel} | Note ${data1} | Velocity ${data2}`);
} else if (command === 0x80 || (command === 0x90 && data2 === 0)) {
console.log(`Note Off | Kanal ${channel} | Note ${data1}`);
}
};
const start = async () => {
if (!navigator.requestMIDIAccess) {
console.warn('Web MIDI wird nicht unterstützt.');
return;
}
const access = await navigator.requestMIDIAccess();
for (const input of access.inputs.values()) {
await input.open();
input.addEventListener('midimessage', onMessage);
console.log(`Lausche auf: ${input.name}`);
}
};
start().catch((err) => console.error('MIDI-Zugriff verweigert:', err));
Drehregler (Control Change) über onmidimessage an ein Audio-Gain koppeln
const audioCtx = new AudioContext();
const gain = audioCtx.createGain();
gain.connect(audioCtx.destination);
const access = await navigator.requestMIDIAccess();
const [input] = access.inputs.values();
// onmidimessage öffnet den Port automatisch
input.onmidimessage = ({ data }) => {
const [status, controller, value] = data;
if ((status & 0xf0) === 0xb0 && controller === 7) { // CC 7 = Lautstärke
gain.gain.setTargetAtTime(value / 127, audioCtx.currentTime, 0.01);
console.log(`Lautstärke: ${Math.round((value / 127) * 100)} %`);
}
};
Listener per AbortController wieder entfernen
const controller = new AbortController();
const access = await navigator.requestMIDIAccess();
for (const input of access.inputs.values()) {
await input.open();
input.addEventListener('midimessage', (e) => {
console.log('Bytes:', Array.from(e.data, (b) => b.toString(16).padStart(2, '0')).join(' '));
}, { signal: controller.signal });
}
// Nach 10 Sekunden alle Listener entfernen
setTimeout(() => {
controller.abort();
console.log('MIDI-Logging beendet');
// Alternativ bei on-Property: input.onmidimessage = null;
}, 10_000);
// Wichtig · Fallstricke
- Browser-Kompatibilität: Unterstützt in Chromium-Browsern und Firefox (ab 108, dort nur mit installiertem Site-Permission-Add-on bzw. Nutzerfreigabe). Safari unterstützt Web MIDI nicht. Immer auf
navigator.requestMIDIAccessprüfen. - Sicherer Kontext & Berechtigung: Die API ist nur über HTTPS (oder
localhost) verfügbar und kann per Permissions-Policymidiin iframes gesperrt sein. Der Nutzer muss den Zugriff bestätigen. - SysEx: System-Exclusive-Nachrichten (
0xF0…0xF7) kommen nur an, wennrequestMIDIAccess({ sysex: true })angefragt und erlaubt wurde. SysEx kann Geräte-Firmware verändern – daher eine separate, strengere Berechtigung. - Note On mit Velocity 0 ist laut MIDI-Standard gleichbedeutend mit Note Off (Running Status). Viele Geräte senden Note-Offs genau so.
- Port öffnen: Nur
onmidimessageöffnet den Port implizit; beiaddEventListenerggf.input.open()aufrufen. - Performance: Clock-Nachrichten (
0xF8, 24 pro Viertelnote) und Active Sensing (0xFE) kommen sehr häufig. Früh herausfiltern und im Handler keine teuren DOM-Operationen ausführen, sondern z. B. perrequestAnimationFramebündeln. - Neu angeschlossene Geräte erkennt man über das
statechange-Ereignis vonMIDIAccess; dort müssen Listener nachträglich registriert werden.