Create a gist now

Instantly share code, notes, and snippets.

A simple proposal for the interfaces needed for useful MIDI access
// Example usage, a MIDI proxy that picks the first available MIDI input and routes its messages to the first MIDI outputs
navigator.getUserMedia({midi: true}, function (MIDIAccess) {
try {
var input = MIDIAccess.getInput(MIDIAccess.enumerateInputs()[0]);
var output = MIDIAccess.getOutput(MIDIAccess.enumerateInputs()[0]);
} catch (e) {
console.error("Couldn't find suitable MIDI devices");
}
console.log('Input:', input.deviceManufacturer, input.deviceName);
console.log('Output:', output.deviceManufacturer, output.deviceName);
input.addEventListener('midimessage', function (e) {
e.MIDIEvents.forEach(function (e) {
console.log('Received a MIDI event (status:', e.status, 'data:', e.data, ')');
output.sendMIDIMessage(e);
});
});
});
// This example shows how to interpret key presses and releases from the MIDI data, extracted from the first available input device.
var noteNames = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
navigator.getUserMedia({midi: true}, function (MIDIAccess) {
// Try to get access to the first available MIDI input
try {
var input = MIDIAccess.getInput(MIDIAccess.enumerateInputs()[0]);
} catch (e) {
console.error("Couldn't find a suitable input");
return;
}
input.addEventListener('midimessage', function (e) {
// Discard all but NoteOff and NoteOn
if (e.status !== 0x8C && e.status !== 0x9C) return;
// Calculate the note name.
var note = noteNames[e.data[0] % 12] + (Math.floor(e.data[0] / 12) - 1);
// Log the input
console.log(e.status === 0x8C ? 'NoteOn' : 'NoteOff', ':', note, 'velocity:', e.data[1]);
});
});
// This example shows how to create a simple JS sequencer that can be enumerated by DAW software and then chosen by the user to play a virtual instrument or whatever.
var sequence = [36, 48, 55, 48]; // C3 C4 G4 C4
var seqPos = 0;
navigator.getUserMedia({midi: true}, function (MIDIAccess) {
// Attempt to create a virtual output device.
try {
var outputID = MIDIAccess.createVirtualDevice("JS Sequencer", "World Wide Web");
var output = MIDIAccess.getOutput(outputID);
} catch (e) {
console.error("The system doesn't support creating virtual MIDI devices.");
}
console.log('Output:', output.deviceManufacturer, output.deviceName);
setInterval(function () {
// Send NoteOff for the previous note
output.sendMIDIMessage(
MIDIAccess.createMIDIMessage(0x9C, sequence[seqPos], 127)
);
// Progress the sequencer
seqPos = (seqPos + 1) % sequence.length;
// Send NoteOn for the current note
output.sendMIDIMessage(
MIDIAccess.createMIDIMessage(0x8C, sequence[seqPos], 127)
);
}, 250);
});
interface MIDIMessage {
readonly attribute unsigned long timeStamp;
readonly attribute short status;
readonly attribute Uint8Array data;
readonly attribute short channel;
}
interface MIDIDataEvent : Event {
readonly attribute sequence<MIDIMessage> MIDIMessages;
}
partial interface ProcessMediaEvent implements MIDIDataEvent {
// FIXME: This should be a property of the MIDIMessage's in the MIDIMessageSequence of MIDIDataEvent.
readonly attribute float sampleTime;
}
interface MIDIDevice implements EventTarget {
readonly attribute string deviceName;
readonly attribute string deviceManufacturer;
readonly attribute string deviceVersion;
// A unique ID for a certain device, up to the browser vendor to detect how it's generated, because it doesn't really matter, as long as they're unique.
readonly attribute string deviceFingerprint;
attribute EventListener ondevicedisconnect;
}
interface MIDIInputDevice {
readonly attribute string deviceType = "INPUT";
attribute EventListener onmidimessage;
}
interface MIDIOutputDevice {
readonly attribute string deviceType = "OUTPUT";
// Return success
boolean sendMIDIMessage(MIDIMessage);
}
interface MIDIAccess {
sequence<MIDIDevice> enumerateInputs();
sequence<MIDIDevice> enumerateOutputs();
// Return the deviceFingerprint of the created device, throw an exception if feature not implemented.
string createVirtualDevice(string deviceName, string deviceManufacturer);
MIDIInputDevice getInput(MIDIDevice target);
MIDIOutputDevice getOutput(MIDIDevice target);
MIDIInputDevice getInput(String targetFingerprint);
MIDIOutputDevice getOutput(String targetFingerprint);
MIDIMessage createMIDIMessage(short status, short data1 = 0, short data2 = 0, short channel = 0, timeStamp = CURRENT_TIME);
MIDIMessage createMIDIMessage(short status, Uint8Array data, timeStamp = CURRENT_TIME);
}
partial interface ProcessedMediaStream {
void addInput(MIDIInputDevice device);
void addOutput(MIDIOutputDevice device);
}
partial dictionary MediaStreamOptions {
boolean midi;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment