summaryrefslogtreecommitdiff
path: root/src/http/static/trading-in-the-rain/MIDI.js/js/midi
diff options
context:
space:
mode:
Diffstat (limited to 'src/http/static/trading-in-the-rain/MIDI.js/js/midi')
-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
8 files changed, 1730 insertions, 0 deletions
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