diff options
author | Brian Picciano <mediocregopher@gmail.com> | 2021-07-31 11:35:39 -0600 |
---|---|---|
committer | Brian Picciano <mediocregopher@gmail.com> | 2021-07-31 11:35:39 -0600 |
commit | f1998c321a4eec6d75b58d84aa8610971bf21979 (patch) | |
tree | a90783eb296cc50e1c48433f241624f26b99be27 /static/src/assets/trading-in-the-rain/MIDI.js/js/midi/player.js | |
parent | 03a35dcc38b055f15df160bd300969e3b703d4b1 (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.js | 380 |
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 |