123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- /*
- ----------------------------------------------------------
- Web Audio API - OGG or MPEG Soundbank
- ----------------------------------------------------------
- http://webaudio.github.io/web-audio-api/
- ----------------------------------------------------------
- */
- (function(root) { 'use strict';
- window.AudioContext && (function() {
- var audioContext = null; // new AudioContext();
- var useStreamingBuffer = false; // !!audioContext.createMediaElementSource;
- var midi = root.WebAudio = {api: 'webaudio'};
- var ctx; // audio context
- var sources = {};
- var effects = {};
- var masterVolume = 127;
- var audioBuffers = {};
- ///
- midi.audioBuffers = audioBuffers;
- midi.send = function(data, delay) { };
- midi.setController = function(channelId, type, value, delay) { };
- midi.setVolume = function(channelId, volume, delay) {
- if (delay) {
- setTimeout(function() {
- masterVolume = volume;
- }, delay * 1000);
- } else {
- masterVolume = volume;
- }
- };
- midi.programChange = function(channelId, program, delay) {
- // if (delay) {
- // return setTimeout(function() {
- // var channel = root.channels[channelId];
- // channel.instrument = program;
- // }, delay);
- // } else {
- var channel = root.channels[channelId];
- channel.instrument = program;
- // }
- };
- midi.pitchBend = function(channelId, program, delay) {
- // if (delay) {
- // setTimeout(function() {
- // var channel = root.channels[channelId];
- // channel.pitchBend = program;
- // }, delay);
- // } else {
- var channel = root.channels[channelId];
- channel.pitchBend = program;
- // }
- };
- midi.noteOn = function(channelId, noteId, velocity, delay) {
- delay = delay || 0;
- /// check whether the note exists
- var channel = root.channels[channelId];
- var instrument = channel.instrument;
- var bufferId = instrument + '' + noteId;
- var buffer = audioBuffers[bufferId];
- if (!buffer) {
- // console.log(MIDI.GM.byId[instrument].id, instrument, channelId);
- return;
- }
- /// convert relative delay to absolute delay
- if (delay < ctx.currentTime) {
- delay += ctx.currentTime;
- }
-
- /// create audio buffer
- if (useStreamingBuffer) {
- var source = ctx.createMediaElementSource(buffer);
- } else { // XMLHTTP buffer
- var source = ctx.createBufferSource();
- source.buffer = buffer;
- }
- /// add effects to buffer
- if (effects) {
- var chain = source;
- for (var key in effects) {
- chain.connect(effects[key].input);
- chain = effects[key];
- }
- }
- /// add gain + pitchShift
- var gain = (velocity / 127) * (masterVolume / 127) * 2 - 1;
- source.connect(ctx.destination);
- source.playbackRate.value = 1; // pitch shift
- source.gainNode = ctx.createGain(); // gain
- source.gainNode.connect(ctx.destination);
- source.gainNode.gain.value = Math.min(1.0, Math.max(-1.0, gain));
- source.connect(source.gainNode);
- ///
- if (useStreamingBuffer) {
- if (delay) {
- return setTimeout(function() {
- buffer.currentTime = 0;
- buffer.play()
- }, delay * 1000);
- } else {
- buffer.currentTime = 0;
- buffer.play()
- }
- } else {
- source.start(delay || 0);
- }
- ///
- sources[channelId + '' + noteId] = source;
- ///
- return source;
- };
- midi.noteOff = function(channelId, noteId, delay) {
- delay = delay || 0;
- /// check whether the note exists
- var channel = root.channels[channelId];
- var instrument = channel.instrument;
- var bufferId = instrument + '' + noteId;
- var buffer = audioBuffers[bufferId];
- if (buffer) {
- if (delay < ctx.currentTime) {
- delay += ctx.currentTime;
- }
- ///
- var source = sources[channelId + '' + noteId];
- if (source) {
- if (source.gainNode) {
- // @Miranet: 'the values of 0.2 and 0.3 could of course 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.0, delay + 0.3);
- }
- ///
- if (useStreamingBuffer) {
- if (delay) {
- setTimeout(function() {
- buffer.pause();
- }, delay * 1000);
- } else {
- buffer.pause();
- }
- } else {
- if (source.noteOff) {
- source.noteOff(delay + 0.5);
- } else {
- source.stop(delay + 0.5);
- }
- }
- ///
- delete sources[channelId + '' + noteId];
- ///
- return source;
- }
- }
- };
- midi.chordOn = function(channel, chord, velocity, delay) {
- var res = {};
- for (var n = 0, note, len = chord.length; n < len; n++) {
- res[note = chord[n]] = midi.noteOn(channel, note, velocity, delay);
- }
- return res;
- };
- midi.chordOff = function(channel, chord, delay) {
- var res = {};
- for (var n = 0, note, len = chord.length; n < len; n++) {
- res[note = chord[n]] = midi.noteOff(channel, note, delay);
- }
- return res;
- };
- midi.stopAllNotes = function() {
- for (var sid in sources) {
- var delay = 0;
- if (delay < ctx.currentTime) {
- delay += ctx.currentTime;
- }
- var source = sources[sid];
- source.gain.linearRampToValueAtTime(1, delay);
- source.gain.linearRampToValueAtTime(0, delay + 0.3);
- if (source.noteOff) { // old api
- source.noteOff(delay + 0.3);
- } else { // new api
- source.stop(delay + 0.3);
- }
- delete sources[sid];
- }
- };
- midi.setEffects = function(list) {
- if (ctx.tunajs) {
- for (var n = 0; n < list.length; n ++) {
- var data = list[n];
- var effect = new ctx.tunajs[data.type](data);
- effect.connect(ctx.destination);
- effects[data.type] = effect;
- }
- } else {
- return console.log('Effects module not installed.');
- }
- };
- midi.connect = function(opts) {
- root.setDefaultPlugin(midi);
- midi.setContext(ctx || createAudioContext(), opts.onsuccess);
- };
-
- midi.getContext = function() {
- return ctx;
- };
-
- midi.setContext = function(newCtx, onload, onprogress, onerror) {
- ctx = newCtx;
- /// tuna.js effects module - https://github.com/Dinahmoe/tuna
- if (typeof Tuna !== 'undefined' && !ctx.tunajs) {
- ctx.tunajs = new Tuna(ctx);
- }
-
- /// loading audio files
- var urls = [];
- var notes = root.keyToNote;
- for (var key in notes) urls.push(key);
- ///
- var waitForEnd = function(instrument) {
- for (var key in bufferPending) { // has pending items
- if (bufferPending[key]) return;
- }
- ///
- if (onload) { // run onload once
- onload();
- onload = null;
- }
- };
- ///
- var requestAudio = function(soundfont, instrumentId, index, key) {
- var url = soundfont[key];
- if (url) {
- bufferPending[instrumentId] ++;
- loadAudio(url, function(buffer) {
- buffer.id = key;
- var noteId = root.keyToNote[key];
- audioBuffers[instrumentId + '' + noteId] = buffer;
- ///
- if (-- bufferPending[instrumentId] === 0) {
- var percent = index / 87;
- // console.log(MIDI.GM.byId[instrumentId], 'processing: ', percent);
- soundfont.isLoaded = true;
- waitForEnd(instrument);
- }
- }, function(err) {
- // console.log(err);
- });
- }
- };
- ///
- var bufferPending = {};
- for (var instrument in root.Soundfont) {
- var soundfont = root.Soundfont[instrument];
- if (soundfont.isLoaded) {
- continue;
- }
- ///
- var synth = root.GM.byName[instrument];
- var instrumentId = synth.number;
- ///
- bufferPending[instrumentId] = 0;
- ///
- for (var index = 0; index < urls.length; index++) {
- var key = urls[index];
- requestAudio(soundfont, instrumentId, index, key);
- }
- }
- ///
- setTimeout(waitForEnd, 1);
- };
- /* Load audio file: streaming | base64 | arraybuffer
- ---------------------------------------------------------------------- */
- function loadAudio(url, onload, onerror) {
- if (useStreamingBuffer) {
- var audio = new Audio();
- audio.src = url;
- audio.controls = false;
- audio.autoplay = false;
- audio.preload = false;
- audio.addEventListener('canplay', function() {
- onload && onload(audio);
- });
- audio.addEventListener('error', function(err) {
- onerror && onerror(err);
- });
- document.body.appendChild(audio);
- } else if (url.indexOf('data:audio') === 0) { // Base64 string
- var base64 = url.split(',')[1];
- var buffer = Base64Binary.decodeArrayBuffer(base64);
- ctx.decodeAudioData(buffer, onload, onerror);
- } else { // XMLHTTP buffer
- var request = new XMLHttpRequest();
- request.open('GET', url, true);
- request.responseType = 'arraybuffer';
- request.onload = function() {
- ctx.decodeAudioData(request.response, onload, onerror);
- };
- request.send();
- }
- };
-
- function createAudioContext() {
- return new (window.AudioContext || window.webkitAudioContext)();
- };
- })();
- })(MIDI);
|