4 changed files with 1060 additions and 0 deletions
@ -0,0 +1,57 @@
@@ -0,0 +1,57 @@
|
||||
if (typeof(MIDI) === "undefined") var MIDI = {}; |
||||
|
||||
(function() { "use strict"; |
||||
|
||||
var supports = {};
|
||||
var pending = 0; |
||||
var canPlayThrough = function (src) { |
||||
pending ++; |
||||
var audio = new Audio(); |
||||
var mime = src.split(";")[0]; |
||||
audio.id = "audio"; |
||||
audio.setAttribute("preload", "auto"); |
||||
audio.setAttribute("audiobuffer", true); |
||||
audio.addEventListener("error", function() { |
||||
supports[mime] = false; |
||||
pending --; |
||||
}, false); |
||||
audio.addEventListener("canplaythrough", function() { |
||||
supports[mime] = true; |
||||
pending --; |
||||
}, false); |
||||
audio.src = "data:" + src; |
||||
document.body.appendChild(audio); |
||||
}; |
||||
|
||||
MIDI.audioDetect = function(callback) { |
||||
// check whether <audio> tag is supported
|
||||
if (typeof(Audio) === "undefined") return callback({}); |
||||
// check whether canPlayType is supported
|
||||
var audio = new Audio(); |
||||
if (typeof(audio.canPlayType) === "undefined") return callback(supports); |
||||
// see what we can learn from the browser
|
||||
var vorbis = audio.canPlayType('audio/ogg; codecs="vorbis"'); |
||||
vorbis = (vorbis === "probably" || vorbis === "maybe"); |
||||
var mpeg = audio.canPlayType('audio/mpeg'); |
||||
mpeg = (mpeg === "probably" || mpeg === "maybe"); |
||||
// maybe nothing is supported
|
||||
if (!vorbis && !mpeg) { |
||||
callback(supports); |
||||
return; |
||||
} |
||||
// or maybe something is supported
|
||||
if (vorbis) canPlayThrough("audio/ogg;base64,T2dnUwACAAAAAAAAAADqnjMlAAAAAOyyzPIBHgF2b3JiaXMAAAAAAUAfAABAHwAAQB8AAEAfAACZAU9nZ1MAAAAAAAAAAAAA6p4zJQEAAAANJGeqCj3//////////5ADdm9yYmlzLQAAAFhpcGguT3JnIGxpYlZvcmJpcyBJIDIwMTAxMTAxIChTY2hhdWZlbnVnZ2V0KQAAAAABBXZvcmJpcw9CQ1YBAAABAAxSFCElGVNKYwiVUlIpBR1jUFtHHWPUOUYhZBBTiEkZpXtPKpVYSsgRUlgpRR1TTFNJlVKWKUUdYxRTSCFT1jFloXMUS4ZJCSVsTa50FkvomWOWMUYdY85aSp1j1jFFHWNSUkmhcxg6ZiVkFDpGxehifDA6laJCKL7H3lLpLYWKW4q91xpT6y2EGEtpwQhhc+211dxKasUYY4wxxsXiUyiC0JBVAAABAABABAFCQ1YBAAoAAMJQDEVRgNCQVQBABgCAABRFcRTHcRxHkiTLAkJDVgEAQAAAAgAAKI7hKJIjSZJkWZZlWZameZaouaov+64u667t6roOhIasBACAAAAYRqF1TCqDEEPKQ4QUY9AzoxBDDEzGHGNONKQMMogzxZAyiFssLqgQBKEhKwKAKAAAwBjEGGIMOeekZFIi55iUTkoDnaPUUcoolRRLjBmlEluJMYLOUeooZZRCjKXFjFKJscRUAABAgAMAQICFUGjIigAgCgCAMAYphZRCjCnmFHOIMeUcgwwxxiBkzinoGJNOSuWck85JiRhjzjEHlXNOSuekctBJyaQTAAAQ4AAAEGAhFBqyIgCIEwAwSJKmWZomipamiaJniqrqiaKqWp5nmp5pqqpnmqpqqqrrmqrqypbnmaZnmqrqmaaqiqbquqaquq6nqrZsuqoum65q267s+rZru77uqapsm6or66bqyrrqyrbuurbtS56nqqKquq5nqq6ruq5uq65r25pqyq6purJtuq4tu7Js664s67pmqq5suqotm64s667s2rYqy7ovuq5uq7Ks+6os+75s67ru2rrwi65r66os674qy74x27bwy7ouHJMnqqqnqq7rmarrqq5r26rr2rqmmq5suq4tm6or26os67Yry7aumaosm64r26bryrIqy77vyrJui67r66Ys67oqy8Lu6roxzLat+6Lr6roqy7qvyrKuu7ru+7JuC7umqrpuyrKvm7Ks+7auC8us27oxuq7vq7It/KosC7+u+8Iy6z5jdF1fV21ZGFbZ9n3d95Vj1nVhWW1b+V1bZ7y+bgy7bvzKrQvLstq2scy6rSyvrxvDLux8W/iVmqratum6um7Ksq/Lui60dd1XRtf1fdW2fV+VZd+3hV9pG8OwjK6r+6os68Jry8ov67qw7MIvLKttK7+r68ow27qw3L6wLL/uC8uq277v6rrStXVluX2fsSu38QsAABhwAAAIMKEMFBqyIgCIEwBAEHIOKQahYgpCCKGkEEIqFWNSMuakZM5JKaWUFEpJrWJMSuaclMwxKaGUlkopqYRSWiqlxBRKaS2l1mJKqcVQSmulpNZKSa2llGJMrcUYMSYlc05K5pyUklJrJZXWMucoZQ5K6iCklEoqraTUYuacpA46Kx2E1EoqMZWUYgupxFZKaq2kFGMrMdXUWo4hpRhLSrGVlFptMdXWWqs1YkxK5pyUzDkqJaXWSiqtZc5J6iC01DkoqaTUYiopxco5SR2ElDLIqJSUWiupxBJSia20FGMpqcXUYq4pxRZDSS2WlFosqcTWYoy1tVRTJ6XFklKMJZUYW6y5ttZqDKXEVkqLsaSUW2sx1xZjjqGkFksrsZWUWmy15dhayzW1VGNKrdYWY40x5ZRrrT2n1mJNMdXaWqy51ZZbzLXnTkprpZQWS0oxttZijTHmHEppraQUWykpxtZara3FXEMpsZXSWiypxNhirLXFVmNqrcYWW62ltVprrb3GVlsurdXcYqw9tZRrrLXmWFNtBQAADDgAAASYUAYKDVkJAEQBAADGMMYYhEYpx5yT0ijlnHNSKucghJBS5hyEEFLKnINQSkuZcxBKSSmUklJqrYVSUmqttQIAAAocAAACbNCUWByg0JCVAEAqAIDBcTRNFFXVdX1fsSxRVFXXlW3jVyxNFFVVdm1b+DVRVFXXtW3bFn5NFFVVdmXZtoWiqrqybduybgvDqKqua9uybeuorqvbuq3bui9UXVmWbVu3dR3XtnXd9nVd+Bmzbeu2buu+8CMMR9/4IeTj+3RCCAAAT3AAACqwYXWEk6KxwEJDVgIAGQAAgDFKGYUYM0gxphhjTDHGmAAAgAEHAIAAE8pAoSErAoAoAADAOeecc84555xzzjnnnHPOOeecc44xxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY0wAwE6EA8BOhIVQaMhKACAcAABACCEpKaWUUkoRU85BSSmllFKqFIOMSkoppZRSpBR1lFJKKaWUIqWgpJJSSimllElJKaWUUkoppYw6SimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaVUSimllFJKKaWUUkoppRQAYPLgAACVYOMMK0lnhaPBhYasBAByAwAAhRiDEEJpraRUUkolVc5BKCWUlEpKKZWUUqqYgxBKKqmlklJKKbXSQSihlFBKKSWUUkooJYQQSgmhlFRCK6mEUkoHoYQSQimhhFRKKSWUzkEoIYUOQkmllNRCSB10VFIpIZVSSiklpZQ6CKGUklJLLZVSWkqpdBJSKamV1FJqqbWSUgmhpFZKSSWl0lpJJbUSSkklpZRSSymFVFJJJYSSUioltZZaSqm11lJIqZWUUkqppdRSSiWlkEpKqZSSUmollZRSaiGVlEpJKaTUSimlpFRCSamlUlpKLbWUSkmptFRSSaWUlEpJKaVSSksppRJKSqmllFpJKYWSUkoplZJSSyW1VEoKJaWUUkmptJRSSymVklIBAEAHDgAAAUZUWoidZlx5BI4oZJiAAgAAQABAgAkgMEBQMApBgDACAQAAAADAAAAfAABHARAR0ZzBAUKCwgJDg8MDAAAAAAAAAAAAAACAT2dnUwAEAAAAAAAAAADqnjMlAgAAADzQPmcBAQA="); |
||||
if (mpeg) canPlayThrough("audio/mpeg;base64,/+MYxAAAAANIAUAAAASEEB/jwOFM/0MM/90b/+RhST//w4NFwOjf///PZu////9lns5GFDv//l9GlUIEEIAAAgIg8Ir/JGq3/+MYxDsLIj5QMYcoAP0dv9HIjUcH//yYSg+CIbkGP//8w0bLVjUP///3Z0x5QCAv/yLjwtGKTEFNRTMuOTeqqqqqqqqqqqqq/+MYxEkNmdJkUYc4AKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"); |
||||
// lets find out!
|
||||
var time = (new Date()).getTime();
|
||||
var interval = window.setInterval(function() { |
||||
var now = (new Date()).getTime(); |
||||
var maxExecution = now - time > 5000; |
||||
if (!pending || maxExecution) { |
||||
window.clearInterval(interval); |
||||
callback(supports); |
||||
} |
||||
}, 1); |
||||
}; |
||||
|
||||
})(); |
||||
@ -0,0 +1,167 @@
@@ -0,0 +1,167 @@
|
||||
if (typeof (MIDI) === "undefined") var MIDI = {}; |
||||
if (typeof (MIDI.Soundfont) === "undefined") MIDI.Soundfont = {}; |
||||
|
||||
(function() { "use strict"; |
||||
|
||||
var USE_JAZZMIDI = false; // Turn on to support JazzMIDI Plugin
|
||||
|
||||
MIDI.loadPlugin = function(conf) { |
||||
if (typeof(conf) === "function") conf = { |
||||
callback: conf |
||||
}; |
||||
/// Get the instrument name.
|
||||
var instruments = conf.instruments || conf.instrument || "acoustic_grand_piano"; |
||||
if (typeof(instruments) !== "object") instruments = [ instruments ]; |
||||
///
|
||||
for (var n = 0; n < instruments.length; n ++) { |
||||
var instrument = instruments[n]; |
||||
if (typeof(instrument) === "number") { |
||||
instruments[n] = MIDI.GeneralMIDI.byId[instrument]; |
||||
} |
||||
}; |
||||
///
|
||||
MIDI.soundfontUrl = conf.soundfontUrl || MIDI.soundfontUrl || "./soundfont/"; |
||||
/// Detect the best type of audio to use.
|
||||
MIDI.audioDetect(function(types) { |
||||
var api = ""; |
||||
// use the most appropriate plugin if not specified
|
||||
if (apis[conf.api]) { |
||||
api = conf.api; |
||||
} else if (apis[window.location.hash.substr(1)]) { |
||||
api = window.location.hash.substr(1); |
||||
} else if (USE_JAZZMIDI && navigator.requestMIDIAccess) { |
||||
api = "webmidi"; |
||||
} else if (window.webkitAudioContext || window.AudioContext) { // Chrome
|
||||
api = "webaudio"; |
||||
} else if (window.Audio) { // Firefox
|
||||
api = "audiotag"; |
||||
} else { // Internet Explorer
|
||||
api = "flash"; |
||||
} |
||||
///
|
||||
if (!connect[api]) return; |
||||
// use audio/ogg when supported
|
||||
if (conf.targetFormat) { |
||||
var filetype = conf.targetFormat; |
||||
} else { // use best quality
|
||||
var filetype = types["audio/ogg"] ? "ogg" : "mp3"; |
||||
} |
||||
// load the specified plugin
|
||||
MIDI.lang = api; |
||||
MIDI.supports = types; |
||||
connect[api](filetype, instruments, conf); |
||||
}); |
||||
}; |
||||
|
||||
///
|
||||
|
||||
var connect = {}; |
||||
|
||||
connect.webmidi = function(filetype, instruments, conf) { |
||||
if (MIDI.loader) MIDI.loader.message("Web MIDI API..."); |
||||
MIDI.WebMIDI.connect(conf); |
||||
}; |
||||
|
||||
connect.flash = function(filetype, instruments, conf) { |
||||
// fairly quick, but requires loading of individual MP3s (more http requests).
|
||||
if (MIDI.loader) MIDI.loader.message("Flash API..."); |
||||
DOMLoader.script.add({ |
||||
src: conf.soundManagerUrl || "./inc/SoundManager2/script/soundmanager2.js", |
||||
verify: "SoundManager", |
||||
callback: function () { |
||||
MIDI.Flash.connect(instruments, conf); |
||||
} |
||||
}); |
||||
}; |
||||
|
||||
connect.audiotag = function(filetype, instruments, conf) { |
||||
if (MIDI.loader) MIDI.loader.message("HTML5 Audio API..."); |
||||
// works ok, kinda like a drunken tuna fish, across the board.
|
||||
var queue = createQueue({ |
||||
items: instruments, |
||||
getNext: function(instrumentId) { |
||||
DOMLoader.sendRequest({ |
||||
url: MIDI.soundfontUrl + instrumentId + "-" + filetype + ".js", |
||||
onprogress: getPercent, |
||||
onload: function (response) { |
||||
addSoundfont(response.responseText); |
||||
if (MIDI.loader) MIDI.loader.update(null, "Downloading", 100); |
||||
queue.getNext(); |
||||
} |
||||
}); |
||||
}, |
||||
onComplete: function() { |
||||
MIDI.AudioTag.connect(conf); |
||||
} |
||||
}); |
||||
}; |
||||
|
||||
connect.webaudio = function(filetype, instruments, conf) { |
||||
if (MIDI.loader) MIDI.loader.message("Web Audio API..."); |
||||
// works awesome! safari, chrome and firefox support.
|
||||
var queue = createQueue({ |
||||
items: instruments, |
||||
getNext: function(instrumentId) { |
||||
DOMLoader.sendRequest({ |
||||
url: MIDI.soundfontUrl + instrumentId + "-" + filetype + ".js", |
||||
onprogress: getPercent, |
||||
onload: function(response) { |
||||
addSoundfont(response.responseText); |
||||
if (MIDI.loader) MIDI.loader.update(null, "Downloading...", 100); |
||||
queue.getNext(); |
||||
} |
||||
}); |
||||
}, |
||||
onComplete: function() { |
||||
MIDI.WebAudio.connect(conf); |
||||
} |
||||
}); |
||||
}; |
||||
|
||||
/// Helpers
|
||||
|
||||
var apis = { |
||||
"webmidi": true, |
||||
"webaudio": true, |
||||
"audiotag": true, |
||||
"flash": true |
||||
}; |
||||
|
||||
var addSoundfont = function(text) { |
||||
var script = document.createElement("script"); |
||||
script.language = "javascript"; |
||||
script.type = "text/javascript"; |
||||
script.text = text; |
||||
document.body.appendChild(script); |
||||
}; |
||||
|
||||
var getPercent = function(event) { |
||||
if (!this.totalSize) { |
||||
if (this.getResponseHeader("Content-Length-Raw")) { |
||||
this.totalSize = parseInt(this.getResponseHeader("Content-Length-Raw")); |
||||
} else { |
||||
this.totalSize = event.total; |
||||
} |
||||
} |
||||
///
|
||||
var percent = this.totalSize ? Math.round(event.loaded / this.totalSize * 100) : ""; |
||||
if (MIDI.loader) MIDI.loader.update(null, "Downloading...", percent); |
||||
}; |
||||
|
||||
var createQueue = function(conf) { |
||||
var self = {}; |
||||
self.queue = []; |
||||
for (var key in conf.items) { |
||||
if (conf.items.hasOwnProperty(key)) { |
||||
self.queue.push(conf.items[key]); |
||||
} |
||||
} |
||||
self.getNext = function() { |
||||
if (!self.queue.length) return conf.onComplete(); |
||||
conf.getNext(self.queue.shift()); |
||||
}; |
||||
setTimeout(self.getNext, 1); |
||||
return self; |
||||
}; |
||||
|
||||
})(); |
||||
@ -0,0 +1,267 @@
@@ -0,0 +1,267 @@
|
||||
if (typeof (MIDI) === "undefined") var MIDI = {}; |
||||
if (typeof (MIDI.Player) === "undefined") MIDI.Player = {}; |
||||
|
||||
(function() { "use strict"; |
||||
|
||||
var root = MIDI.Player; |
||||
root.callback = undefined; // your custom callback goes here!
|
||||
root.currentTime = 0; |
||||
root.endTime = 0;
|
||||
root.restart = 0;
|
||||
root.playing = false; |
||||
root.timeWarp = 1; |
||||
|
||||
//
|
||||
root.start = |
||||
root.resume = function () { |
||||
if (root.currentTime < -1) root.currentTime = -1; |
||||
startAudio(root.currentTime); |
||||
}; |
||||
|
||||
root.pause = function () { |
||||
var tmp = root.restart; |
||||
stopAudio(); |
||||
root.restart = tmp; |
||||
}; |
||||
|
||||
root.stop = function () { |
||||
stopAudio(); |
||||
root.restart = 0; |
||||
root.currentTime = 0; |
||||
}; |
||||
|
||||
root.addListener = function(callback) { |
||||
onMidiEvent = callback; |
||||
}; |
||||
|
||||
root.removeListener = function() { |
||||
onMidiEvent = undefined; |
||||
}; |
||||
|
||||
root.clearAnimation = function() { |
||||
if (root.interval) { |
||||
window.clearInterval(root.interval); |
||||
} |
||||
}; |
||||
|
||||
root.setAnimation = function(config) { |
||||
var callback = (typeof(config) === "function") ? config : config.callback; |
||||
var interval = config.interval || 30; |
||||
var currentTime = 0; |
||||
var tOurTime = 0; |
||||
var tTheirTime = 0; |
||||
//
|
||||
root.clearAnimation(); |
||||
root.interval = window.setInterval(function () { |
||||
if (root.endTime === 0) return; |
||||
if (root.playing) { |
||||
currentTime = (tTheirTime === root.currentTime) ? tOurTime - (new Date).getTime() : 0; |
||||
if (root.currentTime === 0) { |
||||
currentTime = 0; |
||||
} else { |
||||
currentTime = root.currentTime - currentTime; |
||||
} |
||||
if (tTheirTime !== root.currentTime) { |
||||
tOurTime = (new Date).getTime(); |
||||
tTheirTime = root.currentTime; |
||||
} |
||||
} else { // paused
|
||||
currentTime = root.currentTime; |
||||
} |
||||
var endTime = root.endTime; |
||||
var percent = currentTime / endTime; |
||||
var total = currentTime / 1000; |
||||
var minutes = total / 60; |
||||
var seconds = total - (minutes * 60); |
||||
var t1 = minutes * 60 + seconds; |
||||
var t2 = (endTime / 1000); |
||||
if (t2 - t1 < -1) return; |
||||
callback({ |
||||
now: t1, |
||||
end: t2, |
||||
events: noteRegistrar |
||||
}); |
||||
}, interval); |
||||
}; |
||||
|
||||
// helpers
|
||||
|
||||
root.loadMidiFile = function() { // reads midi into javascript array of events
|
||||
root.replayer = new Replayer(MidiFile(root.currentData), root.timeWarp); |
||||
root.data = root.replayer.getData(); |
||||
root.endTime = getLength(); |
||||
}; |
||||
|
||||
root.loadFile = function (file, callback) { |
||||
root.stop(); |
||||
if (file.indexOf("base64,") !== -1) { |
||||
var data = window.atob(file.split(",")[1]); |
||||
root.currentData = data; |
||||
root.loadMidiFile(); |
||||
if (callback) callback(data); |
||||
return; |
||||
} |
||||
///
|
||||
var fetch = new XMLHttpRequest(); |
||||
fetch.open('GET', file); |
||||
fetch.overrideMimeType("text/plain; charset=x-user-defined"); |
||||
fetch.onreadystatechange = function () { |
||||
if (this.readyState === 4 && this.status === 200) { |
||||
var t = this.responseText || ""; |
||||
var ff = []; |
||||
var mx = t.length; |
||||
var scc = String.fromCharCode; |
||||
for (var z = 0; z < mx; z++) { |
||||
ff[z] = scc(t.charCodeAt(z) & 255); |
||||
} |
||||
var data = ff.join(""); |
||||
root.currentData = data; |
||||
root.loadMidiFile(); |
||||
if (callback) callback(data); |
||||
} |
||||
}; |
||||
fetch.send(); |
||||
}; |
||||
|
||||
// Playing the audio
|
||||
|
||||
var eventQueue = []; // hold events to be triggered
|
||||
var queuedTime; //
|
||||
var startTime = 0; // to measure time elapse
|
||||
var noteRegistrar = {}; // get event for requested note
|
||||
var onMidiEvent = undefined; // listener callback
|
||||
var scheduleTracking = function (channel, note, currentTime, offset, message, velocity) { |
||||
var interval = window.setTimeout(function () { |
||||
var data = { |
||||
channel: channel, |
||||
note: note, |
||||
now: currentTime, |
||||
end: root.endTime, |
||||
message: message, |
||||
velocity: velocity |
||||
}; |
||||
//
|
||||
if (message === 128) { |
||||
delete noteRegistrar[note]; |
||||
} else { |
||||
noteRegistrar[note] = data; |
||||
} |
||||
if (onMidiEvent) { |
||||
onMidiEvent(data); |
||||
} |
||||
root.currentTime = currentTime; |
||||
if (root.currentTime === queuedTime && queuedTime < root.endTime) { // grab next sequence
|
||||
startAudio(queuedTime, true); |
||||
} |
||||
}, currentTime - offset); |
||||
return interval; |
||||
}; |
||||
|
||||
var getContext = function() { |
||||
if (MIDI.lang === 'WebAudioAPI') { |
||||
return MIDI.Player.ctx; |
||||
} else if (!root.ctx) { |
||||
root.ctx = { currentTime: 0 }; |
||||
} |
||||
return root.ctx; |
||||
}; |
||||
|
||||
var getLength = function() { |
||||
var data = root.data; |
||||
var length = data.length; |
||||
var totalTime = 0.5; |
||||
for (var n = 0; n < length; n++) { |
||||
totalTime += data[n][1]; |
||||
} |
||||
return totalTime; |
||||
}; |
||||
|
||||
var startAudio = function (currentTime, fromCache) { |
||||
if (!root.replayer) return; |
||||
if (!fromCache) { |
||||
if (typeof (currentTime) === "undefined") currentTime = root.restart; |
||||
if (root.playing) stopAudio(); |
||||
root.playing = true; |
||||
root.data = root.replayer.getData(); |
||||
root.endTime = getLength(); |
||||
} |
||||
var note; |
||||
var offset = 0; |
||||
var messages = 0; |
||||
var data = root.data;
|
||||
var ctx = getContext(); |
||||
var length = data.length; |
||||
//
|
||||
queuedTime = 0.5; |
||||
startTime = ctx.currentTime; |
||||
//
|
||||
for (var n = 0; n < length && messages < 100; n++) { |
||||
queuedTime += data[n][1]; |
||||
if (queuedTime < currentTime) { |
||||
offset = queuedTime; |
||||
continue; |
||||
} |
||||
currentTime = queuedTime - offset; |
||||
var event = data[n][0].event; |
||||
if (event.type !== "channel") continue; |
||||
var channel = event.channel; |
||||
switch (event.subtype) { |
||||
case 'noteOn': |
||||
if (MIDI.channels[channel].mute) break; |
||||
note = event.noteNumber - (root.MIDIOffset || 0); |
||||
eventQueue.push({ |
||||
event: event, |
||||
source: MIDI.noteOn(channel, event.noteNumber, event.velocity, currentTime / 1000 + ctx.currentTime), |
||||
interval: scheduleTracking(channel, note, queuedTime, offset, 144, event.velocity) |
||||
}); |
||||
messages ++; |
||||
break; |
||||
case 'noteOff': |
||||
if (MIDI.channels[channel].mute) break; |
||||
note = event.noteNumber - (root.MIDIOffset || 0); |
||||
eventQueue.push({ |
||||
event: event, |
||||
source: MIDI.noteOff(channel, event.noteNumber, currentTime / 1000 + ctx.currentTime), |
||||
interval: scheduleTracking(channel, note, queuedTime, offset, 128) |
||||
}); |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
var stopAudio = function () { |
||||
var ctx = getContext(); |
||||
root.playing = false; |
||||
root.restart += (ctx.currentTime - startTime) * 1000; |
||||
// stop the audio, and intervals
|
||||
while (eventQueue.length) { |
||||
var o = eventQueue.pop(); |
||||
window.clearInterval(o.interval); |
||||
if (!o.source) continue; // is not webaudio
|
||||
if (typeof(o.source) === "number") { |
||||
window.clearTimeout(o.source); |
||||
} else { // webaudio
|
||||
o.source.disconnect(0); |
||||
} |
||||
} |
||||
// run callback to cancel any notes still playing
|
||||
for (var key in noteRegistrar) { |
||||
var o = noteRegistrar[key] |
||||
if (noteRegistrar[key].message === 144 && onMidiEvent) { |
||||
onMidiEvent({ |
||||
channel: o.channel, |
||||
note: o.note, |
||||
now: o.now, |
||||
end: o.end, |
||||
message: 128, |
||||
velocity: o.velocity |
||||
}); |
||||
} |
||||
} |
||||
// reset noteRegistrar
|
||||
noteRegistrar = {}; |
||||
}; |
||||
|
||||
})(); |
||||
@ -0,0 +1,569 @@
@@ -0,0 +1,569 @@
|
||||
if (typeof (MIDI) === "undefined") var MIDI = {}; |
||||
|
||||
(function() { "use strict"; |
||||
|
||||
var setPlugin = function(root) { |
||||
MIDI.api = root.api; |
||||
MIDI.setVolume = root.setVolume; |
||||
MIDI.programChange = root.programChange; |
||||
MIDI.noteOn = root.noteOn; |
||||
MIDI.noteOff = root.noteOff; |
||||
MIDI.chordOn = root.chordOn; |
||||
MIDI.chordOff = root.chordOff; |
||||
MIDI.stopAllNotes = root.stopAllNotes; |
||||
MIDI.getInput = root.getInput; |
||||
MIDI.getOutputs = root.getOutputs; |
||||
}; |
||||
|
||||
(function () { |
||||
var plugin = null; |
||||
var output = null; |
||||
var channels = []; |
||||
var root = MIDI.WebMIDI = { |
||||
api: "webmidi" |
||||
}; |
||||
root.setVolume = function (channel, volume) { // set channel volume
|
||||
output.send([0xB0 + channel, 0x07, volume]); |
||||
}; |
||||
|
||||
root.programChange = function (channel, program) { // change channel instrument
|
||||
output.send([0xC0 + channel, program]); |
||||
}; |
||||
|
||||
root.noteOn = function (channel, note, velocity, delay) { |
||||
output.send([0x90 + channel, note, velocity], delay * 1000); |
||||
}; |
||||
|
||||
root.noteOff = function (channel, note, delay) { |
||||
output.send([0x80 + channel, note, 0], delay * 1000); |
||||
}; |
||||
|
||||
root.chordOn = function (channel, chord, velocity, delay) { |
||||
for (var n = 0; n < chord.length; n ++) { |
||||
var note = chord[n]; |
||||
output.send([0x90 + channel, note, velocity], delay * 1000); |
||||
} |
||||
}; |
||||
|
||||
root.chordOff = function (channel, chord, delay) { |
||||
for (var n = 0; n < chord.length; n ++) { |
||||
var note = chord[n]; |
||||
output.send([0x80 + channel, note, 0], delay * 1000); |
||||
} |
||||
}; |
||||
|
||||
root.stopAllNotes = function () { |
||||
for (var channel = 0; channel < 16; channel ++) { |
||||
output.send([0xB0 + channel, 0x7B, 0]); |
||||
} |
||||
}; |
||||
|
||||
root.getInput = function () { |
||||
return plugin.getInputs(); |
||||
}; |
||||
|
||||
root.getOutputs = function () { |
||||
return plugin.getOutputs(); |
||||
}; |
||||
|
||||
root.connect = function (conf) { |
||||
setPlugin(root); |
||||
navigator.requestMIDIAccess().then(function (access) { |
||||
plugin = access; |
||||
output = plugin.outputs()[0]; |
||||
if (conf.callback) conf.callback(); |
||||
}, function (err) { // well at least we tried!
|
||||
if (window.AudioContext || window.webkitAudioContext) { // Chrome
|
||||
conf.api = "webaudio"; |
||||
} else if (window.Audio) { // Firefox
|
||||
conf.api = "audiotag"; |
||||
} else { // Internet Explorer
|
||||
conf.api = "flash"; |
||||
} |
||||
MIDI.loadPlugin(conf); |
||||
}); |
||||
}; |
||||
})(); |
||||
|
||||
if (window.AudioContext || window.webkitAudioContext) (function () { |
||||
|
||||
var AudioContext = window.AudioContext || window.webkitAudioContext; |
||||
var root = MIDI.WebAudio = { |
||||
api: "webaudio" |
||||
}; |
||||
var ctx; |
||||
var sources = {}; |
||||
var masterVolume = 127; |
||||
var audioBuffers = {}; |
||||
var audioLoader = function (instrument, urlList, index, bufferList, callback) { |
||||
var synth = MIDI.GeneralMIDI.byName[instrument]; |
||||
var instrumentId = synth.number; |
||||
var url = urlList[index]; |
||||
if (!MIDI.Soundfont[instrument][url]) { // missing soundfont
|
||||
return callback(instrument); |
||||
} |
||||
var base64 = MIDI.Soundfont[instrument][url].split(",")[1]; |
||||
var buffer = Base64Binary.decodeArrayBuffer(base64); |
||||
ctx.decodeAudioData(buffer, function (buffer) { |
||||
var msg = url; |
||||
while (msg.length < 3) msg += " "; |
||||
if (typeof (MIDI.loader) !== "undefined") { |
||||
MIDI.loader.update(null, synth.instrument + "<br>Processing: " + (index / 87 * 100 >> 0) + "%<br>" + msg); |
||||
} |
||||
buffer.id = url; |
||||
bufferList[index] = buffer; |
||||
//
|
||||
if (bufferList.length === urlList.length) { |
||||
while (bufferList.length) { |
||||
buffer = bufferList.pop(); |
||||
if (!buffer) continue; |
||||
var nodeId = MIDI.keyToNote[buffer.id]; |
||||
audioBuffers[instrumentId + "" + nodeId] = buffer; |
||||
} |
||||
callback(instrument); |
||||
} |
||||
}); |
||||
}; |
||||
|
||||
root.setVolume = function (channel, volume) { |
||||
masterVolume = volume; |
||||
}; |
||||
|
||||
root.programChange = function (channel, program) { |
||||
MIDI.channels[channel].instrument = program; |
||||
}; |
||||
|
||||
root.noteOn = function (channel, note, velocity, delay) { |
||||
/// check whether the note exists
|
||||
if (!MIDI.channels[channel]) return; |
||||
var instrument = MIDI.channels[channel].instrument; |
||||
if (!audioBuffers[instrument + "" + note]) return; |
||||
/// convert relative delay to absolute delay
|
||||
if (delay < ctx.currentTime) delay += ctx.currentTime; |
||||
/// crate audio buffer
|
||||
var source = ctx.createBufferSource(); |
||||
sources[channel + "" + note] = source; |
||||
source.buffer = audioBuffers[instrument + "" + note]; |
||||
source.connect(ctx.destination); |
||||
///
|
||||
if (ctx.createGain) { // firefox
|
||||
source.gainNode = ctx.createGain(); |
||||
} else { // chrome
|
||||
source.gainNode = ctx.createGainNode(); |
||||
} |
||||
var value = (velocity / 127) * (masterVolume / 127) * 2 - 1; |
||||
source.gainNode.connect(ctx.destination); |
||||
source.gainNode.gain.value = Math.max(-1, value); |
||||
source.connect(source.gainNode); |
||||
if (source.noteOn) { // old api
|
||||
source.noteOn(delay || 0); |
||||
} else { // new api
|
||||
source.start(delay || 0); |
||||
} |
||||
return source; |
||||
}; |
||||
|
||||
root.noteOff = function (channel, note, delay) { |
||||
delay = delay || 0; |
||||
if (delay < ctx.currentTime) delay += ctx.currentTime; |
||||
var source = sources[channel + "" + note]; |
||||
if (!source) return; |
||||
if (source.gainNode) { |
||||
// @Miranet: "the values of 0.2 and 0.3 could ofcourse be used as
|
||||
// a 'release' parameter for ADSR like time settings."
|
||||
// add { "metadata": { release: 0.3 } } to soundfont files
|
||||
var gain = source.gainNode.gain; |
||||
gain.linearRampToValueAtTime(gain.value, delay); |
||||
gain.linearRampToValueAtTime(-1, delay + 0.2); |
||||
} |
||||
if (source.noteOff) { // old api
|
||||
source.noteOff(delay + 0.3); |
||||
} else { |
||||
source.stop(delay + 0.3); |
||||
} |
||||
///
|
||||
delete sources[channel + "" + note]; |
||||
}; |
||||
|
||||
root.chordOn = function (channel, chord, velocity, delay) { |
||||
var ret = {}, note; |
||||
for (var n = 0, length = chord.length; n < length; n++) { |
||||
ret[note = chord[n]] = root.noteOn(channel, note, velocity, delay); |
||||
} |
||||
return ret; |
||||
}; |
||||
|
||||
root.chordOff = function (channel, chord, delay) { |
||||
var ret = {}, note; |
||||
for (var n = 0, length = chord.length; n < length; n++) { |
||||
ret[note = chord[n]] = root.noteOff(channel, note, delay); |
||||
} |
||||
return ret; |
||||
}; |
||||
|
||||
root.stopAllNotes = function () { |
||||
for (var source in sources) { |
||||
var delay = 0; |
||||
if (delay < ctx.currentTime) delay += ctx.currentTime; |
||||
// @Miranet: "the values of 0.2 and 0.3 could ofcourse be used as
|
||||
// a 'release' parameter for ADSR like time settings."
|
||||
// add { "metadata": { release: 0.3 } } to soundfont files
|
||||
sources[source].gain.linearRampToValueAtTime(1, delay); |
||||
sources[source].gain.linearRampToValueAtTime(0, delay + 0.2); |
||||
sources[source].noteOff(delay + 0.3); |
||||
delete sources[source]; |
||||
} |
||||
}; |
||||
|
||||
root.connect = function (conf) { |
||||
setPlugin(root); |
||||
//
|
||||
MIDI.Player.ctx = ctx = new AudioContext(); |
||||
///
|
||||
var urlList = []; |
||||
var keyToNote = MIDI.keyToNote; |
||||
for (var key in keyToNote) urlList.push(key); |
||||
var bufferList = []; |
||||
var pending = {}; |
||||
var oncomplete = function(instrument) { |
||||
delete pending[instrument]; |
||||
for (var key in pending) break; |
||||
if (!key) conf.callback(); |
||||
}; |
||||
for (var instrument in MIDI.Soundfont) { |
||||
pending[instrument] = true; |
||||
for (var i = 0; i < urlList.length; i++) { |
||||
audioLoader(instrument, urlList, i, bufferList, oncomplete); |
||||
} |
||||
} |
||||
}; |
||||
})(); |
||||
|
||||
if (window.Audio) (function () { |
||||
|
||||
var root = MIDI.AudioTag = { |
||||
api: "audiotag" |
||||
}; |
||||
var note2id = {}; |
||||
var volume = 127; // floating point
|
||||
var channel_nid = -1; // current channel
|
||||
var channels = []; // the audio channels
|
||||
var channelInstrumentNoteIds = []; // instrumentId + noteId that is currently playing in each 'channel', for routing noteOff/chordOff calls
|
||||
var notes = {}; // the piano keys
|
||||
for (var nid = 0; nid < 12; nid++) { |
||||
channels[nid] = new Audio(); |
||||
} |
||||
|
||||
var playChannel = function (channel, note) { |
||||
if (!MIDI.channels[channel]) return; |
||||
var instrument = MIDI.channels[channel].instrument; |
||||
var instrumentId = MIDI.GeneralMIDI.byId[instrument].id; |
||||
var note = notes[note]; |
||||
if (!note) return; |
||||
var instrumentNoteId = instrumentId + "" + note.id; |
||||
var nid = (channel_nid + 1) % channels.length; |
||||
var audio = channels[nid]; |
||||
channelInstrumentNoteIds[ nid ] = instrumentNoteId; |
||||
audio.src = MIDI.Soundfont[instrumentId][note.id]; |
||||
audio.volume = volume / 127; |
||||
audio.play(); |
||||
channel_nid = nid; |
||||
}; |
||||
|
||||
var stopChannel = function (channel, note) { |
||||
if (!MIDI.channels[channel]) return; |
||||
var instrument = MIDI.channels[channel].instrument; |
||||
var instrumentId = MIDI.GeneralMIDI.byId[instrument].id; |
||||
var note = notes[note]; |
||||
if (!note) return; |
||||
var instrumentNoteId = instrumentId + "" + note.id; |
||||
|
||||
for(var i=0;i<channels.length;i++){ |
||||
var nid = (i + channel_nid + 1) % channels.length; |
||||
var cId = channelInstrumentNoteIds[nid]; |
||||
|
||||
if(cId && cId == instrumentNoteId){ |
||||
channels[nid].pause(); |
||||
channelInstrumentNoteIds[nid] = null; |
||||
return; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
root.programChange = function (channel, program) { |
||||
MIDI.channels[channel].instrument = program; |
||||
}; |
||||
|
||||
root.setVolume = function (channel, n) { |
||||
volume = n; //- should be channel specific volume
|
||||
}; |
||||
|
||||
root.noteOn = function (channel, note, velocity, delay) { |
||||
var id = note2id[note]; |
||||
if (!notes[id]) return; |
||||
if (delay) { |
||||
return window.setTimeout(function () { |
||||
playChannel(channel, id); |
||||
}, delay * 1000); |
||||
} else { |
||||
playChannel(channel, id); |
||||
} |
||||
}; |
||||
|
||||
root.noteOff = function (channel, note, delay) { |
||||
var id = note2id[note]; |
||||
if (!notes[id]) return; |
||||
if (delay) { |
||||
return setTimeout(function() { |
||||
stopChannel(channel, id); |
||||
}, delay * 1000) |
||||
} else { |
||||
stopChannel(channel, id); |
||||
} |
||||
}; |
||||
|
||||
root.chordOn = function (channel, chord, velocity, delay) { |
||||
for (var idx = 0; idx < chord.length; idx ++) { |
||||
var n = chord[idx]; |
||||
var id = note2id[n]; |
||||
if (!notes[id]) continue; |
||||
if (delay) { |
||||
return window.setTimeout(function () { |
||||
playChannel(channel, id); |
||||
}, delay * 1000); |
||||
} else { |
||||
playChannel(channel, id); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
root.chordOff = function (channel, chord, delay) { |
||||
for (var idx = 0; idx < chord.length; idx ++) { |
||||
var n = chord[idx]; |
||||
var id = note2id[n]; |
||||
if (!notes[id]) continue; |
||||
if (delay) { |
||||
return window.setTimeout(function () { |
||||
stopChannel(channel, id); |
||||
}, delay * 1000); |
||||
} else { |
||||
stopChannel(channel, id); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
root.stopAllNotes = function () { |
||||
for (var nid = 0, length = channels.length; nid < length; nid++) { |
||||
channels[nid].pause(); |
||||
} |
||||
}; |
||||
|
||||
root.connect = function (conf) { |
||||
for (var key in MIDI.keyToNote) { |
||||
note2id[MIDI.keyToNote[key]] = key; |
||||
notes[key] = { |
||||
id: key |
||||
}; |
||||
} |
||||
setPlugin(root); |
||||
///
|
||||
if (conf.callback) conf.callback(); |
||||
}; |
||||
})(); |
||||
|
||||
(function () { |
||||
|
||||
var root = MIDI.Flash = { |
||||
api: "flash" |
||||
}; |
||||
var noteReverse = {}; |
||||
var notes = {}; |
||||
|
||||
root.programChange = function (channel, program) { |
||||
MIDI.channels[channel].instrument = program; |
||||
}; |
||||
|
||||
root.setVolume = function (channel, note) { |
||||
|
||||
}; |
||||
|
||||
root.noteOn = function (channel, note, velocity, delay) { |
||||
if (!MIDI.channels[channel]) return; |
||||
var instrument = MIDI.channels[channel].instrument; |
||||
var id = MIDI.GeneralMIDI.byId[instrument].number; |
||||
note = id + "" + noteReverse[note]; |
||||
if (!notes[note]) return; |
||||
if (delay) { |
||||
return window.setTimeout(function() {
|
||||
notes[note].play({ volume: velocity * 2 }); |
||||
}, delay * 1000); |
||||
} else { |
||||
notes[note].play({ volume: velocity * 2 }); |
||||
} |
||||
}; |
||||
|
||||
root.noteOff = function (channel, note, delay) { |
||||
|
||||
}; |
||||
|
||||
root.chordOn = function (channel, chord, velocity, delay) { |
||||
if (!MIDI.channels[channel]) return; |
||||
var instrument = MIDI.channels[channel].instrument; |
||||
var id = MIDI.GeneralMIDI.byId[instrument].number; |
||||
for (var key in chord) { |
||||
var n = chord[key]; |
||||
var note = id + "" + noteReverse[n]; |
||||
if (notes[note]) { |
||||
notes[note].play({ volume: velocity * 2 }); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
root.chordOff = function (channel, chord, delay) { |
||||
|
||||
}; |
||||
|
||||
root.stopAllNotes = function () { |
||||
|
||||
}; |
||||
|
||||
root.connect = function (instruments, conf) { |
||||
soundManager.flashVersion = 9; |
||||
soundManager.useHTML5Audio = true; |
||||
soundManager.url = conf.soundManagerSwfUrl || '../inc/SoundManager2/swf/'; |
||||
soundManager.useHighPerformance = true; |
||||
soundManager.wmode = 'transparent'; |
||||
soundManager.flashPollingInterval = 1; |
||||
soundManager.debugMode = false; |
||||
soundManager.onload = function () { |
||||
var createBuffer = function(instrument, id, onload) { |
||||
var synth = MIDI.GeneralMIDI.byName[instrument]; |
||||
var instrumentId = synth.number; |
||||
notes[instrumentId+""+id] = soundManager.createSound({ |
||||
id: id, |
||||
url: MIDI.soundfontUrl + instrument + "-mp3/" + id + ".mp3", |
||||
multiShot: true, |
||||
autoLoad: true, |
||||
onload: onload |
||||
});
|
||||
}; |
||||
var loaded = []; |
||||
var samplesPerInstrument = 88; |
||||
var samplesToLoad = instruments.length * samplesPerInstrument; |
||||
|
||||
for (var i = 0; i < instruments.length; i++) { |
||||
var instrument = instruments[i]; |
||||
var onload = function () { |
||||
loaded.push(this.sID); |
||||
if (typeof (MIDI.loader) === "undefined") return; |
||||
MIDI.loader.update(null, "Processing: " + this.sID); |
||||
}; |
||||
for (var j = 0; j < samplesPerInstrument; j++) { |
||||
var id = noteReverse[j + 21]; |
||||
createBuffer(instrument, id, onload); |
||||
} |
||||
} |
||||
///
|
||||
setPlugin(root); |
||||
//
|
||||
var interval = window.setInterval(function () { |
||||
if (loaded.length < samplesToLoad) return; |
||||
window.clearInterval(interval); |
||||
if (conf.callback) conf.callback(); |
||||
}, 25); |
||||
}; |
||||
soundManager.onerror = function () { |
||||
|
||||
}; |
||||
for (var key in MIDI.keyToNote) { |
||||
noteReverse[MIDI.keyToNote[key]] = key; |
||||
} |
||||
}; |
||||
})(); |
||||
|
||||
/* |
||||
helper functions |
||||
*/ |
||||
|
||||
// instrument-tracker
|
||||
MIDI.GeneralMIDI = (function (arr) { |
||||
var clean = function(v) { |
||||
return v.replace(/[^a-z0-9 ]/gi, "").replace(/[ ]/g, "_").toLowerCase(); |
||||
}; |
||||
var ret = { |
||||
byName: {}, |
||||
byId: {}, |
||||
byCategory: {} |
||||
}; |
||||
for (var key in arr) { |
||||
var list = arr[key]; |
||||
for (var n = 0, length = list.length; n < length; n++) { |
||||
var instrument = list[n]; |
||||
if (!instrument) continue; |
||||
var num = parseInt(instrument.substr(0, instrument.indexOf(" ")), 10); |
||||
instrument = instrument.replace(num + " ", ""); |
||||
ret.byId[--num] =
|
||||
ret.byName[clean(instrument)] =
|
||||
ret.byCategory[clean(key)] = { |
||||
id: clean(instrument), |
||||
instrument: instrument, |
||||
number: num, |
||||
category: key |
||||
}; |
||||
} |
||||
} |
||||
return ret; |
||||
})({ |
||||
'Piano': ['1 Acoustic Grand Piano', '2 Bright Acoustic Piano', '3 Electric Grand Piano', '4 Honky-tonk Piano', '5 Electric Piano 1', '6 Electric Piano 2', '7 Harpsichord', '8 Clavinet'], |
||||
'Chromatic Percussion': ['9 Celesta', '10 Glockenspiel', '11 Music Box', '12 Vibraphone', '13 Marimba', '14 Xylophone', '15 Tubular Bells', '16 Dulcimer'], |
||||
'Organ': ['17 Drawbar Organ', '18 Percussive Organ', '19 Rock Organ', '20 Church Organ', '21 Reed Organ', '22 Accordion', '23 Harmonica', '24 Tango Accordion'], |
||||
'Guitar': ['25 Acoustic Guitar (nylon)', '26 Acoustic Guitar (steel)', '27 Electric Guitar (jazz)', '28 Electric Guitar (clean)', '29 Electric Guitar (muted)', '30 Overdriven Guitar', '31 Distortion Guitar', '32 Guitar Harmonics'], |
||||
'Bass': ['33 Acoustic Bass', '34 Electric Bass (finger)', '35 Electric Bass (pick)', '36 Fretless Bass', '37 Slap Bass 1', '38 Slap Bass 2', '39 Synth Bass 1', '40 Synth Bass 2'], |
||||
'Strings': ['41 Violin', '42 Viola', '43 Cello', '44 Contrabass', '45 Tremolo Strings', '46 Pizzicato Strings', '47 Orchestral Harp', '48 Timpani'], |
||||
'Ensemble': ['49 String Ensemble 1', '50 String Ensemble 2', '51 Synth Strings 1', '52 Synth Strings 2', '53 Choir Aahs', '54 Voice Oohs', '55 Synth Choir', '56 Orchestra Hit'], |
||||
'Brass': ['57 Trumpet', '58 Trombone', '59 Tuba', '60 Muted Trumpet', '61 French Horn', '62 Brass Section', '63 Synth Brass 1', '64 Synth Brass 2'], |
||||
'Reed': ['65 Soprano Sax', '66 Alto Sax', '67 Tenor Sax', '68 Baritone Sax', '69 Oboe', '70 English Horn', '71 Bassoon', '72 Clarinet'], |
||||
'Pipe': ['73 Piccolo', '74 Flute', '75 Recorder', '76 Pan Flute', '77 Blown Bottle', '78 Shakuhachi', '79 Whistle', '80 Ocarina'], |
||||
'Synth Lead': ['81 Lead 1 (square)', '82 Lead 2 (sawtooth)', '83 Lead 3 (calliope)', '84 Lead 4 (chiff)', '85 Lead 5 (charang)', '86 Lead 6 (voice)', '87 Lead 7 (fifths)', '88 Lead 8 (bass + lead)'], |
||||
'Synth Pad': ['89 Pad 1 (new age)', '90 Pad 2 (warm)', '91 Pad 3 (polysynth)', '92 Pad 4 (choir)', '93 Pad 5 (bowed)', '94 Pad 6 (metallic)', '95 Pad 7 (halo)', '96 Pad 8 (sweep)'], |
||||
'Synth Effects': ['97 FX 1 (rain)', '98 FX 2 (soundtrack)', '99 FX 3 (crystal)', '100 FX 4 (atmosphere)', '101 FX 5 (brightness)', '102 FX 6 (goblins)', '103 FX 7 (echoes)', '104 FX 8 (sci-fi)'], |
||||
'Ethnic': ['105 Sitar', '106 Banjo', '107 Shamisen', '108 Koto', '109 Kalimba', '110 Bagpipe', '111 Fiddle', '112 Shanai'], |
||||
'Percussive': ['113 Tinkle Bell', '114 Agogo', '115 Steel Drums', '116 Woodblock', '117 Taiko Drum', '118 Melodic Tom', '119 Synth Drum'], |
||||
'Sound effects': ['120 Reverse Cymbal', '121 Guitar Fret Noise', '122 Breath Noise', '123 Seashore', '124 Bird Tweet', '125 Telephone Ring', '126 Helicopter', '127 Applause', '128 Gunshot'] |
||||
}); |
||||
|
||||
// channel-tracker
|
||||
MIDI.channels = (function () { // 0 - 15 channels
|
||||
var channels = {}; |
||||
for (var n = 0; n < 16; n++) { |
||||
channels[n] = { // default values
|
||||
instrument: 0, |
||||
// Acoustic Grand Piano
|
||||
mute: false, |
||||
mono: false, |
||||
omni: false, |
||||
solo: false |
||||
}; |
||||
} |
||||
return channels; |
||||
})(); |
||||
|
||||
//
|
||||
MIDI.pianoKeyOffset = 21; |
||||
|
||||
// note conversions
|
||||
MIDI.keyToNote = {}; // C8 == 108
|
||||
MIDI.noteToKey = {}; // 108 == C8
|
||||
(function () { |
||||
var A0 = 0x15; // first note
|
||||
var C8 = 0x6C; // last note
|
||||
var number2key = ["C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B"]; |
||||
for (var n = A0; n <= C8; n++) { |
||||
var octave = (n - 12) / 12 >> 0; |
||||
var name = number2key[n % 12] + octave; |
||||
MIDI.keyToNote[name] = n; |
||||
MIDI.noteToKey[n] = name; |
||||
} |
||||
})(); |
||||
|
||||
})(); |
||||
Loading…
Reference in new issue