qambi
Version:
MIDI sequencer, loads MIDI files, can record and playback MIDI, uses WebMIDI and WebAudio
1,481 lines (1,441 loc) • 445 kB
JavaScript
(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.qambi = 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<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/* FileSaver.js
* A saveAs() FileSaver implementation.
* 1.1.20160328
*
* By Eli Grey, http://eligrey.com
* License: MIT
* See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
*/
/*global self */
/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
var saveAs = saveAs || (function(view) {
"use strict";
// IE <10 is explicitly unsupported
if (typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
return;
}
var
doc = view.document
// only get URL when necessary in case Blob.js hasn't overridden it yet
, get_URL = function() {
return view.URL || view.webkitURL || view;
}
, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
, can_use_save_link = "download" in save_link
, click = function(node) {
var event = new MouseEvent("click");
node.dispatchEvent(event);
}
, is_safari = /Version\/[\d\.]+.*Safari/.test(navigator.userAgent)
, webkit_req_fs = view.webkitRequestFileSystem
, req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem
, throw_outside = function(ex) {
(view.setImmediate || view.setTimeout)(function() {
throw ex;
}, 0);
}
, force_saveable_type = "application/octet-stream"
, fs_min_size = 0
// the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
, arbitrary_revoke_timeout = 1000 * 40 // in ms
, revoke = function(file) {
var revoker = function() {
if (typeof file === "string") { // file is an object URL
get_URL().revokeObjectURL(file);
} else { // file is a File
file.remove();
}
};
/* // Take note W3C:
var
uri = typeof file === "string" ? file : file.toURL()
, revoker = function(evt) {
// idealy DownloadFinishedEvent.data would be the URL requested
if (evt.data === uri) {
if (typeof file === "string") { // file is an object URL
get_URL().revokeObjectURL(file);
} else { // file is a File
file.remove();
}
}
}
;
view.addEventListener("downloadfinished", revoker);
*/
setTimeout(revoker, arbitrary_revoke_timeout);
}
, dispatch = function(filesaver, event_types, event) {
event_types = [].concat(event_types);
var i = event_types.length;
while (i--) {
var listener = filesaver["on" + event_types[i]];
if (typeof listener === "function") {
try {
listener.call(filesaver, event || filesaver);
} catch (ex) {
throw_outside(ex);
}
}
}
}
, auto_bom = function(blob) {
// prepend BOM for UTF-8 XML and text/* types (including HTML)
if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
return new Blob(["\ufeff", blob], {type: blob.type});
}
return blob;
}
, FileSaver = function(blob, name, no_auto_bom) {
if (!no_auto_bom) {
blob = auto_bom(blob);
}
// First try a.download, then web filesystem, then object URLs
var
filesaver = this
, type = blob.type
, blob_changed = false
, object_url
, target_view
, dispatch_all = function() {
dispatch(filesaver, "writestart progress write writeend".split(" "));
}
// on any filesys errors revert to saving with object URLs
, fs_error = function() {
if (target_view && is_safari && typeof FileReader !== "undefined") {
// Safari doesn't allow downloading of blob urls
var reader = new FileReader();
reader.onloadend = function() {
var base64Data = reader.result;
target_view.location.href = "data:attachment/file" + base64Data.slice(base64Data.search(/[,;]/));
filesaver.readyState = filesaver.DONE;
dispatch_all();
};
reader.readAsDataURL(blob);
filesaver.readyState = filesaver.INIT;
return;
}
// don't create more object URLs than needed
if (blob_changed || !object_url) {
object_url = get_URL().createObjectURL(blob);
}
if (target_view) {
target_view.location.href = object_url;
} else {
var new_tab = view.open(object_url, "_blank");
if (new_tab === undefined && is_safari) {
//Apple do not allow window.open, see http://bit.ly/1kZffRI
view.location.href = object_url
}
}
filesaver.readyState = filesaver.DONE;
dispatch_all();
revoke(object_url);
}
, abortable = function(func) {
return function() {
if (filesaver.readyState !== filesaver.DONE) {
return func.apply(this, arguments);
}
};
}
, create_if_not_found = {create: true, exclusive: false}
, slice
;
filesaver.readyState = filesaver.INIT;
if (!name) {
name = "download";
}
if (can_use_save_link) {
object_url = get_URL().createObjectURL(blob);
setTimeout(function() {
save_link.href = object_url;
save_link.download = name;
click(save_link);
dispatch_all();
revoke(object_url);
filesaver.readyState = filesaver.DONE;
});
return;
}
// Object and web filesystem URLs have a problem saving in Google Chrome when
// viewed in a tab, so I force save with application/octet-stream
// http://code.google.com/p/chromium/issues/detail?id=91158
// Update: Google errantly closed 91158, I submitted it again:
// https://code.google.com/p/chromium/issues/detail?id=389642
if (view.chrome && type && type !== force_saveable_type) {
slice = blob.slice || blob.webkitSlice;
blob = slice.call(blob, 0, blob.size, force_saveable_type);
blob_changed = true;
}
// Since I can't be sure that the guessed media type will trigger a download
// in WebKit, I append .download to the filename.
// https://bugs.webkit.org/show_bug.cgi?id=65440
if (webkit_req_fs && name !== "download") {
name += ".download";
}
if (type === force_saveable_type || webkit_req_fs) {
target_view = view;
}
if (!req_fs) {
fs_error();
return;
}
fs_min_size += blob.size;
req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) {
fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) {
var save = function() {
dir.getFile(name, create_if_not_found, abortable(function(file) {
file.createWriter(abortable(function(writer) {
writer.onwriteend = function(event) {
target_view.location.href = file.toURL();
filesaver.readyState = filesaver.DONE;
dispatch(filesaver, "writeend", event);
revoke(file);
};
writer.onerror = function() {
var error = writer.error;
if (error.code !== error.ABORT_ERR) {
fs_error();
}
};
"writestart progress write abort".split(" ").forEach(function(event) {
writer["on" + event] = filesaver["on" + event];
});
writer.write(blob);
filesaver.abort = function() {
writer.abort();
filesaver.readyState = filesaver.DONE;
};
filesaver.readyState = filesaver.WRITING;
}), fs_error);
}), fs_error);
};
dir.getFile(name, {create: false}, abortable(function(file) {
// delete file if it already exists
file.remove();
save();
}), abortable(function(ex) {
if (ex.code === ex.NOT_FOUND_ERR) {
save();
} else {
fs_error();
}
}));
}), fs_error);
}), fs_error);
}
, FS_proto = FileSaver.prototype
, saveAs = function(blob, name, no_auto_bom) {
return new FileSaver(blob, name, no_auto_bom);
}
;
// IE 10+ (native saveAs)
if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
return function(blob, name, no_auto_bom) {
if (!no_auto_bom) {
blob = auto_bom(blob);
}
return navigator.msSaveOrOpenBlob(blob, name || "download");
};
}
FS_proto.abort = function() {
var filesaver = this;
filesaver.readyState = filesaver.DONE;
dispatch(filesaver, "abort");
};
FS_proto.readyState = FS_proto.INIT = 0;
FS_proto.WRITING = 1;
FS_proto.DONE = 2;
FS_proto.error =
FS_proto.onwritestart =
FS_proto.onprogress =
FS_proto.onwrite =
FS_proto.onabort =
FS_proto.onerror =
FS_proto.onwriteend =
null;
return saveAs;
}(
typeof self !== "undefined" && self
|| typeof window !== "undefined" && window
|| this.content
));
// `self` is undefined in Firefox for Android content script context
// while `this` is nsIContentFrameMessageManager
// with an attribute `content` that corresponds to the window
if (typeof module !== "undefined" && module.exports) {
module.exports.saveAs = saveAs;
} else if ((typeof define !== "undefined" && define !== null) && (define.amd !== null)) {
define([], function() {
return saveAs;
});
}
},{}],2:[function(require,module,exports){
// the whatwg-fetch polyfill installs the fetch() function
// on the global object (window or self)
//
// Return that as the export for use in Webpack, Browserify etc.
require('whatwg-fetch');
module.exports = self.fetch.bind(self);
},{"whatwg-fetch":6}],3:[function(require,module,exports){
(function (process,__dirname){
var path='./bin/';
var v=process.versions.node.split('.');
if (v[0]==0 && v[1]<=10) path+='0_10/';
else if (v[0]==0 && v[1]<=12) path+='0_12/';
else if (v[0]<=4) path+='4_8/';
else if (v[0]<=5) path+='5_12/';
else if (v[0]<=6) path+='6_12/';
else if (v[0]<=7) path+='7_10/';
else if (v[0]<=8) path+='8_9/';
if(process.platform=="win32"&&process.arch=="ia32") path+='win32/jazz';
else if(process.platform=="win32"&&process.arch=="x64") path+='win64/jazz';
else if(process.platform=="darwin"&&process.arch=="x64") path+='macos64/jazz';
else if(process.platform=="darwin"&&process.arch=="ia32") path+='macos32/jazz';
else if(process.platform=="linux"&&process.arch=="x64") path+='linux64/jazz';
else if(process.platform=="linux"&&process.arch=="ia32") path+='linux32/jazz';
else if(process.platform=="linux"&&process.arch=="arm") path+='linuxa7/jazz';
module.exports=require(path);
module.exports.package=require(__dirname + '/package.json');
}).call(this,require('_process'),"/node_modules/jazz-midi")
},{"_process":5}],4:[function(require,module,exports){
(function (global){
(function(global, factory) {
if (typeof exports === 'object' && typeof module !== 'undefined') {
module.exports = factory();
}
else if (typeof define === 'function' && define.amd) {
define('JZZ', [], factory);
}
else {
if (global.JZZ && global.JZZ.MIDI) return;
global.JZZ = factory();
}
})(this, function(){
var _scope = typeof window === 'undefined' ? global : window;
var _version = '0.4.6';
var i, j, k, m, n;
var _time = Date.now || function () { return new Date().getTime(); };
var _startTime = _time();
var _now = typeof performance != 'undefined' && performance.now ?
function() { return performance.now(); } : function() { return _time() - _startTime; };
// _R: common root for all async objects
function _R() {
this._orig = this;
this._ready = false;
this._queue = [];
this._err = [];
}
_R.prototype._exec = function() {
while (this._ready && this._queue.length) {
var x = this._queue.shift();
if (this._orig._bad) {
if (this._orig._hope && x[0] == _or) {
this._orig._hope = false;
x[0].apply(this, x[1]);
}
else {
this._queue = [];
this._orig._hope = false;
}
}
else if (x[0] != _or) {
x[0].apply(this, x[1]);
}
}
};
_R.prototype._push = function(func, arg) { this._queue.push([func, arg]); _R.prototype._exec.apply(this); };
_R.prototype._slip = function(func, arg) { this._queue.unshift([func, arg]); };
_R.prototype._pause = function() { this._ready = false; };
_R.prototype._resume = function() { this._ready = true; _R.prototype._exec.apply(this); };
_R.prototype._break = function(err) { this._orig._bad = true; this._orig._hope = true; if (err) this._orig._err.push(err); };
_R.prototype._repair = function() { this._orig._bad = false; };
_R.prototype._crash = function(err) { this._break(err); this._resume(); };
_R.prototype.err = function() { return _clone(this._err); };
_R.prototype._image = function() {
var F = function() {}; F.prototype = this._orig;
var ret = new F();
ret._ready = false;
ret._queue = [];
return ret;
};
function _wait(obj, delay) { setTimeout(function() { obj._resume(); }, delay); }
_R.prototype.wait = function(delay) {
if (!delay) return this;
var ret = this._image();
this._push(_wait, [ret, delay]);
return ret;
};
function _kick(obj) { obj._resume(); }
function _rechain(self, obj, name) {
self[name] = function() {
var arg = arguments;
var ret = obj._image();
this._push(_kick, [ret]);
return ret[name].apply(ret, arg);
};
}
function _and(q) { if (q instanceof Function) q.apply(this); else console.log(q); }
_R.prototype.and = function(func) { this._push(_and, [func]); return this; };
function _or(q) { if (q instanceof Function) q.apply(this); else console.log(q); }
_R.prototype.or = function(func) { this._push(_or, [func]); return this; };
_R.prototype._info = {};
_R.prototype.info = function() {
var info = _clone(this._orig._info);
if (typeof info.engine == 'undefined') info.engine = 'none';
if (typeof info.sysex == 'undefined') info.sysex = true;
return info;
};
_R.prototype.name = function() { return this.info().name; };
function _close(obj) {
this._break('closed');
obj._resume();
}
_R.prototype.close = function() {
var ret = new _R();
if (this._close) this._push(this._close, []);
this._push(_close, [ret]);
return ret;
};
function _tryAny(arr) {
if (!arr.length) {
this._break();
return;
}
var func = arr.shift();
if (arr.length) {
var self = this;
this._slip(_or, [ function(){ _tryAny.apply(self,[arr]);} ]);
}
try {
this._repair();
func.apply(this);
}
catch (err) {
this._break(err.toString());
}
}
function _push(arr, obj) {
for (var i = 0; i < arr.length; i++) if (arr[i] === obj) return;
arr.push(obj);
}
function _pop(arr, obj) {
for (var i = 0; i < arr.length; i++) if (arr[i] === obj) {
arr.splice(i, 1);
return;
}
}
// _J: JZZ object
function _J() {
_R.apply(this);
}
_J.prototype = new _R();
_J.prototype.time = function() { return 0; };
if (typeof performance != 'undefined' && performance.now) _J.prototype._time = function() { return performance.now(); };
function _initTimer() {
if (!_J.prototype._time) _J.prototype._time = function() { return Date.now(); };
_J.prototype._startTime = _J.prototype._time();
_J.prototype.time = function() { return _J.prototype._time() - _J.prototype._startTime; };
}
function _clone(obj, key, val) {
if (typeof key == 'undefined') return _clone(obj, [], []);
if (obj instanceof Object) {
for (var i = 0; i < key.length; i++) if (key[i] === obj) return val[i];
var ret;
if (obj instanceof Array) ret = []; else ret = {};
key.push(obj); val.push(ret);
for(var k in obj) if (obj.hasOwnProperty(k)) ret[k] = _clone(obj[k], key, val);
return ret;
}
return obj;
}
_J.prototype._info = { name: 'JZZ.js', ver: _version, version: _version };
var _outs = [];
var _ins = [];
function _postRefresh() {
this._orig._info.engine = _engine._type;
this._orig._info.version = _engine._version;
this._orig._info.sysex = _engine._sysex;
this._orig._info.inputs = [];
this._orig._info.outputs = [];
_outs = [];
_ins = [];
_engine._allOuts = {};
_engine._allIns = {};
var i, x;
for (i = 0; i < _engine._outs.length; i++) {
x = _engine._outs[i];
x.engine = _engine;
_engine._allOuts[x.name] = x;
this._orig._info.outputs.push({
name: x.name,
manufacturer: x.manufacturer,
version: x.version,
engine: _engine._type
});
_outs.push(x);
}
for (i = 0; i < _virtual._outs.length; i++) {
x = _virtual._outs[i];
this._orig._info.outputs.push({
name: x.name,
manufacturer: x.manufacturer,
version: x.version,
engine: x.type
});
_outs.push(x);
}
for (i = 0; i < _engine._ins.length; i++) {
x = _engine._ins[i];
x.engine = _engine;
_engine._allIns[x.name] = x;
this._orig._info.inputs.push({
name: x.name,
manufacturer: x.manufacturer,
version: x.version,
engine: _engine._type
});
_ins.push(x);
}
for (i = 0; i < _virtual._ins.length; i++) {
x = _virtual._ins[i];
this._orig._info.inputs.push({
name: x.name,
manufacturer: x.manufacturer,
version: x.version,
engine: x.type
});
_ins.push(x);
}
}
function _refresh() {
this._slip(_postRefresh, []);
_engine._refresh(this);
}
_J.prototype.refresh = function() {
this._push(_refresh, []);
return this;
};
function _filterList(q, arr) {
if (typeof q == 'undefined') return arr.slice();
var i, n;
var a = [];
if (q instanceof RegExp) {
for (n=0; n<arr.length; n++) if (q.test(arr[n].name)) a.push(arr[n]);
return a;
}
if (q instanceof Function) q = q(arr);
if (!(q instanceof Array)) q = [q];
for (i=0; i<q.length; i++) {
for (n=0; n<arr.length; n++) {
if (q[i]+'' === n+'' || q[i] === arr[n].name || (q[i] instanceof Object && q[i].name === arr[n].name)) a.push(arr[n]);
}
}
return a;
}
function _notFound(port, q) {
var msg;
if (q instanceof RegExp) msg = 'Port matching ' + q + ' not found';
else if (q instanceof Object || typeof q == 'undefined') msg = 'Port not found';
else msg = 'Port "' + q + '" not found';
port._crash(msg);
}
function _openMidiOut(port, arg) {
var arr = _filterList(arg, _outs);
if (!arr.length) { _notFound(port, arg); return; }
var pack = function(x) { return function() { x.engine._openOut(this, x.name); }; };
for (var i=0; i<arr.length; i++) arr[i] = pack(arr[i]);
port._slip(_tryAny, [arr]);
port._resume();
}
_J.prototype.openMidiOut = function(arg) {
var port = new _M();
this._push(_refresh, []);
this._push(_openMidiOut, [port, arg]);
return port;
};
function _openMidiIn(port, arg) {
var arr = _filterList(arg, _ins);
if (!arr.length) { _notFound(port, arg); return; }
var pack = function(x) { return function() { x.engine._openIn(this, x.name); }; };
for (var i=0; i<arr.length; i++) arr[i] = pack(arr[i]);
port._slip(_tryAny, [arr]);
port._resume();
}
_J.prototype.openMidiIn = function(arg) {
var port = new _M();
this._push(_refresh, []);
this._push(_openMidiIn, [port, arg]);
return port;
};
function _onChange(watcher, arg) {
watcher._slip(_connectW, [arg]);
watcher._resume();
}
_J.prototype.onChange = function(arg) {
if (!this._orig._watcher) this._orig._watcher = new _W();
var watcher = this._orig._watcher._image();
this._push(_onChange, [watcher, arg]);
return watcher;
};
_J.prototype._close = function() {
_engine._close();
};
// _M: MIDI-In/Out object
function _M() {
_R.apply(this);
this._handles = [];
this._outs = [];
}
_M.prototype = new _R();
_M.prototype._filter = function(msg) {
if (this._orig._mpe) {
var out;
var outs = 0;
if (this._handles && this._handles.length) {
outs = this._handles.length;
out = this._handles[0];
}
if (this._outs && this._outs.length) {
outs = this._outs.length;
out = this._outs[0];
}
if (outs == 1 && !out._mpe) {
msg = this._orig._mpe.filter(msg);
}
}
return msg;
};
_M.prototype._receive = function(msg) { this._emit(this._filter(msg)); };
function _receive(msg) { this._receive(msg); }
_M.prototype.send = function() {
this._push(_receive, [MIDI.apply(null, arguments)]);
return this;
};
_M.prototype.note = function(c, n, v, t) {
this.noteOn(c, n, v);
if (t) this.wait(t).noteOff(c, n);
return this;
};
_M.prototype._emit = function(msg) {
var i;
for (i = 0; i < this._handles.length; i++) this._handles[i].apply(this, [MIDI(msg)._stamp(this)]);
for (i = 0; i < this._outs.length; i++) {
var m = MIDI(msg);
if (!m._stamped(this._outs[i])) this._outs[i].send(m._stamp(this));
}
};
function _emit(msg) { this._emit(msg); }
_M.prototype.emit = function(msg) {
this._push(_emit, [msg]);
return this;
};
function _connect(arg) {
if (arg instanceof Function) _push(this._orig._handles, arg);
else _push(this._orig._outs, arg);
}
function _disconnect(arg) {
if (typeof arg == 'undefined') {
this._orig._handles = [];
this._orig._outs = [];
}
else if (arg instanceof Function) _pop(this._orig._handles, arg);
else _pop(this._orig._outs, arg);
}
_M.prototype.connect = function(arg) {
this._push(_connect, [arg]);
return this;
};
_M.prototype.disconnect = function(arg) {
this._push(_disconnect, [arg]);
return this;
};
_M.prototype.ch = function(n) {
if (typeof n == 'undefined') return this;
if (n != parseInt(n) || n < 0 || n > 15) throw RangeError('Bad channel value: ' + n + ' (must be from 0 to 15)');
var chan = new _C(this, n);
this._push(_kick, [chan]);
return chan;
};
function _mpe(m, n) {
if (!this._orig._mpe) this._orig._mpe = new MPE();
this._orig._mpe.setup(m, n);
}
_M.prototype.mpe = function(m, n) {
if (typeof m == 'undefined' && typeof n == 'undefined') return this;
MPE.validate(m, n);
var chan = n ? new _E(this, m, n) : new _C(this, m);
this._push(_mpe, [m, n]);
this._push(_kick, [chan]);
return chan;
};
// _C: MIDI Channel object
function _C(port, chan) {
_M.apply(this);
this._port = port._orig;
this._chan = chan;
_rechain(this, this._port, 'ch');
_rechain(this, this._port, 'mpe');
_rechain(this, this._port, 'connect');
_rechain(this, this._port, 'disconnect');
_rechain(this, this._port, 'close');
}
_C.prototype = new _M();
_C.prototype.channel = function() { return this._chan; };
_C.prototype._receive = function(msg) { this._port._receive(msg); };
_C.prototype.note = function(n, v, t) {
this.noteOn(n, v);
if (t) this.wait(t).noteOff(n);
return this;
};
// _E: MPE Channel object
function _E(port, m, n) {
_M.apply(this);
this._port = port._orig;
this._master = m;
this._band = n;
_rechain(this, this._port, 'ch');
_rechain(this, this._port, 'mpe');
_rechain(this, this._port, 'connect');
_rechain(this, this._port, 'disconnect');
_rechain(this, this._port, 'close');
}
_E.prototype = new _M();
_E.prototype.channel = function() { return this._master; };
_E.prototype._receive = function(msg) { this._port._receive(msg); };
_E.prototype.note = function(n, v, t) {
this.noteOn(n, v);
if (t) this.wait(t).noteOff(n);
return this;
};
// _W: Watcher object ~ MIDIAccess.onstatechange
function _W() {
_R.apply(this);
this._handles = [];
_rechain(this, _jzz, 'refresh');
_rechain(this, _jzz, 'openMidiOut');
_rechain(this, _jzz, 'openMidiIn');
_rechain(this, _jzz, 'onChange');
_rechain(this, _jzz, 'close');
}
_W.prototype = new _R();
function _connectW(arg) {
if (arg instanceof Function) {
if (!this._orig._handles.length) _engine._watch();
_push(this._orig._handles, arg);
}
}
function _disconnectW(arg) {
if (typeof arg == 'undefined') this._orig._handles = [];
else _pop(this._orig._handles, arg);
if (!this._orig._handles.length) _engine._unwatch();
}
_W.prototype.connect = function(arg) {
this._push(_connectW, [arg]);
return this;
};
_W.prototype.disconnect = function(arg) {
this._push(_disconnectW, [arg]);
return this;
};
function _changed(x0, y0, x1, y1) {
var i;
if (x0.length != x1.length || y0.length != y1.length) return true;
for (i = 0; i < x0.length; i++) if (x0[i].name != x1[i].name) return true;
for (i = 0; i < y0.length; i++) if (y0[i].name != y1[i].name) return true;
return false;
}
function _diff(x0, y0, x1, y1) {
if (!_changed(x0, y0, x1, y1)) return;
var ax = []; // added
var ay = [];
var rx = []; // removed
var ry = [];
var i;
var h = {};
for (i = 0; i < x0.length; i++) h[x0[i].name] = true;
for (i = 0; i < x1.length; i++) if (!h[x1[i].name]) ax.push(x1[i]);
h = {};
for (i = 0; i < x1.length; i++) h[x1[i].name] = true;
for (i = 0; i < x0.length; i++) if (!h[x0[i].name]) rx.push(x0[i]);
h = {};
for (i = 0; i < y0.length; i++) h[y0[i].name] = true;
for (i = 0; i < y1.length; i++) if (!h[y1[i].name]) ay.push(y1[i]);
h = {};
for (i = 0; i < y1.length; i++) h[y1[i].name] = true;
for (i = 0; i < y0.length; i++) if (!h[y0[i].name]) ry.push(y0[i]);
if (ax.length || rx.length || ay.length || ry.length) {
return { inputs: { added: ax, removed: rx }, outputs: { added: ay, removed: ry } };
}
}
function _fireW(arg) {
for (i = 0; i < _jzz._watcher._handles.length; i++) _jzz._watcher._handles[i].apply(_jzz, [arg]);
}
var _jzz;
var _engine = {};
var _virtual = { _outs: [], _ins: []};
// Node.js
function _tryNODE() {
if (typeof module != 'undefined' && module.exports) {
_initNode(require('jazz-midi'));
return;
}
this._break();
}
// Jazz-Plugin
function _tryJazzPlugin() {
var div = document.createElement('div');
div.style.visibility='hidden';
document.body.appendChild(div);
var obj = document.createElement('object');
obj.style.visibility='hidden';
obj.style.width='0px'; obj.style.height='0px';
obj.classid = 'CLSID:1ACE1618-1C7D-4561-AEE1-34842AA85E90';
obj.type = 'audio/x-jazz';
document.body.appendChild(obj);
if (obj.isJazz) {
_initJazzPlugin(obj);
return;
}
this._break();
}
// Web MIDI API
function _tryWebMIDI() {
if (navigator.requestMIDIAccess) {
var self = this;
var onGood = function(midi) {
_initWebMIDI(midi);
self._resume();
};
var onBad = function(msg) {
self._crash(msg);
};
var opt = {};
navigator.requestMIDIAccess(opt).then(onGood, onBad);
this._pause();
return;
}
this._break();
}
function _tryWebMIDIsysex() {
if (navigator.requestMIDIAccess) {
var self = this;
var onGood = function(midi) {
_initWebMIDI(midi, true);
self._resume();
};
var onBad = function(msg) {
self._crash(msg);
};
var opt = {sysex:true};
navigator.requestMIDIAccess(opt).then(onGood, onBad);
this._pause();
return;
}
this._break();
}
// Web-extension
function _tryCRX() {
var self = this;
var inst;
var msg;
function eventHandle(e) {
inst = true;
if (!msg) msg = document.getElementById('jazz-midi-msg');
if (!msg) return;
var a = [];
try { a = JSON.parse(msg.innerText);} catch (err) {}
msg.innerText = '';
document.removeEventListener('jazz-midi-msg', eventHandle);
if (a[0] === 'version') {
_initCRX(msg, a[2]);
self._resume();
}
else {
self._crash();
}
}
this._pause();
document.addEventListener('jazz-midi-msg', eventHandle);
try { document.dispatchEvent(new Event('jazz-midi'));} catch (err) {}
window.setTimeout(function() { if (!inst) self._crash();}, 0);
}
function _zeroBreak() {
this._pause();
var self = this;
setTimeout(function(){ self._crash();}, 0);
}
function _filterEngines(opt) {
var ret = [_tryNODE, _zeroBreak];
var arr = _filterEngineNames(opt);
for (var i=0; i<arr.length; i++) {
if (arr[i] == 'webmidi') {
if (opt && opt.sysex === true) ret.push(_tryWebMIDIsysex);
if (!opt || opt.sysex !== true || opt.degrade === true) ret.push(_tryWebMIDI);
}
else if (arr[i] == 'extension') ret.push(_tryCRX);
else if (arr[i] == 'plugin') ret.push(_tryJazzPlugin);
}
ret.push(_initNONE);
return ret;
}
function _filterEngineNames(opt) {
var web = ['extension', 'plugin', 'webmidi'];
if (!opt || !opt.engine) return web;
var arr = opt.engine instanceof Array ? opt.engine : [opt.engine];
var dup = {};
var none;
var etc;
var head = [];
var tail = [];
var i;
for (i = 0; i < arr.length; i++) {
var name = arr[i].toString().toLowerCase();
if (dup[name]) continue;
dup[name] = true;
if (name === 'none') none = true;
if (name === 'etc') etc = true;
if (etc) tail.push(name); else head.push(name);
_pop(web, name);
}
if (etc || head.length || tail.length) none = false;
return none ? [] : head.concat(etc ? web : tail);
}
function _initJZZ(opt) {
_initAudioContext();
_jzz = new _J();
_jzz._options = opt;
_jzz._push(_tryAny, [_filterEngines(opt)]);
_jzz.refresh();
_jzz._push(_initTimer, []);
_jzz._push(function(){ if (!_outs.length && !_ins.length) this._break(); }, []);
_jzz._resume();
}
function _initNONE() {
_engine._type = 'none';
_engine._sysex = true;
_engine._refresh = function() { _engine._outs = []; _engine._ins = []; };
_engine._watch = function() {};
_engine._unwatch = function() {};
}
// common initialization for Jazz-Plugin and jazz-midi
function _initEngineJP() {
_engine._inArr = [];
_engine._outArr = [];
_engine._inMap = {};
_engine._outMap = {};
_engine._outsW = [];
_engine._insW = [];
_engine._version = _engine._main.version;
_engine._sysex = true;
var watcher;
_closeAll = function() {
for (var i = 0; i < this.clients.length; i++) this._close(this.clients[i]);
};
_engine._refresh = function() {
_engine._outs = [];
_engine._ins = [];
var i, x;
for (i = 0; (x = _engine._main.MidiOutInfo(i)).length; i++) {
_engine._outs.push({ type: _engine._type, name: x[0], manufacturer: x[1], version: x[2] });
}
for (i = 0; (x = _engine._main.MidiInInfo(i)).length; i++) {
_engine._ins.push({ type: _engine._type, name: x[0], manufacturer: x[1], version: x[2] });
}
var diff = _diff(_engine._insW, _engine._outsW, _engine._ins, _engine._outs);
if (diff) {
for (j = 0; j < diff.inputs.removed.length; j++) {
impl = _engine._inMap[diff.inputs.removed[j].name];
if (impl) impl._closeAll();
}
for (j = 0; j < diff.outputs.removed.length; j++) {
impl = _engine._outMap[diff.inputs.removed[j].name];
if (impl) impl._closeAll();
}
_engine._insW = _engine._ins;
_engine._outsW = _engine._outs;
if (watcher) _fireW(diff);
}
};
_engine._openOut = function(port, name) {
var impl = _engine._outMap[name];
if (!impl) {
if (_engine._pool.length <= _engine._outArr.length) _engine._pool.push(_engine._newPlugin());
impl = {
name: name,
clients: [],
info: {
name: name,
manufacturer: _engine._allOuts[name].manufacturer,
version: _engine._allOuts[name].version,
type: 'MIDI-out',
sysex: _engine._sysex,
engine: _engine._type
},
_close: function(port){ _engine._closeOut(port); },
_closeAll: _closeAll,
_receive: function(a){ this.plugin.MidiOutRaw(a.slice()); }
};
var plugin = _engine._pool[_engine._outArr.length];
impl.plugin = plugin;
_engine._outArr.push(impl);
_engine._outMap[name] = impl;
}
if (!impl.open) {
var s = impl.plugin.MidiOutOpen(name);
if (s !== name) {
if (s) impl.plugin.MidiOutClose();
port._break(); return;
}
impl.open = true;
}
port._orig._impl = impl;
_push(impl.clients, port._orig);
port._info = impl.info;
port._receive = function(arg) { impl._receive(arg); };
port._close = function() { impl._close(this); };
};
_engine._openIn = function(port, name) {
var impl = _engine._inMap[name];
if (!impl) {
if (_engine._pool.length <= _engine._inArr.length) _engine._pool.push(_engine._newPlugin());
impl = {
name: name,
clients: [],
info: {
name: name,
manufacturer: _engine._allIns[name].manufacturer,
version: _engine._allIns[name].version,
type: 'MIDI-in',
sysex: _engine._sysex,
engine: _engine._type
},
_close: function(port){ _engine._closeIn(port); },
_closeAll: _closeAll,
handle: function(t, a) {
for (var i = 0; i < this.clients.length; i++) {
var msg = MIDI(a);
this.clients[i]._emit(msg);
}
}
};
var makeHandle = function(x) { return function(t, a) { x.handle(t, a); }; };
impl.onmidi = makeHandle(impl);
var plugin = _engine._pool[_engine._inArr.length];
impl.plugin = plugin;
_engine._inArr.push(impl);
_engine._inMap[name] = impl;
}
if (!impl.open) {
var s = impl.plugin.MidiInOpen(name, impl.onmidi);
if (s !== name) {
if (s) impl.plugin.MidiInClose();
port._break(); return;
}
impl.open = true;
}
port._orig._impl = impl;
_push(impl.clients, port._orig);
port._info = impl.info;
port._close = function() { impl._close(this); };
};
_engine._closeOut = function(port) {
var impl = port._impl;
_pop(impl.clients, port._orig);
if (!impl.clients.length) {
impl.open = false;
impl.plugin.MidiOutClose();
}
};
_engine._closeIn = function(port) {
var impl = port._impl;
_pop(impl.clients, port._orig);
if (!impl.clients.length) {
impl.open = false;
impl.plugin.MidiInClose();
}
};
_engine._close = function() {
for (var i = 0; i < _engine._inArr.length; i++) if (_engine._inArr[i].open) _engine._inArr[i].plugin.MidiInClose();
_engine._unwatch();
};
function onChange() {
if (watcher) {
_engine._refresh();
watcher = false;
}
}
function watch(name) {
watcher = true;
setTimeout(onChange, 0);
}
_engine._watch = function() {
_engine._main.OnConnectMidiIn(watch);
_engine._main.OnConnectMidiOut(watch);
_engine._main.OnDisconnectMidiIn(watch);
_engine._main.OnDisconnectMidiOut(watch);
};
_engine._unwatch = function() {
_engine._main.OnConnectMidiIn();
_engine._main.OnConnectMidiOut();
_engine._main.OnDisconnectMidiIn();
_engine._main.OnDisconnectMidiOut();
};
_J.prototype._time = function() { return _engine._main.Time(); };
}
function _initNode(obj) {
_engine._type = 'node';
_engine._main = obj;
_engine._pool = [];
_engine._newPlugin = function() { return new obj.MIDI(); };
_initEngineJP();
}
function _initJazzPlugin(obj) {
_engine._type = 'plugin';
_engine._main = obj;
_engine._pool = [obj];
_engine._newPlugin = function() {
var plg = document.createElement('object');
plg.style.visibility='hidden';
plg.style.width='0px'; obj.style.height='0px';
plg.classid = 'CLSID:1ACE1618-1C7D-4561-AEE1-34842AA85E90';
plg.type = 'audio/x-jazz';
document.body.appendChild(plg);
return plg.isJazz ? plg : undefined;
};
_initEngineJP();
}
function _initWebMIDI(access, sysex) {
_engine._type = 'webmidi';
_engine._version = 43;
_engine._sysex = !!sysex;
_engine._access = access;
_engine._inMap = {};
_engine._outMap = {};
_engine._outsW = [];
_engine._insW = [];
var watcher;
_closeAll = function() {
for (var i = 0; i < this.clients.length; i++) this._close(this.clients[i]);
};
_engine._refresh = function() {
_engine._outs = [];
_engine._ins = [];
_engine._access.outputs.forEach(function(port, key) {
_engine._outs.push({type: _engine._type, name: port.name, manufacturer: port.manufacturer, version: port.version});
});
_engine._access.inputs.forEach(function(port, key) {
_engine._ins.push({type: _engine._type, name: port.name, manufacturer: port.manufacturer, version: port.version});
});
var diff = _diff(_engine._insW, _engine._outsW, _engine._ins, _engine._outs);
if (diff) {
for (j = 0; j < diff.inputs.removed.length; j++) {
impl = _engine._inMap[diff.inputs.removed[j].name];
if (impl) impl._closeAll();
}
for (j = 0; j < diff.outputs.removed.length; j++) {
impl = _engine._outMap[diff.inputs.removed[j].name];
if (impl) impl._closeAll();
}
_engine._insW = _engine._ins;
_engine._outsW = _engine._outs;
if (watcher) _fireW(diff);
}
};
_engine._openOut = function(port, name) {
var impl = _engine._outMap[name];
if (!impl) {
impl = {
name: name,
clients: [],
info: {
name: name,
manufacturer: _engine._allOuts[name].manufacturer,
version: _engine._allOuts[name].version,
type: 'MIDI-out',
sysex: _engine._sysex,
engine: _engine._type
},
_close: function(port){ _engine._closeOut(port); },
_closeAll: _closeAll,
_receive: function(a){ if (impl.dev) this.dev.send(a.slice());}
};
}
var found;
_engine._access.outputs.forEach(function(dev, key) {
if (dev.name === name) found = dev;
});
if (found) {
impl.dev = found;
_engine._outMap[name] = impl;
if (impl.dev.open) impl.dev.open();
port._orig._impl = impl;
_push(impl.clients, port._orig);
port._info = impl.info;
port._receive = function(arg) { impl._receive(arg); };
port._close = function() { impl._close(this); };
}
else port._break();
};
_engine._openIn = function(port, name) {
var impl = _engine._inMap[name];
if (!impl) {
impl = {
name: name,
clients: [],
info: {
name: name,
manufacturer: _engine._allIns[name].manufacturer,
version: _engine._allIns[name].version,
type: 'MIDI-in',
sysex: _engine._sysex,
engine: _engine._type
},
_close: function(port){ _engine._closeIn(port); },
_closeAll: _closeAll,
handle: function(evt) {
for (var i = 0; i < this.clients.length; i++) {
var msg = MIDI([].slice.call(evt.data));
this.clients[i]._emit(msg);
}
}
};
}
var found;
_engine._access.inputs.forEach(function(dev, key) {
if (dev.name === name) found = dev;
});
if (found) {
impl.dev = found;
var makeHandle = function(x) { return function(evt) { x.handle(evt); }; };
impl.dev.onmidimessage = makeHandle(impl);
_engine._inMap[name] = impl;
if (impl.dev.open) impl.dev.open();
port._orig._impl = impl;
_push(impl.clients, port._orig);
port._info = impl.info;
port._close = function() { impl._close(this); };
}
else port._break();
};
_engine._closeOut = function(port) {
var impl = port._impl;
_pop(impl.clients, port._orig);
if (!impl.clients.length) {
if (impl.dev && impl.dev.close) impl.dev.close();
impl.dev = undefined;
}
};
_engine._closeIn = function(port) {
var impl = port._impl;
_pop(impl.clients, port._orig);
if (!impl.clients.length) {
if (impl.dev && impl.dev.close) impl.dev.close();
impl.dev = undefined;
}
};
_engine._close = function() {
};
_engine._watch = function() {
_engine._access.onstatechange = function() {
watcher = true;
setTimeout(function() {
if (watcher) {
_engine._refresh();
watcher = false;
}
}, 0);
};
};
_engine._unwatch = function() {
_engine._access.onstatechange = undefined;
};
}
function _initCRX(msg, ver) {
_engine._type = 'extension';
_engine._version = ver;
_engine._sysex = true;
_engine._pool = [];
_engine._outs = [];
_engine._ins = [];
_engine._inArr = [];
_engine._outArr = [];
_engine._inMap = {};
_engine._outMap = {};
_engine._outsW = [];
_engine._insW = [];
_engine.refreshClients = [];
_engine._msg = msg;
_engine._newPlugin = function() {
var plugin = { id: _engine._pool.length };
if (!plugin.id) plugin.ready = true;
else document.dispatchEvent(new CustomEvent('jazz-midi', {detail:['new']}));
_engine._pool.push(plugin);
};
_engine._newPlugin();
_engine._refresh = function(client) {
_engine.refreshClients.push(client);
client._pause();
setTimeout(function() {
document.dispatchEvent(new CustomEvent('jazz-midi', { detail: ['refresh'] }));
}, 0);
};
_closeAll = function() {
for (var i = 0; i < this.clients.length; i++) this._close(this.clients[i]);
};
_engine._openOut = function(port, name) {
var impl = _engine._outMap[name];
if (!impl) {
if (_engine._pool.length <= _engine._outArr.length) _engine._newPlugin();
var plugin = _engine._pool[_engine._outArr.length];
impl = {
name: name,
clients: [],
info: {
name: name,
manufacturer: _engine._allOuts[name].manufacturer,
version: _engine._allOuts[name].version,
type: 'MIDI-out',
sysex: _engine._sysex,
engine: _engine._type
},
_start: function(){ document.dispatchEvent(new CustomEvent('jazz-midi', {detail:['openout', plugin.id, name]})); },
_close: function(port){ _engine._closeOut(port); },
_closeAll: _closeAll,
_receive: function(a){ var v = a.slice(); v.splice(0, 0, 'play', plugin.id); document.dispatchEvent(new CustomEvent('jazz-midi', {detail: v})); }
};
impl.plugin = plugin;
plugin.output = impl;
_engine._outArr.push(impl);
_engine._outMap[name] = impl;
}
port._orig._impl = impl;
_push(impl.clients, port._orig);
port._info = impl.info;
port._receive = function(arg) { impl._receive(arg); };
port._close = function() { impl._close(this); };
if (!impl.open) {
if (impl.plugin.ready) impl._start();
port._pause();
}
};
_engine._openIn = function(port, name) {
var impl = _engine._inMap[name];
if (!impl) {
if (_engine._pool.length <= _engine._inArr.length) _engine._newPlugin();
var plugin = _engine._pool[_engine._inArr.length];
impl = {
name: name,
clients: [],
info: {
name: name,
manufacturer: _engine._allIns[name].manufacturer,
version: _engine._allIns[name].version,
type: 'MIDI-in',
sysex: _engine._sysex,
engine: _engine._type
},
_start: function(){ document.dispatchEvent(new CustomEvent('jazz-midi', {detail:['openin', plugin.id, name]})); },
_close: function(port){ _engine._closeIn(port); },
_closeAll: _closeAll
};
impl.plugin = plugin;
plugin.input = impl;
_engine._inArr.push(impl);
_engine._inMap[name] = impl;
}
port._orig._impl = impl;
_push(impl.clients, port._orig);
port._info = impl.info;
port._close = function() { impl._close(this); };
if (!impl.open) {
if (impl.plugin.ready) impl._start();
port._pause();
}
};
_engine._closeOut = function(port) {
var impl = port._impl;
_pop(impl.clients, port._orig);
if (!impl.clients.length) {
impl.open = false;
document.dispatchEvent(new CustomEvent('jazz-midi', {detail:['closeout', impl.plugin.id]}));
}
};
_engine._closeIn = function(port) {
var impl = port._impl;
_pop(impl.clients, port._orig);
if (!impl.clients.length) {
impl.open = false;
document.dispatchEvent(new CustomEvent('jazz-midi', {detail:['closein', impl.plugin.id]}));
}
};
_engine._close = function() {
};
var watcher;
_engine._watch = function() {
_engine._insW = _engine._ins;
_engine._outsW = _engine._outs;
watcher = setInterval(function() {
document.dispatchEvent(new CustomEvent('jazz-midi', {detail:['refresh']}));
}, 250);
};
_engine._unwatch = function() {
clearInterval(watcher);
watcher = undefined;
};
document.addEventListener('jazz-midi-msg', function(e) {
var v = _engine._msg.innerText.split('\n');
var impl, i, j;
_engine._msg.innerText = '';
for (i = 0; i < v.length; i++) {
var a = [];
try { a = JSON.parse(v[i]);} catch (err) {}
if (!a.length) continue;
if (a[0] === 'refresh') {
if (a[1].ins) {
for (j = 0; j < a[1].ins.length; j++) a[1].ins[j].type = _engine._type;
_engine._ins = a[1].ins;
}
if (a[1].outs) {
for (j = 0; j < a[1].outs.length; j++) a[1].outs[j].type = _engine._type;
_engine._outs = a[1].outs;
}
for (j = 0; j < _engine.refreshClients.length; j++) _engine.refreshClients[j]._resume();
_engine.refreshClients = [];
var diff = _diff(_engine._insW, _engine._outsW, _engine._ins, _engine._outs);
if (diff) {
_engine._insW = _engine._ins;
_engine._outsW = _engine._outs;
for (j = 0; j < diff.inputs.removed.length; j++) {
impl = _engine._inMap[diff.inputs.removed[j].name];
if (impl) impl._closeAll();
}
for (j = 0; j < diff.outputs.removed.length; j++) {
impl = _engine._outMap[diff.outputs.removed[j].name];
if (impl) impl._closeAll();
}
if (watcher) _fireW(diff);
}
}
else if (a[0] === 'version') {
var plugin = _engine._pool[a[1]];
if (plugin) {
plugin.ready = true;
if (plugin.input) plugin.input._start();
if (plugin.output) plugin.output._start();
}
}
else if (a[0] === 'openout') {
impl = _engine._pool[a[1]].output;
if (impl) {
if (a[2] == impl.name) {
impl.open = true;
if (impl.clients) for (j = 0; j < impl.clients.length; j++) impl.clients[j]._resume();
}
else if (impl.clients) for (j = 0; j < impl.clients.length; j++) impl.clients[j]._crash();
}
}
else if (a[0] === 'openin') {
impl = _engine._pool[a[1