summaryrefslogtreecommitdiff
path: root/src/http/static/trading-in-the-rain/MIDI.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/http/static/trading-in-the-rain/MIDI.js')
-rw-r--r--src/http/static/trading-in-the-rain/MIDI.js/LICENSE.txt21
-rw-r--r--src/http/static/trading-in-the-rain/MIDI.js/inc/shim/Base64.js61
-rw-r--r--src/http/static/trading-in-the-rain/MIDI.js/inc/shim/Base64binary.js81
-rw-r--r--src/http/static/trading-in-the-rain/MIDI.js/inc/shim/WebAudioAPI.js111
-rw-r--r--src/http/static/trading-in-the-rain/MIDI.js/inc/shim/WebMIDIAPI.js421
-rw-r--r--src/http/static/trading-in-the-rain/MIDI.js/js/midi/audioDetect.js101
-rw-r--r--src/http/static/trading-in-the-rain/MIDI.js/js/midi/gm.js161
-rw-r--r--src/http/static/trading-in-the-rain/MIDI.js/js/midi/loader.js199
-rw-r--r--src/http/static/trading-in-the-rain/MIDI.js/js/midi/player.js380
-rw-r--r--src/http/static/trading-in-the-rain/MIDI.js/js/midi/plugin.audiotag.js150
-rw-r--r--src/http/static/trading-in-the-rain/MIDI.js/js/midi/plugin.webaudio.js326
-rw-r--r--src/http/static/trading-in-the-rain/MIDI.js/js/midi/plugin.webmidi.js93
-rw-r--r--src/http/static/trading-in-the-rain/MIDI.js/js/midi/synesthesia.js320
-rw-r--r--src/http/static/trading-in-the-rain/MIDI.js/js/util/dom_request_script.js225
-rw-r--r--src/http/static/trading-in-the-rain/MIDI.js/js/util/dom_request_xhr.js146
15 files changed, 2796 insertions, 0 deletions
diff --git a/src/http/static/trading-in-the-rain/MIDI.js/LICENSE.txt b/src/http/static/trading-in-the-rain/MIDI.js/LICENSE.txt
new file mode 100644
index 0000000..0dca6fb
--- /dev/null
+++ b/src/http/static/trading-in-the-rain/MIDI.js/LICENSE.txt
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2010-2013 MIDI.js Authors. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. \ No newline at end of file
diff --git a/src/http/static/trading-in-the-rain/MIDI.js/inc/shim/Base64.js b/src/http/static/trading-in-the-rain/MIDI.js/inc/shim/Base64.js
new file mode 100644
index 0000000..b5a59ce
--- /dev/null
+++ b/src/http/static/trading-in-the-rain/MIDI.js/inc/shim/Base64.js
@@ -0,0 +1,61 @@
+//https://github.com/davidchambers/Base64.js
+
+;(function () {
+ var object = typeof exports != 'undefined' ? exports : this; // #8: web workers
+ var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
+
+ function InvalidCharacterError(message) {
+ this.message = message;
+ }
+ InvalidCharacterError.prototype = new Error;
+ InvalidCharacterError.prototype.name = 'InvalidCharacterError';
+
+ // encoder
+ // [https://gist.github.com/999166] by [https://github.com/nignag]
+ object.btoa || (
+ object.btoa = function (input) {
+ for (
+ // initialize result and counter
+ var block, charCode, idx = 0, map = chars, output = '';
+ // if the next input index does not exist:
+ // change the mapping table to "="
+ // check if d has no fractional digits
+ input.charAt(idx | 0) || (map = '=', idx % 1);
+ // "8 - idx % 1 * 8" generates the sequence 2, 4, 6, 8
+ output += map.charAt(63 & block >> 8 - idx % 1 * 8)
+ ) {
+ charCode = input.charCodeAt(idx += 3/4);
+ if (charCode > 0xFF) {
+ throw new InvalidCharacterError("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");
+ }
+ block = block << 8 | charCode;
+ }
+ return output;
+ });
+
+ // decoder
+ // [https://gist.github.com/1020396] by [https://github.com/atk]
+ object.atob || (
+ object.atob = function (input) {
+ input = input.replace(/=+$/, '')
+ if (input.length % 4 == 1) {
+ throw new InvalidCharacterError("'atob' failed: The string to be decoded is not correctly encoded.");
+ }
+ for (
+ // initialize result and counters
+ var bc = 0, bs, buffer, idx = 0, output = '';
+ // get next character
+ buffer = input.charAt(idx++);
+ // character found in table? initialize bit storage and add its ascii value;
+ ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
+ // and if not first of each 4 characters,
+ // convert the first 8 bits to one ascii character
+ bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
+ ) {
+ // try to find character in table (0-63, not found => -1)
+ buffer = chars.indexOf(buffer);
+ }
+ return output;
+ });
+
+}()); \ No newline at end of file
diff --git a/src/http/static/trading-in-the-rain/MIDI.js/inc/shim/Base64binary.js b/src/http/static/trading-in-the-rain/MIDI.js/inc/shim/Base64binary.js
new file mode 100644
index 0000000..2c59f8f
--- /dev/null
+++ b/src/http/static/trading-in-the-rain/MIDI.js/inc/shim/Base64binary.js
@@ -0,0 +1,81 @@
+/**
+ * @license -------------------------------------------------------------------
+ * module: Base64Binary
+ * src: http://blog.danguer.com/2011/10/24/base64-binary-decoding-in-javascript/
+ * license: Simplified BSD License
+ * -------------------------------------------------------------------
+ * Copyright 2011, Daniel Guerrero. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL DANIEL GUERRERO BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+var Base64Binary = {
+ _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
+
+ /* will return a Uint8Array type */
+ decodeArrayBuffer: function(input) {
+ var bytes = Math.ceil( (3*input.length) / 4.0);
+ var ab = new ArrayBuffer(bytes);
+ this.decode(input, ab);
+
+ return ab;
+ },
+
+ decode: function(input, arrayBuffer) {
+ //get last chars to see if are valid
+ var lkey1 = this._keyStr.indexOf(input.charAt(input.length-1));
+ var lkey2 = this._keyStr.indexOf(input.charAt(input.length-1));
+
+ var bytes = Math.ceil( (3*input.length) / 4.0);
+ if (lkey1 == 64) bytes--; //padding chars, so skip
+ if (lkey2 == 64) bytes--; //padding chars, so skip
+
+ var uarray;
+ var chr1, chr2, chr3;
+ var enc1, enc2, enc3, enc4;
+ var i = 0;
+ var j = 0;
+
+ if (arrayBuffer)
+ uarray = new Uint8Array(arrayBuffer);
+ else
+ uarray = new Uint8Array(bytes);
+
+ input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
+
+ for (i=0; i<bytes; i+=3) {
+ //get the 3 octects in 4 ascii chars
+ enc1 = this._keyStr.indexOf(input.charAt(j++));
+ enc2 = this._keyStr.indexOf(input.charAt(j++));
+ enc3 = this._keyStr.indexOf(input.charAt(j++));
+ enc4 = this._keyStr.indexOf(input.charAt(j++));
+
+ chr1 = (enc1 << 2) | (enc2 >> 4);
+ chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
+ chr3 = ((enc3 & 3) << 6) | enc4;
+
+ uarray[i] = chr1;
+ if (enc3 != 64) uarray[i+1] = chr2;
+ if (enc4 != 64) uarray[i+2] = chr3;
+ }
+
+ return uarray;
+ }
+}; \ No newline at end of file
diff --git a/src/http/static/trading-in-the-rain/MIDI.js/inc/shim/WebAudioAPI.js b/src/http/static/trading-in-the-rain/MIDI.js/inc/shim/WebAudioAPI.js
new file mode 100644
index 0000000..17e9eb9
--- /dev/null
+++ b/src/http/static/trading-in-the-rain/MIDI.js/inc/shim/WebAudioAPI.js
@@ -0,0 +1,111 @@
+/**
+ * @license -------------------------------------------------------------------
+ * module: WebAudioShim - Fix naming issues for WebAudioAPI supports
+ * src: https://github.com/Dinahmoe/webaudioshim
+ * author: Dinahmoe AB
+ * -------------------------------------------------------------------
+ * Copyright (c) 2012 DinahMoe AB
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+window.AudioContext = window.AudioContext || window.webkitAudioContext || null;
+window.OfflineAudioContext = window.OfflineAudioContext || window.webkitOfflineAudioContext || null;
+
+(function (Context) {
+ var isFunction = function (f) {
+ return Object.prototype.toString.call(f) === "[object Function]" ||
+ Object.prototype.toString.call(f) === "[object AudioContextConstructor]";
+ };
+ var contextMethods = [
+ ["createGainNode", "createGain"],
+ ["createDelayNode", "createDelay"],
+ ["createJavaScriptNode", "createScriptProcessor"]
+ ];
+ ///
+ var proto;
+ var instance;
+ var sourceProto;
+ ///
+ if (!isFunction(Context)) {
+ return;
+ }
+ instance = new Context();
+ if (!instance.destination || !instance.sampleRate) {
+ return;
+ }
+ proto = Context.prototype;
+ sourceProto = Object.getPrototypeOf(instance.createBufferSource());
+
+ if (!isFunction(sourceProto.start)) {
+ if (isFunction(sourceProto.noteOn)) {
+ sourceProto.start = function (when, offset, duration) {
+ switch (arguments.length) {
+ case 0:
+ throw new Error("Not enough arguments.");
+ case 1:
+ this.noteOn(when);
+ break;
+ case 2:
+ if (this.buffer) {
+ this.noteGrainOn(when, offset, this.buffer.duration - offset);
+ } else {
+ throw new Error("Missing AudioBuffer");
+ }
+ break;
+ case 3:
+ this.noteGrainOn(when, offset, duration);
+ }
+ };
+ }
+ }
+
+ if (!isFunction(sourceProto.noteOn)) {
+ sourceProto.noteOn = sourceProto.start;
+ }
+
+ if (!isFunction(sourceProto.noteGrainOn)) {
+ sourceProto.noteGrainOn = sourceProto.start;
+ }
+
+ if (!isFunction(sourceProto.stop)) {
+ sourceProto.stop = sourceProto.noteOff;
+ }
+
+ if (!isFunction(sourceProto.noteOff)) {
+ sourceProto.noteOff = sourceProto.stop;
+ }
+
+ contextMethods.forEach(function (names) {
+ var name1;
+ var name2;
+ while (names.length) {
+ name1 = names.pop();
+ if (isFunction(this[name1])) {
+ this[names.pop()] = this[name1];
+ } else {
+ name2 = names.pop();
+ this[name1] = this[name2];
+ }
+ }
+ }, proto);
+})(window.AudioContext); \ No newline at end of file
diff --git a/src/http/static/trading-in-the-rain/MIDI.js/inc/shim/WebMIDIAPI.js b/src/http/static/trading-in-the-rain/MIDI.js/inc/shim/WebMIDIAPI.js
new file mode 100644
index 0000000..000a916
--- /dev/null
+++ b/src/http/static/trading-in-the-rain/MIDI.js/inc/shim/WebMIDIAPI.js
@@ -0,0 +1,421 @@
+/* Copyright 2013 Chris Wilson
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+// Initialize the MIDI library.
+(function (global) {
+ 'use strict';
+ var midiIO, _requestMIDIAccess, MIDIAccess, _onReady, MIDIPort, MIDIInput, MIDIOutput, _midiProc;
+
+ function Promise() {
+
+ }
+
+ Promise.prototype.then = function(accept, reject) {
+ this.accept = accept;
+ this.reject = reject;
+ }
+
+ Promise.prototype.succeed = function(access) {
+ if (this.accept)
+ this.accept(access);
+ }
+
+ Promise.prototype.fail = function(error) {
+ if (this.reject)
+ this.reject(error);
+ }
+
+ function _JazzInstance() {
+ this.inputInUse = false;
+ this.outputInUse = false;
+
+ // load the Jazz plugin
+ var o1 = document.createElement("object");
+ o1.id = "_Jazz" + Math.random() + "ie";
+ o1.classid = "CLSID:1ACE1618-1C7D-4561-AEE1-34842AA85E90";
+
+ this.activeX = o1;
+
+ var o2 = document.createElement("object");
+ o2.id = "_Jazz" + Math.random();
+ o2.type="audio/x-jazz";
+ o1.appendChild(o2);
+
+ this.objRef = o2;
+
+ var e = document.createElement("p");
+ e.appendChild(document.createTextNode("This page requires the "));
+
+ var a = document.createElement("a");
+ a.appendChild(document.createTextNode("Jazz plugin"));
+ a.href = "http://jazz-soft.net/";
+
+ e.appendChild(a);
+ e.appendChild(document.createTextNode("."));
+ o2.appendChild(e);
+
+ var insertionPoint = document.getElementById("MIDIPlugin");
+ if (!insertionPoint) {
+ // Create hidden element
+ var insertionPoint = document.createElement("div");
+ insertionPoint.id = "MIDIPlugin";
+ insertionPoint.style.position = "absolute";
+ insertionPoint.style.visibility = "hidden";
+ insertionPoint.style.left = "-9999px";
+ insertionPoint.style.top = "-9999px";
+ document.body.appendChild(insertionPoint);
+ }
+ insertionPoint.appendChild(o1);
+
+ if (this.objRef.isJazz)
+ this._Jazz = this.objRef;
+ else if (this.activeX.isJazz)
+ this._Jazz = this.activeX;
+ else
+ this._Jazz = null;
+ if (this._Jazz) {
+ this._Jazz._jazzTimeZero = this._Jazz.Time();
+ this._Jazz._perfTimeZero = window.performance.now();
+ }
+ }
+
+ _requestMIDIAccess = function _requestMIDIAccess() {
+ var access = new MIDIAccess();
+ return access._promise;
+ };
+
+ // API Methods
+
+ MIDIAccess = function() {
+ this._jazzInstances = new Array();
+ this._jazzInstances.push( new _JazzInstance() );
+ this._promise = new Promise;
+
+ if (this._jazzInstances[0]._Jazz) {
+ this._Jazz = this._jazzInstances[0]._Jazz;
+ window.setTimeout( _onReady.bind(this), 3 );
+ } else {
+ window.setTimeout( _onNotReady.bind(this), 3 );
+ }
+ };
+
+ _onReady = function _onReady() {
+ if (this._promise)
+ this._promise.succeed(this);
+ };
+
+ function _onNotReady() {
+ if (this._promise)
+ this._promise.fail( { code: 1 } );
+ };
+
+ MIDIAccess.prototype.inputs = function( ) {
+ if (!this._Jazz)
+ return null;
+ var list=this._Jazz.MidiInList();
+ var inputs = new Array( list.length );
+
+ for ( var i=0; i<list.length; i++ ) {
+ inputs[i] = new MIDIInput( this, list[i], i );
+ }
+ return inputs;
+ }
+
+ MIDIAccess.prototype.outputs = function( ) {
+ if (!this._Jazz)
+ return null;
+ var list=this._Jazz.MidiOutList();
+ var outputs = new Array( list.length );
+
+ for ( var i=0; i<list.length; i++ ) {
+ outputs[i] = new MIDIOutput( this, list[i], i );
+ }
+ return outputs;
+ };
+
+ MIDIInput = function MIDIInput( midiAccess, name, index ) {
+ this._listeners = [];
+ this._midiAccess = midiAccess;
+ this._index = index;
+ this._inLongSysexMessage = false;
+ this._sysexBuffer = new Uint8Array();
+ this.id = "" + index + "." + name;
+ this.manufacturer = "";
+ this.name = name;
+ this.type = "input";
+ this.version = "";
+ this.onmidimessage = null;
+
+ var inputInstance = null;
+ for (var i=0; (i<midiAccess._jazzInstances.length)&&(!inputInstance); i++) {
+ if (!midiAccess._jazzInstances[i].inputInUse)
+ inputInstance=midiAccess._jazzInstances[i];
+ }
+ if (!inputInstance) {
+ inputInstance = new _JazzInstance();
+ midiAccess._jazzInstances.push( inputInstance );
+ }
+ inputInstance.inputInUse = true;
+
+ this._jazzInstance = inputInstance._Jazz;
+ this._input = this._jazzInstance.MidiInOpen( this._index, _midiProc.bind(this) );
+ };
+
+ // Introduced in DOM Level 2:
+ MIDIInput.prototype.addEventListener = function (type, listener, useCapture ) {
+ if (type !== "midimessage")
+ return;
+ for (var i=0; i<this._listeners.length; i++)
+ if (this._listeners[i] == listener)
+ return;
+ this._listeners.push( listener );
+ };
+
+ MIDIInput.prototype.removeEventListener = function (type, listener, useCapture ) {
+ if (type !== "midimessage")
+ return;
+ for (var i=0; i<this._listeners.length; i++)
+ if (this._listeners[i] == listener) {
+ this._listeners.splice( i, 1 ); //remove it
+ return;
+ }
+ };
+
+ MIDIInput.prototype.preventDefault = function() {
+ this._pvtDef = true;
+ };
+
+ MIDIInput.prototype.dispatchEvent = function (evt) {
+ this._pvtDef = false;
+
+ // dispatch to listeners
+ for (var i=0; i<this._listeners.length; i++)
+ if (this._listeners[i].handleEvent)
+ this._listeners[i].handleEvent.bind(this)( evt );
+ else
+ this._listeners[i].bind(this)( evt );
+
+ if (this.onmidimessage)
+ this.onmidimessage( evt );
+
+ return this._pvtDef;
+ };
+
+ MIDIInput.prototype.appendToSysexBuffer = function ( data ) {
+ var oldLength = this._sysexBuffer.length;
+ var tmpBuffer = new Uint8Array( oldLength + data.length );
+ tmpBuffer.set( this._sysexBuffer );
+ tmpBuffer.set( data, oldLength );
+ this._sysexBuffer = tmpBuffer;
+ };
+
+ MIDIInput.prototype.bufferLongSysex = function ( data, initialOffset ) {
+ var j = initialOffset;
+ while (j<data.length) {
+ if (data[j] == 0xF7) {
+ // end of sysex!
+ j++;
+ this.appendToSysexBuffer( data.slice(initialOffset, j) );
+ return j;
+ }
+ j++;
+ }
+ // didn't reach the end; just tack it on.
+ this.appendToSysexBuffer( data.slice(initialOffset, j) );
+ this._inLongSysexMessage = true;
+ return j;
+ };
+
+ _midiProc = function _midiProc( timestamp, data ) {
+ // Have to use createEvent/initEvent because IE10 fails on new CustomEvent. Thanks, IE!
+ var length = 0;
+ var i,j;
+ var isSysexMessage = false;
+
+ // Jazz sometimes passes us multiple messages at once, so we need to parse them out
+ // and pass them one at a time.
+
+ for (i=0; i<data.length; i+=length) {
+ if (this._inLongSysexMessage) {
+ i = this.bufferLongSysex(data,i);
+ if ( data[i-1] != 0xf7 ) {
+ // ran off the end without hitting the end of the sysex message
+ return;
+ }
+ isSysexMessage = true;
+ } else {
+ isSysexMessage = false;
+ switch (data[i] & 0xF0) {
+ case 0x80: // note off
+ case 0x90: // note on
+ case 0xA0: // polyphonic aftertouch
+ case 0xB0: // control change
+ case 0xE0: // channel mode
+ length = 3;
+ break;
+
+ case 0xC0: // program change
+ case 0xD0: // channel aftertouch
+ length = 2;
+ break;
+
+ case 0xF0:
+ switch (data[i]) {
+ case 0xf0: // variable-length sysex.
+ i = this.bufferLongSysex(data,i);
+ if ( data[i-1] != 0xf7 ) {
+ // ran off the end without hitting the end of the sysex message
+ return;
+ }
+ isSysexMessage = true;
+ break;
+
+ case 0xF1: // MTC quarter frame
+ case 0xF3: // song select
+ length = 2;
+ break;
+
+ case 0xF2: // song position pointer
+ length = 3;
+ break;
+
+ default:
+ length = 1;
+ break;
+ }
+ break;
+ }
+ }
+ var evt = document.createEvent( "Event" );
+ evt.initEvent( "midimessage", false, false );
+ evt.receivedTime = parseFloat( timestamp.toString()) + this._jazzInstance._perfTimeZero;
+ if (isSysexMessage || this._inLongSysexMessage) {
+ evt.data = new Uint8Array( this._sysexBuffer );
+ this._sysexBuffer = new Uint8Array(0);
+ this._inLongSysexMessage = false;
+ } else
+ evt.data = new Uint8Array(data.slice(i, length+i));
+ this.dispatchEvent( evt );
+ }
+ };
+
+ MIDIOutput = function MIDIOutput( midiAccess, name, index ) {
+ this._listeners = [];
+ this._midiAccess = midiAccess;
+ this._index = index;
+ this.id = "" + index + "." + name;
+ this.manufacturer = "";
+ this.name = name;
+ this.type = "output";
+ this.version = "";
+
+ var outputInstance = null;
+ for (var i=0; (i<midiAccess._jazzInstances.length)&&(!outputInstance); i++) {
+ if (!midiAccess._jazzInstances[i].outputInUse)
+ outputInstance=midiAccess._jazzInstances[i];
+ }
+ if (!outputInstance) {
+ outputInstance = new _JazzInstance();
+ midiAccess._jazzInstances.push( outputInstance );
+ }
+ outputInstance.outputInUse = true;
+
+ this._jazzInstance = outputInstance._Jazz;
+ this._jazzInstance.MidiOutOpen(this.name);
+ };
+
+ function _sendLater() {
+ this.jazz.MidiOutLong( this.data ); // handle send as sysex
+ }
+
+ MIDIOutput.prototype.send = function( data, timestamp ) {
+ var delayBeforeSend = 0;
+ if (data.length === 0)
+ return false;
+
+ if (timestamp)
+ delayBeforeSend = Math.floor( timestamp - window.performance.now() );
+
+ if (timestamp && (delayBeforeSend>1)) {
+ var sendObj = new Object();
+ sendObj.jazz = this._jazzInstance;
+ sendObj.data = data;
+
+ window.setTimeout( _sendLater.bind(sendObj), delayBeforeSend );
+ } else {
+ this._jazzInstance.MidiOutLong( data );
+ }
+ return true;
+ };
+
+ //init: create plugin
+ if (!window.navigator.requestMIDIAccess)
+ window.navigator.requestMIDIAccess = _requestMIDIAccess;
+
+}(window));
+
+// Polyfill window.performance.now() if necessary.
+(function (exports) {
+ var perf = {}, props;
+
+ function findAlt() {
+ var prefix = ['moz', 'webkit', 'o', 'ms'],
+ i = prefix.length,
+ //worst case, we use Date.now()
+ props = {
+ value: (function (start) {
+ return function () {
+ return Date.now() - start;
+ };
+ }(Date.now()))
+ };
+
+ //seach for vendor prefixed version
+ for (; i >= 0; i--) {
+ if ((prefix[i] + "Now") in exports.performance) {
+ props.value = function (method) {
+ return function () {
+ exports.performance[method]();
+ }
+ }(prefix[i] + "Now");
+ return props;
+ }
+ }
+
+ //otherwise, try to use connectionStart
+ if ("timing" in exports.performance && "connectStart" in exports.performance.timing) {
+ //this pretty much approximates performance.now() to the millisecond
+ props.value = (function (start) {
+ return function() {
+ Date.now() - start;
+ };
+ }(exports.performance.timing.connectStart));
+ }
+ return props;
+ }
+
+ //if already defined, bail
+ if (("performance" in exports) && ("now" in exports.performance))
+ return;
+ if (!("performance" in exports))
+ Object.defineProperty(exports, "performance", {
+ get: function () {
+ return perf;
+ }});
+ //otherwise, performance is there, but not "now()"
+
+ props = findAlt();
+ Object.defineProperty(exports.performance, "now", props);
+}(window));
diff --git a/src/http/static/trading-in-the-rain/MIDI.js/js/midi/audioDetect.js b/src/http/static/trading-in-the-rain/MIDI.js/js/midi/audioDetect.js
new file mode 100644
index 0000000..957605d
--- /dev/null
+++ b/src/http/static/trading-in-the-rain/MIDI.js/js/midi/audioDetect.js
@@ -0,0 +1,101 @@
+/*
+ ----------------------------------------------------------
+ MIDI.audioDetect : 0.3.2 : 2015-03-26
+ ----------------------------------------------------------
+ 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') MIDI = {};
+
+(function(root) { 'use strict';
+
+ var supports = {}; // object of supported file types
+ var pending = 0; // pending file types to process
+ var canPlayThrough = function (src) { // check whether format plays through
+ pending ++;
+ var body = document.body;
+ var audio = new Audio();
+ var mime = src.split(';')[0];
+ audio.id = 'audio';
+ audio.setAttribute('preload', 'auto');
+ audio.setAttribute('audiobuffer', true);
+ audio.addEventListener('error', function() {
+ body.removeChild(audio);
+ supports[mime] = false;
+ pending --;
+ }, false);
+ audio.addEventListener('canplaythrough', function() {
+ body.removeChild(audio);
+ supports[mime] = true;
+ pending --;
+ }, false);
+ audio.src = 'data:' + src;
+ body.appendChild(audio);
+ };
+
+ root.audioDetect = function(onsuccess) {
+ /// detect jazz-midi plugin
+ if (navigator.requestMIDIAccess) {
+ var isNative = Function.prototype.toString.call(navigator.requestMIDIAccess).indexOf('[native code]');
+ if (isNative) { // has native midiapi support
+ supports['webmidi'] = true;
+ } else { // check for jazz plugin midiapi support
+ for (var n = 0; navigator.plugins.length > n; n ++) {
+ var plugin = navigator.plugins[n];
+ if (plugin.name.indexOf('Jazz-Plugin') >= 0) {
+ supports['webmidi'] = true;
+ }
+ }
+ }
+ }
+
+ /// check whether <audio> tag is supported
+ if (typeof(Audio) === 'undefined') {
+ return onsuccess({});
+ } else {
+ supports['audiotag'] = true;
+ }
+
+ /// check for webaudio api support
+ if (window.AudioContext || window.webkitAudioContext) {
+ supports['webaudio'] = true;
+ }
+
+ /// check whether canPlayType is supported
+ var audio = new Audio();
+ if (typeof(audio.canPlayType) === 'undefined') {
+ return onsuccess(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) {
+ onsuccess(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);
+ onsuccess(supports);
+ }
+ }, 1);
+ };
+
+})(MIDI); \ No newline at end of file
diff --git a/src/http/static/trading-in-the-rain/MIDI.js/js/midi/gm.js b/src/http/static/trading-in-the-rain/MIDI.js/js/midi/gm.js
new file mode 100644
index 0000000..2e1278c
--- /dev/null
+++ b/src/http/static/trading-in-the-rain/MIDI.js/js/midi/gm.js
@@ -0,0 +1,161 @@
+/*
+ ----------------------------------------------------------
+ GeneralMIDI
+ ----------------------------------------------------------
+*/
+
+(function(root) { 'use strict';
+
+ root.GM = (function(arr) {
+ var clean = function(name) {
+ return name.replace(/[^a-z0-9 ]/gi, '').replace(/[ ]/g, '_').toLowerCase();
+ };
+ var res = {
+ 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 + ' ', '');
+ res.byId[--num] =
+ res.byName[clean(instrument)] =
+ res.byCategory[clean(key)] = {
+ id: clean(instrument),
+ instrument: instrument,
+ number: num,
+ category: key
+ };
+ }
+ }
+ return res;
+ })({
+ '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']
+ });
+
+ /* get/setInstrument
+ --------------------------------------------------- */
+ root.getInstrument = function(channelId) {
+ var channel = root.channels[channelId];
+ return channel && channel.instrument;
+ };
+
+ root.setInstrument = function(channelId, program, delay) {
+ var channel = root.channels[channelId];
+ if (delay) {
+ return setTimeout(function() {
+ channel.instrument = program;
+ }, delay);
+ } else {
+ channel.instrument = program;
+ }
+ };
+
+ /* get/setMono
+ --------------------------------------------------- */
+ root.getMono = function(channelId) {
+ var channel = root.channels[channelId];
+ return channel && channel.mono;
+ };
+
+ root.setMono = function(channelId, truthy, delay) {
+ var channel = root.channels[channelId];
+ if (delay) {
+ return setTimeout(function() {
+ channel.mono = truthy;
+ }, delay);
+ } else {
+ channel.mono = truthy;
+ }
+ };
+
+ /* get/setOmni
+ --------------------------------------------------- */
+ root.getOmni = function(channelId) {
+ var channel = root.channels[channelId];
+ return channel && channel.omni;
+ };
+
+ root.setOmni = function(channelId, truthy) {
+ var channel = root.channels[channelId];
+ if (delay) {
+ return setTimeout(function() {
+ channel.omni = truthy;
+ }, delay);
+ } else {
+ channel.omni = truthy;
+ }
+ };
+
+ /* get/setSolo
+ --------------------------------------------------- */
+ root.getSolo = function(channelId) {
+ var channel = root.channels[channelId];
+ return channel && channel.solo;
+ };
+
+ root.setSolo = function(channelId, truthy) {
+ var channel = root.channels[channelId];
+ if (delay) {
+ return setTimeout(function() {
+ channel.solo = truthy;
+ }, delay);
+ } else {
+ channel.solo = truthy;
+ }
+ };
+
+ /* channels
+ --------------------------------------------------- */
+ root.channels = (function() { // 0 - 15 channels
+ var channels = {};
+ for (var i = 0; i < 16; i++) {
+ channels[i] = { // default values
+ instrument: i,
+ pitchBend: 0,
+ mute: false,
+ mono: false,
+ omni: false,
+ solo: false
+ };
+ }
+ return channels;
+ })();
+
+ /* note conversions
+ --------------------------------------------------- */
+ root.keyToNote = {}; // C8 == 108
+ root.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;
+ root.keyToNote[name] = n;
+ root.noteToKey[n] = name;
+ }
+ })();
+
+})(MIDI); \ No newline at end of file
diff --git a/src/http/static/trading-in-the-rain/MIDI.js/js/midi/loader.js b/src/http/static/trading-in-the-rain/MIDI.js/js/midi/loader.js
new file mode 100644
index 0000000..02136f6
--- /dev/null
+++ b/src/http/static/trading-in-the-rain/MIDI.js/js/midi/loader.js
@@ -0,0 +1,199 @@
+/*
+ ----------------------------------------------------------
+ MIDI.Plugin : 0.3.4 : 2015-03-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
+ ----------------------------------------------------------
+ Web MIDI API - no native support yet (jazzplugin)
+ Web Audio API - firefox 25+, chrome 10+, safari 6+, opera 15+
+ HTML5 Audio Tag - ie 9+, firefox 3.5+, chrome 4+, safari 4+, opera 9.5+, ios 4+, android 2.3+
+ ----------------------------------------------------------
+*/
+
+if (typeof MIDI === 'undefined') MIDI = {};
+
+MIDI.Soundfont = MIDI.Soundfont || {};
+MIDI.Player = MIDI.Player || {};
+
+(function(root) { 'use strict';
+
+ root.DEBUG = true;
+ root.USE_XHR = true;
+ root.soundfontUrl = './soundfont/';
+
+ /*
+ MIDI.loadPlugin({
+ onsuccess: function() { },
+ onprogress: function(state, percent) { },
+ 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
+ });
+ */
+
+ root.loadPlugin = function(opts) {
+ if (typeof opts === 'function') {
+ opts = {onsuccess: opts};
+ }
+
+ root.soundfontUrl = opts.soundfontUrl || root.soundfontUrl;
+
+ /// Detect the best type of audio to use
+ root.audioDetect(function(supports) {
+ var hash = window.location.hash;
+ var api = '';
+
+ /// use the most appropriate plugin if not specified
+ if (supports[opts.api]) {
+ api = opts.api;
+ } else if (supports[hash.substr(1)]) {
+ api = hash.substr(1);
+ } else if (supports.webmidi) {
+ api = 'webmidi';
+ } else if (window.AudioContext) { // Chrome
+ api = 'webaudio';
+ } else if (window.Audio) { // Firefox
+ api = 'audiotag';
+ }
+
+ if (connect[api]) {
+ /// use audio/ogg when supported
+ if (opts.targetFormat) {
+ var audioFormat = opts.targetFormat;
+ } else { // use best quality
+ var audioFormat = supports['audio/ogg'] ? 'ogg' : 'mp3';
+ }
+
+ /// load the specified plugin
+ root.__api = api;
+ root.__audioFormat = audioFormat;
+ root.supports = supports;
+ root.loadResource(opts);
+ }
+ });
+ };
+
+ /*
+ root.loadResource({
+ onsuccess: function() { },
+ onprogress: function(state, percent) { },
+ instrument: 'banjo'
+ })
+ */
+
+ root.loadResource = function(opts) {
+ var instruments = opts.instruments || opts.instrument || 'acoustic_grand_piano';
+ ///
+ if (typeof instruments !== 'object') {
+ if (instruments || instruments === 0) {
+ instruments = [instruments];
+ } else {
+ instruments = [];
+ }
+ }
+ /// convert numeric ids into strings
+ for (var i = 0; i < instruments.length; i ++) {
+ var instrument = instruments[i];
+ if (instrument === +instrument) { // is numeric
+ if (root.GM.byId[instrument]) {
+ instruments[i] = root.GM.byId[instrument].id;
+ }
+ }
+ }
+ ///
+ opts.format = root.__audioFormat;
+ opts.instruments = instruments;
+ ///
+ connect[root.__api](opts);
+ };
+
+ var connect = {
+ webmidi: function(opts) {
+ // cant wait for this to be standardized!
+ root.WebMIDI.connect(opts);
+ },
+ audiotag: function(opts) {
+ // works ok, kinda like a drunken tuna fish, across the board
+ // http://caniuse.com/audio
+ requestQueue(opts, 'AudioTag');
+ },
+ webaudio: function(opts) {
+ // works awesome! safari, chrome and firefox support
+ // http://caniuse.com/web-audio
+ requestQueue(opts, 'WebAudio');
+ }
+ };
+
+ var requestQueue = function(opts, context) {
+ var audioFormat = opts.format;
+ var instruments = opts.instruments;
+ var onprogress = opts.onprogress;
+ var onerror = opts.onerror;
+ ///
+ var length = instruments.length;
+ var pending = length;
+ var waitForEnd = function() {
+ if (!--pending) {
+ onprogress && onprogress('load', 1.0);
+ root[context].connect(opts);
+ }
+ };
+ ///
+ for (var i = 0; i < length; i ++) {
+ var instrumentId = instruments[i];
+ if (MIDI.Soundfont[instrumentId]) { // already loaded
+ waitForEnd();
+ } else { // needs to be requested
+ sendRequest(instruments[i], audioFormat, function(evt, progress) {
+ var fileProgress = progress / length;
+ var queueProgress = (length - pending) / length;
+ onprogress && onprogress('load', fileProgress + queueProgress, instrumentId);
+ }, function() {
+ waitForEnd();
+ }, onerror);
+ }
+ };
+ };
+
+ var sendRequest = function(instrumentId, audioFormat, onprogress, onsuccess, onerror) {
+ var soundfontPath = root.soundfontUrl + instrumentId + '-' + audioFormat + '.js';
+ if (root.USE_XHR) {
+ root.util.request({
+ url: soundfontPath,
+ format: 'text',
+ onerror: onerror,
+ onprogress: onprogress,
+ onsuccess: function(event, responseText) {
+ var script = document.createElement('script');
+ script.language = 'javascript';
+ script.type = 'text/javascript';
+ script.text = responseText;
+ document.body.appendChild(script);
+ ///
+ onsuccess();
+ }
+ });
+ } else {
+ dom.loadScript.add({
+ url: soundfontPath,
+ verify: 'MIDI.Soundfont["' + instrumentId + '"]',
+ onerror: onerror,
+ onsuccess: function() {
+ onsuccess();
+ }
+ });
+ }
+ };
+
+ root.setDefaultPlugin = function(midi) {
+ for (var key in midi) {
+ root[key] = midi[key];
+ }
+ };
+
+})(MIDI); \ No newline at end of file
diff --git a/src/http/static/trading-in-the-rain/MIDI.js/js/midi/player.js b/src/http/static/trading-in-the-rain/MIDI.js/js/midi/player.js
new file mode 100644
index 0000000..08f298f
--- /dev/null
+++ b/src/http/static/trading-in-the-rain/MIDI.js/js/midi/player.js
@@ -0,0 +1,380 @@
+/*
+ ----------------------------------------------------------
+ MIDI.Player : 0.3.1 : 2015-03-26
+ ----------------------------------------------------------
+ https://github.com/mudcube/MIDI.js
+ ----------------------------------------------------------
+*/
+
+if (typeof MIDI === 'undefined') MIDI = {};
+if (typeof MIDI.Player === 'undefined') MIDI.Player = {};
+
+(function() { 'use strict';
+
+var midi = MIDI.Player;
+midi.currentTime = 0;
+midi.endTime = 0;
+midi.restart = 0;
+midi.playing = false;
+midi.timeWarp = 1;
+midi.startDelay = 0;
+midi.BPM = 120;
+
+midi.start =
+midi.resume = function(onsuccess) {
+ if (midi.currentTime < -1) {
+ midi.currentTime = -1;
+ }
+ startAudio(midi.currentTime, null, onsuccess);
+};
+
+midi.pause = function() {
+ var tmp = midi.restart;
+ stopAudio();
+ midi.restart = tmp;
+};
+
+midi.stop = function() {
+ stopAudio();
+ midi.restart = 0;
+ midi.currentTime = 0;
+};
+
+midi.addListener = function(onsuccess) {
+ onMidiEvent = onsuccess;
+};
+
+midi.removeListener = function() {
+ onMidiEvent = undefined;
+};
+
+midi.clearAnimation = function() {
+ if (midi.animationFrameId) {
+ cancelAnimationFrame(midi.animationFrameId);
+ }
+};
+
+midi.setAnimation = function(callback) {
+ var currentTime = 0;
+ var tOurTime = 0;
+ var tTheirTime = 0;
+ //
+ midi.clearAnimation();
+ ///
+ var frame = function() {
+ midi.animationFrameId = requestAnimationFrame(frame);
+ ///
+ if (midi.endTime === 0) {
+ return;
+ }
+ if (midi.playing) {
+ currentTime = (tTheirTime === midi.currentTime) ? tOurTime - Date.now() : 0;
+ if (midi.currentTime === 0) {
+ currentTime = 0;
+ } else {
+ currentTime = midi.currentTime - currentTime;
+ }
+ if (tTheirTime !== midi.currentTime) {
+ tOurTime = Date.now();
+ tTheirTime = midi.currentTime;
+ }
+ } else { // paused
+ currentTime = midi.currentTime;
+ }
+ ///
+ var endTime = midi.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.0) {
+ return;
+ } else {
+ callback({
+ now: t1,
+ end: t2,
+ events: noteRegistrar
+ });
+ }
+ };
+ ///
+ requestAnimationFrame(frame);
+};
+
+// helpers
+
+midi.loadMidiFile = function(onsuccess, onprogress, onerror) {
+ try {
+ midi.replayer = new Replayer(MidiFile(midi.currentData), midi.timeWarp, null, midi.BPM);
+ midi.data = midi.replayer.getData();
+ midi.endTime = getLength();
+ ///
+ MIDI.loadPlugin({
+// instruments: midi.getFileInstruments(),
+ onsuccess: onsuccess,
+ onprogress: onprogress,
+ onerror: onerror
+ });
+ } catch(event) {
+ onerror && onerror(event);
+ }
+};
+
+midi.loadFile = function(file, onsuccess, onprogress, onerror) {
+ midi.stop();
+ if (file.indexOf('base64,') !== -1) {
+ var data = window.atob(file.split(',')[1]);
+ midi.currentData = data;
+ midi.loadMidiFile(onsuccess, onprogress, onerror);
+ } else {
+ var fetch = new XMLHttpRequest();
+ fetch.open('GET', file);
+ fetch.overrideMimeType('text/plain; charset=x-user-defined');
+ fetch.onreadystatechange = function() {
+ if (this.readyState === 4) {
+ if (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('');
+ midi.currentData = data;
+ midi.loadMidiFile(onsuccess, onprogress, onerror);
+ } else {
+ onerror && onerror('Unable to load MIDI file');
+ }
+ }
+ };
+ fetch.send();
+ }
+};
+
+midi.getFileInstruments = function() {
+ var instruments = {};
+ var programs = {};
+ for (var n = 0; n < midi.data.length; n ++) {
+ var event = midi.data[n][0].event;
+ if (event.type !== 'channel') {
+ continue;
+ }
+ var channel = event.channel;
+ switch(event.subtype) {
+ case 'controller':
+// console.log(event.channel, MIDI.defineControl[event.controllerType], event.value);
+ break;
+ case 'programChange':
+ programs[channel] = event.programNumber;
+ break;
+ case 'noteOn':
+ var program = programs[channel];
+ var gm = MIDI.GM.byId[isFinite(program) ? program : channel];
+ instruments[gm.id] = true;
+ break;
+ }
+ }
+ var ret = [];
+ for (var key in instruments) {
+ ret.push(key);
+ }
+ return ret;
+};
+
+// 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
+var scheduleTracking = function(channel, note, currentTime, offset, message, velocity, time) {
+ return setTimeout(function() {
+ var data = {
+ channel: channel,
+ note: note,
+ now: currentTime,
+ end: midi.endTime,
+ message: message,
+ velocity: velocity
+ };
+ //
+ if (message === 128) {
+ delete noteRegistrar[note];
+ } else {
+ noteRegistrar[note] = data;
+ }
+ if (onMidiEvent) {
+ onMidiEvent(data);
+ }
+ midi.currentTime = currentTime;
+ ///
+ eventQueue.shift();
+ ///
+ if (eventQueue.length < 1000) {
+ startAudio(queuedTime, true);
+ } else if (midi.currentTime === queuedTime && queuedTime < midi.endTime) { // grab next sequence
+ startAudio(queuedTime, true);
+ }
+ }, currentTime - offset);
+};
+
+var getContext = function() {
+ if (MIDI.api === 'webaudio') {
+ return MIDI.WebAudio.getContext();
+ } else {
+ midi.ctx = {currentTime: 0};
+ }
+ return midi.ctx;
+};
+
+var getLength = function() {
+ var data = midi.data;
+ var length = data.length;
+ var totalTime = 0.5;
+ for (var n = 0; n < length; n++) {
+ totalTime += data[n][1];
+ }
+ return totalTime;
+};
+
+var __now;
+var getNow = function() {
+ if (window.performance && window.performance.now) {
+ return window.performance.now();
+ } else {
+ return Date.now();
+ }
+};
+
+var startAudio = function(currentTime, fromCache, onsuccess) {
+ if (!midi.replayer) {
+ return;
+ }
+ if (!fromCache) {
+ if (typeof currentTime === 'undefined') {
+ currentTime = midi.restart;
+ }
+ ///
+ midi.playing && stopAudio();
+ midi.playing = true;
+ midi.data = midi.replayer.getData();
+ midi.endTime = getLength();
+ }
+ ///
+ var note;
+ var offset = 0;
+ var messages = 0;
+ var data = midi.data;
+ var ctx = getContext();
+ var length = data.length;
+ //
+ queuedTime = 0.5;
+ ///
+ var interval = eventQueue[0] && eventQueue[0].interval || 0;
+ var foffset = currentTime - midi.currentTime;
+ ///
+ if (MIDI.api !== 'webaudio') { // set currentTime on ctx
+ var now = getNow();
+ __now = __now || now;
+ ctx.currentTime = (now - __now) / 1000;
+ }
+ ///
+ startTime = ctx.currentTime;
+ ///
+ for (var n = 0; n < length && messages < 100; n++) {
+ var obj = data[n];
+ if ((queuedTime += obj[1]) <= currentTime) {
+ offset = queuedTime;
+ continue;
+ }
+ ///
+ currentTime = queuedTime - offset;
+ ///
+ var event = obj[0].event;
+ if (event.type !== 'channel') {
+ continue;
+ }
+ ///
+ var channelId = event.channel;
+ var channel = MIDI.channels[channelId];
+ var delay = ctx.currentTime + ((currentTime + foffset + midi.startDelay) / 1000);
+ var queueTime = queuedTime - offset + midi.startDelay;
+ switch (event.subtype) {
+ case 'controller':
+ MIDI.setController(channelId, event.controllerType, event.value, delay);
+ break;
+ case 'programChange':
+ MIDI.programChange(channelId, event.programNumber, delay);
+ break;
+ case 'pitchBend':
+ MIDI.pitchBend(channelId, event.value, delay);
+ break;
+ case 'noteOn':
+ if (channel.mute) break;
+ note = event.noteNumber - (midi.MIDIOffset || 0);
+ eventQueue.push({
+ event: event,
+ time: queueTime,
+ source: MIDI.noteOn(channelId, event.noteNumber, event.velocity, delay),
+ interval: scheduleTracking(channelId, note, queuedTime + midi.startDelay, offset - foffset, 144, event.velocity)
+ });
+ messages++;
+ break;
+ case 'noteOff':
+ if (channel.mute) break;
+ note = event.noteNumber - (midi.MIDIOffset || 0);
+ eventQueue.push({
+ event: event,
+ time: queueTime,
+ source: MIDI.noteOff(channelId, event.noteNumber, delay),
+ interval: scheduleTracking(channelId, note, queuedTime, offset - foffset, 128, 0)
+ });
+ break;
+ default:
+ break;
+ }
+ }
+ ///
+ onsuccess && onsuccess(eventQueue);
+};
+
+var stopAudio = function() {
+ var ctx = getContext();
+ midi.playing = false;
+ midi.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/src/http/static/trading-in-the-rain/MIDI.js/js/midi/plugin.audiotag.js b/src/http/static/trading-in-the-rain/MIDI.js/js/midi/plugin.audiotag.js
new file mode 100644
index 0000000..ec361c6
--- /dev/null
+++ b/src/http/static/trading-in-the-rain/MIDI.js/js/midi/plugin.audiotag.js
@@ -0,0 +1,150 @@
+/*
+ ----------------------------------------------------------------------
+ AudioTag <audio> - OGG or MPEG Soundbank
+ ----------------------------------------------------------------------
+ http://dev.w3.org/html5/spec/Overview.html#the-audio-element
+ ----------------------------------------------------------------------
+*/
+
+(function(root) { 'use strict';
+
+ window.Audio && (function() {
+ var midi = root.AudioTag = { api: 'audiotag' };
+ var noteToKey = {};
+ var volume = 127; // floating point
+ var buffer_nid = -1; // current channel
+ var audioBuffers = []; // the audio channels
+ var notesOn = []; // 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 ++) {
+ audioBuffers[nid] = new Audio();
+ }
+
+ var playChannel = function(channel, note) {
+ if (!root.channels[channel]) return;
+ var instrument = root.channels[channel].instrument;
+ var instrumentId = root.GM.byId[instrument].id;
+ var note = notes[note];
+ if (note) {
+ var instrumentNoteId = instrumentId + '' + note.id;
+ var nid = (buffer_nid + 1) % audioBuffers.length;
+ var audio = audioBuffers[nid];
+ notesOn[ nid ] = instrumentNoteId;
+ if (!root.Soundfont[instrumentId]) {
+ if (root.DEBUG) {
+ console.log('404', instrumentId);
+ }
+ return;
+ }
+ audio.src = root.Soundfont[instrumentId][note.id];
+ audio.volume = volume / 127;
+ audio.play();
+ buffer_nid = nid;
+ }
+ };
+
+ var stopChannel = function(channel, note) {
+ if (!root.channels[channel]) return;
+ var instrument = root.channels[channel].instrument;
+ var instrumentId = root.GM.byId[instrument].id;
+ var note = notes[note];
+ if (note) {
+ var instrumentNoteId = instrumentId + '' + note.id;
+ for (var i = 0, len = audioBuffers.length; i < len; i++) {
+ var nid = (i + buffer_nid + 1) % len;
+ var cId = notesOn[nid];
+ if (cId && cId == instrumentNoteId) {
+ audioBuffers[nid].pause();
+ notesOn[nid] = null;
+ return;
+ }
+ }
+ }
+ };
+
+ midi.audioBuffers = audioBuffers;
+ midi.send = function(data, delay) { };
+ midi.setController = function(channel, type, value, delay) { };
+ midi.setVolume = function(channel, n) {
+ volume = n; //- should be channel specific volume
+ };
+
+ midi.programChange = function(channel, program) {
+ root.channels[channel].instrument = program;
+ };
+
+ midi.pitchBend = function(channel, program, delay) { };
+
+ midi.noteOn = function(channel, note, velocity, delay) {
+ var id = noteToKey[note];
+ if (!notes[id]) return;
+ if (delay) {
+ return setTimeout(function() {
+ playChannel(channel, id);
+ }, delay * 1000);
+ } else {
+ playChannel(channel, id);
+ }
+ };
+
+ midi.noteOff = function(channel, note, delay) {
+// var id = noteToKey[note];
+// if (!notes[id]) return;
+// if (delay) {
+// return setTimeout(function() {
+// stopChannel(channel, id);
+// }, delay * 1000)
+// } else {
+// stopChannel(channel, id);
+// }
+ };
+
+ midi.chordOn = function(channel, chord, velocity, delay) {
+ for (var idx = 0; idx < chord.length; idx ++) {
+ var n = chord[idx];
+ var id = noteToKey[n];
+ if (!notes[id]) continue;
+ if (delay) {
+ return setTimeout(function() {
+ playChannel(channel, id);
+ }, delay * 1000);
+ } else {
+ playChannel(channel, id);
+ }
+ }
+ };
+
+ midi.chordOff = function(channel, chord, delay) {
+ for (var idx = 0; idx < chord.length; idx ++) {
+ var n = chord[idx];
+ var id = noteToKey[n];
+ if (!notes[id]) continue;
+ if (delay) {
+ return setTimeout(function() {
+ stopChannel(channel, id);
+ }, delay * 1000);
+ } else {
+ stopChannel(channel, id);
+ }
+ }
+ };
+
+ midi.stopAllNotes = function() {
+ for (var nid = 0, length = audioBuffers.length; nid < length; nid++) {
+ audioBuffers[nid].pause();
+ }
+ };
+
+ midi.connect = function(opts) {
+ root.setDefaultPlugin(midi);
+ ///
+ for (var key in root.keyToNote) {
+ noteToKey[root.keyToNote[key]] = key;
+ notes[key] = {id: key};
+ }
+ ///
+ opts.onsuccess && opts.onsuccess();
+ };
+ })();
+
+})(MIDI); \ No newline at end of file
diff --git a/src/http/static/trading-in-the-rain/MIDI.js/js/midi/plugin.webaudio.js b/src/http/static/trading-in-the-rain/MIDI.js/js/midi/plugin.webaudio.js
new file mode 100644
index 0000000..15a6f0a
--- /dev/null
+++ b/src/http/static/trading-in-the-rain/MIDI.js/js/midi/plugin.webaudio.js
@@ -0,0 +1,326 @@
+/*
+ ----------------------------------------------------------
+ Web Audio API - OGG or MPEG Soundbank
+ ----------------------------------------------------------
+ http://webaudio.github.io/web-audio-api/
+ ----------------------------------------------------------
+*/
+
+(function(root) { 'use strict';
+
+ window.AudioContext && (function() {
+ var audioContext = null; // new AudioContext();
+ var useStreamingBuffer = false; // !!audioContext.createMediaElementSource;
+ var midi = root.WebAudio = {api: 'webaudio'};
+ var ctx; // audio context
+ var sources = {};
+ var effects = {};
+ var masterVolume = 127;
+ var audioBuffers = {};
+ ///
+ midi.audioBuffers = audioBuffers;
+ midi.send = function(data, delay) { };
+ midi.setController = function(channelId, type, value, delay) { };
+
+ midi.setVolume = function(channelId, volume, delay) {
+ if (delay) {
+ setTimeout(function() {
+ masterVolume = volume;
+ }, delay * 1000);
+ } else {
+ masterVolume = volume;
+ }
+ };
+
+ midi.programChange = function(channelId, program, delay) {
+// if (delay) {
+// return setTimeout(function() {
+// var channel = root.channels[channelId];
+// channel.instrument = program;
+// }, delay);
+// } else {
+ var channel = root.channels[channelId];
+ channel.instrument = program;
+// }
+ };
+
+ midi.pitchBend = function(channelId, program, delay) {
+// if (delay) {
+// setTimeout(function() {
+// var channel = root.channels[channelId];
+// channel.pitchBend = program;
+// }, delay);
+// } else {
+ var channel = root.channels[channelId];
+ channel.pitchBend = program;
+// }
+ };
+
+ midi.noteOn = function(channelId, noteId, velocity, delay) {
+ delay = delay || 0;
+
+ /// check whether the note exists
+ var channel = root.channels[channelId];
+ var instrument = channel.instrument;
+ var bufferId = instrument + '' + noteId;
+ var buffer = audioBuffers[bufferId];
+ if (!buffer) {
+// console.log(MIDI.GM.byId[instrument].id, instrument, channelId);
+ return;
+ }
+
+ /// convert relative delay to absolute delay
+ if (delay < ctx.currentTime) {
+ delay += ctx.currentTime;
+ }
+
+ /// create audio buffer
+ if (useStreamingBuffer) {
+ var source = ctx.createMediaElementSource(buffer);
+ } else { // XMLHTTP buffer
+ var source = ctx.createBufferSource();
+ source.buffer = buffer;
+ }
+
+ /// add effects to buffer
+ if (effects) {
+ var chain = source;
+ for (var key in effects) {
+ chain.connect(effects[key].input);
+ chain = effects[key];
+ }
+ }
+
+ /// add gain + pitchShift
+ var gain = (velocity / 127) * (masterVolume / 127) * 2 - 1;
+ source.connect(ctx.destination);
+ source.playbackRate.value = 1; // pitch shift
+ source.gainNode = ctx.createGain(); // gain
+ source.gainNode.connect(ctx.destination);
+ source.gainNode.gain.value = Math.min(1.0, Math.max(-1.0, gain));
+ source.connect(source.gainNode);
+ ///
+ if (useStreamingBuffer) {
+ if (delay) {
+ return setTimeout(function() {
+ buffer.currentTime = 0;
+ buffer.play()
+ }, delay * 1000);
+ } else {
+ buffer.currentTime = 0;
+ buffer.play()
+ }
+ } else {
+ source.start(delay || 0);
+ }
+ ///
+ sources[channelId + '' + noteId] = source;
+ ///
+ return source;
+ };
+
+ midi.noteOff = function(channelId, noteId, delay) {
+ delay = delay || 0;
+
+ /// check whether the note exists
+ var channel = root.channels[channelId];
+ var instrument = channel.instrument;
+ var bufferId = instrument + '' + noteId;
+ var buffer = audioBuffers[bufferId];
+ if (buffer) {
+ if (delay < ctx.currentTime) {
+ delay += ctx.currentTime;
+ }
+ ///
+ var source = sources[channelId + '' + noteId];
+ if (source) {
+ if (source.gainNode) {
+ // @Miranet: 'the values of 0.2 and 0.3 could of course 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.0, delay + 0.3);
+ }
+ ///
+ if (useStreamingBuffer) {
+ if (delay) {
+ setTimeout(function() {
+ buffer.pause();
+ }, delay * 1000);
+ } else {
+ buffer.pause();
+ }
+ } else {
+ if (source.noteOff) {
+ source.noteOff(delay + 0.5);
+ } else {
+ source.stop(delay + 0.5);
+ }
+ }
+ ///
+ delete sources[channelId + '' + noteId];
+ ///
+ return source;
+ }
+ }
+ };
+
+ midi.chordOn = function(channel, chord, velocity, delay) {
+ var res = {};
+ for (var n = 0, note, len = chord.length; n < len; n++) {
+ res[note = chord[n]] = midi.noteOn(channel, note, velocity, delay);
+ }
+ return res;
+ };
+
+ midi.chordOff = function(channel, chord, delay) {
+ var res = {};
+ for (var n = 0, note, len = chord.length; n < len; n++) {
+ res[note = chord[n]] = midi.noteOff(channel, note, delay);
+ }
+ return res;
+ };
+
+ midi.stopAllNotes = function() {
+ for (var sid in sources) {
+ var delay = 0;
+ if (delay < ctx.currentTime) {
+ delay += ctx.currentTime;
+ }
+ var source = sources[sid];
+ source.gain.linearRampToValueAtTime(1, delay);
+ source.gain.linearRampToValueAtTime(0, delay + 0.3);
+ if (source.noteOff) { // old api
+ source.noteOff(delay + 0.3);
+ } else { // new api
+ source.stop(delay + 0.3);
+ }
+ delete sources[sid];
+ }
+ };
+
+ midi.setEffects = function(list) {
+ if (ctx.tunajs) {
+ for (var n = 0; n < list.length; n ++) {
+ var data = list[n];
+ var effect = new ctx.tunajs[data.type](data);
+ effect.connect(ctx.destination);
+ effects[data.type] = effect;
+ }
+ } else {
+ return console.log('Effects module not installed.');
+ }
+ };
+
+ midi.connect = function(opts) {
+ root.setDefaultPlugin(midi);
+ midi.setContext(ctx || createAudioContext(), opts.onsuccess);
+ };
+
+ midi.getContext = function() {
+ return ctx;
+ };
+
+ midi.setContext = function(newCtx, onload, onprogress, onerror) {
+ ctx = newCtx;
+
+ /// tuna.js effects module - https://github.com/Dinahmoe/tuna
+ if (typeof Tuna !== 'undefined' && !ctx.tunajs) {
+ ctx.tunajs = new Tuna(ctx);
+ }
+
+ /// loading audio files
+ var urls = [];
+ var notes = root.keyToNote;
+ for (var key in notes) urls.push(key);
+ ///
+ var waitForEnd = function(instrument) {
+ for (var key in bufferPending) { // has pending items
+ if (bufferPending[key]) return;
+ }
+ ///
+ if (onload) { // run onload once
+ onload();
+ onload = null;
+ }
+ };
+ ///
+ var requestAudio = function(soundfont, instrumentId, index, key) {
+ var url = soundfont[key];
+ if (url) {
+ bufferPending[instrumentId] ++;
+ loadAudio(url, function(buffer) {
+ buffer.id = key;
+ var noteId = root.keyToNote[key];
+ audioBuffers[instrumentId + '' + noteId] = buffer;
+ ///
+ if (-- bufferPending[instrumentId] === 0) {
+ var percent = index / 87;
+// console.log(MIDI.GM.byId[instrumentId], 'processing: ', percent);
+ soundfont.isLoaded = true;
+ waitForEnd(instrument);
+ }
+ }, function(err) {
+ // console.log(err);
+ });
+ }
+ };
+ ///
+ var bufferPending = {};
+ for (var instrument in root.Soundfont) {
+ var soundfont = root.Soundfont[instrument];
+ if (soundfont.isLoaded) {
+ continue;
+ }
+ ///
+ var synth = root.GM.byName[instrument];
+ var instrumentId = synth.number;
+ ///
+ bufferPending[instrumentId] = 0;
+ ///
+ for (var index = 0; index < urls.length; index++) {
+ var key = urls[index];
+ requestAudio(soundfont, instrumentId, index, key);
+ }
+ }
+ ///
+ setTimeout(waitForEnd, 1);
+ };
+
+ /* Load audio file: streaming | base64 | arraybuffer
+ ---------------------------------------------------------------------- */
+ function loadAudio(url, onload, onerror) {
+ if (useStreamingBuffer) {
+ var audio = new Audio();
+ audio.src = url;
+ audio.controls = false;
+ audio.autoplay = false;
+ audio.preload = false;
+ audio.addEventListener('canplay', function() {
+ onload && onload(audio);
+ });
+ audio.addEventListener('error', function(err) {
+ onerror && onerror(err);
+ });
+ document.body.appendChild(audio);
+ } else if (url.indexOf('data:audio') === 0) { // Base64 string
+ var base64 = url.split(',')[1];
+ var buffer = Base64Binary.decodeArrayBuffer(base64);
+ ctx.decodeAudioData(buffer, onload, onerror);
+ } else { // XMLHTTP buffer
+ var request = new XMLHttpRequest();
+ request.open('GET', url, true);
+ request.responseType = 'arraybuffer';
+ request.onload = function() {
+ ctx.decodeAudioData(request.response, onload, onerror);
+ };
+ request.send();
+ }
+ };
+
+ function createAudioContext() {
+ return new (window.AudioContext || window.webkitAudioContext)();
+ };
+ })();
+})(MIDI); \ No newline at end of file
diff --git a/src/http/static/trading-in-the-rain/MIDI.js/js/midi/plugin.webmidi.js b/src/http/static/trading-in-the-rain/MIDI.js/js/midi/plugin.webmidi.js
new file mode 100644
index 0000000..33e244b
--- /dev/null
+++ b/src/http/static/trading-in-the-rain/MIDI.js/js/midi/plugin.webmidi.js
@@ -0,0 +1,93 @@
+/*
+ ----------------------------------------------------------------------
+ Web MIDI API - Native Soundbanks
+ ----------------------------------------------------------------------
+ http://webaudio.github.io/web-midi-api/
+ ----------------------------------------------------------------------
+*/
+
+(function(root) { 'use strict';
+
+ var plugin = null;
+ var output = null;
+ var channels = [];
+ var midi = root.WebMIDI = {api: 'webmidi'};
+ midi.send = function(data, delay) { // set channel volume
+ output.send(data, delay * 1000);
+ };
+
+ midi.setController = function(channel, type, value, delay) {
+ output.send([channel, type, value], delay * 1000);
+ };
+
+ midi.setVolume = function(channel, volume, delay) { // set channel volume
+ output.send([0xB0 + channel, 0x07, volume], delay * 1000);
+ };
+
+ midi.programChange = function(channel, program, delay) { // change patch (instrument)
+ output.send([0xC0 + channel, program], delay * 1000);
+ };
+
+ midi.pitchBend = function(channel, program, delay) { // pitch bend
+ output.send([0xE0 + channel, program], delay * 1000);
+ };
+
+ midi.noteOn = function(channel, note, velocity, delay) {
+ output.send([0x90 + channel, note, velocity], delay * 1000);
+ };
+
+ midi.noteOff = function(channel, note, delay) {
+ output.send([0x80 + channel, note, 0], delay * 1000);
+ };
+
+ midi.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);
+ }
+ };
+
+ midi.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);
+ }
+ };
+
+ midi.stopAllNotes = function() {
+ output.cancel();
+ for (var channel = 0; channel < 16; channel ++) {
+ output.send([0xB0 + channel, 0x7B, 0]);
+ }
+ };
+
+ midi.connect = function(opts) {
+ root.setDefaultPlugin(midi);
+ var errFunction = function(err) { // well at least we tried!
+ if (window.AudioContext) { // Chrome
+ opts.api = 'webaudio';
+ } else if (window.Audio) { // Firefox
+ opts.api = 'audiotag';
+ } else { // no support
+ return;
+ }
+ root.loadPlugin(opts);
+ };
+ ///
+ navigator.requestMIDIAccess().then(function(access) {
+ plugin = access;
+ var pluginOutputs = plugin.outputs;
+ if (typeof pluginOutputs == 'function') { // Chrome pre-43
+ output = pluginOutputs()[0];
+ } else { // Chrome post-43
+ output = pluginOutputs[0];
+ }
+ if (output === undefined) { // nothing there...
+ errFunction();
+ } else {
+ opts.onsuccess && opts.onsuccess();
+ }
+ }, errFunction);
+ };
+
+})(MIDI); \ No newline at end of file
diff --git a/src/http/static/trading-in-the-rain/MIDI.js/js/midi/synesthesia.js b/src/http/static/trading-in-the-rain/MIDI.js/js/midi/synesthesia.js
new file mode 100644
index 0000000..709cb91
--- /dev/null
+++ b/src/http/static/trading-in-the-rain/MIDI.js/js/midi/synesthesia.js
@@ -0,0 +1,320 @@
+/*
+ ----------------------------------------------------------
+ MIDI.Synesthesia : 0.3.1 : 2012-01-06
+ ----------------------------------------------------------
+ Peacock: “Instruments to perform color-music: Two centuries of technological experimentation,” Leonardo, 21 (1988), 397-406.
+ Gerstner: Karl Gerstner, The Forms of Color 1986
+ Klein: Colour-Music: The art of light, London: Crosby Lockwood and Son, 1927.
+ Jameson: “Visual music in a visual programming language,” IEEE Symposium on Visual Languages, 1999, 111-118.
+ Helmholtz: Treatise on Physiological Optics, New York: Dover Books, 1962
+ Jones: The art of light & color, New York: Van Nostrand Reinhold, 1972
+ ----------------------------------------------------------
+ Reference: http://rhythmiclight.com/archives/ideas/colorscales.html
+ ----------------------------------------------------------
+*/
+
+if (typeof MIDI === 'undefined') var MIDI = {};
+
+MIDI.Synesthesia = MIDI.Synesthesia || {};
+
+(function(root) {
+ root.data = {
+ 'Isaac Newton (1704)': {
+ format: 'HSL',
+ ref: 'Gerstner, p.167',
+ english: ['red',null,'orange',null,'yellow','green',null,'blue',null,'indigo',null,'violet'],
+ 0: [ 0, 96, 51 ], // C
+ 1: [ 0, 0, 0 ], // C#
+ 2: [ 29, 94, 52 ], // D
+ 3: [ 0, 0, 0 ], // D#
+ 4: [ 60, 90, 60 ], // E
+ 5: [ 135, 76, 32 ], // F
+ 6: [ 0, 0, 0 ], // F#
+ 7: [ 248, 82, 28 ], // G
+ 8: [ 0, 0, 0 ], // G#
+ 9: [ 302, 88, 26 ], // A
+ 10: [ 0, 0, 0 ], // A#
+ 11: [ 325, 84, 46 ] // B
+ },
+ 'Louis Bertrand Castel (1734)': {
+ format: 'HSL',
+ ref: 'Peacock, p.400',
+ english: ['blue','blue-green','green','olive green','yellow','yellow-orange','orange','red','crimson','violet','agate','indigo'],
+ 0: [ 248, 82, 28 ],
+ 1: [ 172, 68, 34 ],
+ 2: [ 135, 76, 32 ],
+ 3: [ 79, 59, 36 ],
+ 4: [ 60, 90, 60 ],
+ 5: [ 49, 90, 60 ],
+ 6: [ 29, 94, 52 ],
+ 7: [ 360, 96, 51 ],
+ 8: [ 1, 89, 33 ],
+ 9: [ 325, 84, 46 ],
+ 10: [ 273, 80, 27 ],
+ 11: [ 302, 88, 26 ]
+ },
+ 'George Field (1816)': {
+ format: 'HSL',
+ ref: 'Klein, p.69',
+ english: ['blue',null,'purple',null,'red','orange',null,'yellow',null,'yellow green',null,'green'],
+ 0: [ 248, 82, 28 ],
+ 1: [ 0, 0, 0 ],
+ 2: [ 302, 88, 26 ],
+ 3: [ 0, 0, 0 ],
+ 4: [ 360, 96, 51 ],
+ 5: [ 29, 94, 52 ],
+ 6: [ 0, 0, 0 ],
+ 7: [ 60, 90, 60 ],
+ 8: [ 0, 0, 0 ],
+ 9: [ 79, 59, 36 ],
+ 10: [ 0, 0, 0 ],
+ 11: [ 135, 76, 32 ]
+ },
+ 'D. D. Jameson (1844)': {
+ format: 'HSL',
+ ref: 'Jameson, p.12',
+ english: ['red','red-orange','orange','orange-yellow','yellow','green','green-blue','blue','blue-purple','purple','purple-violet','violet'],
+ 0: [ 360, 96, 51 ],
+ 1: [ 14, 91, 51 ],
+ 2: [ 29, 94, 52 ],
+ 3: [ 49, 90, 60 ],
+ 4: [ 60, 90, 60 ],
+ 5: [ 135, 76, 32 ],
+ 6: [ 172, 68, 34 ],
+ 7: [ 248, 82, 28 ],
+ 8: [ 273, 80, 27 ],
+ 9: [ 302, 88, 26 ],
+ 10: [ 313, 78, 37 ],
+ 11: [ 325, 84, 46 ]
+ },
+ 'Theodor Seemann (1881)': {
+ format: 'HSL',
+ ref: 'Klein, p.86',
+ english: ['carmine','scarlet','orange','yellow-orange','yellow','green','green blue','blue','indigo','violet','brown','black'],
+ 0: [ 0, 58, 26 ],
+ 1: [ 360, 96, 51 ],
+ 2: [ 29, 94, 52 ],
+ 3: [ 49, 90, 60 ],
+ 4: [ 60, 90, 60 ],
+ 5: [ 135, 76, 32 ],
+ 6: [ 172, 68, 34 ],
+ 7: [ 248, 82, 28 ],
+ 8: [ 302, 88, 26 ],
+ 9: [ 325, 84, 46 ],
+ 10: [ 0, 58, 26 ],
+ 11: [ 0, 0, 3 ]
+ },
+ 'A. Wallace Rimington (1893)': {
+ format: 'HSL',
+ ref: 'Peacock, p.402',
+ english: ['deep red','crimson','orange-crimson','orange','yellow','yellow-green','green','blueish green','blue-green','indigo','deep blue','violet'],
+ 0: [ 360, 96, 51 ],
+ 1: [ 1, 89, 33 ],
+ 2: [ 14, 91, 51 ],
+ 3: [ 29, 94, 52 ],
+ 4: [ 60, 90, 60 ],
+ 5: [ 79, 59, 36 ],
+ 6: [ 135, 76, 32 ],
+ 7: [ 163, 62, 40 ],
+ 8: [ 172, 68, 34 ],
+ 9: [ 302, 88, 26 ],
+ 10: [ 248, 82, 28 ],
+ 11: [ 325, 84, 46 ]
+ },
+ 'Bainbridge Bishop (1893)': {
+ format: 'HSL',
+ ref: 'Bishop, p.11',
+ english: ['red','orange-red or scarlet','orange','gold or yellow-orange','yellow or green-gold','yellow-green','green','greenish-blue or aquamarine','blue','indigo or violet-blue','violet','violet-red','red'],
+ 0: [ 360, 96, 51 ],
+ 1: [ 1, 89, 33 ],
+ 2: [ 29, 94, 52 ],
+ 3: [ 50, 93, 52 ],
+ 4: [ 60, 90, 60 ],
+ 5: [ 73, 73, 55 ],
+ 6: [ 135, 76, 32 ],
+ 7: [ 163, 62, 40 ],
+ 8: [ 302, 88, 26 ],
+ 9: [ 325, 84, 46 ],
+ 10: [ 343, 79, 47 ],
+ 11: [ 360, 96, 51 ]
+ },
+ 'H. von Helmholtz (1910)': {
+ format: 'HSL',
+ ref: 'Helmholtz, p.22',
+ english: ['yellow','green','greenish blue','cayan-blue','indigo blue','violet','end of red','red','red','red','red orange','orange'],
+ 0: [ 60, 90, 60 ],
+ 1: [ 135, 76, 32 ],
+ 2: [ 172, 68, 34 ],
+ 3: [ 211, 70, 37 ],
+ 4: [ 302, 88, 26 ],
+ 5: [ 325, 84, 46 ],
+ 6: [ 330, 84, 34 ],
+ 7: [ 360, 96, 51 ],
+ 8: [ 10, 91, 43 ],
+ 9: [ 10, 91, 43 ],
+ 10: [ 8, 93, 51 ],
+ 11: [ 28, 89, 50 ]
+ },
+ 'Alexander Scriabin (1911)': {
+ format: 'HSL',
+ ref: 'Jones, p.104',
+ english: ['red','violet','yellow','steely with the glint of metal','pearly blue the shimmer of moonshine','dark red','bright blue','rosy orange','purple','green','steely with a glint of metal','pearly blue the shimmer of moonshine'],
+ 0: [ 360, 96, 51 ],
+ 1: [ 325, 84, 46 ],
+ 2: [ 60, 90, 60 ],
+ 3: [ 245, 21, 43 ],
+ 4: [ 211, 70, 37 ],
+ 5: [ 1, 89, 33 ],
+ 6: [ 248, 82, 28 ],
+ 7: [ 29, 94, 52 ],
+ 8: [ 302, 88, 26 ],
+ 9: [ 135, 76, 32 ],
+ 10: [ 245, 21, 43 ],
+ 11: [ 211, 70, 37 ]
+ },
+ 'Adrian Bernard Klein (1930)': {
+ format: 'HSL',
+ ref: 'Klein, p.209',
+ english: ['dark red','red','red orange','orange','yellow','yellow green','green','blue-green','blue','blue violet','violet','dark violet'],
+ 0: [ 0, 91, 40 ],
+ 1: [ 360, 96, 51 ],
+ 2: [ 14, 91, 51 ],
+ 3: [ 29, 94, 52 ],
+ 4: [ 60, 90, 60 ],
+ 5: [ 73, 73, 55 ],
+ 6: [ 135, 76, 32 ],
+ 7: [ 172, 68, 34 ],
+ 8: [ 248, 82, 28 ],
+ 9: [ 292, 70, 31 ],
+ 10: [ 325, 84, 46 ],
+ 11: [ 330, 84, 34 ]
+ },
+ 'August Aeppli (1940)': {
+ format: 'HSL',
+ ref: 'Gerstner, p.169',
+ english: ['red',null,'orange',null,'yellow',null,'green','blue-green',null,'ultramarine blue','violet','purple'],
+ 0: [ 0, 96, 51 ],
+ 1: [ 0, 0, 0 ],
+ 2: [ 29, 94, 52 ],
+ 3: [ 0, 0, 0 ],
+ 4: [ 60, 90, 60 ],
+ 5: [ 0, 0, 0 ],
+ 6: [ 135, 76, 32 ],
+ 7: [ 172, 68, 34 ],
+ 8: [ 0, 0, 0 ],
+ 9: [ 211, 70, 37 ],
+ 10: [ 273, 80, 27 ],
+ 11: [ 302, 88, 26 ]
+ },
+ 'I. J. Belmont (1944)': {
+ ref: 'Belmont, p.226',
+ english: ['red','red-orange','orange','yellow-orange','yellow','yellow-green','green','blue-green','blue','blue-violet','violet','red-violet'],
+ 0: [ 360, 96, 51 ],
+ 1: [ 14, 91, 51 ],
+ 2: [ 29, 94, 52 ],
+ 3: [ 50, 93, 52 ],
+ 4: [ 60, 90, 60 ],
+ 5: [ 73, 73, 55 ],
+ 6: [ 135, 76, 32 ],
+ 7: [ 172, 68, 34 ],
+ 8: [ 248, 82, 28 ],
+ 9: [ 313, 78, 37 ],
+ 10: [ 325, 84, 46 ],
+ 11: [ 338, 85, 37 ]
+ },
+ 'Steve Zieverink (2004)': {
+ format: 'HSL',
+ ref: 'Cincinnati Contemporary Art Center',
+ english: ['yellow-green','green','blue-green','blue','indigo','violet','ultra violet','infra red','red','orange','yellow-white','yellow'],
+ 0: [ 73, 73, 55 ],
+ 1: [ 135, 76, 32 ],
+ 2: [ 172, 68, 34 ],
+ 3: [ 248, 82, 28 ],
+ 4: [ 302, 88, 26 ],
+ 5: [ 325, 84, 46 ],
+ 6: [ 326, 79, 24 ],
+ 7: [ 1, 89, 33 ],
+ 8: [ 360, 96, 51 ],
+ 9: [ 29, 94, 52 ],
+ 10: [ 62, 78, 74 ],
+ 11: [ 60, 90, 60 ]
+ },
+ 'Circle of Fifths (Johnston 2003)': {
+ format: 'RGB',
+ ref: 'Joseph Johnston',
+ english: ['yellow', 'blue', 'orange', 'teal', 'red', 'green', 'purple', 'light orange', 'light blue', 'dark orange', 'dark green', 'violet' ],
+ 0: [ 255, 255, 0 ],
+ 1: [ 50, 0, 255 ],
+ 2: [ 255, 150, 0 ],
+ 3: [ 0, 210, 180 ],
+ 4: [ 255, 0, 0 ],
+ 5: [ 130, 255, 0 ],
+ 6: [ 150, 0, 200 ],
+ 7: [ 255, 195, 0 ],
+ 8: [ 30, 130, 255 ],
+ 9: [ 255, 100, 0 ],
+ 10: [ 0, 200, 0 ],
+ 11: [ 225, 0, 225 ]
+ },
+ 'Circle of Fifths (Wheatman 2002)': {
+ format: 'HEX',
+ ref: 'Stuart Wheatman', // http://www.valleysfamilychurch.org/
+ english: [],
+ data: ['#122400', '#2E002E', '#002914', '#470000', '#002142', '#2E2E00', '#290052', '#003D00', '#520029', '#003D3D', '#522900', '#000080', '#244700', '#570057', '#004D26', '#7A0000', '#003B75', '#4C4D00', '#47008F', '#006100', '#850042', '#005C5C', '#804000', '#0000C7', '#366B00', '#80007F', '#00753B', '#B80000', '#0057AD', '#6B6B00', '#6600CC', '#008A00', '#B8005C', '#007F80', '#B35900', '#2424FF', '#478F00', '#AD00AD', '#00994D', '#F00000', '#0073E6', '#8F8F00', '#8A14FF', '#00AD00', '#EB0075', '#00A3A3', '#E07000', '#6B6BFF', '#5CB800', '#DB00DB', '#00C261', '#FF5757', '#3399FF', '#ADAD00', '#B56BFF', '#00D600', '#FF57AB', '#00C7C7', '#FF9124', '#9999FF', '#6EDB00', '#FF29FF', '#00E070', '#FF9999', '#7ABDFF', '#D1D100', '#D1A3FF', '#00FA00', '#FFA3D1', '#00E5E6', '#FFC285', '#C2C2FF', '#80FF00', '#FFA8FF', '#00E070', '#FFCCCC', '#C2E0FF', '#F0F000', '#EBD6FF', '#ADFFAD', '#FFD6EB', '#8AFFFF', '#FFEBD6', '#EBEBFF', '#E0FFC2', '#FFEBFF', '#E5FFF2', '#FFF5F5'] }
+ };
+
+ root.map = function(type) {
+ var data = {};
+ var blend = function(a, b) {
+ return [ // blend two colors and round results
+ (a[0] * 0.5 + b[0] * 0.5 + 0.5) >> 0,
+ (a[1] * 0.5 + b[1] * 0.5 + 0.5) >> 0,
+ (a[2] * 0.5 + b[2] * 0.5 + 0.5) >> 0
+ ];
+ };
+ ///
+ var syn = root.data;
+ var colors = syn[type] || syn['D. D. Jameson (1844)'];
+ for (var note = 0, pclr, H, S, L; note <= 88; note ++) { // creates mapping for 88 notes
+ if (colors.data) {
+ data[note] = {
+ hsl: colors.data[note],
+ hex: colors.data[note]
+ };
+ } else {
+ var clr = colors[(note + 9) % 12];
+ ///
+ switch(colors.format) {
+ case 'RGB':
+ clr = Color.Space(clr, 'RGB>HSL');
+ H = clr.H >> 0;
+ S = clr.S >> 0;
+ L = clr.L >> 0;
+ break;
+ case 'HSL':
+ H = clr[0];
+ S = clr[1];
+ L = clr[2];
+ break;
+ }
+ ///
+ if (H === S && S === L) { // note color is unset
+ clr = blend(pclr, colors[(note + 10) % 12]);
+ }
+ ///
+// var amount = L / 10;
+// var octave = note / 12 >> 0;
+// var octaveLum = L + amount * octave - 3.0 * amount; // map luminance to octave
+ ///
+ data[note] = {
+ hsl: 'hsla(' + H + ',' + S + '%,' + L + '%, 1)',
+ hex: Color.Space({H: H, S: S, L: L}, 'HSL>RGB>HEX>W3')
+ };
+ ///
+ pclr = clr;
+ }
+ }
+ return data;
+ };
+
+})(MIDI.Synesthesia); \ No newline at end of file
diff --git a/src/http/static/trading-in-the-rain/MIDI.js/js/util/dom_request_script.js b/src/http/static/trading-in-the-rain/MIDI.js/js/util/dom_request_script.js
new file mode 100644
index 0000000..8f1ea5a
--- /dev/null
+++ b/src/http/static/trading-in-the-rain/MIDI.js/js/util/dom_request_script.js
@@ -0,0 +1,225 @@
+/*
+ -----------------------------------------------------------
+ dom.loadScript.js : 0.1.4 : 2014/02/12 : http://mudcu.be
+ -----------------------------------------------------------
+ Copyright 2011-2014 Mudcube. All rights reserved.
+ -----------------------------------------------------------
+ /// No verification
+ dom.loadScript.add("../js/jszip/jszip.js");
+ /// Strict loading order and verification.
+ dom.loadScript.add({
+ strictOrder: true,
+ urls: [
+ {
+ url: "../js/jszip/jszip.js",
+ verify: "JSZip",
+ onsuccess: function() {
+ console.log(1)
+ }
+ },
+ {
+ url: "../inc/downloadify/js/swfobject.js",
+ verify: "swfobject",
+ onsuccess: function() {
+ console.log(2)
+ }
+ }
+ ],
+ onsuccess: function() {
+ console.log(3)
+ }
+ });
+ /// Just verification.
+ dom.loadScript.add({
+ url: "../js/jszip/jszip.js",
+ verify: "JSZip",
+ onsuccess: function() {
+ console.log(1)
+ }
+ });
+*/
+
+if (typeof(dom) === "undefined") var dom = {};
+
+(function() { "use strict";
+
+dom.loadScript = function() {
+ this.loaded = {};
+ this.loading = {};
+ return this;
+};
+
+dom.loadScript.prototype.add = function(config) {
+ var that = this;
+ if (typeof(config) === "string") {
+ config = { url: config };
+ }
+ var urls = config.urls;
+ if (typeof(urls) === "undefined") {
+ urls = [{
+ url: config.url,
+ verify: config.verify
+ }];
+ }
+ /// adding the elements to the head
+ var doc = document.getElementsByTagName("head")[0];
+ ///
+ var testElement = function(element, test) {
+ if (that.loaded[element.url]) return;
+ if (test && globalExists(test) === false) return;
+ that.loaded[element.url] = true;
+ //
+ if (that.loading[element.url]) that.loading[element.url]();
+ delete that.loading[element.url];
+ //
+ if (element.onsuccess) element.onsuccess();
+ if (typeof(getNext) !== "undefined") getNext();
+ };
+ ///
+ var hasError = false;
+ var batchTest = [];
+ var addElement = function(element) {
+ if (typeof(element) === "string") {
+ element = {
+ url: element,
+ verify: config.verify
+ };
+ }
+ if (/([\w\d.\[\]\'\"])$/.test(element.verify)) { // check whether its a variable reference
+ var verify = element.test = element.verify;
+ if (typeof(verify) === "object") {
+ for (var n = 0; n < verify.length; n ++) {
+ batchTest.push(verify[n]);
+ }
+ } else {
+ batchTest.push(verify);
+ }
+ }
+ if (that.loaded[element.url]) 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() {
+ hasError = true;
+ delete that.loading[element.url];
+ if (typeof(element.test) === "object") {
+ for (var key in element.test) {
+ removeTest(element.test[key]);
+ }
+ } else {
+ removeTest(element.test);
+ }
+ };
+ script.setAttribute("type", "text/javascript");
+ script.setAttribute("src", element.url);
+ doc.appendChild(script);
+ that.loading[element.url] = function() {};
+ };
+ /// checking to see whether everything loaded properly
+ var removeTest = function(test) {
+ var ret = [];
+ for (var n = 0; n < batchTest.length; n ++) {
+ if (batchTest[n] === test) continue;
+ ret.push(batchTest[n]);
+ }
+ batchTest = ret;
+ };
+ var onLoad = function(element) {
+ if (element) {
+ testElement(element, element.test);
+ } else {
+ for (var n = 0; n < urls.length; n ++) {
+ testElement(urls[n], urls[n].test);
+ }
+ }
+ var istrue = true;
+ for (var n = 0; n < batchTest.length; n ++) {
+ if (globalExists(batchTest[n]) === false) {
+ istrue = false;
+ }
+ }
+ if (!config.strictOrder && istrue) { // finished loading all the requested scripts
+ if (hasError) {
+ if (config.error) {
+ config.error();
+ }
+ } else if (config.onsuccess) {
+ config.onsuccess();
+ }
+ } 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 (!urls[ID]) { // all elements are loaded
+ if (hasError) {
+ if (config.error) {
+ config.error();
+ }
+ } else if (config.onsuccess) {
+ config.onsuccess();
+ }
+ } else { // loading new script
+ var element = urls[ID];
+ var url = element.url;
+ if (that.loading[url]) { // already loading from another call (attach to event)
+ that.loading[url] = function() {
+ if (element.onsuccess) element.onsuccess();
+ getNext();
+ }
+ } else if (!that.loaded[url]) { // create script element
+ addElement(element);
+ onLoad(element);
+ } else { // it's already been successfully loaded
+ getNext();
+ }
+ }
+ };
+ getNext();
+ } else { // loose ordering
+ for (var ID = 0; ID < urls.length; ID ++) {
+ addElement(urls[ID]);
+ onLoad(urls[ID]);
+ }
+ }
+};
+
+dom.loadScript = new dom.loadScript();
+
+var globalExists = function(path, root) {
+ try {
+ path = path.split('"').join('').split("'").join('').split(']').join('').split('[').join('.');
+ var parts = path.split(".");
+ var length = parts.length;
+ var object = root || window;
+ for (var n = 0; n < length; n ++) {
+ var key = parts[n];
+ if (object[key] == null) {
+ return false;
+ } else { //
+ object = object[key];
+ }
+ }
+ return true;
+ } catch(e) {
+ return false;
+ }
+};
+
+})();
+
+/// For NodeJS
+if (typeof (module) !== "undefined" && module.exports) {
+ module.exports = dom.loadScript;
+} \ No newline at end of file
diff --git a/src/http/static/trading-in-the-rain/MIDI.js/js/util/dom_request_xhr.js b/src/http/static/trading-in-the-rain/MIDI.js/js/util/dom_request_xhr.js
new file mode 100644
index 0000000..84c208e
--- /dev/null
+++ b/src/http/static/trading-in-the-rain/MIDI.js/js/util/dom_request_xhr.js
@@ -0,0 +1,146 @@
+/*
+ ----------------------------------------------------------
+ util/Request : 0.1.1 : 2015-03-26
+ ----------------------------------------------------------
+ util.request({
+ url: './dir/something.extension',
+ data: 'test!',
+ format: 'text', // text | xml | json | binary
+ responseType: 'text', // arraybuffer | blob | document | json | text
+ headers: {},
+ withCredentials: true, // true | false
+ ///
+ onerror: function(evt, percent) {
+ console.log(evt);
+ },
+ onsuccess: function(evt, responseText) {
+ console.log(responseText);
+ },
+ onprogress: function(evt, percent) {
+ percent = Math.round(percent * 100);
+ loader.create('thread', 'loading... ', percent);
+ }
+ });
+*/
+
+if (typeof MIDI === 'undefined') MIDI = {};
+
+(function(root) {
+
+ var util = root.util || (root.util = {});
+
+ util.request = function(opts, onsuccess, onerror, onprogress) { 'use strict';
+ if (typeof opts === 'string') opts = {url: opts};
+ ///
+ var data = opts.data;
+ var url = opts.url;
+ var method = opts.method || (opts.data ? 'POST' : 'GET');
+ var format = opts.format;
+ var headers = opts.headers;
+ var responseType = opts.responseType;
+ var withCredentials = opts.withCredentials || false;
+ ///
+ var onsuccess = onsuccess || opts.onsuccess;
+ var onerror = onerror || opts.onerror;
+ var onprogress = onprogress || opts.onprogress;
+ ///
+ if (typeof NodeFS !== 'undefined' && root.loc.isLocalUrl(url)) {
+ NodeFS.readFile(url, 'utf8', function(err, res) {
+ if (err) {
+ onerror && onerror(err);
+ } else {
+ onsuccess && onsuccess({responseText: res});
+ }
+ });
+ return;
+ }
+ ///
+ var xhr = new XMLHttpRequest();
+ xhr.open(method, url, true);
+ ///
+ if (headers) {
+ for (var type in headers) {
+ xhr.setRequestHeader(type, headers[type]);
+ }
+ } else if (data) { // set the default headers for POST
+ xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
+ }
+ if (format === 'binary') { //- default to responseType="blob" when supported
+ if (xhr.overrideMimeType) {
+ xhr.overrideMimeType('text/plain; charset=x-user-defined');
+ }
+ }
+ if (responseType) {
+ xhr.responseType = responseType;
+ }
+ if (withCredentials) {
+ xhr.withCredentials = 'true';
+ }
+ if (onerror && 'onerror' in xhr) {
+ xhr.onerror = onerror;
+ }
+ if (onprogress && xhr.upload && 'onprogress' in xhr.upload) {
+ if (data) {
+ xhr.upload.onprogress = function(evt) {
+ onprogress.call(xhr, evt, event.loaded / event.total);
+ };
+ } else {
+ xhr.addEventListener('progress', function(evt) {
+ var totalBytes = 0;
+ if (evt.lengthComputable) {
+ totalBytes = evt.total;
+ } else if (xhr.totalBytes) {
+ totalBytes = xhr.totalBytes;
+ } else {
+ var rawBytes = parseInt(xhr.getResponseHeader('Content-Length-Raw'));
+ if (isFinite(rawBytes)) {
+ xhr.totalBytes = totalBytes = rawBytes;
+ } else {
+ return;
+ }
+ }
+ onprogress.call(xhr, evt, evt.loaded / totalBytes);
+ });
+ }
+ }
+ ///
+ xhr.onreadystatechange = function(evt) {
+ if (xhr.readyState === 4) { // The request is complete
+ if (xhr.status === 200 || // Response OK
+ xhr.status === 304 || // Not Modified
+ xhr.status === 308 || // Permanent Redirect
+ xhr.status === 0 && root.client.cordova // Cordova quirk
+ ) {
+ if (onsuccess) {
+ var res;
+ if (format === 'xml') {
+ res = evt.target.responseXML;
+ } else if (format === 'text') {
+ res = evt.target.responseText;
+ } else if (format === 'json') {
+ try {
+ res = JSON.parse(evt.target.response);
+ } catch(err) {
+ onerror && onerror.call(xhr, evt);
+ }
+ }
+ ///
+ onsuccess.call(xhr, evt, res);
+ }
+ } else {
+ onerror && onerror.call(xhr, evt);
+ }
+ }
+ };
+ xhr.send(data);
+ return xhr;
+ };
+
+ /// NodeJS
+ if (typeof module !== 'undefined' && module.exports) {
+ var NodeFS = require('fs');
+ XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;
+ module.exports = root.util.request;
+ }
+
+})(MIDI); \ No newline at end of file