Forked from jussi-kalliokoski/1-midi-stream-simple-example.js
Created
June 12, 2025 08:56
-
-
Save sc0ttj/88817d406d706f0da92b1f04d99475d7 to your computer and use it in GitHub Desktop.
Web MIDI Streams examples
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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