123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421 |
- /* Copyright 2013 Chris Wilson
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- // Initialize the MIDI library.
- (function (global) {
- 'use strict';
- var midiIO, _requestMIDIAccess, MIDIAccess, _onReady, MIDIPort, MIDIInput, MIDIOutput, _midiProc;
- function Promise() {
- }
- Promise.prototype.then = function(accept, reject) {
- this.accept = accept;
- this.reject = reject;
- }
- Promise.prototype.succeed = function(access) {
- if (this.accept)
- this.accept(access);
- }
- Promise.prototype.fail = function(error) {
- if (this.reject)
- this.reject(error);
- }
- function _JazzInstance() {
- this.inputInUse = false;
- this.outputInUse = false;
- // load the Jazz plugin
- var o1 = document.createElement("object");
- o1.id = "_Jazz" + Math.random() + "ie";
- o1.classid = "CLSID:1ACE1618-1C7D-4561-AEE1-34842AA85E90";
- this.activeX = o1;
- var o2 = document.createElement("object");
- o2.id = "_Jazz" + Math.random();
- o2.type="audio/x-jazz";
- o1.appendChild(o2);
- this.objRef = o2;
- var e = document.createElement("p");
- e.appendChild(document.createTextNode("This page requires the "));
- var a = document.createElement("a");
- a.appendChild(document.createTextNode("Jazz plugin"));
- a.href = "http://jazz-soft.net/";
- e.appendChild(a);
- e.appendChild(document.createTextNode("."));
- o2.appendChild(e);
- var insertionPoint = document.getElementById("MIDIPlugin");
- if (!insertionPoint) {
- // Create hidden element
- var insertionPoint = document.createElement("div");
- insertionPoint.id = "MIDIPlugin";
- insertionPoint.style.position = "absolute";
- insertionPoint.style.visibility = "hidden";
- insertionPoint.style.left = "-9999px";
- insertionPoint.style.top = "-9999px";
- document.body.appendChild(insertionPoint);
- }
- insertionPoint.appendChild(o1);
- if (this.objRef.isJazz)
- this._Jazz = this.objRef;
- else if (this.activeX.isJazz)
- this._Jazz = this.activeX;
- else
- this._Jazz = null;
- if (this._Jazz) {
- this._Jazz._jazzTimeZero = this._Jazz.Time();
- this._Jazz._perfTimeZero = window.performance.now();
- }
- }
- _requestMIDIAccess = function _requestMIDIAccess() {
- var access = new MIDIAccess();
- return access._promise;
- };
- // API Methods
- MIDIAccess = function() {
- this._jazzInstances = new Array();
- this._jazzInstances.push( new _JazzInstance() );
- this._promise = new Promise;
- if (this._jazzInstances[0]._Jazz) {
- this._Jazz = this._jazzInstances[0]._Jazz;
- window.setTimeout( _onReady.bind(this), 3 );
- } else {
- window.setTimeout( _onNotReady.bind(this), 3 );
- }
- };
- _onReady = function _onReady() {
- if (this._promise)
- this._promise.succeed(this);
- };
- function _onNotReady() {
- if (this._promise)
- this._promise.fail( { code: 1 } );
- };
- MIDIAccess.prototype.inputs = function( ) {
- if (!this._Jazz)
- return null;
- var list=this._Jazz.MidiInList();
- var inputs = new Array( list.length );
- for ( var i=0; i<list.length; i++ ) {
- inputs[i] = new MIDIInput( this, list[i], i );
- }
- return inputs;
- }
- MIDIAccess.prototype.outputs = function( ) {
- if (!this._Jazz)
- return null;
- var list=this._Jazz.MidiOutList();
- var outputs = new Array( list.length );
- for ( var i=0; i<list.length; i++ ) {
- outputs[i] = new MIDIOutput( this, list[i], i );
- }
- return outputs;
- };
- MIDIInput = function MIDIInput( midiAccess, name, index ) {
- this._listeners = [];
- this._midiAccess = midiAccess;
- this._index = index;
- this._inLongSysexMessage = false;
- this._sysexBuffer = new Uint8Array();
- this.id = "" + index + "." + name;
- this.manufacturer = "";
- this.name = name;
- this.type = "input";
- this.version = "";
- this.onmidimessage = null;
- var inputInstance = null;
- for (var i=0; (i<midiAccess._jazzInstances.length)&&(!inputInstance); i++) {
- if (!midiAccess._jazzInstances[i].inputInUse)
- inputInstance=midiAccess._jazzInstances[i];
- }
- if (!inputInstance) {
- inputInstance = new _JazzInstance();
- midiAccess._jazzInstances.push( inputInstance );
- }
- inputInstance.inputInUse = true;
- this._jazzInstance = inputInstance._Jazz;
- this._input = this._jazzInstance.MidiInOpen( this._index, _midiProc.bind(this) );
- };
- // Introduced in DOM Level 2:
- MIDIInput.prototype.addEventListener = function (type, listener, useCapture ) {
- if (type !== "midimessage")
- return;
- for (var i=0; i<this._listeners.length; i++)
- if (this._listeners[i] == listener)
- return;
- this._listeners.push( listener );
- };
- MIDIInput.prototype.removeEventListener = function (type, listener, useCapture ) {
- if (type !== "midimessage")
- return;
- for (var i=0; i<this._listeners.length; i++)
- if (this._listeners[i] == listener) {
- this._listeners.splice( i, 1 ); //remove it
- return;
- }
- };
- MIDIInput.prototype.preventDefault = function() {
- this._pvtDef = true;
- };
- MIDIInput.prototype.dispatchEvent = function (evt) {
- this._pvtDef = false;
- // dispatch to listeners
- for (var i=0; i<this._listeners.length; i++)
- if (this._listeners[i].handleEvent)
- this._listeners[i].handleEvent.bind(this)( evt );
- else
- this._listeners[i].bind(this)( evt );
- if (this.onmidimessage)
- this.onmidimessage( evt );
- return this._pvtDef;
- };
- MIDIInput.prototype.appendToSysexBuffer = function ( data ) {
- var oldLength = this._sysexBuffer.length;
- var tmpBuffer = new Uint8Array( oldLength + data.length );
- tmpBuffer.set( this._sysexBuffer );
- tmpBuffer.set( data, oldLength );
- this._sysexBuffer = tmpBuffer;
- };
- MIDIInput.prototype.bufferLongSysex = function ( data, initialOffset ) {
- var j = initialOffset;
- while (j<data.length) {
- if (data[j] == 0xF7) {
- // end of sysex!
- j++;
- this.appendToSysexBuffer( data.slice(initialOffset, j) );
- return j;
- }
- j++;
- }
- // didn't reach the end; just tack it on.
- this.appendToSysexBuffer( data.slice(initialOffset, j) );
- this._inLongSysexMessage = true;
- return j;
- };
- _midiProc = function _midiProc( timestamp, data ) {
- // Have to use createEvent/initEvent because IE10 fails on new CustomEvent. Thanks, IE!
- var length = 0;
- var i,j;
- var isSysexMessage = false;
- // Jazz sometimes passes us multiple messages at once, so we need to parse them out
- // and pass them one at a time.
- for (i=0; i<data.length; i+=length) {
- if (this._inLongSysexMessage) {
- i = this.bufferLongSysex(data,i);
- if ( data[i-1] != 0xf7 ) {
- // ran off the end without hitting the end of the sysex message
- return;
- }
- isSysexMessage = true;
- } else {
- isSysexMessage = false;
- switch (data[i] & 0xF0) {
- case 0x80: // note off
- case 0x90: // note on
- case 0xA0: // polyphonic aftertouch
- case 0xB0: // control change
- case 0xE0: // channel mode
- length = 3;
- break;
- case 0xC0: // program change
- case 0xD0: // channel aftertouch
- length = 2;
- break;
- case 0xF0:
- switch (data[i]) {
- case 0xf0: // variable-length sysex.
- i = this.bufferLongSysex(data,i);
- if ( data[i-1] != 0xf7 ) {
- // ran off the end without hitting the end of the sysex message
- return;
- }
- isSysexMessage = true;
- break;
- case 0xF1: // MTC quarter frame
- case 0xF3: // song select
- length = 2;
- break;
- case 0xF2: // song position pointer
- length = 3;
- break;
- default:
- length = 1;
- break;
- }
- break;
- }
- }
- var evt = document.createEvent( "Event" );
- evt.initEvent( "midimessage", false, false );
- evt.receivedTime = parseFloat( timestamp.toString()) + this._jazzInstance._perfTimeZero;
- if (isSysexMessage || this._inLongSysexMessage) {
- evt.data = new Uint8Array( this._sysexBuffer );
- this._sysexBuffer = new Uint8Array(0);
- this._inLongSysexMessage = false;
- } else
- evt.data = new Uint8Array(data.slice(i, length+i));
- this.dispatchEvent( evt );
- }
- };
- MIDIOutput = function MIDIOutput( midiAccess, name, index ) {
- this._listeners = [];
- this._midiAccess = midiAccess;
- this._index = index;
- this.id = "" + index + "." + name;
- this.manufacturer = "";
- this.name = name;
- this.type = "output";
- this.version = "";
- var outputInstance = null;
- for (var i=0; (i<midiAccess._jazzInstances.length)&&(!outputInstance); i++) {
- if (!midiAccess._jazzInstances[i].outputInUse)
- outputInstance=midiAccess._jazzInstances[i];
- }
- if (!outputInstance) {
- outputInstance = new _JazzInstance();
- midiAccess._jazzInstances.push( outputInstance );
- }
- outputInstance.outputInUse = true;
- this._jazzInstance = outputInstance._Jazz;
- this._jazzInstance.MidiOutOpen(this.name);
- };
- function _sendLater() {
- this.jazz.MidiOutLong( this.data ); // handle send as sysex
- }
- MIDIOutput.prototype.send = function( data, timestamp ) {
- var delayBeforeSend = 0;
- if (data.length === 0)
- return false;
- if (timestamp)
- delayBeforeSend = Math.floor( timestamp - window.performance.now() );
- if (timestamp && (delayBeforeSend>1)) {
- var sendObj = new Object();
- sendObj.jazz = this._jazzInstance;
- sendObj.data = data;
- window.setTimeout( _sendLater.bind(sendObj), delayBeforeSend );
- } else {
- this._jazzInstance.MidiOutLong( data );
- }
- return true;
- };
- //init: create plugin
- if (!window.navigator.requestMIDIAccess)
- window.navigator.requestMIDIAccess = _requestMIDIAccess;
- }(window));
- // Polyfill window.performance.now() if necessary.
- (function (exports) {
- var perf = {}, props;
- function findAlt() {
- var prefix = ['moz', 'webkit', 'o', 'ms'],
- i = prefix.length,
- //worst case, we use Date.now()
- props = {
- value: (function (start) {
- return function () {
- return Date.now() - start;
- };
- }(Date.now()))
- };
- //seach for vendor prefixed version
- for (; i >= 0; i--) {
- if ((prefix[i] + "Now") in exports.performance) {
- props.value = function (method) {
- return function () {
- exports.performance[method]();
- }
- }(prefix[i] + "Now");
- return props;
- }
- }
- //otherwise, try to use connectionStart
- if ("timing" in exports.performance && "connectStart" in exports.performance.timing) {
- //this pretty much approximates performance.now() to the millisecond
- props.value = (function (start) {
- return function() {
- Date.now() - start;
- };
- }(exports.performance.timing.connectStart));
- }
- return props;
- }
- //if already defined, bail
- if (("performance" in exports) && ("now" in exports.performance))
- return;
- if (!("performance" in exports))
- Object.defineProperty(exports, "performance", {
- get: function () {
- return perf;
- }});
- //otherwise, performance is there, but not "now()"
- props = findAlt();
- Object.defineProperty(exports.performance, "now", props);
- }(window));
|