Skip to content

Instantly share code, notes, and snippets.

@jussi-kalliokoski
Created February 15, 2016 07:42
Show Gist options
  • Save jussi-kalliokoski/9e6e1fd28c08b973cf2b to your computer and use it in GitHub Desktop.
Save jussi-kalliokoski/9e6e1fd28c08b973cf2b to your computer and use it in GitHub Desktop.
Web MIDI Streams examples
const NOTE = 69;
const NOTE_ON = 0x90;
const NOTE_OFF = 0x80;
const SECONDS = 1000;
function startSequencer (destination) {
let active = false;
setInterval(() => {
const now = performance.now();
const type = active ? NOTE_OFF : NOTE_ON;
destination.write({ data: [type, NOTE, VELOCITY], timestamp: now });
active = !active;
}, 1 * SECONDS);
}
function handleMIDIError (error) {
alert("The MIDI system failed to start. You're gonna have a bad time.");
}
function handleMIDIReady (midiAccess) {
const destination = [...midiAccess.outputs.values()][0];
if ( !destination ) { throw new Error("You don't have any MIDI output devices. You're gonna have a bad time."); }
return destination.openAsStream()
.then(destinationStream => {
startSequencer(destinationStream);
});
}
window.addEventListener("load", () => {
if ( !navigator.requestMIDIAccess ) {
alert("No MIDI support in your browser. You're gonna have a bad time.");
return;
}
navigator.requestMIDIAccess()
.then(handleMIDIReady, handleMIDIError)
.catch(error => alert(error.message));
});
const OCTAVES = 12;
const NOTE_ON = 0x90;
const NOTE_OFF = 0x80;
const CHANNEL_MASK = 0xF0;
function filter (condition) {
return new TransformStream({
transform (chunk, enqueue, done) {
if ( condition(chunk) ) { enqueue(chunk); }
done();
},
});
}
function map (transform) {
return new TransformStream({
transform (chunk, enqueue, done) {
enqueue(transform(chunk));
done();
},
});
}
/**
* Pushes all messages to given channel number.
*/
function pushToChannel (channel) {
return map(chunk => {
chunk.data &= CHANNEL_MASK;
chunk.data += channel;
return chunk;
});
}
function isNoteOnOrNoteOff (chunk) {
const messageType = chunk.data[0] & CHANNEL_MASK;
return messageType === NOTE_ON || messageType === NOTE_OFF;
}
function pitchShift (amount) {
return map(chunk => {
chunk.data[1] += amount;
return chunk;
});
}
function octaver (octaves) {
return pitchShift(octaves * OCTAVES);
}
function prepareConnections (source, destination) {
source
// move all messages to channel 3
.pipeThrough(pushToChannel(3))
// filter out non-note messages
.pipeThrough(filter(isNoteOnOrNoteOff))
// raise the pitch by two octaves
.pipeThrough(octaver(2))
// pipe to destination port
.pipeTo(destination);
}
function handleMIDIError (error) {
alert("The MIDI system failed to start. You're gonna have a bad time.");
}
function handleMIDIReady (midiAccess) {
const source = [...midiAccess.inputs.values()][0];
const destination = [...midiAccess.outputs.values()][0];
if ( !source ) { throw new Error("You don't have any MIDI input devices. You're gonna have a bad time."); }
if ( !destination ) { throw new Error("You don't have any MIDI output devices. You're gonna have a bad time."); }
return Promise.all([
source.openAsStream(),
// Hold a FIFO queue of 1000 items
destination.openAsStream(new CountQueuingStrategy({ highWaterMark: 1000 })),
])
.then(([sourceStream, destinationStream]) => {
prepareConnections(sourceStream, destinationStream);
});
}
window.addEventListener("load", () => {
if ( !navigator.requestMIDIAccess ) {
alert("No MIDI support in your browser. You're gonna have a bad time.");
return;
}
navigator.requestMIDIAccess()
.then(handleMIDIReady, handleMIDIError)
.catch(error => alert(error.data));
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment