diff --git a/README.md b/README.md
index 6ea6682..11bd49c 100644
--- a/README.md
+++ b/README.md
@@ -11,5 +11,5 @@ notation for real musicians
here's an inventory of "stuff we need to find first":
- [x] find out "music note fonts" Finale uses and get ahold of a .ttf or .otf file of one of them (I like the Jazz font)~~
-- [ ] an open source alternative to garritan instruments for playback. MIDIjs seems to have a bunch of options (http://galacticmilk.com/midi-js/) - abc.js looks interesting (https://github.com/paulrosen/abcjs) - blog post on midi/browser problems (http://abcnotation.com/blog/2013/04/10/the-problem-with-midi/)
+- [ ] an open source alternative to garritan instruments for playback. MIDIjs seems to have a bunch of options (http://galacticmilk.com/midi-js/) -
- [ ] setup Gulp to compile JSX files with gulp-nodemon and gulp-react
diff --git a/app/demo.mp3 b/app/demo.mp3
new file mode 100644
index 0000000..cb1594a
Binary files /dev/null and b/app/demo.mp3 differ
diff --git a/app/index.html b/app/index.html
index 2630926..55f8aa2 100644
--- a/app/index.html
+++ b/app/index.html
@@ -3,11 +3,7 @@
Hello World!
-
-
-
-
-
+
diff --git a/app/index.js b/app/index.js
index 685a941..8c69e1a 100644
--- a/app/index.js
+++ b/app/index.js
@@ -7,75 +7,7 @@ const render = ReactDOM.render
render(React.createElement('div', {}, 'this is just a
tag rendered in React. you can open command line with ALT+COMMAND+I!'), document.body.firstElementChild)
-var midiAccess;
-var midiPlayer;
-var outputs;
-// Requesting Midi Access
-navigator.requestMIDIAccess().then(function onsuccesscallback(access) {
- outputs = access.outputs;
- var iter = outputs.values();
- var output;
- while(output = iter.next()) {
- if(output.done) {
- break;
- }
- var opt = document.createElement('option');
- opt.value = output.value.id;
- opt.text = output.value.name;
- document.getElementById('inputportselector').add(opt);
- }
-},function onerrorcallback(err) {
- console.log('uh-oh! Something went wrong! Error code: ' + err.code);
-});
+var sound = new Howl({
+ urls: ['./demo.mp3']
+}).play();
-// File handlers
-function readFile(input) {
- var reader = new FileReader();
- reader.readAsArrayBuffer(input.files[0]);
- reader.onloadend = function(event) {
- playFile(event.target.result);
- }
-}
-function downloadFile(input) {
- if(!input.value)
- return;
- var oReq = new XMLHttpRequest();
- oReq.open('GET', 'http://github.com/nfroidure/MIDIFile/master/sounds/' + input.value, true);
- oReq.responseType = 'arraybuffer';
- oReq.onload = function(oEvent) {
- playFile(oReq.response);
- };
- oReq.send(null);
-}
-
-// Player
-function playFile(buffer) {
- var outputKeys = [];
- // testing output
- if(outputs) {
- // Stopping previous play if exists
- if(midiPlayer) {
- midiPlayer.stop();
- }
-
- // Creating player
- midiPlayer = new MIDIPlayer({output: outputs.get(
- document.getElementById('inputportselector').value
- )});
-
- // creating the MidiFile instance from a buffer (view MIDIFile README)
- midiFile = new MIDIFile(buffer);
- midiPlayer.load(midiFile);
-
- // Playing
- midiPlayer.play(function() {
- console.log('Play ended.');
- });
- console.log('Playing.');
-
- } else {
- console.log('No access to MIDI output.');
- }
-}
-
-playFile('./demo.mid');
\ No newline at end of file
diff --git a/app/js/MIDIFile.js b/app/js/MIDIFile.js
deleted file mode 100755
index 096f448..0000000
--- a/app/js/MIDIFile.js
+++ /dev/null
@@ -1,1366 +0,0 @@
-(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.MIDIFile = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o i++) {
- b = this.readUint8();
-
- if (b & 0x80) {
- v += (b & 0x7f);
- v <<= 7;
- } else {
- return v + b;
- }
- }
- throw new Error('0x' + this.position.toString(16) + ':' +
- ' Variable integer length cannot exceed 4 bytes');
- },
- readBytes: function(length) {
- var bytes = [];
-
- for(; 0 < length; length--) {
- bytes.push(this.readUint8());
- }
- return bytes;
- },
- pos: function() {
- return '0x' + (this.buffer.byteOffset + this.position).toString(16);
- },
- end: function() {
- return this.position === this.buffer.byteLength;
- },
- };
- startAt = 0;
- }
- // Consume stream till not at start index
- if(0 < startAt) {
- while(startAt--) {
- stream.readUint8();
- }
- }
- // creating the parser object
- return {
- // Read the next event
- next: function() {
- // Check available datas
- if(stream.end()) {
- return null;
- }
- // Creating the event
- event = {
- // Memoize the event index
- index: stream.pos(),
- // Read the delta time
- delta: stream.readVarInt(),
- };
- // Read the eventTypeByte
- eventTypeByte = stream.readUint8();
- if(0xF0 === (eventTypeByte & 0xF0)) {
- // Meta events
- if(eventTypeByte === MIDIEvents.EVENT_META) {
- event.type = MIDIEvents.EVENT_META;
- event.subtype = stream.readUint8();
- event.length = stream.readVarInt();
- switch(event.subtype) {
- case MIDIEvents.EVENT_META_SEQUENCE_NUMBER:
- if(strictMode && 2 !== event.length) {
- throw new Error(stream.pos() + ' Bad metaevent length.');
- }
- event.msb = stream.readUint8();
- event.lsb = stream.readUint8();
- return event;
- case MIDIEvents.EVENT_META_TEXT:
- case MIDIEvents.EVENT_META_COPYRIGHT_NOTICE:
- case MIDIEvents.EVENT_META_TRACK_NAME:
- case MIDIEvents.EVENT_META_INSTRUMENT_NAME:
- case MIDIEvents.EVENT_META_LYRICS:
- case MIDIEvents.EVENT_META_MARKER:
- case MIDIEvents.EVENT_META_CUE_POINT:
- event.data = stream.readBytes(event.length);
- return event;
- case MIDIEvents.EVENT_META_MIDI_CHANNEL_PREFIX:
- if(strictMode && 1 !== event.length) {
- throw new Error(stream.pos() + ' Bad metaevent length.');
- }
- event.prefix = stream.readUint8();
- return event;
- case MIDIEvents.EVENT_META_END_OF_TRACK:
- if(strictMode && 0 !== event.length) {
- throw new Error(stream.pos() + ' Bad metaevent length.');
- }
- return event;
- case MIDIEvents.EVENT_META_SET_TEMPO:
- if(strictMode && 3 !== event.length) {
- throw new Error(stream.pos() + ' Tempo meta event length must be 3.');
- }
- event.tempo = (
- (stream.readUint8() << 16) +
- (stream.readUint8() << 8) +
- stream.readUint8()
- );
- event.tempoBPM = 60000000 / event.tempo;
- return event;
- case MIDIEvents.EVENT_META_SMTPE_OFFSET:
- if(strictMode && 5 !== event.length) {
- throw new Error(stream.pos() + ' Bad metaevent length.');
- }
- event.hour = stream.readUint8();
- if(strictMode && 23 < event.hour) {
- throw new Error(stream.pos() + ' SMTPE offset hour value must' +
- ' be part of 0-23.');
- }
- event.minutes = stream.readUint8();
- if(strictMode && 59 < event.minutes) {
- throw new Error(stream.pos() + ' SMTPE offset minutes value' +
- ' must be part of 0-59.');
- }
- event.seconds = stream.readUint8();
- if(strictMode && 59 < event.seconds) {
- throw new Error(stream.pos() + ' SMTPE offset seconds value' +
- ' must be part of 0-59.');
- }
- event.frames = stream.readUint8();
- if(strictMode && 30 < event.frames) {
- throw new Error(stream.pos() + ' SMTPE offset frames value must' +
- ' be part of 0-30.');
- }
- event.subframes = stream.readUint8();
- if(strictMode && 99 < event.subframes) {
- throw new Error(stream.pos() + ' SMTPE offset subframes value' +
- ' must be part of 0-99.');
- }
- return event;
- case MIDIEvents.EVENT_META_KEY_SIGNATURE:
- if(strictMode && 2 !== event.length) {
- throw new Error(stream.pos() + ' Bad metaevent length.');
- }
- event.key = stream.readUint8();
- if(strictMode && (-7 > event.key || 7 < event.key)) {
- throw new Error(stream.pos() + ' Bad metaevent length.');
- }
- event.scale = stream.readUint8();
- if(strictMode && 0 !== event.scale && 1 !== event.scale) {
- throw new Error(stream.pos() + ' Key signature scale value must' +
- ' be 0 or 1.');
- }
- return event;
- case MIDIEvents.EVENT_META_TIME_SIGNATURE:
- if(strictMode && 4 !== event.length) {
- throw new Error(stream.pos() + ' Bad metaevent length.');
- }
- event.data = stream.readBytes(event.length);
- event.param1 = event.data[0];
- event.param2 = event.data[1];
- event.param3 = event.data[2];
- event.param4 = event.data[3];
- return event;
- case MIDIEvents.EVENT_META_SEQUENCER_SPECIFIC:
- event.data = stream.readBytes(event.length);
- return event;
- default:
- if(strictMode) {
- throw new Error(stream.pos() + ' Unknown meta event type ' +
- '(' + event.subtype.toString(16) + ').');
- }
- event.data = stream.readBytes(event.length);
- return event;
- }
- // System events
- } else if(eventTypeByte === MIDIEvents.EVENT_SYSEX ||
- eventTypeByte === MIDIEvents.EVENT_DIVSYSEX) {
- event.type = eventTypeByte;
- event.length = stream.readVarInt();
- event.data = stream.readBytes(event.length);
- return event;
- // Unknown event, assuming it's system like event
- } else {
- if(strictMode) {
- throw new Error(stream.pos() + ' Unknown event type ' +
- eventTypeByte.toString(16) + ', Delta: ' + event.delta + '.');
- }
- event.type = eventTypeByte;
- event.badsubtype = stream.readVarInt();
- event.length = stream.readUint8();
- event.data = stream.readBytes(event.length);
- return event;
- }
- // MIDI eventsdestination[index++]
- } else {
- // running status
- if(0 === (eventTypeByte & 0x80)) {
- if(!(MIDIEventType)) {
- throw new Error(stream.pos() + ' Running status without previous event');
- }
- MIDIEventParam1 = eventTypeByte;
- } else {
- MIDIEventType = eventTypeByte >> 4;
- MIDIEventChannel = eventTypeByte & 0x0F;
- MIDIEventParam1 = stream.readUint8();
- }
- event.type = MIDIEvents.EVENT_MIDI;
- event.subtype = MIDIEventType;
- event.channel = MIDIEventChannel;
- event.param1 = MIDIEventParam1;
- switch(MIDIEventType) {
- case MIDIEvents.EVENT_MIDI_NOTE_OFF:
- event.param2 = stream.readUint8();
- return event;
- case MIDIEvents.EVENT_MIDI_NOTE_ON:
- event.param2 = stream.readUint8();
-
- // If velocity is 0, it's a note off event in fact
- if(!event.param2) {
- event.subtype = MIDIEvents.EVENT_MIDI_NOTE_OFF;
- event.param2 = 127; // Find a standard telling what to do here
- }
- return event;
- case MIDIEvents.EVENT_MIDI_NOTE_AFTERTOUCH:
- event.param2 = stream.readUint8();
- return event;
- case MIDIEvents.EVENT_MIDI_CONTROLLER:
- event.param2 = stream.readUint8();
- return event;
- case MIDIEvents.EVENT_MIDI_PROGRAM_CHANGE:
- return event;
- case MIDIEvents.EVENT_MIDI_CHANNEL_AFTERTOUCH:
- return event;
- case MIDIEvents.EVENT_MIDI_PITCH_BEND:
- event.param2 = stream.readUint8();
- return event;
- default:
- if(strictMode) {
- throw new Error(stream.pos() + ' Unknown MIDI event type ' +
- '(' + MIDIEventType.toString(16) + ').');
- }
- return event;
- }
- }
- },
- };
-};
-
-// Return the buffer length needed to encode the given events
-MIDIEvents.writeToTrack = function midiEventsWriteToTrack(events, destination, strictMode) {
- var index = 0;
- var i;
- var j;
- var k;
- var l;
-
- // Converting each event to binary MIDI datas
- for(i = 0, j = events.length; i < j; i++) {
- // Writing delta value
- if(events[i].delta >>> 28) {
- throw Error('Event #' + i + ': Maximum delta time value reached (' +
- events[i].delta + '/134217728 max)');
- }
- if(events[i].delta >>> 21) {
- destination[index++] = ((events[i].delta >>> 21) & 0x7F) | 0x80;
- }
- if(events[i].delta >>> 14) {
- destination[index++] = ((events[i].delta >>> 14) & 0x7F) | 0x80;
- }
- if(events[i].delta >>> 7) {
- destination[index++] = ((events[i].delta >>> 7) & 0x7F) | 0x80;
- }
- destination[index++] = (events[i].delta & 0x7F);
- // MIDI Events encoding
- if(events[i].type === MIDIEvents.EVENT_MIDI) {
- // Adding the byte of subtype + channel
- destination[index++] = (events[i].subtype << 4) + events[i].channel;
- // Adding the byte of the first params
- destination[index++] = events[i].param1;
- // Adding a byte for the optionnal second param
- if(-1 !== MIDIEvents.MIDI_2PARAMS_EVENTS.indexOf(events[i].subtype)) {
- destination[index++] = events[i].param2;
- }
- // META / SYSEX events encoding
- } else {
- // Adding the event type byte
- destination[index++] = events[i].type;
- // Adding the META event subtype byte
- if(events[i].type === MIDIEvents.EVENT_META) {
- destination[index++] = events[i].subtype;
- }
- // Writing the event length bytes
- if(events[i].length >>> 28) {
- throw Error('Event #' + i + ': Maximum length reached (' +
- events[i].length + '/134217728 max)');
- }
- if(events[i].length >>> 21) {
- destination[index++] = ((events[i].length >>> 21) & 0x7F) | 0x80;
- }
- if(events[i].length >>> 14) {
- destination[index++] = ((events[i].length >>> 14) & 0x7F) | 0x80;
- }
- if(events[i].length >>> 7) {
- destination[index++] = ((events[i].length >>> 7) & 0x7F) | 0x80;
- }
- destination[index++] = (events[i].length & 0x7F);
- if(events[i].type === MIDIEvents.EVENT_META) {
- switch(events[i].subtype) {
- case MIDIEvents.EVENT_META_SEQUENCE_NUMBER:
- destination[index++] = events[i].msb;
- destination[index++] = events[i].lsb;
- break;
- case MIDIEvents.EVENT_META_TEXT:
- case MIDIEvents.EVENT_META_COPYRIGHT_NOTICE:
- case MIDIEvents.EVENT_META_TRACK_NAME:
- case MIDIEvents.EVENT_META_INSTRUMENT_NAME:
- case MIDIEvents.EVENT_META_LYRICS:
- case MIDIEvents.EVENT_META_MARKER:
- case MIDIEvents.EVENT_META_CUE_POINT:
- for(k = 0, l = events[i].length; k < l; k++) {
- destination[index++] = events[i].data[k];
- }
- break;
- case MIDIEvents.EVENT_META_MIDI_CHANNEL_PREFIX:
- destination[index++] = events[i].prefix;
- break;
- case MIDIEvents.EVENT_META_END_OF_TRACK:
- break;
- case MIDIEvents.EVENT_META_SET_TEMPO:
- destination[index++] = (events[i].tempo >> 16);
- destination[index++] = (events[i].tempo >> 8) & 0xFF;
- destination[index++] = events[i].tempo & 0xFF;
- break;
- case MIDIEvents.EVENT_META_SMTPE_OFFSET:
- if(strictMode && 23 < events[i].hour) {
- throw new Error('Event #' + i + ': SMTPE offset hour value must be' +
- ' part of 0-23.');
- }
- destination[index++] = events[i].hour;
- if(strictMode && 59 < events[i].minutes) {
- throw new Error('Event #' + i + ': SMTPE offset minutes value must' +
- ' be part of 0-59.');
- }
- destination[index++] = events[i].minutes;
- if(strictMode && 59 < events[i].seconds) {
- throw new Error('Event #' + i + ': SMTPE offset seconds value must' +
- ' be part of 0-59.');
- }
- destination[index++] = events[i].seconds;
- if(strictMode && 30 < events[i].frames) {
- throw new Error('Event #' + i + ': SMTPE offset frames amount must' +
- ' be part of 0-30.');
- }
- destination[index++] = events[i].frames;
- if(strictMode && 99 < events[i].subframes) {
- throw new Error('Event #' + i + ': SMTPE offset subframes amount' +
- ' must be part of 0-99.');
- }
- destination[index++] = events[i].subframes;
- break;
- case MIDIEvents.EVENT_META_KEY_SIGNATURE:
- if('number' != typeof events[i].key || -7 > events[i].key ||
- 7 < events[i].scale) {
- throw new Error('Event #' + i + ':The key signature key must be' +
- ' between -7 and 7');
- }
- if('number' !== typeof events[i].scale ||
- 0 > events[i].scale || 1 < events[i].scale) {
- throw new Error('Event #' + i + ':' +
- 'The key signature scale must be 0 or 1');
- }
- destination[index++] = events[i].key;
- destination[index++] = events[i].scale;
- break;
- // Not implemented
- case MIDIEvents.EVENT_META_TIME_SIGNATURE:
- case MIDIEvents.EVENT_META_SEQUENCER_SPECIFIC:
- default:
- for(k = 0, l = events[i].length; k < l; k++) {
- destination[index++] = events[i].data[k];
- }
- break;
- }
- // Adding bytes corresponding to the sysex event datas
- } else {
- for(k = 0, l = events[i].length; k < l; k++) {
- destination[index++] = events[i].data[k];
- }
- }
- }
- }
-};
-
-// Return the buffer length needed to encode the given events
-MIDIEvents.getRequiredBufferLength = function(events) {
- var bufferLength = 0;
- var i = 0;
- var j;
-
- // Calculating the track size by adding events lengths
- for(i = 0, j = events.length; i < j; i++) {
- // Computing necessary bytes to encode the delta value
- bufferLength +=
- events[i].delta >>> 21 ? 4 :
- events[i].delta >>> 14 ? 3 :
- events[i].delta >>> 7 ? 2 : 1;
- // MIDI Events have various fixed lengths
- if(events[i].type === MIDIEvents.EVENT_MIDI) {
- // Adding a byte for subtype + channel
- bufferLength++;
- // Adding a byte for the first params
- bufferLength++;
- // Adding a byte for the optionnal second param
- if(-1 !== MIDIEvents.MIDI_2PARAMS_EVENTS.indexOf(events[i].subtype)) {
- bufferLength++;
- }
- // META / SYSEX events lengths are self defined
- } else {
- // Adding a byte for the event type
- bufferLength++;
- // Adding a byte for META events subtype
- if(events[i].type === MIDIEvents.EVENT_META) {
- bufferLength++;
- }
- // Adding necessary bytes to encode the length
- bufferLength +=
- events[i].length >>> 21 ? 4 :
- events[i].length >>> 14 ? 3 :
- events[i].length >>> 7 ? 2 : 1;
- // Adding bytes corresponding to the event length
- bufferLength += events[i].length;
- }
- }
- return bufferLength;
-};
-
-module.exports = MIDIEvents;
-
-},{}],2:[function(require,module,exports){
-/*! http://mths.be/fromcodepoint v0.2.1 by @mathias */
-if (!String.fromCodePoint) {
- (function() {
- var defineProperty = (function() {
- // IE 8 only supports `Object.defineProperty` on DOM elements
- try {
- var object = {};
- var $defineProperty = Object.defineProperty;
- var result = $defineProperty(object, object, object) && $defineProperty;
- } catch(error) {}
- return result;
- }());
- var stringFromCharCode = String.fromCharCode;
- var floor = Math.floor;
- var fromCodePoint = function(_) {
- var MAX_SIZE = 0x4000;
- var codeUnits = [];
- var highSurrogate;
- var lowSurrogate;
- var index = -1;
- var length = arguments.length;
- if (!length) {
- return '';
- }
- var result = '';
- while (++index < length) {
- var codePoint = Number(arguments[index]);
- if (
- !isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity`
- codePoint < 0 || // not a valid Unicode code point
- codePoint > 0x10FFFF || // not a valid Unicode code point
- floor(codePoint) != codePoint // not an integer
- ) {
- throw RangeError('Invalid code point: ' + codePoint);
- }
- if (codePoint <= 0xFFFF) { // BMP code point
- codeUnits.push(codePoint);
- } else { // Astral code point; split in surrogate halves
- // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
- codePoint -= 0x10000;
- highSurrogate = (codePoint >> 10) + 0xD800;
- lowSurrogate = (codePoint % 0x400) + 0xDC00;
- codeUnits.push(highSurrogate, lowSurrogate);
- }
- if (index + 1 == length || codeUnits.length > MAX_SIZE) {
- result += stringFromCharCode.apply(null, codeUnits);
- codeUnits.length = 0;
- }
- }
- return result;
- };
- if (defineProperty) {
- defineProperty(String, 'fromCodePoint', {
- 'value': fromCodePoint,
- 'configurable': true,
- 'writable': true
- });
- } else {
- String.fromCodePoint = fromCodePoint;
- }
- }());
-}
-
-},{}],3:[function(require,module,exports){
-/*! http://mths.be/codepointat v0.2.0 by @mathias */
-if (!String.prototype.codePointAt) {
- (function() {
- 'use strict'; // needed to support `apply`/`call` with `undefined`/`null`
- var defineProperty = (function() {
- // IE 8 only supports `Object.defineProperty` on DOM elements
- try {
- var object = {};
- var $defineProperty = Object.defineProperty;
- var result = $defineProperty(object, object, object) && $defineProperty;
- } catch(error) {}
- return result;
- }());
- var codePointAt = function(position) {
- if (this == null) {
- throw TypeError();
- }
- var string = String(this);
- var size = string.length;
- // `ToInteger`
- var index = position ? Number(position) : 0;
- if (index != index) { // better `isNaN`
- index = 0;
- }
- // Account for out-of-bounds indices:
- if (index < 0 || index >= size) {
- return undefined;
- }
- // Get the first code unit
- var first = string.charCodeAt(index);
- var second;
- if ( // check if it’s the start of a surrogate pair
- first >= 0xD800 && first <= 0xDBFF && // high surrogate
- size > index + 1 // there is a next code unit
- ) {
- second = string.charCodeAt(index + 1);
- if (second >= 0xDC00 && second <= 0xDFFF) { // low surrogate
- // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
- return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
- }
- }
- return first;
- };
- if (defineProperty) {
- defineProperty(String.prototype, 'codePointAt', {
- 'value': codePointAt,
- 'configurable': true,
- 'writable': true
- });
- } else {
- String.prototype.codePointAt = codePointAt;
- }
- }());
-}
-
-},{}],4:[function(require,module,exports){
-// UTF8 : Manage UTF-8 strings in ArrayBuffers
-if(module.require) {
- require('string.fromcodepoint');
- require('string.prototype.codepointat');
-}
-
-var UTF8={
- // non UTF8 encoding detection (cf README file for details)
- 'isNotUTF8': function(bytes, byteOffset, byteLength) {
- try {
- UTF8.getStringFromBytes(bytes, byteOffset, byteLength, true);
- } catch(e) {
- return true;
- }
- return false;
- },
- // UTF8 decoding functions
- 'getCharLength': function(theByte) {
- // 4 bytes encoded char (mask 11110000)
- if(0xF0 == (theByte&0xF0)) {
- return 4;
- // 3 bytes encoded char (mask 11100000)
- } else if(0xE0 == (theByte&0xE0)) {
- return 3;
- // 2 bytes encoded char (mask 11000000)
- } else if(0xC0 == (theByte&0xC0)) {
- return 2;
- // 1 bytes encoded char
- } else if(theByte == (theByte&0x7F)) {
- return 1;
- }
- return 0;
- },
- 'getCharCode': function(bytes, byteOffset, charLength) {
- var charCode = 0, mask = '';
- byteOffset = byteOffset || 0;
- // Retrieve charLength if not given
- charLength = charLength || UTF8.getCharLength(bytes[byteOffset]);
- if(charLength == 0) {
- throw new Error(bytes[byteOffset].toString(2)+' is not a significative' +
- ' byte (offset:'+byteOffset+').');
- }
- // Return byte value if charlength is 1
- if(1 === charLength) {
- return bytes[byteOffset];
- }
- // Test UTF8 integrity
- mask = '00000000'.slice(0, charLength) + 1 + '00000000'.slice(charLength + 1);
- if(bytes[byteOffset]&(parseInt(mask, 2))) {
- throw Error('Index ' + byteOffset + ': A ' + charLength + ' bytes' +
- ' encoded char' +' cannot encode the '+(charLength+1)+'th rank bit to 1.');
- }
- // Reading the first byte
- mask='0000'.slice(0,charLength+1)+'11111111'.slice(charLength+1);
- charCode+=(bytes[byteOffset]&parseInt(mask,2))<<((--charLength)*6);
- // Reading the next bytes
- while(charLength) {
- if(0x80!==(bytes[byteOffset+1]&0x80)
- ||0x40===(bytes[byteOffset+1]&0x40)) {
- throw Error('Index '+(byteOffset+1)+': Next bytes of encoded char'
- +' must begin with a "10" bit sequence.');
- }
- charCode += ((bytes[++byteOffset]&0x3F) << ((--charLength) * 6));
- }
- return charCode;
- },
- 'getStringFromBytes': function(bytes, byteOffset, byteLength, strict) {
- var charLength, chars = [];
- byteOffset = byteOffset|0;
- byteLength=('number' === typeof byteLength ?
- byteLength :
- bytes.byteLength || bytes.length
- );
- for(; byteOffset < byteLength; byteOffset++) {
- charLength = UTF8.getCharLength(bytes[byteOffset]);
- if(byteOffset + charLength > byteLength) {
- if(strict) {
- throw Error('Index ' + byteOffset + ': Found a ' + charLength +
- ' bytes encoded char declaration but only ' +
- (byteLength - byteOffset) +' bytes are available.');
- }
- } else {
- chars.push(String.fromCodePoint(
- UTF8.getCharCode(bytes, byteOffset, charLength, strict)
- ));
- }
- byteOffset += charLength - 1;
- }
- return chars.join('');
- },
- // UTF8 encoding functions
- 'getBytesForCharCode': function(charCode) {
- if(charCode < 128) {
- return 1;
- } else if(charCode < 2048) {
- return 2;
- } else if(charCode < 65536) {
- return 3;
- } else if(charCode < 2097152) {
- return 4;
- }
- throw new Error('CharCode '+charCode+' cannot be encoded with UTF8.');
- },
- 'setBytesFromCharCode': function(charCode, bytes, byteOffset, neededBytes) {
- charCode = charCode|0;
- bytes = bytes || [];
- byteOffset = byteOffset|0;
- neededBytes = neededBytes || UTF8.getBytesForCharCode(charCode);
- // Setting the charCode as it to bytes if the byte length is 1
- if(1 == neededBytes) {
- bytes[byteOffset] = charCode;
- } else {
- // Computing the first byte
- bytes[byteOffset++] =
- (parseInt('1111'.slice(0, neededBytes), 2) << 8 - neededBytes) +
- (charCode >>> ((--neededBytes) * 6));
- // Computing next bytes
- for(;neededBytes>0;) {
- bytes[byteOffset++] = ((charCode>>>((--neededBytes) * 6))&0x3F)|0x80;
- }
- }
- return bytes;
- },
- 'setBytesFromString': function(string, bytes, byteOffset, byteLength, strict) {
- string = string || '';
- bytes = bytes || [];
- byteOffset = byteOffset|0;
- byteLength = ('number' === typeof byteLength ?
- byteLength :
- bytes.byteLength||Infinity
- );
- for(var i = 0, j = string.length; i < j; i++) {
- var neededBytes = UTF8.getBytesForCharCode(string[i].codePointAt(0));
- if(strict && byteOffset + neededBytes > byteLength) {
- throw new Error('Not enought bytes to encode the char "' + string[i] +
- '" at the offset "' + byteOffset + '".');
- }
- UTF8.setBytesFromCharCode(string[i].codePointAt(0),
- bytes, byteOffset, neededBytes, strict);
- byteOffset += neededBytes;
- }
- return bytes;
- }
-};
-
-if('undefined' !== typeof module) {
- module.exports = UTF8;
-}
-
-
-},{"string.fromcodepoint":2,"string.prototype.codepointat":3}],5:[function(require,module,exports){
-'use strict';
-
-// MIDIFile : Read (and soon edit) a MIDI file in a given ArrayBuffer
-
-// Dependencies
-var MIDIFileHeader = require('./MIDIFileHeader');
-var MIDIFileTrack = require('./MIDIFileTrack');
-var MIDIEvents = require('midievents');
-var UTF8 = require('utf-8');
-
-// Constructor
-function MIDIFile(buffer, strictMode) {
- var track;
- var curIndex;
- var i;
- var j;
-
- // If not buffer given, creating a new MIDI file
- if(!buffer) {
- // Creating the content
- this.header = new MIDIFileHeader();
- this.tracks = [new MIDIFileTrack()];
- // if a buffer is provided, parsing him
- } else {
- if(!(buffer instanceof ArrayBuffer)) {
- throw new Error('Invalid buffer received.');
- }
- // Minimum MIDI file size is a headerChunk size (14bytes)
- // and an empty track (8+3bytes)
- if(25 > buffer.byteLength) {
- throw new Error('A buffer of a valid MIDI file must have, at least, a' +
- ' size of 25bytes.');
- }
- // Reading header
- this.header = new MIDIFileHeader(buffer, strictMode);
- this.tracks = [];
- curIndex = MIDIFileHeader.HEADER_LENGTH;
- // Reading tracks
- for(i = 0, j = this.header.getTracksCount(); i < j; i++) {
- // Testing the buffer length
- if(strictMode && curIndex >= buffer.byteLength - 1) {
- throw new Error('Couldn\'t find datas corresponding to the track #' + i + '.');
- }
- // Creating the track object
- track = new MIDIFileTrack(buffer, curIndex, strictMode);
- this.tracks.push(track);
- // Updating index to the track end
- curIndex += track.getTrackLength() + 8;
- }
- // Testing integrity : curIndex should be at the end of the buffer
- if(strictMode && curIndex !== buffer.byteLength) {
- throw new Error('It seems that the buffer contains too much datas.');
- }
- }
-}
-
-// Events reading helpers
-MIDIFile.prototype.getEvents = function(type, subtype) {
- var events;
- var event;
- var playTime = 0;
- var filteredEvents = [];
- var format = this.header.getFormat();
- var tickResolution = this.header.getTickResolution();
- var i;
- var j;
- var trackParsers;
- var smallestDelta;
-
- // Reading events
- // if the read is sequential
- if(1 !== format || 1 === this.tracks.length) {
- for(i = 0, j = this.tracks.length; i < j; i++) {
- // reset playtime if format is 2
- playTime = (2 === format && playTime ? playTime : 0);
- events = MIDIEvents.createParser(this.tracks[i].getTrackContent(), 0, false);
- // loooping through events
- event = events.next();
- while(event) {
- playTime += event.delta ? (event.delta * tickResolution) / 1000 : 0;
- if(event.type === MIDIEvents.EVENT_META) {
- // tempo change events
- if(event.subtype === MIDIEvents.EVENT_META_SET_TEMPO) {
- tickResolution = this.header.getTickResolution(event.tempo);
- }
- }
- // push the asked events
- if(((!type) || event.type === type) &&
- ((!subtype) || (event.subtype && event.subtype === type))) {
- event.playTime = playTime;
- filteredEvents.push(event);
- }
- event = events.next();
- }
- }
- // the read is concurrent
- } else {
- trackParsers = [];
- smallestDelta = -1;
-
- // Creating parsers
- for(i = 0, j = this.tracks.length; i < j; i++) {
- trackParsers[i] = {};
- trackParsers[i].parser = MIDIEvents.createParser(
- this.tracks[i].getTrackContent(), 0, false);
- trackParsers[i].curEvent = trackParsers[i].parser.next();
- }
- // Filling events
- do {
- smallestDelta = -1;
- // finding the smallest event
- for(i = 0, j = trackParsers.length; i < j; i++) {
- if(trackParsers[i].curEvent) {
- if(-1 === smallestDelta || trackParsers[i].curEvent.delta <
- trackParsers[smallestDelta].curEvent.delta) {
- smallestDelta = i;
- }
- }
- }
- if(-1 !== smallestDelta) {
- // removing the delta of previous events
- for(i = 0, j = trackParsers.length; i < j; i++) {
- if(i !== smallestDelta && trackParsers[i].curEvent) {
- trackParsers[i].curEvent.delta -= trackParsers[smallestDelta].curEvent.delta;
- }
- }
- // filling values
- event = trackParsers[smallestDelta].curEvent;
- playTime += (event.delta ? (event.delta * tickResolution) / 1000 : 0);
- if(event.type === MIDIEvents.EVENT_META) {
- // tempo change events
- if(event.subtype === MIDIEvents.EVENT_META_SET_TEMPO) {
- tickResolution = this.header.getTickResolution(event.tempo);
- }
- }
- // push midi events
- if(((!type) || event.type === type) &&
- ((!subtype) || (event.subtype && event.subtype === type))) {
- event.playTime = playTime;
- event.track = smallestDelta;
- filteredEvents.push(event);
- }
- // getting next event
- trackParsers[smallestDelta].curEvent = trackParsers[smallestDelta].parser.next();
- }
- } while(-1 !== smallestDelta);
- }
- return filteredEvents;
-};
-
-MIDIFile.prototype.getMidiEvents = function() {
- return this.getEvents(MIDIEvents.EVENT_MIDI);
-};
-
-MIDIFile.prototype.getLyrics = function() {
- var events = this.getEvents(MIDIEvents.EVENT_META);
- var texts = [];
- var lyrics = [];
- var event;
- var i;
- var j;
-
- for(i = 0, j = events.length; i < j; i++) {
- event = events[i];
- // Lyrics
- if(event.subtype === MIDIEvents.EVENT_META_LYRICS) {
- lyrics.push(event);
- // Texts
- } else if(event.subtype === MIDIEvents.EVENT_META_TEXT) {
- // Ignore special texts
- if('@' === String.fromCharCode(event.data[0])) {
- if('T' === String.fromCharCode(event.data[1])) {
- // console.log('Title : ' + event.text.substring(2));
- } else if('I' === String.fromCharCode(event.data[1])) {
- // console.log('Info : ' + event.text.substring(2));
- } else if('L' === String.fromCharCode(event.data[1])) {
- // console.log('Lang : ' + event.text.substring(2));
- }
- // karaoke text follows, remove all previous text
- } else if(0 === String.fromCharCode.apply(String, event.data).indexOf('words')) {
- texts.length = 0;
- // console.log('Word marker found');
- // Karaoke texts
- // If playtime is greater than 0
- } else if(0 !== event.playTime) {
- texts.push(event);
- }
- }
- }
- // Choosing the right lyrics
- if(2 < lyrics.length) {
- texts = lyrics;
- } else if(!texts.length) {
- texts = [];
- }
- // Convert texts and detect encoding
- try {
- texts.forEach(function(event) {
- event.text = UTF8.getStringFromBytes(event.data, 0, event.length, true);
- });
- } catch (e) {
- texts.forEach(function(event) {
- event.text = event.data.map(function(c) {
- return String.fromCharCode(c);
- }).join('');
- });
- }
- return texts;
-};
-
-// Basic events reading
-MIDIFile.prototype.getTrackEvents = function(index) {
- var event;
- var events = [];
- var parser;
- if(index > this.tracks.length || 0 > index) {
- throw Error('Invalid track index (' + index + ')');
- }
- parser = MIDIEvents.createParser(
- this.tracks[index].getTrackContent(), 0, false
- );
- event = parser.next();
- do {
- events.push(event);
- event = parser.next();
- } while(event);
- return events;
-};
-
-// Basic events writting
-MIDIFile.prototype.setTrackEvents = function(index, events) {
- var bufferLength;
- var destination;
-
- if(index > this.tracks.length || 0 > index) {
- throw Error('Invalid track index (' + index + ')');
- }
- if((!events) || (!events.length)) {
- throw Error('A track must contain at least one event, none given.');
- }
- bufferLength = MIDIEvents.getRequiredBufferLength(events);
- destination = new Uint8Array(bufferLength);
- MIDIEvents.writeToTrack(events, destination);
- this.tracks[index].setTrackContent(destination);
-};
-
-// Remove a track
-MIDIFile.prototype.deleteTrack = function(index) {
- if(index > this.tracks.length || 0 > index) {
- throw Error('Invalid track index (' + index + ')');
- }
- this.tracks.splice(index, 1);
- this.header.setTracksCount(this.tracks.length);
-};
-
-// Add a track
-MIDIFile.prototype.addTrack = function(index) {
- var track;
-
- if(index > this.tracks.length || 0 > index) {
- throw Error('Invalid track index (' + index + ')');
- }
- track = new MIDIFileTrack();
- if(index === this.tracks.length) {
- this.tracks.push(track);
- } else {
- this.tracks.splice(index, 0, track);
- }
- this.header.setTracksCount(this.tracks.length);
-};
-
-// Retrieve the content in a buffer
-MIDIFile.prototype.getContent = function() {
- var bufferLength;
- var destination;
- var origin;
- var i;
- var j;
- var k;
- var l;
- var m;
- var n;
-
- // Calculating the buffer content
- // - initialize with the header length
- bufferLength = MIDIFileHeader.HEADER_LENGTH;
- // - add tracks length
- for(i = 0, j = this.tracks.length; i < j; i++) {
- bufferLength += this.tracks[i].getTrackLength() + 8;
- }
- // Creating the destination buffer
- destination = new Uint8Array(bufferLength);
- // Adding header
- origin = new Uint8Array(this.header.datas.buffer,
- this.header.datas.byteOffset,
- MIDIFileHeader.HEADER_LENGTH);
- for(i = 0, j = MIDIFileHeader.HEADER_LENGTH; i < j; i++) {
- destination[i] = origin[i];
- }
- // Adding tracks
- for(k = 0, l = this.tracks.length; k < l; k++) {
- origin = new Uint8Array(this.tracks[k].datas.buffer,
- this.tracks[k].datas.byteOffset,
- this.tracks[k].datas.byteLength);
- for(m = 0, n = this.tracks[k].datas.byteLength; m < n; m++) {
- destination[i++] = origin[m];
- }
- }
- return destination.buffer;
-};
-
-// Exports Track/Header constructors
-MIDIFile.Header = MIDIFileHeader;
-MIDIFile.Track = MIDIFileTrack;
-
-module.exports = MIDIFile;
-
-},{"./MIDIFileHeader":6,"./MIDIFileTrack":7,"midievents":1,"utf-8":4}],6:[function(require,module,exports){
-'use strict';
-
-// MIDIFileHeader : Read and edit a MIDI header chunk in a given ArrayBuffer
-function MIDIFileHeader(buffer) {
- var a;
- // No buffer creating him
- if(!buffer) {
- a = new Uint8Array(MIDIFileHeader.HEADER_LENGTH);
- // Adding the header id (MThd)
- a[0] = 0x4D; a[1] = 0x54; a[2] = 0x68; a[3] = 0x64;
- // Adding the header chunk size
- a[4] = 0x00; a[5] = 0x00; a[6] = 0x00; a[7] = 0x06;
- // Adding the file format (1 here cause it's the most commonly used)
- a[8] = 0x00; a[9] = 0x01;
- // Adding the track count (1 cause it's a new file)
- a[10] = 0x00; a[11] = 0x01;
- // Adding the time division (192 ticks per beat)
- a[12] = 0x00; a[13] = 0xC0;
- // saving the buffer
- this.datas = new DataView(a.buffer, 0, MIDIFileHeader.HEADER_LENGTH);
- // Parsing the given buffer
- } else {
- if(!(buffer instanceof ArrayBuffer)) {
- throw Error('Invalid buffer received.');
- }
- this.datas = new DataView(buffer, 0, MIDIFileHeader.HEADER_LENGTH);
- // Reading MIDI header chunk
- if(!(
- 'M' === String.fromCharCode(this.datas.getUint8(0)) &&
- 'T' === String.fromCharCode(this.datas.getUint8(1)) &&
- 'h' === String.fromCharCode(this.datas.getUint8(2)) &&
- 'd' === String.fromCharCode(this.datas.getUint8(3))
- )) {
- throw new Error('Invalid MIDIFileHeader : MThd prefix not found');
- }
- // Reading chunk length
- if(6 !== this.datas.getUint32(4)) {
- throw new Error('Invalid MIDIFileHeader : Chunk length must be 6');
- }
- }
-}
-
-// Static constants
-MIDIFileHeader.HEADER_LENGTH = 14;
-MIDIFileHeader.FRAMES_PER_SECONDS = 1;
-MIDIFileHeader.TICKS_PER_BEAT = 2;
-
-// MIDI file format
-MIDIFileHeader.prototype.getFormat = function() {
- var format = this.datas.getUint16(8);
- if(0 !== format && 1 !== format && 2 !== format) {
- throw new Error('Invalid MIDI file : MIDI format (' + format + '),' +
- ' format can be 0, 1 or 2 only.');
- }
- return format;
-};
-
-MIDIFileHeader.prototype.setFormat = function(format) {
- if(0 !== format && 1 !== format && 2 !== format) {
- throw new Error('Invalid MIDI format given (' + format + '),' +
- ' format can be 0, 1 or 2 only.');
- }
- this.datas.setUint16(8, format);
-};
-
-// Number of tracks
-MIDIFileHeader.prototype.getTracksCount = function() {
- return this.datas.getUint16(10);
-};
-
-MIDIFileHeader.prototype.setTracksCount = function(n) {
- return this.datas.setUint16(10, n);
-};
-
-// Tick compute
-MIDIFileHeader.prototype.getTickResolution = function(tempo) {
- // Frames per seconds
- if(this.datas.getUint16(12) & 0x8000) {
- return 1000000 / (this.getSMPTEFrames() * this.getTicksPerFrame());
- // Ticks per beat
- }
- // Default MIDI tempo is 120bpm, 500ms per beat
- tempo = tempo || 500000;
- return tempo / this.getTicksPerBeat();
-};
-
-// Time division type
-MIDIFileHeader.prototype.getTimeDivision = function() {
- if(this.datas.getUint16(12) & 0x8000) {
- return MIDIFileHeader.FRAMES_PER_SECONDS;
- }
- return MIDIFileHeader.TICKS_PER_BEAT;
-};
-
-// Ticks per beat
-MIDIFileHeader.prototype.getTicksPerBeat = function() {
- var divisionWord = this.datas.getUint16(12);
- if(divisionWord & 0x8000) {
- throw new Error('Time division is not expressed as ticks per beat.');
- }
- return divisionWord;
-};
-
-MIDIFileHeader.prototype.setTicksPerBeat = function(ticksPerBeat) {
- this.datas.setUint16(12, ticksPerBeat & 0x7FFF);
-};
-
-// Frames per seconds
-MIDIFileHeader.prototype.getSMPTEFrames = function() {
- var divisionWord = this.datas.getUint16(12);
- var smpteFrames;
-
- if(!(divisionWord & 0x8000)) {
- throw new Error('Time division is not expressed as frames per seconds.');
- }
- smpteFrames = divisionWord & 0x7F00;
- if(-1 === [24, 25, 29, 30].indexOf(smpteFrames)) {
- throw new Error('Invalid SMPTE frames value (' + smpteFrames + ').');
- }
- return 29 === smpteFrames ? 29.97 : smpteFrames;
-};
-
-MIDIFileHeader.prototype.getTicksPerFrame = function() {
- var divisionWord = this.datas.getUint16(12);
-
- if(!(divisionWord & 0x8000)) {
- throw new Error('Time division is not expressed as frames per seconds.');
- }
- return divisionWord & 0x00FF;
-};
-
-MIDIFileHeader.prototype.setSMTPEDivision = function(smpteFrames, ticksPerFrame) {
- if(29.97 === smpteFrames) {
- smpteFrames = 29;
- }
- if(-1 === [24, 25, 29, 30].indexOf(smpteFrames)) {
- throw new Error('Invalid SMPTE frames value given (' + smpteFrames + ').');
- }
- if(0 > ticksPerFrame || 0xFF < ticksPerFrame) {
- throw new Error('Invalid ticks per frame value given (' + smpteFrames + ').');
- }
- this.datas.setUint8(12, 0x80 | smpteFrames);
- this.datas.setUint8(13, ticksPerFrame);
-};
-
-module.exports = MIDIFileHeader;
-
-},{}],7:[function(require,module,exports){
-'use strict';
-
-// MIDIFileTrack : Read and edit a MIDI track chunk in a given ArrayBuffer
-function MIDIFileTrack(buffer, start) {
- var a;
- var trackLength;
-
- // no buffer, creating him
- if(!buffer) {
- a = new Uint8Array(12);
- // Adding the empty track header (MTrk)
- a[0] = 0x4D; a[1] = 0x54; a[2] = 0x72; a[3] = 0x6B;
- // Adding the empty track size (4)
- a[4] = 0x00; a[5] = 0x00; a[6] = 0x00; a[7] = 0x04;
- // Adding the track end event
- a[8] = 0x00; a[9] = 0xFF; a[10] = 0x2F; a[11] = 0x00;
- // Saving the buffer
- this.datas = new DataView(a.buffer, 0, MIDIFileTrack.HDR_LENGTH + 4);
- // parsing the given buffer
- } else {
- if(!(buffer instanceof ArrayBuffer)) {
- throw new Error('Invalid buffer received.');
- }
- // Buffer length must size at least like an empty track (8+3bytes)
- if(12 > buffer.byteLength - start) {
- throw new Error('Invalid MIDIFileTrack (0x' + start.toString(16) + ') :' +
- ' Buffer length must size at least 12bytes');
- }
- // Creating a temporary view to read the track header
- this.datas = new DataView(buffer, start, MIDIFileTrack.HDR_LENGTH);
- // Reading MIDI track header chunk
- if(!(
- 'M' === String.fromCharCode(this.datas.getUint8(0)) &&
- 'T' === String.fromCharCode(this.datas.getUint8(1)) &&
- 'r' === String.fromCharCode(this.datas.getUint8(2)) &&
- 'k' === String.fromCharCode(this.datas.getUint8(3))
- )) {
- throw new Error('Invalid MIDIFileTrack (0x' + start.toString(16) + ') :' +
- ' MTrk prefix not found');
- }
- // Reading the track length
- trackLength = this.getTrackLength();
- if(buffer.byteLength - start < trackLength) {
- throw new Error('Invalid MIDIFileTrack (0x' + start.toString(16) + ') :' +
- ' The track size exceed the buffer length.');
- }
- // Creating the final DataView
- this.datas = new DataView(buffer, start, MIDIFileTrack.HDR_LENGTH + trackLength);
- // Trying to find the end of track event
- if(!(
- 0xFF === this.datas.getUint8(MIDIFileTrack.HDR_LENGTH + trackLength - 3) &&
- 0x2F === this.datas.getUint8(MIDIFileTrack.HDR_LENGTH + trackLength - 2) &&
- 0x00 === this.datas.getUint8(MIDIFileTrack.HDR_LENGTH + trackLength - 1)
- )) {
- throw new Error('Invalid MIDIFileTrack (0x' + start.toString(16) + ') :' +
- ' No track end event found at the expected index' +
- ' (' + (MIDIFileTrack.HDR_LENGTH + trackLength - 1).toString(16) + ').');
- }
- }
-}
-
-// Static constants
-MIDIFileTrack.HDR_LENGTH = 8;
-
-// Track length
-MIDIFileTrack.prototype.getTrackLength = function() {
- return this.datas.getUint32(4);
-};
-
-MIDIFileTrack.prototype.setTrackLength = function(trackLength) {
- return this.datas.setUint32(4, trackLength);
-};
-
-// Read track contents
-MIDIFileTrack.prototype.getTrackContent = function() {
- return new DataView(this.datas.buffer,
- this.datas.byteOffset + MIDIFileTrack.HDR_LENGTH,
- this.datas.byteLength - MIDIFileTrack.HDR_LENGTH);
-};
-
-// Set track content
-MIDIFileTrack.prototype.setTrackContent = function(dataView) {
- var origin;
- var destination;
- var i;
- var j;
- // Calculating the track length
- var trackLength = dataView.byteLength - dataView.byteOffset;
- // Track length must size at least like an empty track (4bytes)
- if(4 > trackLength) {
- throw new Error('Invalid track length, must size at least 4bytes');
- }
- this.datas = new DataView(
- new Uint8Array(MIDIFileTrack.HDR_LENGTH + trackLength).buffer);
- // Adding the track header (MTrk)
- this.datas.setUint8(0, 0x4D); // M
- this.datas.setUint8(1, 0x54); // T
- this.datas.setUint8(2, 0x72); // r
- this.datas.setUint8(3, 0x6B); // k
- // Adding the track size
- this.datas.setUint32(4, trackLength);
- // Copying the content
- origin = new Uint8Array(dataView.buffer, dataView.byteOffset,
- dataView.byteLength);
- destination = new Uint8Array(this.datas.buffer,
- MIDIFileTrack.HDR_LENGTH,
- trackLength);
- for(i = 0, j = origin.length; i < j; i++) {
- destination[i] = origin[i];
- }
-};
-
-module.exports = MIDIFileTrack;
-
-},{}]},{},[5])(5)
-});
\ No newline at end of file
diff --git a/app/js/MIDIPlayer.js b/app/js/MIDIPlayer.js
deleted file mode 100755
index 6e89211..0000000
--- a/app/js/MIDIPlayer.js
+++ /dev/null
@@ -1,644 +0,0 @@
-(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.MIDIPlayer = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o i++) {
- b = this.readUint8();
-
- if (b & 0x80) {
- v += (b & 0x7f);
- v <<= 7;
- } else {
- return v + b;
- }
- }
- throw new Error('0x' + this.position.toString(16) + ':' +
- ' Variable integer length cannot exceed 4 bytes');
- },
- readBytes: function(length) {
- var bytes = [];
-
- for(; 0 < length; length--) {
- bytes.push(this.readUint8());
- }
- return bytes;
- },
- pos: function() {
- return '0x' + (this.buffer.byteOffset + this.position).toString(16);
- },
- end: function() {
- return this.position === this.buffer.byteLength;
- },
- };
- startAt = 0;
- }
- // Consume stream till not at start index
- if(0 < startAt) {
- while(startAt--) {
- stream.readUint8();
- }
- }
- // creating the parser object
- return {
- // Read the next event
- next: function() {
- // Check available datas
- if(stream.end()) {
- return null;
- }
- // Creating the event
- event = {
- // Memoize the event index
- index: stream.pos(),
- // Read the delta time
- delta: stream.readVarInt(),
- };
- // Read the eventTypeByte
- eventTypeByte = stream.readUint8();
- if(0xF0 === (eventTypeByte & 0xF0)) {
- // Meta events
- if(eventTypeByte === MIDIEvents.EVENT_META) {
- event.type = MIDIEvents.EVENT_META;
- event.subtype = stream.readUint8();
- event.length = stream.readVarInt();
- switch(event.subtype) {
- case MIDIEvents.EVENT_META_SEQUENCE_NUMBER:
- if(strictMode && 2 !== event.length) {
- throw new Error(stream.pos() + ' Bad metaevent length.');
- }
- event.msb = stream.readUint8();
- event.lsb = stream.readUint8();
- return event;
- case MIDIEvents.EVENT_META_TEXT:
- case MIDIEvents.EVENT_META_COPYRIGHT_NOTICE:
- case MIDIEvents.EVENT_META_TRACK_NAME:
- case MIDIEvents.EVENT_META_INSTRUMENT_NAME:
- case MIDIEvents.EVENT_META_LYRICS:
- case MIDIEvents.EVENT_META_MARKER:
- case MIDIEvents.EVENT_META_CUE_POINT:
- event.data = stream.readBytes(event.length);
- return event;
- case MIDIEvents.EVENT_META_MIDI_CHANNEL_PREFIX:
- if(strictMode && 1 !== event.length) {
- throw new Error(stream.pos() + ' Bad metaevent length.');
- }
- event.prefix = stream.readUint8();
- return event;
- case MIDIEvents.EVENT_META_END_OF_TRACK:
- if(strictMode && 0 !== event.length) {
- throw new Error(stream.pos() + ' Bad metaevent length.');
- }
- return event;
- case MIDIEvents.EVENT_META_SET_TEMPO:
- if(strictMode && 3 !== event.length) {
- throw new Error(stream.pos() + ' Tempo meta event length must be 3.');
- }
- event.tempo = (
- (stream.readUint8() << 16) +
- (stream.readUint8() << 8) +
- stream.readUint8()
- );
- event.tempoBPM = 60000000 / event.tempo;
- return event;
- case MIDIEvents.EVENT_META_SMTPE_OFFSET:
- if(strictMode && 5 !== event.length) {
- throw new Error(stream.pos() + ' Bad metaevent length.');
- }
- event.hour = stream.readUint8();
- if(strictMode && 23 < event.hour) {
- throw new Error(stream.pos() + ' SMTPE offset hour value must' +
- ' be part of 0-23.');
- }
- event.minutes = stream.readUint8();
- if(strictMode && 59 < event.minutes) {
- throw new Error(stream.pos() + ' SMTPE offset minutes value' +
- ' must be part of 0-59.');
- }
- event.seconds = stream.readUint8();
- if(strictMode && 59 < event.seconds) {
- throw new Error(stream.pos() + ' SMTPE offset seconds value' +
- ' must be part of 0-59.');
- }
- event.frames = stream.readUint8();
- if(strictMode && 30 < event.frames) {
- throw new Error(stream.pos() + ' SMTPE offset frames value must' +
- ' be part of 0-30.');
- }
- event.subframes = stream.readUint8();
- if(strictMode && 99 < event.subframes) {
- throw new Error(stream.pos() + ' SMTPE offset subframes value' +
- ' must be part of 0-99.');
- }
- return event;
- case MIDIEvents.EVENT_META_KEY_SIGNATURE:
- if(strictMode && 2 !== event.length) {
- throw new Error(stream.pos() + ' Bad metaevent length.');
- }
- event.key = stream.readUint8();
- if(strictMode && (-7 > event.key || 7 < event.key)) {
- throw new Error(stream.pos() + ' Bad metaevent length.');
- }
- event.scale = stream.readUint8();
- if(strictMode && 0 !== event.scale && 1 !== event.scale) {
- throw new Error(stream.pos() + ' Key signature scale value must' +
- ' be 0 or 1.');
- }
- return event;
- case MIDIEvents.EVENT_META_TIME_SIGNATURE:
- if(strictMode && 4 !== event.length) {
- throw new Error(stream.pos() + ' Bad metaevent length.');
- }
- event.data = stream.readBytes(event.length);
- event.param1 = event.data[0];
- event.param2 = event.data[1];
- event.param3 = event.data[2];
- event.param4 = event.data[3];
- return event;
- case MIDIEvents.EVENT_META_SEQUENCER_SPECIFIC:
- event.data = stream.readBytes(event.length);
- return event;
- default:
- if(strictMode) {
- throw new Error(stream.pos() + ' Unknown meta event type ' +
- '(' + event.subtype.toString(16) + ').');
- }
- event.data = stream.readBytes(event.length);
- return event;
- }
- // System events
- } else if(eventTypeByte === MIDIEvents.EVENT_SYSEX ||
- eventTypeByte === MIDIEvents.EVENT_DIVSYSEX) {
- event.type = eventTypeByte;
- event.length = stream.readVarInt();
- event.data = stream.readBytes(event.length);
- return event;
- // Unknown event, assuming it's system like event
- } else {
- if(strictMode) {
- throw new Error(stream.pos() + ' Unknown event type ' +
- eventTypeByte.toString(16) + ', Delta: ' + event.delta + '.');
- }
- event.type = eventTypeByte;
- event.badsubtype = stream.readVarInt();
- event.length = stream.readUint8();
- event.data = stream.readBytes(event.length);
- return event;
- }
- // MIDI eventsdestination[index++]
- } else {
- // running status
- if(0 === (eventTypeByte & 0x80)) {
- if(!(MIDIEventType)) {
- throw new Error(stream.pos() + ' Running status without previous event');
- }
- MIDIEventParam1 = eventTypeByte;
- } else {
- MIDIEventType = eventTypeByte >> 4;
- MIDIEventChannel = eventTypeByte & 0x0F;
- MIDIEventParam1 = stream.readUint8();
- }
- event.type = MIDIEvents.EVENT_MIDI;
- event.subtype = MIDIEventType;
- event.channel = MIDIEventChannel;
- event.param1 = MIDIEventParam1;
- switch(MIDIEventType) {
- case MIDIEvents.EVENT_MIDI_NOTE_OFF:
- event.param2 = stream.readUint8();
- return event;
- case MIDIEvents.EVENT_MIDI_NOTE_ON:
- event.param2 = stream.readUint8();
-
- // If velocity is 0, it's a note off event in fact
- if(!event.param2) {
- event.subtype = MIDIEvents.EVENT_MIDI_NOTE_OFF;
- event.param2 = 127; // Find a standard telling what to do here
- }
- return event;
- case MIDIEvents.EVENT_MIDI_NOTE_AFTERTOUCH:
- event.param2 = stream.readUint8();
- return event;
- case MIDIEvents.EVENT_MIDI_CONTROLLER:
- event.param2 = stream.readUint8();
- return event;
- case MIDIEvents.EVENT_MIDI_PROGRAM_CHANGE:
- return event;
- case MIDIEvents.EVENT_MIDI_CHANNEL_AFTERTOUCH:
- return event;
- case MIDIEvents.EVENT_MIDI_PITCH_BEND:
- event.param2 = stream.readUint8();
- return event;
- default:
- if(strictMode) {
- throw new Error(stream.pos() + ' Unknown MIDI event type ' +
- '(' + MIDIEventType.toString(16) + ').');
- }
- return event;
- }
- }
- },
- };
-};
-
-// Return the buffer length needed to encode the given events
-MIDIEvents.writeToTrack = function midiEventsWriteToTrack(events, destination, strictMode) {
- var index = 0;
- var i;
- var j;
- var k;
- var l;
-
- // Converting each event to binary MIDI datas
- for(i = 0, j = events.length; i < j; i++) {
- // Writing delta value
- if(events[i].delta >>> 28) {
- throw Error('Event #' + i + ': Maximum delta time value reached (' +
- events[i].delta + '/134217728 max)');
- }
- if(events[i].delta >>> 21) {
- destination[index++] = ((events[i].delta >>> 21) & 0x7F) | 0x80;
- }
- if(events[i].delta >>> 14) {
- destination[index++] = ((events[i].delta >>> 14) & 0x7F) | 0x80;
- }
- if(events[i].delta >>> 7) {
- destination[index++] = ((events[i].delta >>> 7) & 0x7F) | 0x80;
- }
- destination[index++] = (events[i].delta & 0x7F);
- // MIDI Events encoding
- if(events[i].type === MIDIEvents.EVENT_MIDI) {
- // Adding the byte of subtype + channel
- destination[index++] = (events[i].subtype << 4) + events[i].channel;
- // Adding the byte of the first params
- destination[index++] = events[i].param1;
- // Adding a byte for the optionnal second param
- if(-1 !== MIDIEvents.MIDI_2PARAMS_EVENTS.indexOf(events[i].subtype)) {
- destination[index++] = events[i].param2;
- }
- // META / SYSEX events encoding
- } else {
- // Adding the event type byte
- destination[index++] = events[i].type;
- // Adding the META event subtype byte
- if(events[i].type === MIDIEvents.EVENT_META) {
- destination[index++] = events[i].subtype;
- }
- // Writing the event length bytes
- if(events[i].length >>> 28) {
- throw Error('Event #' + i + ': Maximum length reached (' +
- events[i].length + '/134217728 max)');
- }
- if(events[i].length >>> 21) {
- destination[index++] = ((events[i].length >>> 21) & 0x7F) | 0x80;
- }
- if(events[i].length >>> 14) {
- destination[index++] = ((events[i].length >>> 14) & 0x7F) | 0x80;
- }
- if(events[i].length >>> 7) {
- destination[index++] = ((events[i].length >>> 7) & 0x7F) | 0x80;
- }
- destination[index++] = (events[i].length & 0x7F);
- if(events[i].type === MIDIEvents.EVENT_META) {
- switch(events[i].subtype) {
- case MIDIEvents.EVENT_META_SEQUENCE_NUMBER:
- destination[index++] = events[i].msb;
- destination[index++] = events[i].lsb;
- break;
- case MIDIEvents.EVENT_META_TEXT:
- case MIDIEvents.EVENT_META_COPYRIGHT_NOTICE:
- case MIDIEvents.EVENT_META_TRACK_NAME:
- case MIDIEvents.EVENT_META_INSTRUMENT_NAME:
- case MIDIEvents.EVENT_META_LYRICS:
- case MIDIEvents.EVENT_META_MARKER:
- case MIDIEvents.EVENT_META_CUE_POINT:
- for(k = 0, l = events[i].length; k < l; k++) {
- destination[index++] = events[i].data[k];
- }
- break;
- case MIDIEvents.EVENT_META_MIDI_CHANNEL_PREFIX:
- destination[index++] = events[i].prefix;
- break;
- case MIDIEvents.EVENT_META_END_OF_TRACK:
- break;
- case MIDIEvents.EVENT_META_SET_TEMPO:
- destination[index++] = (events[i].tempo >> 16);
- destination[index++] = (events[i].tempo >> 8) & 0xFF;
- destination[index++] = events[i].tempo & 0xFF;
- break;
- case MIDIEvents.EVENT_META_SMTPE_OFFSET:
- if(strictMode && 23 < events[i].hour) {
- throw new Error('Event #' + i + ': SMTPE offset hour value must be' +
- ' part of 0-23.');
- }
- destination[index++] = events[i].hour;
- if(strictMode && 59 < events[i].minutes) {
- throw new Error('Event #' + i + ': SMTPE offset minutes value must' +
- ' be part of 0-59.');
- }
- destination[index++] = events[i].minutes;
- if(strictMode && 59 < events[i].seconds) {
- throw new Error('Event #' + i + ': SMTPE offset seconds value must' +
- ' be part of 0-59.');
- }
- destination[index++] = events[i].seconds;
- if(strictMode && 30 < events[i].frames) {
- throw new Error('Event #' + i + ': SMTPE offset frames amount must' +
- ' be part of 0-30.');
- }
- destination[index++] = events[i].frames;
- if(strictMode && 99 < events[i].subframes) {
- throw new Error('Event #' + i + ': SMTPE offset subframes amount' +
- ' must be part of 0-99.');
- }
- destination[index++] = events[i].subframes;
- break;
- case MIDIEvents.EVENT_META_KEY_SIGNATURE:
- if('number' != typeof events[i].key || -7 > events[i].key ||
- 7 < events[i].scale) {
- throw new Error('Event #' + i + ':The key signature key must be' +
- ' between -7 and 7');
- }
- if('number' !== typeof events[i].scale ||
- 0 > events[i].scale || 1 < events[i].scale) {
- throw new Error('Event #' + i + ':' +
- 'The key signature scale must be 0 or 1');
- }
- destination[index++] = events[i].key;
- destination[index++] = events[i].scale;
- break;
- // Not implemented
- case MIDIEvents.EVENT_META_TIME_SIGNATURE:
- case MIDIEvents.EVENT_META_SEQUENCER_SPECIFIC:
- default:
- for(k = 0, l = events[i].length; k < l; k++) {
- destination[index++] = events[i].data[k];
- }
- break;
- }
- // Adding bytes corresponding to the sysex event datas
- } else {
- for(k = 0, l = events[i].length; k < l; k++) {
- destination[index++] = events[i].data[k];
- }
- }
- }
- }
-};
-
-// Return the buffer length needed to encode the given events
-MIDIEvents.getRequiredBufferLength = function(events) {
- var bufferLength = 0;
- var i = 0;
- var j;
-
- // Calculating the track size by adding events lengths
- for(i = 0, j = events.length; i < j; i++) {
- // Computing necessary bytes to encode the delta value
- bufferLength +=
- events[i].delta >>> 21 ? 4 :
- events[i].delta >>> 14 ? 3 :
- events[i].delta >>> 7 ? 2 : 1;
- // MIDI Events have various fixed lengths
- if(events[i].type === MIDIEvents.EVENT_MIDI) {
- // Adding a byte for subtype + channel
- bufferLength++;
- // Adding a byte for the first params
- bufferLength++;
- // Adding a byte for the optionnal second param
- if(-1 !== MIDIEvents.MIDI_2PARAMS_EVENTS.indexOf(events[i].subtype)) {
- bufferLength++;
- }
- // META / SYSEX events lengths are self defined
- } else {
- // Adding a byte for the event type
- bufferLength++;
- // Adding a byte for META events subtype
- if(events[i].type === MIDIEvents.EVENT_META) {
- bufferLength++;
- }
- // Adding necessary bytes to encode the length
- bufferLength +=
- events[i].length >>> 21 ? 4 :
- events[i].length >>> 14 ? 3 :
- events[i].length >>> 7 ? 2 : 1;
- // Adding bytes corresponding to the event length
- bufferLength += events[i].length;
- }
- }
- return bufferLength;
-};
-
-module.exports = MIDIEvents;
-
-},{}],2:[function(require,module,exports){
-'use strict';
-
-var MIDIEvents = require('midievents');
-
-// Constants
-var PLAY_BUFFER_DELAY = 300;
-var PAGE_HIDDEN_BUFFER_RATIO = 20;
-
-// MIDIPlayer constructor
-function MIDIPlayer(options) {
- var i;
-
- options = options || {};
- this.output = options.output || null; // midi output
- this.volume = options.volume || 100; // volume in percents
- this.startTime = -1; // ms since page load
- this.pauseTime = -1; // ms elapsed before player paused
- this.events = [];
- this.notesOn = new Array(32); // notesOn[channel][note]
- for(i = 31; 0 <= i; i--) {
- this.notesOn[i] = [];
- }
- this.midiFile = null;
- window.addEventListener('unload', this.stop.bind(this));
-}
-
-// Parsing all tracks and add their events in a single event queue
-MIDIPlayer.prototype.load = function(midiFile) {
- this.stop();
- this.position = 0;
- this.midiFile = midiFile;
- this.events = this.midiFile.getMidiEvents();
-};
-
-MIDIPlayer.prototype.play = function(endCallback) {
- this.endCallback = endCallback;
- if(0 === this.position) {
- this.startTime = performance.now();
- this.timeout = setTimeout(this.processPlay.bind(this), 0);
- return 1;
- }
- return 0;
-};
-
-MIDIPlayer.prototype.processPlay = function() {
- var elapsedTime = performance.now() - this.startTime;
- var event;
- var index;
- var param2;
- var bufferDelay = PLAY_BUFFER_DELAY * (
- document.hidden || document.mozHidden || document.webkitHidden ||
- document.msHidden || document.oHidden ?
- PAGE_HIDDEN_BUFFER_RATIO :
- 1
- );
- event = this.events[this.position];
- while(this.events[this.position] && event.playTime - elapsedTime < bufferDelay) {
- param2 = 0;
- if(event.subtype === MIDIEvents.EVENT_MIDI_NOTE_ON) {
- param2 = Math.floor(event.param1 * ((this.volume || 1) / 100));
- this.notesOn[event.channel].push(event.param1);
- } else if(event.subtype === MIDIEvents.EVENT_MIDI_NOTE_OFF) {
- index = this.notesOn[event.channel].indexOf(event.param1);
- if(-1 !== index) {
- this.notesOn[event.channel].splice(index, 1);
- }
- }
- this.output.send(
- -1 !== MIDIEvents.MIDI_1PARAM_EVENTS.indexOf(event.subtype) ?
- [(event.subtype << 4) + event.channel, event.param1] :
- [(event.subtype << 4) + event.channel, event.param1, (param2 || event.param2 || 0x00)],
- Math.floor(event.playTime + this.startTime)
- );
- this.lastPlayTime = event.playTime + this.startTime;
- this.position++;
- event = this.events[this.position];
- }
- if(this.position < this.events.length - 1) {
- this.timeout = setTimeout(this.processPlay.bind(this), PLAY_BUFFER_DELAY - 250);
- } else {
- setTimeout(this.endCallback, PLAY_BUFFER_DELAY + 100);
- this.position = 0;
- }
-};
-
-MIDIPlayer.prototype.pause = function() {
- var i;
- var j;
-
- if(this.timeout) {
- clearTimeout(this.timeout);
- this.timeout = null;
- this.pauseTime = performance.now();
- for(i = this.notesOn.length - 1; 0 <= i; i--) {
- for(j = this.notesOn[i].length - 1; 0 <= j; j--) {
- this.output.send([(MIDIEvents.EVENT_MIDI_NOTE_OFF << 4) + i, this.notesOn[i][j],
- 0x00], this.lastPlayTime + 100);
- }
- }
- return true;
- }
- return false;
-};
-
-MIDIPlayer.prototype.resume = function(endCallback) {
- this.endCallback = endCallback;
- if(this.events && this.events[this.position] && !this.timeout) {
- this.startTime += performance.now() - this.pauseTime;
- this.timeout = setTimeout(this.processPlay.bind(this), 0);
- return this.events[this.position].playTime;
- }
- return 0;
-};
-
-MIDIPlayer.prototype.stop = function() {
- var i;
-
- if(this.pause()) {
- this.position = 0;
- for(i = 31; 0 <= i; i--) {
- this.notesOn[i] = [];
- }
- return true;
- }
- return false;
-};
-
-module.exports = MIDIPlayer;
-
-},{"midievents":1}]},{},[2])(2)
-});
\ No newline at end of file
diff --git a/app/js/howler.min.js b/app/js/howler.min.js
new file mode 100644
index 0000000..0d450ee
--- /dev/null
+++ b/app/js/howler.min.js
@@ -0,0 +1,10 @@
+/*!
+ * howler.js v1.1.29
+ * howlerjs.com
+ *
+ * (c) 2013-2016, James Simpson of GoldFire Studios
+ * goldfirestudios.com
+ *
+ * MIT License
+ */
+!function(){var e={},o=null,n=!0,r=!1;try{"undefined"!=typeof AudioContext?o=new AudioContext:"undefined"!=typeof webkitAudioContext?o=new webkitAudioContext:n=!1}catch(t){n=!1}if(!n)if("undefined"!=typeof Audio)try{new Audio}catch(t){r=!0}else r=!0;if(n){var a="undefined"==typeof o.createGain?o.createGainNode():o.createGain();a.gain.value=1,a.connect(o.destination)}var i=function(e){this._volume=1,this._muted=!1,this.usingWebAudio=n,this.ctx=o,this.noAudio=r,this._howls=[],this._codecs=e,this.iOSAutoEnable=!0};i.prototype={volume:function(e){var o=this;if(e=parseFloat(e),e>=0&&1>=e){o._volume=e,n&&(a.gain.value=e);for(var r in o._howls)if(o._howls.hasOwnProperty(r)&&o._howls[r]._webAudio===!1)for(var t=0;t0?t._pos:r._sprite[e][0]/1e3,i=0;r._webAudio?(i=r._sprite[e][1]/1e3-t._pos,t._pos>0&&(a=r._sprite[e][0]/1e3+a)):i=r._sprite[e][1]/1e3-(a-r._sprite[e][0]/1e3);var u,d=!(!r._loop&&!r._sprite[e][2]),f="string"==typeof n?n:Math.round(Date.now()*Math.random())+"";if(function(){var o={id:f,sprite:e,loop:d};u=setTimeout(function(){!r._webAudio&&d&&r.stop(o.id).play(e,o.id),r._webAudio&&!d&&(r._nodeById(o.id).paused=!0,r._nodeById(o.id)._pos=0,r._clearEndTimer(o.id)),r._webAudio||d||r.stop(o.id),r.on("end",f)},i/r._rate*1e3),r._onendTimer.push({timer:u,id:o.id})}(),r._webAudio){var s=r._sprite[e][0]/1e3,_=r._sprite[e][1]/1e3;t.id=f,t.paused=!1,p(r,[d,s,_],f),r._playStart=o.currentTime,t.gain.value=r._volume,"undefined"==typeof t.bufferSource.start?d?t.bufferSource.noteGrainOn(0,a,86400):t.bufferSource.noteGrainOn(0,a,i):d?t.bufferSource.start(0,a,86400):t.bufferSource.start(0,a,i)}else{if(4!==t.readyState&&(t.readyState||!navigator.isCocoonJS))return r._clearEndTimer(f),function(){var o=r,a=e,i=n,u=t,d=function(){o.play(a,i),u.removeEventListener("canplaythrough",d,!1)};u.addEventListener("canplaythrough",d,!1)}(),r;t.readyState=4,t.id=f,t.currentTime=a,t.muted=l._muted||t.muted,t.volume=r._volume*l.volume(),setTimeout(function(){t.play()},0)}return r.on("play"),"function"==typeof n&&n(f),r}),r):("function"==typeof n&&n(),r):(r.on("load",function(){r.play(e,n)}),r)},pause:function(e){var o=this;if(!o._loaded)return o.on("play",function(){o.pause(e)}),o;o._clearEndTimer(e);var n=e?o._nodeById(e):o._activeNode();if(n)if(n._pos=o.pos(null,e),o._webAudio){if(!n.bufferSource||n.paused)return o;n.paused=!0,"undefined"==typeof n.bufferSource.stop?n.bufferSource.noteOff(0):n.bufferSource.stop(0)}else n.pause();return o.on("pause"),o},stop:function(e){var o=this;if(!o._loaded)return o.on("play",function(){o.stop(e)}),o;o._clearEndTimer(e);var n=e?o._nodeById(e):o._activeNode();if(n)if(n._pos=0,o._webAudio){if(!n.bufferSource||n.paused)return o;n.paused=!0,"undefined"==typeof n.bufferSource.stop?n.bufferSource.noteOff(0):n.bufferSource.stop(0)}else isNaN(n.duration)||(n.pause(),n.currentTime=0);return o},mute:function(e){var o=this;if(!o._loaded)return o.on("play",function(){o.mute(e)}),o;var n=e?o._nodeById(e):o._activeNode();return n&&(o._webAudio?n.gain.value=0:n.muted=!0),o},unmute:function(e){var o=this;if(!o._loaded)return o.on("play",function(){o.unmute(e)}),o;var n=e?o._nodeById(e):o._activeNode();return n&&(o._webAudio?n.gain.value=o._volume:n.muted=!1),o},volume:function(e,o){var n=this;if(e=parseFloat(e),e>=0&&1>=e){if(n._volume=e,!n._loaded)return n.on("play",function(){n.volume(e,o)}),n;var r=o?n._nodeById(o):n._activeNode();return r&&(n._webAudio?r.gain.value=e:r.volume=e*l.volume()),n}return n._volume},loop:function(e){var o=this;return"boolean"==typeof e?(o._loop=e,o):o._loop},sprite:function(e){var o=this;return"object"==typeof e?(o._sprite=e,o):o._sprite},pos:function(e,n){var r=this;if(!r._loaded)return r.on("load",function(){r.pos(e)}),"number"==typeof e?r:r._pos||0;e=parseFloat(e);var t=n?r._nodeById(n):r._activeNode();if(t)return e>=0?(r.pause(n),t._pos=e,r.play(t._sprite,n),r):r._webAudio?t._pos+(o.currentTime-r._playStart):t.currentTime;if(e>=0)return r;for(var a=0;a=0||0>e))return t._pos3d;if(t._webAudio){var a=r?t._nodeById(r):t._activeNode();a&&(t._pos3d=[e,o,n],a.panner.setPosition(e,o,n),a.panner.panningModel=t._model||"HRTF")}return t},fade:function(e,o,n,r,t){var a=this,i=Math.abs(e-o),u=e>o?"down":"up",d=i/.01,l=n/d;if(!a._loaded)return a.on("load",function(){a.fade(e,o,n,r,t)}),a;a.volume(e,t);for(var f=1;d>=f;f++)!function(){var e=a._volume+("up"===u?.01:-.01)*f,n=Math.round(1e3*e)/1e3,i=o;setTimeout(function(){a.volume(n,t),n===i&&r&&r()},l*f)}()},fadeIn:function(e,o,n){return this.volume(0).play().fade(0,e,o,n)},fadeOut:function(e,o,n,r){var t=this;return t.fade(t._volume,e,o,function(){n&&n(),t.pause(r),t.on("end")},r)},_nodeById:function(e){for(var o=this,n=o._audioNode[0],r=0;r=0&&!(5>=n);e--)o._audioNode[e].paused&&(o._webAudio&&o._audioNode[e].disconnect(0),n--,o._audioNode.splice(e,1))},_clearEndTimer:function(e){for(var o=this,n=-1,r=0;r=0&&l._howls.splice(t,1),delete e[o._src],o=null}},n)var s=function(o,n){if(n in e)return o._duration=e[n].duration,void c(o);if(/^data:[^;]+;base64,/.test(n)){for(var r=atob(n.split(",")[1]),t=new Uint8Array(r.length),a=0;a 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