/*
--------------------------------------------
MIDI.Plugin : 0.3.2 : 2013/01/24
--------------------------------------------
https://github.com/mudcube/MIDI.js
--------------------------------------------
Inspired by javax.sound.midi (albeit a super simple version):
http://docs.oracle.com/javase/6/docs/api/javax/sound/midi/package-summary.html
--------------------------------------------
Technologies:
MIDI.WebMIDIAPI
MIDI.WebAudioAPI
MIDI.Flash
MIDI.HTML5
--------------------------------------------
Helpers:
MIDI.GeneralMIDI
MIDI.channels
MIDI.keyToNote
MIDI.noteToKey
*/
if (typeof (MIDI) === "undefined") var MIDI = {};
(function() { "use strict";
var setPlugin = function(root) {
MIDI.technology = root.technology;
MIDI.setVolume = root.setVolume;
MIDI.programChange = root.programChange;
MIDI.noteOn = root.noteOn;
MIDI.noteOff = root.noteOff;
MIDI.chordOn = root.chordOn;
MIDI.chordOff = root.chordOff;
MIDI.stopAllNotes = root.stopAllNotes;
MIDI.getInput = root.getInput;
MIDI.getOutputs = root.getOutputs;
};
/*
--------------------------------------------
Web MIDI API - Native Soundbank
--------------------------------------------
https://dvcs.w3.org/hg/audio/raw-file/tip/midi/specification.html
--------------------------------------------
*/
(function () {
var plugin = null;
var output = null;
var channels = [];
var root = MIDI.WebMIDI = {
technology: "Web MIDI API"
};
root.setVolume = function (channel, volume) { // set channel volume
output.send([0xB0 + channel, 0x07, volume]);
};
root.programChange = function (channel, program) { // change channel instrument
output.send([0xC0 + channel, program]);
};
root.noteOn = function (channel, note, velocity, delay) {
output.send([0x90 + channel, note, velocity], delay * 1000);
};
root.noteOff = function (channel, note, delay) {
output.send([0x80 + channel, note, 0], delay * 1000);
};
root.chordOn = function (channel, chord, velocity, delay) {
for (var n = 0; n < chord.length; n ++) {
var note = chord[n];
output.send([0x90 + channel, note, velocity], delay * 1000);
}
};
root.chordOff = function (channel, chord, delay) {
for (var n = 0; n < chord.length; n ++) {
var note = chord[n];
output.send([0x80 + channel, note, 0], delay * 1000);
}
};
root.stopAllNotes = function () {
for (var channel = 0; channel < 16; channel ++) {
output.send([0xB0 + channel, 0x7B, 0]);
}
};
root.getInput = function () {
return plugin.getInputs();
};
root.getOutputs = function () {
return plugin.getOutputs();
};
root.connect = function (callback) {
setPlugin(root);
navigator.requestMIDIAccess(function (access) {
plugin = access;
output = plugin.getOutput(0);
if (callback) callback();
}, function (err) {
console.log("uh-oh! Something went wrong! Error code: " + err.code );
});
};
})();
/*
--------------------------------------------
Web Audio API - OGG or MPEG Soundbank
--------------------------------------------
https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
--------------------------------------------
*/
if (window.AudioContext || window.webkitAudioContext) (function () {
var AudioContext = window.AudioContext || window.webkitAudioContext;
var root = MIDI.WebAudioAPI = {
technology: "Web Audio API"
};
var ctx;
var sources = {};
var masterVolume = 127;
var audioBuffers = {};
var audioLoader = function (instrument, urlList, index, bufferList, callback) {
var synth = MIDI.GeneralMIDI.byName[instrument];
var instrumentId = synth.number;
var url = urlList[index];
var base64 = MIDI.Soundfont[instrument][url].split(",")[1];
var buffer = Base64Binary.decodeArrayBuffer(base64);
ctx.decodeAudioData(buffer, function (buffer) {
var msg = url;
while (msg.length < 3) msg += " ";
if (typeof (MIDI.loader) !== "undefined") {
MIDI.loader.update(null, synth.instrument + "
Processing: " + (index / 87 * 100 >> 0) + "%
" + msg);
}
buffer.id = url;
bufferList[index] = buffer;
//
if (bufferList.length === urlList.length) {
while (bufferList.length) {
buffer = bufferList.pop();
if (!buffer) continue;
var nodeId = MIDI.keyToNote[buffer.id];
audioBuffers[instrumentId + "" + nodeId] = buffer;
}
callback(instrument);
}
});
};
root.setVolume = function (channel, volume) {
masterVolume = volume;
};
root.programChange = function (channel, program) {
MIDI.channels[channel].instrument = program;
};
root.noteOn = function (channel, note, velocity, delay) {
/// check whether the note exists
if (!MIDI.channels[channel]) return;
var instrument = MIDI.channels[channel].instrument;
if (!audioBuffers[instrument + "" + note]) return;
/// convert relative delay to absolute delay
if (delay < ctx.currentTime) delay += ctx.currentTime;
/// crate audio buffer
var source = ctx.createBufferSource();
sources[channel + "" + note] = source;
source.buffer = audioBuffers[instrument + "" + note];
source.connect(ctx.destination);
///
var gainNode = ctx.createGainNode();
var value = (velocity / 127) * (masterVolume / 127) * 2 - 1;
gainNode.connect(ctx.destination);
gainNode.gain.value = Math.max(-1, value);
source.connect(gainNode);
source.noteOn(delay || 0);
return source;
};
root.noteOff = function (channel, note, delay) {
delay = delay || 0;
if (delay < ctx.currentTime) delay += ctx.currentTime;
var source = sources[channel + "" + note];
if (!source) return;
// @Miranet: "the values of 0.2 and 0.3 could ofcourse be used as
// a 'release' parameter for ADSR like time settings."
// add { "metadata": { release: 0.3 } } to soundfont files
source.gain.linearRampToValueAtTime(1, delay);
source.gain.linearRampToValueAtTime(0, delay + 0.2);
source.noteOff(delay + 0.3);
return source;
};
root.chordOn = function (channel, chord, velocity, delay) {
var ret = {}, note;
for (var n = 0, length = chord.length; n < length; n++) {
ret[note = chord[n]] = root.noteOn(channel, note, velocity, delay);
}
return ret;
};
root.chordOff = function (channel, chord, delay) {
var ret = {}, note;
for (var n = 0, length = chord.length; n < length; n++) {
ret[note = chord[n]] = root.noteOff(channel, note, delay);
}
return ret;
};
root.connect = function (callback) {
setPlugin(root);
//
MIDI.Player.ctx = ctx = new AudioContext();
///
var urlList = [];
var keyToNote = MIDI.keyToNote;
for (var key in keyToNote) urlList.push(key);
var bufferList = [];
var pending = {};
var oncomplete = function(instrument) {
delete pending[instrument];
for (var key in pending) break;
if (!key) callback();
};
for (var instrument in MIDI.Soundfont) {
pending[instrument] = true;
for (var i = 0; i < urlList.length; i++) {
audioLoader(instrument, urlList, i, bufferList, oncomplete);
}
}
};
})();
/*
--------------------------------------------
AudioTag