summaryrefslogtreecommitdiff
path: root/static/src/assets/trading-in-the-rain/MIDI.js/js/midi/player.js
diff options
context:
space:
mode:
authorBrian Picciano <mediocregopher@gmail.com>2021-07-31 11:35:39 -0600
committerBrian Picciano <mediocregopher@gmail.com>2021-07-31 11:35:39 -0600
commitf1998c321a4eec6d75b58d84aa8610971bf21979 (patch)
treea90783eb296cc50e1c48433f241624f26b99be27 /static/src/assets/trading-in-the-rain/MIDI.js/js/midi/player.js
parent03a35dcc38b055f15df160bd300969e3b703d4b1 (diff)
move static files into static sub-dir, refactor nix a bit
Diffstat (limited to 'static/src/assets/trading-in-the-rain/MIDI.js/js/midi/player.js')
-rw-r--r--static/src/assets/trading-in-the-rain/MIDI.js/js/midi/player.js380
1 files changed, 380 insertions, 0 deletions
diff --git a/static/src/assets/trading-in-the-rain/MIDI.js/js/midi/player.js b/static/src/assets/trading-in-the-rain/MIDI.js/js/midi/player.js
new file mode 100644
index 0000000..08f298f
--- /dev/null
+++ b/static/src/assets/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