summaryrefslogtreecommitdiffstats
path: root/Processing-js/libs/js/MIDI
diff options
context:
space:
mode:
Diffstat (limited to 'Processing-js/libs/js/MIDI')
-rw-r--r--Processing-js/libs/js/MIDI/AudioDetect.js69
-rw-r--r--Processing-js/libs/js/MIDI/LoadPlugin.js179
-rw-r--r--Processing-js/libs/js/MIDI/Player.js277
-rw-r--r--Processing-js/libs/js/MIDI/Plugin.js623
4 files changed, 1148 insertions, 0 deletions
diff --git a/Processing-js/libs/js/MIDI/AudioDetect.js b/Processing-js/libs/js/MIDI/AudioDetect.js
new file mode 100644
index 0000000..a5dd465
--- /dev/null
+++ b/Processing-js/libs/js/MIDI/AudioDetect.js
@@ -0,0 +1,69 @@
+/*
+ -------------------------------------
+ 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 pending = 0;
+var canPlayThrough = function (src) {
+ pending ++;
+ var audio = new Audio();
+ var mime = src.split(";")[0];
+ audio.id = "audio";
+ audio.setAttribute("preload", "auto");
+ audio.setAttribute("audiobuffer", true);
+ audio.addEventListener("error", function() {
+ supports[mime] = false;
+ pending --;
+ }, false);
+ audio.addEventListener("canplaythrough", function() {
+ supports[mime] = true;
+ pending --;
+ }, 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() {
+ var now = (new Date()).getTime();
+ var maxExecution = now - time > 5000;
+ if (!pending || maxExecution) {
+ window.clearInterval(interval);
+ callback(supports);
+ }
+ }, 1);
+};
+
+})(); \ No newline at end of file
diff --git a/Processing-js/libs/js/MIDI/LoadPlugin.js b/Processing-js/libs/js/MIDI/LoadPlugin.js
new file mode 100644
index 0000000..dad5cbd
--- /dev/null
+++ b/Processing-js/libs/js/MIDI/LoadPlugin.js
@@ -0,0 +1,179 @@
+/*
+ -----------------------------------------------------------
+ MIDI.loadPlugin : 0.1.2 : 01/22/2014
+ -----------------------------------------------------------
+ https://github.com/mudcube/MIDI.js
+ -----------------------------------------------------------
+ MIDI.loadPlugin({
+ targetFormat: "mp3", // optionally can force to use MP3 (for instance on mobile networks)
+ 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";
+
+var USE_JAZZMIDI = false; // Turn on to support JazzMIDI Plugin
+
+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 ];
+ ///
+ for (var n = 0; n < instruments.length; n ++) {
+ var instrument = instruments[n];
+ if (typeof(instrument) === "number") {
+ instruments[n] = MIDI.GeneralMIDI.byId[instrument];
+ }
+ };
+ ///
+ MIDI.soundfontUrl = conf.soundfontUrl || MIDI.soundfontUrl || "./soundfont/";
+ /// Detect the best type of audio to use.
+ MIDI.audioDetect(function(types) {
+ var api = "";
+ // use the most appropriate plugin if not specified
+ if (apis[conf.api]) {
+ api = conf.api;
+ } else if (apis[window.location.hash.substr(1)]) {
+ api = window.location.hash.substr(1);
+ } else if (USE_JAZZMIDI && navigator.requestMIDIAccess) {
+ api = "webmidi";
+ } else if (window.webkitAudioContext || window.AudioContext) { // Chrome
+ api = "webaudio";
+ } else if (window.Audio) { // Firefox
+ api = "audiotag";
+ } else { // Internet Explorer
+ api = "flash";
+ }
+ ///
+ if (!connect[api]) return;
+ // use audio/ogg when supported
+ if (conf.targetFormat) {
+ var filetype = conf.targetFormat;
+ } else { // use best quality
+ var filetype = types["audio/ogg"] ? "ogg" : "mp3";
+ }
+ // load the specified plugin
+ MIDI.lang = api;
+ MIDI.supports = types;
+ connect[api](filetype, instruments, conf);
+ });
+};
+
+///
+
+var connect = {};
+
+connect.webmidi = function(filetype, instruments, conf) {
+ if (MIDI.loader) MIDI.loader.message("Web MIDI API...");
+ MIDI.WebMIDI.connect(conf);
+};
+
+connect.flash = function(filetype, instruments, conf) {
+ // fairly quick, but requires loading of individual MP3s (more http requests).
+ if (MIDI.loader) MIDI.loader.message("Flash API...");
+ DOMLoader.script.add({
+ src: conf.soundManagerUrl || "./inc/SoundManager2/script/soundmanager2.js",
+ verify: "SoundManager",
+ callback: function () {
+ MIDI.Flash.connect(instruments, conf);
+ }
+ });
+};
+
+connect.audiotag = function(filetype, instruments, conf) {
+ 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) {
+ DOMLoader.sendRequest({
+ url: MIDI.soundfontUrl + instrumentId + "-" + filetype + ".js",
+ onprogress: getPercent,
+ onload: function (response) {
+ addSoundfont(response.responseText);
+ if (MIDI.loader) MIDI.loader.update(null, "Downloading", 100);
+ queue.getNext();
+ }
+ });
+ },
+ onComplete: function() {
+ MIDI.AudioTag.connect(conf);
+ }
+ });
+};
+
+connect.webaudio = function(filetype, instruments, conf) {
+ if (MIDI.loader) MIDI.loader.message("Web Audio API...");
+ // works awesome! safari, chrome and firefox support.
+ var queue = createQueue({
+ items: instruments,
+ getNext: function(instrumentId) {
+ DOMLoader.sendRequest({
+ url: MIDI.soundfontUrl + instrumentId + "-" + filetype + ".js",
+ onprogress: getPercent,
+ onload: function(response) {
+ addSoundfont(response.responseText);
+ if (MIDI.loader) MIDI.loader.update(null, "Downloading...", 100);
+ queue.getNext();
+ }
+ });
+ },
+ onComplete: function() {
+ MIDI.WebAudio.connect(conf);
+ }
+ });
+};
+
+/// Helpers
+
+var apis = {
+ "webmidi": true,
+ "webaudio": true,
+ "audiotag": true,
+ "flash": true
+};
+
+var addSoundfont = function(text) {
+ var script = document.createElement("script");
+ script.language = "javascript";
+ script.type = "text/javascript";
+ script.text = text;
+ document.body.appendChild(script);
+};
+
+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;
+};
+
+})(); \ No newline at end of file
diff --git a/Processing-js/libs/js/MIDI/Player.js b/Processing-js/libs/js/MIDI/Player.js
new file mode 100644
index 0000000..bfa4e3b
--- /dev/null
+++ b/Processing-js/libs/js/MIDI/Player.js
@@ -0,0 +1,277 @@
+/*
+ -------------------------------------
+ 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 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
+ o.source.disconnect(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 = {};
+};
+
+})(); \ No newline at end of file
diff --git a/Processing-js/libs/js/MIDI/Plugin.js b/Processing-js/libs/js/MIDI/Plugin.js
new file mode 100644
index 0000000..7647f96
--- /dev/null
+++ b/Processing-js/libs/js/MIDI/Plugin.js
@@ -0,0 +1,623 @@
+/*
+ --------------------------------------------
+ MIDI.Plugin : 0.3.2 : 2013/01/26
+ --------------------------------------------
+ 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.WebMIDI
+ MIDI.WebAudio
+ MIDI.Flash
+ MIDI.AudioTag
+ --------------------------------------------
+ Helpers:
+ MIDI.GeneralMIDI
+ MIDI.channels
+ MIDI.keyToNote
+ MIDI.noteToKey
+*/
+
+if (typeof (MIDI) === "undefined") var MIDI = {};
+
+(function() { "use strict";
+
+var setPlugin = function(root) {
+ MIDI.api = root.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;
+};
+
+/*
+ --------------------------------------------
+ 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 = {
+ api: "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, 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 (conf) {
+ setPlugin(root);
+ navigator.requestMIDIAccess().then(function (access) {
+ plugin = access;
+ output = plugin.outputs()[0];
+ if (conf.callback) conf.callback();
+ }, function (err) { // well at least we tried!
+ if (window.AudioContext || window.webkitAudioContext) { // Chrome
+ conf.api = "webaudio";
+ } else if (window.Audio) { // Firefox
+ conf.api = "audiotag";
+ } else { // Internet Explorer
+ conf.api = "flash";
+ }
+ MIDI.loadPlugin(conf);
+ });
+ };
+})();
+
+/*
+ --------------------------------------------
+ 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.WebAudio = {
+ api: "webaudio"
+ };
+ 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];
+ if (!MIDI.Soundfont[instrument][url]) { // missing soundfont
+ return callback(instrument);
+ }
+ 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 += "&nbsp;";
+ 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 (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);
+ ///
+ if (ctx.createGain) { // firefox
+ source.gainNode = ctx.createGain();
+ } else { // chrome
+ source.gainNode = ctx.createGainNode();
+ }
+ var value = (velocity / 127) * (masterVolume / 127) * 2 - 1;
+ source.gainNode.connect(ctx.destination);
+ source.gainNode.gain.value = Math.max(-1, value);
+ source.connect(source.gainNode);
+ if (source.noteOn) { // old api
+ source.noteOn(delay || 0);
+ } else { // new api
+ source.start(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;
+ if (source.gainNode) {
+ // @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
+ var gain = source.gainNode.gain;
+ gain.linearRampToValueAtTime(gain.value, delay);
+ gain.linearRampToValueAtTime(-1, delay + 0.2);
+ }
+ if (source.noteOff) { // old api
+ source.noteOff(delay + 0.3);
+ } else {
+ source.stop(delay + 0.3);
+ }
+ ///
+ delete sources[channel + "" + note];
+ };
+
+ 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.stopAllNotes = function () {
+ for (var source in sources) {
+ var delay = 0;
+ if (delay < ctx.currentTime) delay += ctx.currentTime;
+ // @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
+ sources[source].gain.linearRampToValueAtTime(1, delay);
+ sources[source].gain.linearRampToValueAtTime(0, delay + 0.2);
+ sources[source].noteOff(delay + 0.3);
+ delete sources[source];
+ }
+ };
+
+ root.connect = function (conf) {
+ 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) conf.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 = {
+ api: "audiotag"
+ };
+ var note2id = {};
+ var volume = 127; // floating point
+ var channel_nid = -1; // current channel
+ var channels = []; // the audio channels
+ var channelInstrumentNoteIds = []; // instrumentId + noteId that is currently playing in each 'channel', for routing noteOff/chordOff calls
+ 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 instrumentId = MIDI.GeneralMIDI.byId[instrument].id;
+ var note = notes[note];
+ if (!note) return;
+ var instrumentNoteId = instrumentId + "" + note.id;
+ var nid = (channel_nid + 1) % channels.length;
+ var audio = channels[nid];
+ channelInstrumentNoteIds[ nid ] = instrumentNoteId;
+ audio.src = MIDI.Soundfont[instrumentId][note.id];
+ audio.volume = volume / 127;
+ audio.play();
+ channel_nid = nid;
+ };
+
+ var stopChannel = function (channel, note) {
+ if (!MIDI.channels[channel]) return;
+ var instrument = MIDI.channels[channel].instrument;
+ var instrumentId = MIDI.GeneralMIDI.byId[instrument].id;
+ var note = notes[note];
+ if (!note) return;
+ var instrumentNoteId = instrumentId + "" + note.id;
+
+ for(var i=0;i<channels.length;i++){
+ var nid = (i + channel_nid + 1) % channels.length;
+ var cId = channelInstrumentNoteIds[nid];
+
+ if(cId && cId == instrumentNoteId){
+ channels[nid].pause();
+ channelInstrumentNoteIds[nid] = null;
+ return;
+ }
+ }
+ };
+
+ root.programChange = function (channel, program) {
+ MIDI.channels[channel].instrument = program;
+ };
+
+ root.setVolume = function (channel, n) {
+ volume = n; //- should be channel specific volume
+ };
+
+ 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) {
+ var id = note2id[note];
+ if (!notes[id]) return;
+ if (delay) {
+ return setTimeout(function() {
+ stopChannel(channel, id);
+ }, delay * 1000)
+ } else {
+ stopChannel(channel, id);
+ }
+ };
+
+ root.chordOn = function (channel, chord, velocity, delay) {
+ for (var idx = 0; idx < chord.length; idx ++) {
+ var n = chord[idx];
+ var id = note2id[n];
+ if (!notes[id]) continue;
+ if (delay) {
+ return window.setTimeout(function () {
+ playChannel(channel, id);
+ }, delay * 1000);
+ } else {
+ playChannel(channel, id);
+ }
+ }
+ };
+
+ root.chordOff = function (channel, chord, delay) {
+ for (var idx = 0; idx < chord.length; idx ++) {
+ var n = chord[idx];
+ var id = note2id[n];
+ if (!notes[id]) continue;
+ if (delay) {
+ return window.setTimeout(function () {
+ stopChannel(channel, id);
+ }, delay * 1000);
+ } else {
+ stopChannel(channel, id);
+ }
+ }
+ };
+
+ root.stopAllNotes = function () {
+ for (var nid = 0, length = channels.length; nid < length; nid++) {
+ channels[nid].pause();
+ }
+ };
+
+ root.connect = function (conf) {
+ for (var key in MIDI.keyToNote) {
+ note2id[MIDI.keyToNote[key]] = key;
+ notes[key] = {
+ id: key
+ };
+ }
+ setPlugin(root);
+ ///
+ if (conf.callback) conf.callback();
+ };
+})();
+
+/*
+ --------------------------------------------
+ Flash - MP3 Soundbank
+ --------------------------------------------
+ http://www.schillmania.com/projects/soundmanager2/
+ --------------------------------------------
+*/
+
+(function () {
+
+ var root = MIDI.Flash = {
+ api: "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 (instruments, conf) {
+ soundManager.flashVersion = 9;
+ soundManager.useHTML5Audio = true;
+ soundManager.url = conf.soundManagerSwfUrl || '../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
+ });
+ };
+ var loaded = [];
+ var samplesPerInstrument = 88;
+ var samplesToLoad = instruments.length * samplesPerInstrument;
+
+ for (var i = 0; i < instruments.length; i++) {
+ var instrument = instruments[i];
+ var onload = function () {
+ loaded.push(this.sID);
+ if (typeof (MIDI.loader) === "undefined") return;
+ MIDI.loader.update(null, "Processing: " + this.sID);
+ };
+ for (var j = 0; j < samplesPerInstrument; j++) {
+ var id = noteReverse[j + 21];
+ createBuffer(instrument, id, onload);
+ }
+ }
+ ///
+ setPlugin(root);
+ //
+ var interval = window.setInterval(function () {
+ if (loaded.length < samplesToLoad) return;
+ window.clearInterval(interval);
+ if (conf.callback) conf.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;
+ }
+})();
+
+})(); \ No newline at end of file