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