1385 lines
42 KiB
JavaScript
1385 lines
42 KiB
JavaScript
/*
|
|
-------------------------------------
|
|
MIDI.audioDetect : 0.3
|
|
-------------------------------------
|
|
https://github.com/mudcube/MIDI.js
|
|
-------------------------------------
|
|
Probably, Maybe, No... Absolutely!
|
|
-------------------------------------
|
|
Test to see what types of <audio> MIME types are playable by the browser.
|
|
-------------------------------------
|
|
*/
|
|
|
|
if (typeof(MIDI) === "undefined") var MIDI = {};
|
|
|
|
(function() { "use strict";
|
|
|
|
var supports = {};
|
|
var canPlayThrough = function (src) {
|
|
var audio = new Audio();
|
|
var mime = src.split(";")[0];
|
|
audio.id = "audio";
|
|
audio.setAttribute("preload", "auto");
|
|
audio.setAttribute("audiobuffer", true);
|
|
audio.addEventListener("canplaythrough", function() {
|
|
supports[mime] = true;
|
|
}, false);
|
|
audio.src = "data:" + src;
|
|
document.body.appendChild(audio);
|
|
};
|
|
|
|
MIDI.audioDetect = function(callback) {
|
|
// check whether <audio> tag is supported
|
|
if (typeof(Audio) === "undefined") return callback({});
|
|
// check whether canPlayType is supported
|
|
var audio = new Audio();
|
|
if (typeof(audio.canPlayType) === "undefined") return callback(supports);
|
|
// see what we can learn from the browser
|
|
var vorbis = audio.canPlayType('audio/ogg; codecs="vorbis"');
|
|
vorbis = (vorbis === "probably" || vorbis === "maybe");
|
|
var mpeg = audio.canPlayType('audio/mpeg');
|
|
mpeg = (mpeg === "probably" || mpeg === "maybe");
|
|
// maybe nothing is supported
|
|
if (!vorbis && !mpeg) {
|
|
callback(supports);
|
|
return;
|
|
}
|
|
// or maybe something is supported
|
|
if (vorbis) canPlayThrough("audio/ogg;base64,T2dnUwACAAAAAAAAAADqnjMlAAAAAOyyzPIBHgF2b3JiaXMAAAAAAUAfAABAHwAAQB8AAEAfAACZAU9nZ1MAAAAAAAAAAAAA6p4zJQEAAAANJGeqCj3//////////5ADdm9yYmlzLQAAAFhpcGguT3JnIGxpYlZvcmJpcyBJIDIwMTAxMTAxIChTY2hhdWZlbnVnZ2V0KQAAAAABBXZvcmJpcw9CQ1YBAAABAAxSFCElGVNKYwiVUlIpBR1jUFtHHWPUOUYhZBBTiEkZpXtPKpVYSsgRUlgpRR1TTFNJlVKWKUUdYxRTSCFT1jFloXMUS4ZJCSVsTa50FkvomWOWMUYdY85aSp1j1jFFHWNSUkmhcxg6ZiVkFDpGxehifDA6laJCKL7H3lLpLYWKW4q91xpT6y2EGEtpwQhhc+211dxKasUYY4wxxsXiUyiC0JBVAAABAABABAFCQ1YBAAoAAMJQDEVRgNCQVQBABgCAABRFcRTHcRxHkiTLAkJDVgEAQAAAAgAAKI7hKJIjSZJkWZZlWZameZaouaov+64u667t6roOhIasBACAAAAYRqF1TCqDEEPKQ4QUY9AzoxBDDEzGHGNONKQMMogzxZAyiFssLqgQBKEhKwKAKAAAwBjEGGIMOeekZFIi55iUTkoDnaPUUcoolRRLjBmlEluJMYLOUeooZZRCjKXFjFKJscRUAABAgAMAQICFUGjIigAgCgCAMAYphZRCjCnmFHOIMeUcgwwxxiBkzinoGJNOSuWck85JiRhjzjEHlXNOSuekctBJyaQTAAAQ4AAAEGAhFBqyIgCIEwAwSJKmWZomipamiaJniqrqiaKqWp5nmp5pqqpnmqpqqqrrmqrqypbnmaZnmqrqmaaqiqbquqaquq6nqrZsuqoum65q267s+rZru77uqapsm6or66bqyrrqyrbuurbtS56nqqKquq5nqq6ruq5uq65r25pqyq6purJtuq4tu7Js664s67pmqq5suqotm64s667s2rYqy7ovuq5uq7Ks+6os+75s67ru2rrwi65r66os674qy74x27bwy7ouHJMnqqqnqq7rmarrqq5r26rr2rqmmq5suq4tm6or26os67Yry7aumaosm64r26bryrIqy77vyrJui67r66Ys67oqy8Lu6roxzLat+6Lr6roqy7qvyrKuu7ru+7JuC7umqrpuyrKvm7Ks+7auC8us27oxuq7vq7It/KosC7+u+8Iy6z5jdF1fV21ZGFbZ9n3d95Vj1nVhWW1b+V1bZ7y+bgy7bvzKrQvLstq2scy6rSyvrxvDLux8W/iVmqratum6um7Ksq/Lui60dd1XRtf1fdW2fV+VZd+3hV9pG8OwjK6r+6os68Jry8ov67qw7MIvLKttK7+r68ow27qw3L6wLL/uC8uq277v6rrStXVluX2fsSu38QsAABhwAAAIMKEMFBqyIgCIEwBAEHIOKQahYgpCCKGkEEIqFWNSMuakZM5JKaWUFEpJrWJMSuaclMwxKaGUlkopqYRSWiqlxBRKaS2l1mJKqcVQSmulpNZKSa2llGJMrcUYMSYlc05K5pyUklJrJZXWMucoZQ5K6iCklEoqraTUYuacpA46Kx2E1EoqMZWUYgupxFZKaq2kFGMrMdXUWo4hpRhLSrGVlFptMdXWWqs1YkxK5pyUzDkqJaXWSiqtZc5J6iC01DkoqaTUYiopxco5SR2ElDLIqJSUWiupxBJSia20FGMpqcXUYq4pxRZDSS2WlFosqcTWYoy1tVRTJ6XFklKMJZUYW6y5ttZqDKXEVkqLsaSUW2sx1xZjjqGkFksrsZWUWmy15dhayzW1VGNKrdYWY40x5ZRrrT2n1mJNMdXaWqy51ZZbzLXnTkprpZQWS0oxttZijTHmHEppraQUWykpxtZara3FXEMpsZXSWiypxNhirLXFVmNqrcYWW62ltVprrb3GVlsurdXcYqw9tZRrrLXmWFNtBQAADDgAAASYUAYKDVkJAEQBAADGMMYYhEYpx5yT0ijlnHNSKucghJBS5hyEEFLKnINQSkuZcxBKSSmUklJqrYVSUmqttQIAAAocAAACbNCUWByg0JCVAEAqAIDBcTRNFFXVdX1fsSxRVFXXlW3jVyxNFFVVdm1b+DVRVFXXtW3bFn5NFFVVdmXZtoWiqrqybduybgvDqKqua9uybeuorqvbuq3bui9UXVmWbVu3dR3XtnXd9nVd+Bmzbeu2buu+8CMMR9/4IeTj+3RCCAAAT3AAACqwYXWEk6KxwEJDVgIAGQAAgDFKGYUYM0gxphhjTDHGmAAAgAEHAIAAE8pAoSErAoAoAADAOeecc84555xzzjnnnHPOOeecc44xxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY0wAwE6EA8BOhIVQaMhKACAcAABACCEpKaWUUkoRU85BSSmllFKqFIOMSkoppZRSpBR1lFJKKaWUIqWgpJJSSimllElJKaWUUkoppYw6SimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaVUSimllFJKKaWUUkoppRQAYPLgAACVYOMMK0lnhaPBhYasBAByAwAAhRiDEEJpraRUUkolVc5BKCWUlEpKKZWUUqqYgxBKKqmlklJKKbXSQSihlFBKKSWUUkooJYQQSgmhlFRCK6mEUkoHoYQSQimhhFRKKSWUzkEoIYUOQkmllNRCSB10VFIpIZVSSiklpZQ6CKGUklJLLZVSWkqpdBJSKamV1FJqqbWSUgmhpFZKSSWl0lpJJbUSSkklpZRSSymFVFJJJYSSUioltZZaSqm11lJIqZWUUkqppdRSSiWlkEpKqZSSUmollZRSaiGVlEpJKaTUSimlpFRCSamlUlpKLbWUSkmptFRSSaWUlEpJKaVSSksppRJKSqmllFpJKYWSUkoplZJSSyW1VEoKJaWUUkmptJRSSymVklIBAEAHDgAAAUZUWoidZlx5BI4oZJiAAgAAQABAgAkgMEBQMApBgDACAQAAAADAAAAfAABHARAR0ZzBAUKCwgJDg8MDAAAAAAAAAAAAAACAT2dnUwAEAAAAAAAAAADqnjMlAgAAADzQPmcBAQA=");
|
|
if (mpeg) canPlayThrough("audio/mpeg;base64,/+MYxAAAAANIAUAAAASEEB/jwOFM/0MM/90b/+RhST//w4NFwOjf///PZu////9lns5GFDv//l9GlUIEEIAAAgIg8Ir/JGq3/+MYxDsLIj5QMYcoAP0dv9HIjUcH//yYSg+CIbkGP//8w0bLVjUP///3Z0x5QCAv/yLjwtGKTEFNRTMuOTeqqqqqqqqqqqqq/+MYxEkNmdJkUYc4AKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq");
|
|
// lets find out!
|
|
var time = (new Date()).getTime();
|
|
var interval = window.setInterval(function() {
|
|
for (var key in supports) {}
|
|
var now = (new Date()).getTime();
|
|
var maxExecution = now - time > 5000;
|
|
if (key || maxExecution) {
|
|
window.clearInterval(interval);
|
|
callback(supports);
|
|
}
|
|
}, 1);
|
|
};
|
|
|
|
})();
|
|
/*
|
|
-----------------------------------------------------------
|
|
MIDI.loadPlugin : 0.1.2 : 01/18/2012
|
|
-----------------------------------------------------------
|
|
https://github.com/mudcube/MIDI.js
|
|
-----------------------------------------------------------
|
|
MIDI.loadPlugin({
|
|
instrument: "acoustic_grand_piano", // or 1 (default)
|
|
instruments: [ "acoustic_grand_piano", "acoustic_guitar_nylon" ], // or multiple instruments
|
|
callback: function() { }
|
|
});
|
|
*/
|
|
|
|
if (typeof (MIDI) === "undefined") var MIDI = {};
|
|
if (typeof (MIDI.Soundfont) === "undefined") MIDI.Soundfont = {};
|
|
|
|
(function() { "use strict";
|
|
|
|
// Turn on to get "onprogress" event. XHR will not work from file://
|
|
var USE_XHR = false;
|
|
|
|
MIDI.loadPlugin = function(conf) {
|
|
if (typeof(conf) === "function") conf = { callback: conf };
|
|
/// Get the instrument name.
|
|
var instruments = conf.instruments || conf.instrument || "acoustic_grand_piano";
|
|
if (typeof(instruments) !== "object") instruments = [ instruments ];
|
|
instruments.map(function(data) {
|
|
if (typeof(data) === "number") data = MIDI.GeneralMIDI.byId[data];
|
|
return data;
|
|
});
|
|
///
|
|
MIDI.soundfontUrl = conf.soundfontUrl || MIDI.soundfontUrl || "./soundfont/";
|
|
/// Detect the best type of audio to use.
|
|
MIDI.audioDetect(function(types) {
|
|
var type = "";
|
|
// use the most appropriate plugin if not specified
|
|
if (typeof(type) === 'undefined') {
|
|
if (plugins[window.location.hash]) {
|
|
type = window.location.hash.substr(1);
|
|
} else { //
|
|
type = "";
|
|
}
|
|
}
|
|
if (type === "") {
|
|
if (navigator.requestMIDIAccess) {
|
|
type = "webmidi";
|
|
} else if (window.webkitAudioContext) { // Chrome
|
|
type = "webaudio";
|
|
} else if (window.Audio) { // Firefox
|
|
type = "audiotag";
|
|
} else { // Internet Explorer
|
|
type = "flash";
|
|
}
|
|
}
|
|
if (!connect[type]) return;
|
|
// use audio/ogg when supported
|
|
var filetype = types["audio/ogg"] ? "ogg" : "mp3";
|
|
// load the specified plugin
|
|
connect[type](filetype, instruments, conf.callback);
|
|
});
|
|
};
|
|
|
|
///
|
|
|
|
var connect = {};
|
|
|
|
connect.webmidi = function(filetype, instruments, callback) {
|
|
if (MIDI.loader) MIDI.loader.message("Web MIDI API...");
|
|
MIDI.WebMIDI.connect(callback);
|
|
};
|
|
|
|
connect.flash = function(filetype, instruments, callback) {
|
|
// fairly quick, but requires loading of individual MP3s (more http requests).
|
|
if (MIDI.loader) MIDI.loader.message("Flash API...");
|
|
DOMLoader.script.add({
|
|
src: "./inc/SoundManager2/script/soundmanager2.js",
|
|
verify: "SoundManager",
|
|
callback: function () {
|
|
MIDI.Flash.connect(callback);
|
|
}
|
|
});
|
|
};
|
|
|
|
connect.audiotag = function(filetype, instruments, callback) {
|
|
if (MIDI.loader) MIDI.loader.message("HTML5 Audio API...");
|
|
// works ok, kinda like a drunken tuna fish, across the board.
|
|
var queue = createQueue({
|
|
items: instruments,
|
|
getNext: function(instrumentId) {
|
|
if (USE_XHR) {
|
|
DOMLoader.sendRequest({
|
|
url: MIDI.soundfontUrl + instrumentId + "-" + filetype + ".js",
|
|
onprogress: getPercent,
|
|
onload: function (response) {
|
|
MIDI.Soundfont[instrumentId] = JSON.parse(response.responseText);
|
|
if (MIDI.loader) MIDI.loader.update(null, "Downloading", 100);
|
|
queue.getNext();
|
|
}
|
|
});
|
|
} else {
|
|
DOMLoader.script.add({
|
|
src: MIDI.soundfontUrl + instrumentId + "-" + filetype + ".js",
|
|
verify: "MIDI.Soundfont." + instrumentId,
|
|
callback: function() {
|
|
if (MIDI.loader) MIDI.loader.update(null, "Downloading...", 100);
|
|
queue.getNext();
|
|
}
|
|
});
|
|
}
|
|
},
|
|
onComplete: function() {
|
|
MIDI.AudioTag.connect(callback);
|
|
}
|
|
});
|
|
};
|
|
|
|
connect.webaudio = function(filetype, instruments, callback) {
|
|
if (MIDI.loader) MIDI.loader.message("Web Audio API...");
|
|
// works awesome! safari and chrome support
|
|
var queue = createQueue({
|
|
items: instruments,
|
|
getNext: function(instrumentId) {
|
|
if (USE_XHR) {
|
|
DOMLoader.sendRequest({
|
|
url: MIDI.soundfontUrl + instrumentId + "-" + filetype + ".js",
|
|
onprogress: getPercent,
|
|
onload: function(response) {
|
|
MIDI.Soundfont[instrumentId] = JSON.parse(response.responseText);
|
|
if (MIDI.loader) MIDI.loader.update(null, "Downloading...", 100);
|
|
queue.getNext();
|
|
}
|
|
});
|
|
} else {
|
|
DOMLoader.script.add({
|
|
src: MIDI.soundfontUrl + instrumentId + "-" + filetype + ".js",
|
|
verify: "MIDI.Soundfont." + instrumentId,
|
|
callback: function() {
|
|
if (MIDI.loader) MIDI.loader.update(null, "Downloading...", 100);
|
|
queue.getNext();
|
|
}
|
|
});
|
|
}
|
|
},
|
|
onComplete: function() {
|
|
MIDI.WebAudioAPI.connect(callback);
|
|
}
|
|
});
|
|
};
|
|
|
|
/// Helpers
|
|
|
|
var plugins = {
|
|
"#webmidi": true,
|
|
"#webaudio": true,
|
|
"#audiotag": true,
|
|
"#flash": true
|
|
};
|
|
|
|
var getPercent = function(event) {
|
|
if (!this.totalSize) {
|
|
if (this.getResponseHeader("Content-Length-Raw")) {
|
|
this.totalSize = parseInt(this.getResponseHeader("Content-Length-Raw"));
|
|
} else {
|
|
this.totalSize = event.total;
|
|
}
|
|
}
|
|
var percent = this.totalSize ? Math.round(event.loaded / this.totalSize * 100) : "";
|
|
if (MIDI.loader) MIDI.loader.update(null, "Downloading...", percent);
|
|
};
|
|
|
|
var createQueue = function(conf) {
|
|
var self = {};
|
|
self.queue = [];
|
|
for (var key in conf.items) {
|
|
self.queue.push(conf.items[key]);
|
|
}
|
|
self.getNext = function() {
|
|
if (!self.queue.length) return conf.onComplete();
|
|
conf.getNext(self.queue.shift());
|
|
};
|
|
setTimeout(self.getNext, 1);
|
|
return self;
|
|
};
|
|
|
|
})();
|
|
/*
|
|
--------------------------------------------
|
|
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";
|
|
|
|
/*
|
|
--------------------------------------------
|
|
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 = {};
|
|
|
|
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], 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, velocity], 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) {
|
|
MIDI.technology = "Web MIDI API";
|
|
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;
|
|
|
|
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 (typeof (MIDI.WebAudioAPI) === "undefined") MIDI.WebAudioAPI = {};
|
|
|
|
if (window.AudioContext || window.webkitAudioContext) (function () {
|
|
|
|
var AudioContext = window.AudioContext || window.webkitAudioContext;
|
|
var root = MIDI.WebAudioAPI;
|
|
var ctx;
|
|
var sources = {};
|
|
var masterVolume = 1;
|
|
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 + "<br>Processing: " + (index / 87 * 100 >> 0) + "%<br>" + 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 (n) {
|
|
masterVolume = n;
|
|
};
|
|
|
|
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 / 100) * masterVolume * 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."
|
|
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) {
|
|
MIDI.technology = "Web Audio API";
|
|
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.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 <audio> - OGG or MPEG Soundbank
|
|
--------------------------------------
|
|
http://dev.w3.org/html5/spec/Overview.html#the-audio-element
|
|
*/
|
|
|
|
if (window.Audio) (function () {
|
|
|
|
var root = MIDI.AudioTag = {};
|
|
var note2id = {};
|
|
var volume = 1; // floating point
|
|
var channel_nid = -1; // current channel
|
|
var channels = []; // the audio channels
|
|
var notes = {}; // the piano keys
|
|
for (var nid = 0; nid < 12; nid++) {
|
|
channels[nid] = new Audio();
|
|
}
|
|
|
|
var playChannel = function (channel, note) {
|
|
if (!MIDI.channels[channel]) return;
|
|
var instrument = MIDI.channels[channel].instrument;
|
|
var id = MIDI.GeneralMIDI.byId[instrument].id;
|
|
var note = notes[note];
|
|
if (!note) return;
|
|
var nid = (channel_nid + 1) % channels.length;
|
|
var time = (new Date()).getTime();
|
|
var audio = channels[nid];
|
|
audio.src = MIDI.Soundfont[id][note.id];
|
|
audio.volume = volume;
|
|
audio.play();
|
|
channel_nid = nid;
|
|
};
|
|
|
|
root.programChange = function (channel, program) {
|
|
MIDI.channels[channel].instrument = program;
|
|
};
|
|
|
|
root.setVolume = function (n) {
|
|
volume = n;
|
|
};
|
|
|
|
root.noteOn = function (channel, note, velocity, delay) {
|
|
var id = note2id[note];
|
|
if (!notes[id]) return;
|
|
if (delay) {
|
|
return window.setTimeout(function () {
|
|
playChannel(channel, id);
|
|
}, delay * 1000);
|
|
} else {
|
|
playChannel(channel, id);
|
|
}
|
|
};
|
|
|
|
root.noteOff = function (channel, note, delay) {
|
|
|
|
};
|
|
|
|
root.chordOn = function (channel, chord, velocity, delay) {
|
|
for (var key in chord) {
|
|
var n = chord[key];
|
|
var id = note2id[n];
|
|
if (!notes[id]) continue;
|
|
playChannel(channel, id);
|
|
}
|
|
};
|
|
|
|
root.chordOff = function (channel, chord, delay) {
|
|
|
|
};
|
|
|
|
root.stopAllNotes = function () {
|
|
for (var nid = 0, length = channels.length; nid < length; nid++) {
|
|
channels[nid].pause();
|
|
}
|
|
};
|
|
root.connect = function (callback) {
|
|
var loading = {};
|
|
for (var key in MIDI.keyToNote) {
|
|
note2id[MIDI.keyToNote[key]] = key;
|
|
notes[key] = {
|
|
id: key
|
|
};
|
|
}
|
|
MIDI.technology = "HTML Audio Tag";
|
|
MIDI.setVolume = root.setVolume;
|
|
MIDI.programChange = root.programChange;
|
|
MIDI.noteOn = root.noteOn;
|
|
MIDI.noteOff = root.noteOff;
|
|
MIDI.chordOn = root.chordOn;
|
|
MIDI.chordOff = root.chordOff;
|
|
///
|
|
if (callback) callback();
|
|
};
|
|
})();
|
|
|
|
/*
|
|
--------------------------------------------
|
|
Flash - MP3 Soundbank
|
|
--------------------------------------------
|
|
http://www.schillmania.com/projects/soundmanager2/
|
|
--------------------------------------------
|
|
*/
|
|
|
|
(function () {
|
|
|
|
var root = MIDI.Flash = {};
|
|
var noteReverse = {};
|
|
var notes = {};
|
|
|
|
root.programChange = function (channel, program) {
|
|
MIDI.channels[channel].instrument = program;
|
|
};
|
|
|
|
root.setVolume = function (channel, note) {
|
|
|
|
};
|
|
|
|
root.noteOn = function (channel, note, velocity, delay) {
|
|
if (!MIDI.channels[channel]) return;
|
|
var instrument = MIDI.channels[channel].instrument;
|
|
var id = MIDI.GeneralMIDI.byId[instrument].number;
|
|
note = id + "" + noteReverse[note];
|
|
if (!notes[note]) return;
|
|
if (delay) {
|
|
return window.setTimeout(function() {
|
|
notes[note].play({ volume: velocity * 2 });
|
|
}, delay * 1000);
|
|
} else {
|
|
notes[note].play({ volume: velocity * 2 });
|
|
}
|
|
};
|
|
|
|
root.noteOff = function (channel, note, delay) {
|
|
|
|
};
|
|
|
|
root.chordOn = function (channel, chord, velocity, delay) {
|
|
if (!MIDI.channels[channel]) return;
|
|
var instrument = MIDI.channels[channel].instrument;
|
|
var id = MIDI.GeneralMIDI.byId[instrument].number;
|
|
for (var key in chord) {
|
|
var n = chord[key];
|
|
var note = id + "" + noteReverse[n];
|
|
if (notes[note]) {
|
|
notes[note].play({ volume: velocity * 2 });
|
|
}
|
|
}
|
|
};
|
|
|
|
root.chordOff = function (channel, chord, delay) {
|
|
|
|
};
|
|
|
|
root.stopAllNotes = function () {
|
|
|
|
};
|
|
|
|
root.connect = function (callback) {
|
|
soundManager.flashVersion = 9;
|
|
soundManager.useHTML5Audio = true;
|
|
soundManager.url = '../inc/SoundManager2/swf/';
|
|
soundManager.useHighPerformance = true;
|
|
soundManager.wmode = 'transparent';
|
|
soundManager.flashPollingInterval = 1;
|
|
soundManager.debugMode = false;
|
|
soundManager.onload = function () {
|
|
var createBuffer = function(instrument, id, onload) {
|
|
var synth = MIDI.GeneralMIDI.byName[instrument];
|
|
var instrumentId = synth.number;
|
|
notes[instrumentId+""+id] = soundManager.createSound({
|
|
id: id,
|
|
url: MIDI.soundfontUrl + instrument + "-mp3/" + id + ".mp3",
|
|
multiShot: true,
|
|
autoLoad: true,
|
|
onload: onload
|
|
});
|
|
};
|
|
for (var instrument in MIDI.Soundfont) {
|
|
var loaded = [];
|
|
var onload = function () {
|
|
loaded.push(this.sID);
|
|
if (typeof (MIDI.loader) === "undefined") return;
|
|
MIDI.loader.update(null, "Processing: " + this.sID);
|
|
};
|
|
for (var i = 0; i < 88; i++) {
|
|
var id = noteReverse[i + 21];
|
|
createBuffer(instrument, id, onload);
|
|
}
|
|
}
|
|
///
|
|
MIDI.technology = "Flash";
|
|
MIDI.setVolume = root.setVolume;
|
|
MIDI.programChange = root.programChange;
|
|
MIDI.noteOn = root.noteOn;
|
|
MIDI.noteOff = root.noteOff;
|
|
MIDI.chordOn = root.chordOn;
|
|
MIDI.chordOff = root.chordOff;
|
|
//
|
|
var interval = window.setInterval(function () {
|
|
if (loaded.length !== 88) return;
|
|
window.clearInterval(interval);
|
|
if (callback) callback();
|
|
}, 25);
|
|
};
|
|
soundManager.onerror = function () {
|
|
|
|
};
|
|
for (var key in MIDI.keyToNote) {
|
|
noteReverse[MIDI.keyToNote[key]] = key;
|
|
}
|
|
};
|
|
})();
|
|
|
|
/*
|
|
helper functions
|
|
*/
|
|
|
|
// instrument-tracker
|
|
MIDI.GeneralMIDI = (function (arr) {
|
|
var clean = function(v) {
|
|
return v.replace(/[^a-z0-9 ]/gi, "").replace(/[ ]/g, "_").toLowerCase();
|
|
};
|
|
var ret = {
|
|
byName: {},
|
|
byId: {},
|
|
byCategory: {}
|
|
};
|
|
for (var key in arr) {
|
|
var list = arr[key];
|
|
for (var n = 0, length = list.length; n < length; n++) {
|
|
var instrument = list[n];
|
|
if (!instrument) continue;
|
|
var num = parseInt(instrument.substr(0, instrument.indexOf(" ")), 10);
|
|
instrument = instrument.replace(num + " ", "");
|
|
ret.byId[--num] =
|
|
ret.byName[clean(instrument)] =
|
|
ret.byCategory[clean(key)] = {
|
|
id: clean(instrument),
|
|
instrument: instrument,
|
|
number: num,
|
|
category: key
|
|
};
|
|
}
|
|
}
|
|
return ret;
|
|
})({
|
|
'Piano': ['1 Acoustic Grand Piano', '2 Bright Acoustic Piano', '3 Electric Grand Piano', '4 Honky-tonk Piano', '5 Electric Piano 1', '6 Electric Piano 2', '7 Harpsichord', '8 Clavinet'],
|
|
'Chromatic Percussion': ['9 Celesta', '10 Glockenspiel', '11 Music Box', '12 Vibraphone', '13 Marimba', '14 Xylophone', '15 Tubular Bells', '16 Dulcimer'],
|
|
'Organ': ['17 Drawbar Organ', '18 Percussive Organ', '19 Rock Organ', '20 Church Organ', '21 Reed Organ', '22 Accordion', '23 Harmonica', '24 Tango Accordion'],
|
|
'Guitar': ['25 Acoustic Guitar (nylon)', '26 Acoustic Guitar (steel)', '27 Electric Guitar (jazz)', '28 Electric Guitar (clean)', '29 Electric Guitar (muted)', '30 Overdriven Guitar', '31 Distortion Guitar', '32 Guitar Harmonics'],
|
|
'Bass': ['33 Acoustic Bass', '34 Electric Bass (finger)', '35 Electric Bass (pick)', '36 Fretless Bass', '37 Slap Bass 1', '38 Slap Bass 2', '39 Synth Bass 1', '40 Synth Bass 2'],
|
|
'Strings': ['41 Violin', '42 Viola', '43 Cello', '44 Contrabass', '45 Tremolo Strings', '46 Pizzicato Strings', '47 Orchestral Harp', '48 Timpani'],
|
|
'Ensemble': ['49 String Ensemble 1', '50 String Ensemble 2', '51 Synth Strings 1', '52 Synth Strings 2', '53 Choir Aahs', '54 Voice Oohs', '55 Synth Choir', '56 Orchestra Hit'],
|
|
'Brass': ['57 Trumpet', '58 Trombone', '59 Tuba', '60 Muted Trumpet', '61 French Horn', '62 Brass Section', '63 Synth Brass 1', '64 Synth Brass 2'],
|
|
'Reed': ['65 Soprano Sax', '66 Alto Sax', '67 Tenor Sax', '68 Baritone Sax', '69 Oboe', '70 English Horn', '71 Bassoon', '72 Clarinet'],
|
|
'Pipe': ['73 Piccolo', '74 Flute', '75 Recorder', '76 Pan Flute', '77 Blown Bottle', '78 Shakuhachi', '79 Whistle', '80 Ocarina'],
|
|
'Synth Lead': ['81 Lead 1 (square)', '82 Lead 2 (sawtooth)', '83 Lead 3 (calliope)', '84 Lead 4 (chiff)', '85 Lead 5 (charang)', '86 Lead 6 (voice)', '87 Lead 7 (fifths)', '88 Lead 8 (bass + lead)'],
|
|
'Synth Pad': ['89 Pad 1 (new age)', '90 Pad 2 (warm)', '91 Pad 3 (polysynth)', '92 Pad 4 (choir)', '93 Pad 5 (bowed)', '94 Pad 6 (metallic)', '95 Pad 7 (halo)', '96 Pad 8 (sweep)'],
|
|
'Synth Effects': ['97 FX 1 (rain)', '98 FX 2 (soundtrack)', '99 FX 3 (crystal)', '100 FX 4 (atmosphere)', '101 FX 5 (brightness)', '102 FX 6 (goblins)', '103 FX 7 (echoes)', '104 FX 8 (sci-fi)'],
|
|
'Ethnic': ['105 Sitar', '106 Banjo', '107 Shamisen', '108 Koto', '109 Kalimba', '110 Bagpipe', '111 Fiddle', '112 Shanai'],
|
|
'Percussive': ['113 Tinkle Bell', '114 Agogo', '115 Steel Drums', '116 Woodblock', '117 Taiko Drum', '118 Melodic Tom', '119 Synth Drum'],
|
|
'Sound effects': ['120 Reverse Cymbal', '121 Guitar Fret Noise', '122 Breath Noise', '123 Seashore', '124 Bird Tweet', '125 Telephone Ring', '126 Helicopter', '127 Applause', '128 Gunshot']
|
|
});
|
|
|
|
// channel-tracker
|
|
MIDI.channels = (function () { // 0 - 15 channels
|
|
var channels = {};
|
|
for (var n = 0; n < 16; n++) {
|
|
channels[n] = { // default values
|
|
instrument: 0,
|
|
// Acoustic Grand Piano
|
|
mute: false,
|
|
mono: false,
|
|
omni: false,
|
|
solo: false
|
|
};
|
|
}
|
|
return channels;
|
|
})();
|
|
|
|
//
|
|
MIDI.pianoKeyOffset = 21;
|
|
|
|
// note conversions
|
|
MIDI.keyToNote = {}; // C8 == 108
|
|
MIDI.noteToKey = {}; // 108 == C8
|
|
(function () {
|
|
var A0 = 0x15; // first note
|
|
var C8 = 0x6C; // last note
|
|
var number2key = ["C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B"];
|
|
for (var n = A0; n <= C8; n++) {
|
|
var octave = (n - 12) / 12 >> 0;
|
|
var name = number2key[n % 12] + octave;
|
|
MIDI.keyToNote[name] = n;
|
|
MIDI.noteToKey[n] = name;
|
|
}
|
|
})();
|
|
|
|
})();
|
|
/*
|
|
-------------------------------------
|
|
MIDI.Player : 0.3
|
|
-------------------------------------
|
|
https://github.com/mudcube/MIDI.js
|
|
-------------------------------------
|
|
#jasmid
|
|
-------------------------------------
|
|
*/
|
|
|
|
if (typeof (MIDI) === "undefined") var MIDI = {};
|
|
if (typeof (MIDI.Player) === "undefined") MIDI.Player = {};
|
|
|
|
(function() { "use strict";
|
|
|
|
var root = MIDI.Player;
|
|
root.callback = undefined; // your custom callback goes here!
|
|
root.currentTime = 0;
|
|
root.endTime = 0;
|
|
root.restart = 0;
|
|
root.playing = false;
|
|
root.timeWarp = 1;
|
|
|
|
//
|
|
root.start =
|
|
root.resume = function () {
|
|
if (root.currentTime < -1) root.currentTime = -1;
|
|
startAudio(root.currentTime);
|
|
};
|
|
|
|
root.pause = function () {
|
|
var tmp = root.restart;
|
|
stopAudio();
|
|
root.restart = tmp;
|
|
};
|
|
|
|
root.stop = function () {
|
|
stopAudio();
|
|
root.restart = 0;
|
|
root.currentTime = 0;
|
|
};
|
|
|
|
root.addListener = function(callback) {
|
|
onMidiEvent = callback;
|
|
};
|
|
|
|
root.removeListener = function() {
|
|
onMidiEvent = undefined;
|
|
};
|
|
|
|
root.clearAnimation = function() {
|
|
if (root.interval) {
|
|
window.clearInterval(root.interval);
|
|
}
|
|
};
|
|
|
|
root.setAnimation = function(config) {
|
|
var callback = (typeof(config) === "function") ? config : config.callback;
|
|
var interval = config.interval || 30;
|
|
var currentTime = 0;
|
|
var tOurTime = 0;
|
|
var tTheirTime = 0;
|
|
//
|
|
root.clearAnimation();
|
|
root.interval = window.setInterval(function () {
|
|
if (root.endTime === 0) return;
|
|
if (root.playing) {
|
|
currentTime = (tTheirTime === root.currentTime) ? tOurTime - (new Date).getTime() : 0;
|
|
if (root.currentTime === 0) {
|
|
currentTime = 0;
|
|
} else {
|
|
currentTime = root.currentTime - currentTime;
|
|
}
|
|
if (tTheirTime !== root.currentTime) {
|
|
tOurTime = (new Date).getTime();
|
|
tTheirTime = root.currentTime;
|
|
}
|
|
} else { // paused
|
|
currentTime = root.currentTime;
|
|
}
|
|
var endTime = root.endTime;
|
|
var percent = currentTime / endTime;
|
|
var total = currentTime / 1000;
|
|
var minutes = total / 60;
|
|
var seconds = total - (minutes * 60);
|
|
var t1 = minutes * 60 + seconds;
|
|
var t2 = (endTime / 1000);
|
|
if (t2 - t1 < -1) return;
|
|
callback({
|
|
now: t1,
|
|
end: t2,
|
|
events: noteRegistrar
|
|
});
|
|
}, interval);
|
|
};
|
|
|
|
// helpers
|
|
|
|
root.loadMidiFile = function() { // reads midi into javascript array of events
|
|
root.replayer = new Replayer(MidiFile(root.currentData), root.timeWarp);
|
|
root.data = root.replayer.getData();
|
|
root.endTime = getLength();
|
|
};
|
|
|
|
root.loadFile = function (file, callback) {
|
|
root.stop();
|
|
if (file.indexOf("base64,") !== -1) {
|
|
var data = window.atob(file.split(",")[1]);
|
|
root.currentData = data;
|
|
root.loadMidiFile();
|
|
if (callback) callback(data);
|
|
return;
|
|
}
|
|
///
|
|
var title = file.split(" - ")[1] || file;
|
|
document.getElementById("playback-title").innerHTML = title.replace(".mid","");
|
|
///
|
|
var fetch = new XMLHttpRequest();
|
|
fetch.open('GET', file);
|
|
fetch.overrideMimeType("text/plain; charset=x-user-defined");
|
|
fetch.onreadystatechange = function () {
|
|
if (this.readyState === 4 && this.status === 200) {
|
|
var t = this.responseText || "";
|
|
var ff = [];
|
|
var mx = t.length;
|
|
var scc = String.fromCharCode;
|
|
for (var z = 0; z < mx; z++) {
|
|
ff[z] = scc(t.charCodeAt(z) & 255);
|
|
}
|
|
var data = ff.join("");
|
|
root.currentData = data;
|
|
root.loadMidiFile();
|
|
if (callback) callback(data);
|
|
}
|
|
};
|
|
fetch.send();
|
|
};
|
|
|
|
// Playing the audio
|
|
|
|
var eventQueue = []; // hold events to be triggered
|
|
var queuedTime; //
|
|
var startTime = 0; // to measure time elapse
|
|
var noteRegistrar = {}; // get event for requested note
|
|
var onMidiEvent = undefined; // listener callback
|
|
var scheduleTracking = function (channel, note, currentTime, offset, message, velocity) {
|
|
var interval = window.setTimeout(function () {
|
|
var data = {
|
|
channel: channel,
|
|
note: note,
|
|
now: currentTime,
|
|
end: root.endTime,
|
|
message: message,
|
|
velocity: velocity
|
|
};
|
|
//
|
|
if (message === 128) {
|
|
delete noteRegistrar[note];
|
|
} else {
|
|
noteRegistrar[note] = data;
|
|
}
|
|
if (onMidiEvent) {
|
|
onMidiEvent(data);
|
|
}
|
|
root.currentTime = currentTime;
|
|
if (root.currentTime === queuedTime && queuedTime < root.endTime) { // grab next sequence
|
|
startAudio(queuedTime, true);
|
|
}
|
|
}, currentTime - offset);
|
|
return interval;
|
|
};
|
|
|
|
var getContext = function() {
|
|
if (MIDI.lang === 'WebAudioAPI') {
|
|
return MIDI.Player.ctx;
|
|
} else if (!root.ctx) {
|
|
root.ctx = { currentTime: 0 };
|
|
}
|
|
return root.ctx;
|
|
};
|
|
|
|
var getLength = function() {
|
|
var data = root.data;
|
|
var length = data.length;
|
|
var totalTime = 0.5;
|
|
for (var n = 0; n < length; n++) {
|
|
totalTime += data[n][1];
|
|
}
|
|
return totalTime;
|
|
};
|
|
|
|
var startAudio = function (currentTime, fromCache) {
|
|
if (!root.replayer) return;
|
|
if (!fromCache) {
|
|
if (typeof (currentTime) === "undefined") currentTime = root.restart;
|
|
if (root.playing) stopAudio();
|
|
root.playing = true;
|
|
root.data = root.replayer.getData();
|
|
root.endTime = getLength();
|
|
}
|
|
var note;
|
|
var offset = 0;
|
|
var messages = 0;
|
|
var data = root.data;
|
|
var ctx = getContext();
|
|
var length = data.length;
|
|
//
|
|
queuedTime = 0.5;
|
|
startTime = ctx.currentTime;
|
|
//
|
|
for (var n = 0; n < length && messages < 100; n++) {
|
|
queuedTime += data[n][1];
|
|
if (queuedTime < currentTime) {
|
|
offset = queuedTime;
|
|
continue;
|
|
}
|
|
currentTime = queuedTime - offset;
|
|
var event = data[n][0].event;
|
|
if (event.type !== "channel") continue;
|
|
var channel = event.channel;
|
|
switch (event.subtype) {
|
|
case 'noteOn':
|
|
if (MIDI.channels[channel].mute) break;
|
|
note = event.noteNumber - (root.MIDIOffset || 0);
|
|
eventQueue.push({
|
|
event: event,
|
|
source: MIDI.noteOn(channel, event.noteNumber, event.velocity, currentTime / 1000 + ctx.currentTime),
|
|
interval: scheduleTracking(channel, note, queuedTime, offset, 144, event.velocity)
|
|
});
|
|
messages ++;
|
|
break;
|
|
case 'noteOff':
|
|
if (MIDI.channels[channel].mute) break;
|
|
note = event.noteNumber - (root.MIDIOffset || 0);
|
|
eventQueue.push({
|
|
event: event,
|
|
source: MIDI.noteOff(channel, event.noteNumber, currentTime / 1000 + ctx.currentTime),
|
|
interval: scheduleTracking(channel, note, queuedTime, offset, 128)
|
|
});
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
var stopAudio = function () {
|
|
var ctx = getContext();
|
|
root.playing = false;
|
|
root.restart += (ctx.currentTime - startTime) * 1000;
|
|
// stop the audio, and intervals
|
|
while (eventQueue.length) {
|
|
var o = eventQueue.pop();
|
|
window.clearInterval(o.interval);
|
|
if (!o.source) continue; // is not webaudio
|
|
if (typeof(o.source) === "number") {
|
|
window.clearTimeout(o.source);
|
|
} else { // webaudio
|
|
var source = o.source;
|
|
source.disconnect(0);
|
|
source.noteOff(0);
|
|
}
|
|
}
|
|
// run callback to cancel any notes still playing
|
|
for (var key in noteRegistrar) {
|
|
var o = noteRegistrar[key]
|
|
if (noteRegistrar[key].message === 144 && onMidiEvent) {
|
|
onMidiEvent({
|
|
channel: o.channel,
|
|
note: o.note,
|
|
now: o.now,
|
|
end: o.end,
|
|
message: 128,
|
|
velocity: o.velocity
|
|
});
|
|
}
|
|
}
|
|
// reset noteRegistrar
|
|
noteRegistrar = {};
|
|
};
|
|
|
|
})();
|
|
/*
|
|
|
|
DOMLoader.XMLHttp
|
|
--------------------------
|
|
DOMLoader.sendRequest({
|
|
url: "./dir/something.extension",
|
|
data: "test!",
|
|
onerror: function(event) {
|
|
console.log(event);
|
|
},
|
|
onload: function(response) {
|
|
console.log(response.responseText);
|
|
},
|
|
onprogress: function (event) {
|
|
var percent = event.loaded / event.total * 100 >> 0;
|
|
loader.message("loading: " + percent + "%");
|
|
}
|
|
});
|
|
|
|
*/
|
|
|
|
if (typeof(DOMLoader) === "undefined") var DOMLoader = {};
|
|
|
|
// Add XMLHttpRequest when not available
|
|
|
|
if (typeof (XMLHttpRequest) === "undefined") {
|
|
var XMLHttpRequest;
|
|
(function () { // find equivalent for IE
|
|
var factories = [
|
|
function () {
|
|
return new ActiveXObject("Msxml2.XMLHTTP")
|
|
}, function () {
|
|
return new ActiveXObject("Msxml3.XMLHTTP")
|
|
}, function () {
|
|
return new ActiveXObject("Microsoft.XMLHTTP")
|
|
}];
|
|
for (var i = 0; i < factories.length; i++) {
|
|
try {
|
|
factories[i]();
|
|
} catch (e) {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
XMLHttpRequest = factories[i];
|
|
})();
|
|
}
|
|
|
|
if (typeof ((new XMLHttpRequest()).responseText) === "undefined") {
|
|
// http://stackoverflow.com/questions/1919972/how-do-i-access-xhr-responsebody-for-binary-data-from-javascript-in-ie
|
|
var IEBinaryToArray_ByteStr_Script =
|
|
"<!-- IEBinaryToArray_ByteStr -->\r\n"+
|
|
"<script type='text/vbscript'>\r\n"+
|
|
"Function IEBinaryToArray_ByteStr(Binary)\r\n"+
|
|
" IEBinaryToArray_ByteStr = CStr(Binary)\r\n"+
|
|
"End Function\r\n"+
|
|
"Function IEBinaryToArray_ByteStr_Last(Binary)\r\n"+
|
|
" Dim lastIndex\r\n"+
|
|
" lastIndex = LenB(Binary)\r\n"+
|
|
" if lastIndex mod 2 Then\r\n"+
|
|
" IEBinaryToArray_ByteStr_Last = Chr( AscB( MidB( Binary, lastIndex, 1 ) ) )\r\n"+
|
|
" Else\r\n"+
|
|
" IEBinaryToArray_ByteStr_Last = "+'""'+"\r\n"+
|
|
" End If\r\n"+
|
|
"End Function\r\n"+
|
|
"</script>\r\n";
|
|
|
|
// inject VBScript
|
|
document.write(IEBinaryToArray_ByteStr_Script);
|
|
|
|
DOMLoader.sendRequest = function(conf) {
|
|
// helper to convert from responseBody to a "responseText" like thing
|
|
function getResponseText(binary) {
|
|
var byteMapping = {};
|
|
for (var i = 0; i < 256; i++) {
|
|
for (var j = 0; j < 256; j++) {
|
|
byteMapping[String.fromCharCode(i + j * 256)] = String.fromCharCode(i) + String.fromCharCode(j);
|
|
}
|
|
}
|
|
// call into VBScript utility fns
|
|
var rawBytes = IEBinaryToArray_ByteStr(binary);
|
|
var lastChr = IEBinaryToArray_ByteStr_Last(binary);
|
|
return rawBytes.replace(/[\s\S]/g, function (match) {
|
|
return byteMapping[match];
|
|
}) + lastChr;
|
|
};
|
|
//
|
|
var req = XMLHttpRequest();
|
|
req.open("GET", conf.url, true);
|
|
if (conf.responseType) req.responseType = conf.responseType;
|
|
if (conf.onerror) req.onerror = conf.onerror;
|
|
if (conf.onprogress) req.onprogress = conf.onprogress;
|
|
req.onreadystatechange = function (event) {
|
|
if (req.readyState === 4) {
|
|
if (req.status === 200) {
|
|
req.responseText = getResponseText(req.responseBody);
|
|
} else {
|
|
req = false;
|
|
}
|
|
if (conf.onload) conf.onload(req);
|
|
}
|
|
};
|
|
req.setRequestHeader("Accept-Charset", "x-user-defined");
|
|
req.send(null);
|
|
return req;
|
|
}
|
|
} else {
|
|
DOMLoader.sendRequest = function(conf) {
|
|
var req = new XMLHttpRequest();
|
|
req.open(conf.data ? "POST" : "GET", conf.url, true);
|
|
if (req.overrideMimeType) req.overrideMimeType("text/plain; charset=x-user-defined");
|
|
if (conf.data) req.setRequestHeader('Content-type','application/x-www-form-urlencoded');
|
|
if (conf.responseType) req.responseType = conf.responseType;
|
|
if (conf.onerror) req.onerror = conf.onerror;
|
|
if (conf.onprogress) req.onprogress = conf.onprogress;
|
|
req.onreadystatechange = function (event) {
|
|
if (req.readyState === 4) {
|
|
if (req.status !== 200 && req.status != 304) {
|
|
if (conf.onerror) conf.onerror(event, false);
|
|
return;
|
|
}
|
|
if (conf.onload) {
|
|
conf.onload(req);
|
|
}
|
|
}
|
|
};
|
|
req.send(conf.data);
|
|
return req;
|
|
};
|
|
}
|
|
/*
|
|
----------------------------------------------------
|
|
DOMLoader.script.js : 0.1.2 : 2012/09/08 : http://mudcu.be
|
|
----------------------------------------------------
|
|
Copyright 2011-2012 Mudcube. All rights reserved.
|
|
----------------------------------------------------
|
|
/// No verification
|
|
DOMLoader.script.add("../js/jszip/jszip.js");
|
|
/// Strict loading order and verification.
|
|
DOMLoader.script.add({
|
|
strictOrder: true,
|
|
srcs: [
|
|
{
|
|
src: "../js/jszip/jszip.js",
|
|
verify: "JSZip",
|
|
callback: function() {
|
|
console.log(1)
|
|
}
|
|
},
|
|
{
|
|
src: "../inc/downloadify/js/swfobject.js",
|
|
verify: "swfobject",
|
|
callback: function() {
|
|
console.log(2)
|
|
}
|
|
}
|
|
],
|
|
callback: function() {
|
|
console.log(3)
|
|
}
|
|
});
|
|
/// Just verification.
|
|
DOMLoader.script.add({
|
|
src: "../js/jszip/jszip.js",
|
|
verify: "JSZip",
|
|
callback: function() {
|
|
console.log(1)
|
|
}
|
|
});
|
|
*/
|
|
|
|
if (typeof(DOMLoader) === "undefined") var DOMLoader = {};
|
|
|
|
(function() { "use strict";
|
|
|
|
DOMLoader.script = function() {
|
|
this.loaded = {};
|
|
this.loading = {};
|
|
return this;
|
|
};
|
|
|
|
DOMLoader.script.prototype.add = function(config) {
|
|
var that = this;
|
|
if (typeof(config) === "string") {
|
|
config = { src: config };
|
|
}
|
|
var srcs = config.srcs;
|
|
if (typeof(srcs) === "undefined") {
|
|
srcs = [{
|
|
src: config.src,
|
|
verify: config.verify
|
|
}];
|
|
}
|
|
/// adding the elements to the head
|
|
var doc = document.getElementsByTagName("head")[0];
|
|
///
|
|
var testElement = function(element, test) {
|
|
if (that.loaded[element.src]) return;
|
|
if (test && typeof(window[test]) === "undefined") return;
|
|
that.loaded[element.src] = true;
|
|
//
|
|
if (that.loading[element.src]) that.loading[element.src]();
|
|
delete that.loading[element.src];
|
|
//
|
|
if (element.callback) element.callback();
|
|
if (typeof(getNext) !== "undefined") getNext();
|
|
};
|
|
///
|
|
var batchTest = [];
|
|
var addElement = function(element) {
|
|
if (typeof(element) === "string") {
|
|
element = {
|
|
src: element,
|
|
verify: config.verify
|
|
};
|
|
}
|
|
if (/([\w\d.])$/.test(element.verify)) { // check whether its a variable reference
|
|
element.test = element.verify;
|
|
if (typeof(element.test) === "object") {
|
|
for (var key in element.test) {
|
|
batchTest.push(element.test[key]);
|
|
}
|
|
} else {
|
|
batchTest.push(element.test);
|
|
}
|
|
}
|
|
if (that.loaded[element.src]) return;
|
|
var script = document.createElement("script");
|
|
script.onreadystatechange = function() {
|
|
if (this.readyState !== "loaded" && this.readyState !== "complete") return;
|
|
testElement(element);
|
|
};
|
|
script.onload = function() {
|
|
testElement(element);
|
|
};
|
|
script.onerror = function() {
|
|
|
|
};
|
|
script.setAttribute("type", "text/javascript");
|
|
script.setAttribute("src", element.src);
|
|
doc.appendChild(script);
|
|
that.loading[element.src] = function() {};
|
|
};
|
|
/// checking to see whether everything loaded properly
|
|
var onLoad = function(element) {
|
|
if (element) {
|
|
testElement(element, element.test);
|
|
} else {
|
|
for (var n = 0; n < srcs.length; n ++) {
|
|
testElement(srcs[n], srcs[n].test);
|
|
}
|
|
}
|
|
var istrue = true;
|
|
for (var n = 0; n < batchTest.length; n ++) {
|
|
var test = batchTest[n];
|
|
if (test && test.indexOf(".") !== -1) {
|
|
test = test.split(".");
|
|
var level0 = window[test[0]];
|
|
if (typeof(level0) === "undefined") continue;
|
|
if (test.length === 2) { //- this is a bit messy and could handle more cases
|
|
if (typeof(level0[test[1]]) === "undefined") {
|
|
istrue = false;
|
|
}
|
|
} else if (test.length === 3) {
|
|
if (typeof(level0[test[1]][test[2]]) === "undefined") {
|
|
istrue = false;
|
|
}
|
|
}
|
|
} else {
|
|
if (typeof(window[test]) === "undefined") {
|
|
istrue = false;
|
|
}
|
|
}
|
|
}
|
|
if (!config.strictOrder && istrue) { // finished loading all the requested scripts
|
|
if (config.callback) config.callback();
|
|
} else { // keep calling back the function
|
|
setTimeout(function() { //- should get slower over time?
|
|
onLoad(element);
|
|
}, 10);
|
|
}
|
|
};
|
|
/// loading methods; strict ordering or loose ordering
|
|
if (config.strictOrder) {
|
|
var ID = -1;
|
|
var getNext = function() {
|
|
ID ++;
|
|
if (!srcs[ID]) { // all elements are loaded
|
|
if (config.callback) config.callback();
|
|
} else { // loading new script
|
|
var element = srcs[ID];
|
|
var src = element.src;
|
|
if (that.loading[src]) { // already loading from another call (attach to event)
|
|
that.loading[src] = function() {
|
|
if (element.callback) element.callback();
|
|
getNext();
|
|
}
|
|
} else if (!that.loaded[src]) { // create script element
|
|
addElement(element);
|
|
onLoad(element);
|
|
} else { // it's already been successfully loaded
|
|
getNext();
|
|
}
|
|
}
|
|
};
|
|
getNext();
|
|
} else { // loose ordering
|
|
for (var ID = 0; ID < srcs.length; ID ++) {
|
|
addElement(srcs[ID]);
|
|
}
|
|
onLoad();
|
|
}
|
|
};
|
|
|
|
DOMLoader.script = (new DOMLoader.script());
|
|
|
|
})(); |