Last active
July 29, 2017 00:48
-
-
Save softpunch/9a4c760069f44f6635eee42e5dc56fa9 to your computer and use it in GitHub Desktop.
WebMIDI vars & functions (WIP)
This file contains 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
// | |
// useful WebMIDI variables and functions | |
// | |
// | |
// Chrome only; no iOS; does *not* incorporate shims | |
// | |
// Initialize WebMidi; create dropdown selector for output port | |
// | |
// HTML component: | |
// <select id="outputSelector" onchange="updatePort()"> | |
// <option value="null" selected>[none]</option> | |
// </select> | |
// | |
// access output selector | |
var outputSelector = document.querySelector('#outputSelector'); | |
// empty var to hold "midiAccess" | |
var midi; | |
// empty var to hold selected MIDI output port ID | |
var mOutPort; | |
// initialize WebMIDI | |
function onMIDISuccess( midiAccess ) { | |
midi = midiAccess; | |
midi.outputs.forEach( | |
function(port, key) { | |
var portName = port.name; | |
var portID = port.id; | |
var option = document.createElement("option"); | |
option.setAttribute("value", portID); | |
var textNode = document.createTextNode(portName); | |
option.appendChild(textNode); | |
outputSelector.appendChild(option); | |
}); | |
} | |
navigator.requestMIDIAccess().then(onMIDISuccess); | |
// define output port upon selection | |
function updatePort() { | |
var i = outputSelector.selectedIndex; | |
mOutPort = outputSelector.options[i].value; | |
} | |
// | |
// create Web Audio context for access to more convenient timing functions | |
// | |
window.AudioContext = window.AudioContext || window.webkitAudioContext; | |
var audioContext = new AudioContext(); | |
// set latency hint; conditional feature detection to prevent errors | |
if ("latencyHint" in audioContext) { | |
audioContext.latencyHint = "playback"; | |
}; | |
// define current time | |
var now = audioContext.currentTime; | |
// additional basic web audio variables | |
// var baseFreq = 440; | |
// var audioOut = audioContext.destination; | |
// | |
// useful midi variables | |
// | |
// set output channel | |
var midiChannel = 1; // 1-16 | |
// define commands | |
// var noteOn = 0x90 + (midiChannel - 1); | |
// define tempo | |
var midiTempo = 120; | |
// define (note) pitches | |
// see note name to MIDI number conversion here: | |
// https://github.com/sole/MIDIUtils/blob/master/src/MIDIUtils.js | |
// | |
// additional note/frequency conversion algorithms: | |
// https://github.com/sole/MIDIUtils | |
// define velocities | |
var maxVel = 127; | |
var midVel = 64; | |
var muteVel = 0; | |
// define durations | |
// n4 = quarter note; t4 = quarter-note triplet; m4 = four measures | |
// timing in ms (60000 = 60s) | |
var n4 = (60000 / midiTempo); | |
var n1 = n4 * 4; | |
var n2 = n4 * 2; | |
var n8 = n4 / 2; | |
var n16 = n4 / 4; | |
var n32 = n4 / 8; | |
var t4 = (40000 / midiTempo); | |
var t2 = t4 * 2; | |
var t8 = t4 / 2; | |
var t16 = t4 / 4; | |
var t32 = t4 / 8; | |
var m1 = n4 * 4; | |
var m2 = m1 * 2; | |
var m4 = m1 * 4; | |
var m8 = m1 * 8; | |
// add dotted durations? | |
// | |
// define midi messages; [command, data1, data2] | |
// var midiNoteBegin = [noteOn, midiNote, midiVel]; | |
// var midiNoteEnd = [noteOn, midiNote, midiVel]; | |
// midi -- single note function | |
function sendMidiNote( note, vel, dur ) { | |
var noteOn = 0x90 + (midiChannel - 1); | |
var noteOff = 0x80 + (midiChannel - 1); | |
var midiNoteBegin = [noteOn, note, vel]; | |
var midiNoteEnd = [noteOff, note, muteVel]; | |
var midiNoteEndAlt = [noteOn, note, muteVel]; | |
var output = midi.outputs.get(mOutPort); | |
output.send(midiNoteBegin); | |
output.send(midiNoteEnd, now + dur); | |
// | |
// optionally include a safety margin/buffer; | |
// var safety = 150; // 150ms | |
// | |
// output.send(midiNoteBegin, now + safety); | |
// output.send(midiNoteEnd, now + safety + dur); | |
// | |
} | |
// use above function with MIDI note number, velocity (0-127), and duration; | |
// example -- A4 (440Hz), quarter note, at a velocity of 127, | |
// sendMidiNote(69, maxVel, n4); | |
// | |
// TODO fundamentals: | |
// add more MIDI commands; | |
// add clock input/output; | |
// add queue/scheduler (via Tale of Two Clocks); | |
// add chord player function; | |
// add scale creator w/ MIDI pitches/notes; | |
// add roman numeral chord progression vars; | |
// add (global) chord progression player; | |
// add melody generator/arpeggiator; | |
// add chord comp generator; | |
// add bassline generator; | |
// add polyrhythm creator; | |
// add composition-structure manager; | |
// add chaining methods; | |
// add percussion player; | |
// add percussion generator/controller; | |
// add LFO(s); | |
// add more durations; | |
// add MIDI tuning adjuster; | |
// add MIDI-to-freq func; | |
// add method to create html dropdown component in js; | |
// | |
// TODO abstractions: | |
// reich pulse (w/ volume swells) | |
// drone gen | |
// "black midi" | |
// interlocking gens | |
// CC controllers/gens | |
// game of life | |
// stats-based gens | |
// buffer manipulation via midi | |
// midi jitter-er (input/output) | |
// hyper-fast, super-quiet, pseudo-pads | |
// responsive methods -- ie chord gens that dynamically react to external impetus | |
// | |
// random value generator | |
// The returned value is no lower than (and may possibly equal) min, | |
// and is less than (but not equal to) max. | |
function randRange(min, max) { | |
return Math.random() * (max - min) + min; | |
} | |
// optional functions to convert frequencies to MIDI notes | |
// log function for MIDI converstion | |
function getBaseLog(value, base) { | |
return Math.log(value) / Math.log(base); | |
} | |
// Frequency to Midi Note Number | |
function freqNote(f) { | |
return Math.round(12.0 * getBaseLog(f / baseFreq, 2) + 69); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment