UNPKG

sono

Version:

A simple yet powerful JavaScript library for working with Web Audio

1,904 lines (1,590 loc) 166 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.sono = factory()); }(this, (function () { 'use strict'; function createCommonjsModule(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; } var _global = createCommonjsModule(function (module) { // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 var global = module.exports = typeof window != 'undefined' && window.Math == Math ? window : typeof self != 'undefined' && self.Math == Math ? self : Function('return this')(); if(typeof __g == 'number')__g = global; // eslint-disable-line no-undef }); var _core = createCommonjsModule(function (module) { var core = module.exports = {version: '2.4.0'}; if(typeof __e == 'number')__e = core; // eslint-disable-line no-undef }); var _isObject = function(it){ return typeof it === 'object' ? it !== null : typeof it === 'function'; }; var _anObject = function(it){ if(!_isObject(it))throw TypeError(it + ' is not an object!'); return it; }; var _fails = function(exec){ try { return !!exec(); } catch(e){ return true; } }; // Thank's IE8 for his funny defineProperty var _descriptors = !_fails(function(){ return Object.defineProperty({}, 'a', {get: function(){ return 7; }}).a != 7; }); var document$1 = _global.document; var is = _isObject(document$1) && _isObject(document$1.createElement); var _domCreate = function(it){ return is ? document$1.createElement(it) : {}; }; var _ie8DomDefine = !_descriptors && !_fails(function(){ return Object.defineProperty(_domCreate('div'), 'a', {get: function(){ return 7; }}).a != 7; }); // 7.1.1 ToPrimitive(input [, PreferredType]) // instead of the ES6 spec version, we didn't implement @@toPrimitive case // and the second argument - flag - preferred type is a string var _toPrimitive = function(it, S){ if(!_isObject(it))return it; var fn, val; if(S && typeof (fn = it.toString) == 'function' && !_isObject(val = fn.call(it)))return val; if(typeof (fn = it.valueOf) == 'function' && !_isObject(val = fn.call(it)))return val; if(!S && typeof (fn = it.toString) == 'function' && !_isObject(val = fn.call(it)))return val; throw TypeError("Can't convert object to primitive value"); }; var dP = Object.defineProperty; var f = _descriptors ? Object.defineProperty : function defineProperty(O, P, Attributes){ _anObject(O); P = _toPrimitive(P, true); _anObject(Attributes); if(_ie8DomDefine)try { return dP(O, P, Attributes); } catch(e){ /* empty */ } if('get' in Attributes || 'set' in Attributes)throw TypeError('Accessors not supported!'); if('value' in Attributes)O[P] = Attributes.value; return O; }; var _objectDp = { f: f }; var _propertyDesc = function(bitmap, value){ return { enumerable : !(bitmap & 1), configurable: !(bitmap & 2), writable : !(bitmap & 4), value : value }; }; var _hide = _descriptors ? function(object, key, value){ return _objectDp.f(object, key, _propertyDesc(1, value)); } : function(object, key, value){ object[key] = value; return object; }; var hasOwnProperty = {}.hasOwnProperty; var _has = function(it, key){ return hasOwnProperty.call(it, key); }; var id = 0; var px = Math.random(); var _uid = function(key){ return 'Symbol('.concat(key === undefined ? '' : key, ')_', (++id + px).toString(36)); }; var _redefine = createCommonjsModule(function (module) { var SRC = _uid('src') , TO_STRING = 'toString' , $toString = Function[TO_STRING] , TPL = ('' + $toString).split(TO_STRING); _core.inspectSource = function(it){ return $toString.call(it); }; (module.exports = function(O, key, val, safe){ var isFunction = typeof val == 'function'; if(isFunction)_has(val, 'name') || _hide(val, 'name', key); if(O[key] === val)return; if(isFunction)_has(val, SRC) || _hide(val, SRC, O[key] ? '' + O[key] : TPL.join(String(key))); if(O === _global){ O[key] = val; } else { if(!safe){ delete O[key]; _hide(O, key, val); } else { if(O[key])O[key] = val; else _hide(O, key, val); } } // add fake Function#toString for correct work wrapped methods / constructors with methods like LoDash isNative })(Function.prototype, TO_STRING, function toString(){ return typeof this == 'function' && this[SRC] || $toString.call(this); }); }); var _aFunction = function(it){ if(typeof it != 'function')throw TypeError(it + ' is not a function!'); return it; }; // optional / simple context binding var _ctx = function(fn, that, length){ _aFunction(fn); if(that === undefined)return fn; switch(length){ case 1: return function(a){ return fn.call(that, a); }; case 2: return function(a, b){ return fn.call(that, a, b); }; case 3: return function(a, b, c){ return fn.call(that, a, b, c); }; } return function(/* ...args */){ return fn.apply(that, arguments); }; }; var PROTOTYPE = 'prototype'; var $export = function(type, name, source){ var IS_FORCED = type & $export.F , IS_GLOBAL = type & $export.G , IS_STATIC = type & $export.S , IS_PROTO = type & $export.P , IS_BIND = type & $export.B , target = IS_GLOBAL ? _global : IS_STATIC ? _global[name] || (_global[name] = {}) : (_global[name] || {})[PROTOTYPE] , exports = IS_GLOBAL ? _core : _core[name] || (_core[name] = {}) , expProto = exports[PROTOTYPE] || (exports[PROTOTYPE] = {}) , key, own, out, exp; if(IS_GLOBAL)source = name; for(key in source){ // contains in native own = !IS_FORCED && target && target[key] !== undefined; // export native or passed out = (own ? target : source)[key]; // bind timers to global for call from export context exp = IS_BIND && own ? _ctx(out, _global) : IS_PROTO && typeof out == 'function' ? _ctx(Function.call, out) : out; // extend global if(target)_redefine(target, key, out, type & $export.U); // export if(exports[key] != out)_hide(exports, key, exp); if(IS_PROTO && expProto[key] != out)expProto[key] = out; } }; _global.core = _core; // type bitmap $export.F = 1; // forced $export.G = 2; // global $export.S = 4; // static $export.P = 8; // proto $export.B = 16; // bind $export.W = 32; // wrap $export.U = 64; // safe $export.R = 128; // real proto method for `library` var _export = $export; var toString = {}.toString; var _cof = function(it){ return toString.call(it).slice(8, -1); }; // fallback for non-array-like ES3 and non-enumerable old V8 strings var _iobject = Object('z').propertyIsEnumerable(0) ? Object : function(it){ return _cof(it) == 'String' ? it.split('') : Object(it); }; // 7.2.1 RequireObjectCoercible(argument) var _defined = function(it){ if(it == undefined)throw TypeError("Can't call method on " + it); return it; }; // to indexed object, toObject with fallback for non-array-like ES3 strings var _toIobject = function(it){ return _iobject(_defined(it)); }; // 7.1.4 ToInteger var ceil = Math.ceil; var floor = Math.floor; var _toInteger = function(it){ return isNaN(it = +it) ? 0 : (it > 0 ? floor : ceil)(it); }; // 7.1.15 ToLength var min = Math.min; var _toLength = function(it){ return it > 0 ? min(_toInteger(it), 0x1fffffffffffff) : 0; // pow(2, 53) - 1 == 9007199254740991 }; var max = Math.max; var min$1 = Math.min; var _toIndex = function(index, length){ index = _toInteger(index); return index < 0 ? max(index + length, 0) : min$1(index, length); }; // false -> Array#indexOf // true -> Array#includes var _arrayIncludes = function(IS_INCLUDES){ return function($this, el, fromIndex){ var O = _toIobject($this) , length = _toLength(O.length) , index = _toIndex(fromIndex, length) , value; // Array#includes uses SameValueZero equality algorithm if(IS_INCLUDES && el != el)while(length > index){ value = O[index++]; if(value != value)return true; // Array#toIndex ignores holes, Array#includes - not } else for(;length > index; index++)if(IS_INCLUDES || index in O){ if(O[index] === el)return IS_INCLUDES || index || 0; } return !IS_INCLUDES && -1; }; }; var SHARED = '__core-js_shared__'; var store = _global[SHARED] || (_global[SHARED] = {}); var _shared = function(key){ return store[key] || (store[key] = {}); }; var shared = _shared('keys'); var _sharedKey = function(key){ return shared[key] || (shared[key] = _uid(key)); }; var arrayIndexOf = _arrayIncludes(false); var IE_PROTO = _sharedKey('IE_PROTO'); var _objectKeysInternal = function(object, names){ var O = _toIobject(object) , i = 0 , result = [] , key; for(key in O)if(key != IE_PROTO)_has(O, key) && result.push(key); // Don't enum bug & hidden keys while(names.length > i)if(_has(O, key = names[i++])){ ~arrayIndexOf(result, key) || result.push(key); } return result; }; // IE 8- don't enum bug keys var _enumBugKeys = ( 'constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf' ).split(','); // 19.1.2.14 / 15.2.3.14 Object.keys(O) var _objectKeys = Object.keys || function keys(O){ return _objectKeysInternal(O, _enumBugKeys); }; var f$1 = Object.getOwnPropertySymbols; var _objectGops = { f: f$1 }; var f$2 = {}.propertyIsEnumerable; var _objectPie = { f: f$2 }; // 7.1.13 ToObject(argument) var _toObject = function(it){ return Object(_defined(it)); }; // 19.1.2.1 Object.assign(target, source, ...) var $assign = Object.assign; // should work with symbols and should have deterministic property order (V8 bug) var _objectAssign = !$assign || _fails(function(){ var A = {} , B = {} , S = Symbol() , K = 'abcdefghijklmnopqrst'; A[S] = 7; K.split('').forEach(function(k){ B[k] = k; }); return $assign({}, A)[S] != 7 || Object.keys($assign({}, B)).join('') != K; }) ? function assign(target, source){ // eslint-disable-line no-unused-vars var T = _toObject(target) , aLen = arguments.length , index = 1 , getSymbols = _objectGops.f , isEnum = _objectPie.f; while(aLen > index){ var S = _iobject(arguments[index++]) , keys = getSymbols ? _objectKeys(S).concat(getSymbols(S)) : _objectKeys(S) , length = keys.length , j = 0 , key; while(length > j)if(isEnum.call(S, key = keys[j++]))T[key] = S[key]; } return T; } : $assign; // 19.1.3.1 Object.assign(target, source) _export(_export.S + _export.F, 'Object', {assign: _objectAssign}); function dummy(context) { var buffer = context.createBuffer(1, 1, context.sampleRate); var source = context.createBufferSource(); source.buffer = buffer; source.connect(context.destination); source.start(0); source.stop(0); source.disconnect(); } function FakeContext() { var startTime = Date.now(); function fn() {} function param() { return { value: 1, defaultValue: 1, linearRampToValueAtTime: fn, setValueAtTime: fn, exponentialRampToValueAtTime: fn, setTargetAtTime: fn, setValueCurveAtTime: fn, cancelScheduledValues: fn }; } function fakeNode() { return { connect: fn, disconnect: fn, // analyser frequencyBinCount: 0, smoothingTimeConstant: 0, fftSize: 0, minDecibels: 0, maxDecibels: 0, getByteTimeDomainData: fn, getByteFrequencyData: fn, getFloatTimeDomainData: fn, getFloatFrequencyData: fn, // gain gain: param(), // panner panningModel: 0, setPosition: fn, setOrientation: fn, setVelocity: fn, distanceModel: 0, refDistance: 0, maxDistance: 0, rolloffFactor: 0, coneInnerAngle: 360, coneOuterAngle: 360, coneOuterGain: 0, // filter: type: 0, frequency: param(), Q: param(), detune: param(), // delay delayTime: param(), // convolver buffer: 0, // compressor threshold: param(), knee: param(), ratio: param(), attack: param(), release: param(), reduction: param(), // distortion oversample: 0, curve: 0, // buffer sampleRate: 1, length: 0, duration: 0, numberOfChannels: 0, getChannelData: function getChannelData() { return []; }, copyFromChannel: fn, copyToChannel: fn, // listener dopplerFactor: 0, speedOfSound: 0, // osc start: fn }; } // ie9 if (!window.Uint8Array) { window.Uint8Array = window.Float32Array = Array; } return { isFake: true, activeSourceCount: 0, createAnalyser: fakeNode, createBuffer: fakeNode, createBufferSource: fakeNode, createMediaElementSource: fakeNode, createMediaStreamSource: fakeNode, createBiquadFilter: fakeNode, createChannelMerger: fakeNode, createChannelSplitter: fakeNode, createDynamicsCompressor: fakeNode, createConvolver: fakeNode, createDelay: fakeNode, createGain: fakeNode, createOscillator: fakeNode, createPanner: fakeNode, createScriptProcessor: fakeNode, createWaveShaper: fakeNode, decodeAudioData: fn, destination: fakeNode, listener: fakeNode(), sampleRate: 44100, state: '', get currentTime() { return (Date.now() - startTime) / 1000; } }; } var iOS = navigator && /(iPhone|iPad|iPod)/i.test(navigator.userAgent); var desiredSampleRate = 44100; var Ctx = window.AudioContext || window.webkitAudioContext || FakeContext; var context = new Ctx(); if (!context) { context = new FakeContext(); } // Check if hack is necessary. Only occurs in iOS6+ devices // and only when you first boot the iPhone, or play a audio/video // with a different sample rate // https://github.com/Jam3/ios-safe-audio-context/blob/master/index.js if (iOS && context.sampleRate !== desiredSampleRate) { dummy(context); context.close(); // dispose old context context = new Ctx(); } // Handles bug in Safari 9 OSX where AudioContext instance starts in 'suspended' state if (context.state === 'suspended' && typeof context.resume === 'function') { window.setTimeout(function () { return context.resume(); }, 1000); } var context$1 = context; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; var createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var defineEnumerableProperties = function (obj, descs) { for (var key in descs) { var desc = descs[key]; desc.configurable = desc.enumerable = true; if ("value" in desc) desc.writable = true; Object.defineProperty(obj, key, desc); } return obj; }; var inherits = function (subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }; var possibleConstructorReturn = function (self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }; var Effects = function () { function Effects(context) { var _this = this; classCallCheck(this, Effects); this.context = context; this._destination = null; this._source = null; this._nodes = []; this._nodes.has = function (node) { return _this.has(node); }; this._nodes.add = function (node) { return _this.add(node); }; this._nodes.remove = function (node) { return _this.remove(node); }; this._nodes.toggle = function (node, force) { return _this.toggle(node, force); }; this._nodes.removeAll = function () { return _this.removeAll(); }; Object.keys(Effects.prototype).forEach(function (key) { if (!_this._nodes.hasOwnProperty(key) && typeof Effects.prototype[key] === 'function') { _this._nodes[key] = _this[key].bind(_this); } }); } Effects.prototype.setSource = function setSource(node) { this._source = node; this._updateConnections(); return node; }; Effects.prototype.setDestination = function setDestination(node) { this._connectToDestination(node); return node; }; Effects.prototype.has = function has(node) { if (!node) { return false; } return this._nodes.indexOf(node) > -1; }; Effects.prototype.add = function add(node) { if (!node) { return null; } if (this.has(node)) { return node; } if (Array.isArray(node)) { var n = void 0; for (var i = 0; i < node.length; i++) { n = this.add(node[i]); } return n; } this._nodes.push(node); this._updateConnections(); return node; }; Effects.prototype.remove = function remove(node) { if (!node) { return null; } if (!this.has(node)) { return node; } var l = this._nodes.length; for (var i = 0; i < l; i++) { if (node === this._nodes[i]) { this._nodes.splice(i, 1); break; } } node.disconnect(); this._updateConnections(); return node; }; Effects.prototype.toggle = function toggle(node, force) { force = !!force; var hasNode = this.has(node); if (arguments.length > 1 && hasNode === force) { return this; } if (hasNode) { this.remove(node); } else { this.add(node); } return this; }; Effects.prototype.removeAll = function removeAll() { while (this._nodes.length) { var node = this._nodes.pop(); node.disconnect(); } this._updateConnections(); return this; }; Effects.prototype.destroy = function destroy() { this.removeAll(); this.context = null; this._destination = null; if (this._source) { this._source.disconnect(); } this._source = null; }; Effects.prototype._connect = function _connect(a, b) { a.disconnect(); // console.log('> connect output', (a.name || a.constructor.name), 'to input', (b.name || b.constructor.name)); a.connect(b._in || b); }; Effects.prototype._connectToDestination = function _connectToDestination(node) { var lastNode = this._nodes[this._nodes.length - 1] || this._source; if (lastNode) { this._connect(lastNode, node); } this._destination = node; }; Effects.prototype._updateConnections = function _updateConnections() { if (!this._source) { return; } // console.log('updateConnections'); var node = void 0, prev = void 0; for (var i = 0; i < this._nodes.length; i++) { node = this._nodes[i]; prev = i === 0 ? this._source : this._nodes[i - 1]; this._connect(prev, node); } if (this._destination) { this._connectToDestination(this._destination); } }; return Effects; }(); var extensions = []; var canPlay = {}; /* * Initial tests */ var tests = [{ ext: 'ogg', type: 'audio/ogg; codecs="vorbis"' }, { ext: 'mp3', type: 'audio/mpeg;' }, { ext: 'opus', type: 'audio/ogg; codecs="opus"' }, { ext: 'wav', type: 'audio/wav; codecs="1"' }, { ext: 'm4a', type: 'audio/x-m4a;' }, { ext: 'm4a', type: 'audio/aac;' }]; var el = document.createElement('audio'); if (el) { tests.forEach(function (test) { var canPlayType = !!el.canPlayType(test.type); if (canPlayType && extensions.indexOf(test.ext) === -1) { extensions.push(test.ext); } canPlay[test.ext] = canPlayType; }); el = null; } /* * find a supported file */ function getFileExtension(url) { if (typeof url !== 'string') { return ''; } // from DataURL if (url.slice(0, 5) === 'data:') { var match = url.match(/data:audio\/(ogg|mp3|opus|wav|m4a)/i); if (match && match.length > 1) { return match[1].toLowerCase(); } } // from Standard URL url = url.split('?')[0]; url = url.slice(url.lastIndexOf('/') + 1); var a = url.split('.'); if (a.length === 1 || a[0] === '' && a.length === 2) { return ''; } return a.pop().toLowerCase(); } function getSupportedFile(fileNames) { var name = void 0; if (Array.isArray(fileNames)) { // if array get the first one that works for (var i = 0; i < fileNames.length; i++) { name = fileNames[i]; var ext = getFileExtension(name); if (extensions.indexOf(ext) > -1) { break; } } } else if ((typeof fileNames === 'undefined' ? 'undefined' : _typeof(fileNames)) === 'object') { // if not array and is object Object.keys(fileNames).some(function (key) { name = fileNames[key]; var ext = getFileExtension(name); return extensions.indexOf(ext) > -1; }); } // if string just return return name || fileNames; } /* * infer file types */ function isAudioBuffer(data) { return !!(data && window.AudioBuffer && data instanceof window.AudioBuffer); } function isArrayBuffer(data) { return !!(data && window.ArrayBuffer && data instanceof window.ArrayBuffer); } function isMediaElement(data) { return !!(data && window.HTMLMediaElement && data instanceof window.HTMLMediaElement); } function isMediaStream(data) { return !!(data && typeof data.getAudioTracks === 'function' && data.getAudioTracks().length && window.MediaStreamTrack && data.getAudioTracks()[0] instanceof window.MediaStreamTrack); } function isOscillatorType(data) { return !!(data && typeof data === 'string' && (data === 'sine' || data === 'square' || data === 'sawtooth' || data === 'triangle')); } function isURL(data) { return !!(data && typeof data === 'string' && (data.indexOf('.') > -1 || data.slice(0, 5) === 'data:')); } function containsURL(config) { if (!config || isMediaElement(config)) { return false; } // string, array or object with src/url/data property that is string, array or arraybuffer var src = getSrc(config); return isURL(src) || isArrayBuffer(src) || Array.isArray(src) && isURL(src[0]); } function getSrc(config) { return config.src || config.url || config.data || config; } var file = { canPlay: canPlay, containsURL: containsURL, extensions: extensions, getFileExtension: getFileExtension, getSrc: getSrc, getSupportedFile: getSupportedFile, isAudioBuffer: isAudioBuffer, isArrayBuffer: isArrayBuffer, isMediaElement: isMediaElement, isMediaStream: isMediaStream, isOscillatorType: isOscillatorType, isURL: isURL }; function Group(context, destination) { var sounds = []; var effects = new Effects(context); var gain = context.createGain(); var preMuteVolume = 1; var group = null; if (context) { effects.setSource(gain); effects.setDestination(destination || context.destination); } /* * Add / remove */ function find(soundOrId, callback) { var found = void 0; if (!soundOrId && soundOrId !== 0) { return found; } sounds.some(function (sound) { if (sound === soundOrId || sound.id === soundOrId) { found = sound; return true; } return false; }); if (found && callback) { return callback(found); } return found; } function remove(soundOrId) { find(soundOrId, function (sound) { return sounds.splice(sounds.indexOf(sound), 1); }); return group; } function add(sound) { sound.gain.disconnect(); sound.gain.connect(gain); sounds.push(sound); sound.once('destroy', remove); return group; } /* * Controls */ function play(delay, offset) { sounds.forEach(function (sound) { return sound.play(delay, offset); }); return group; } function pause() { sounds.forEach(function (sound) { if (sound.playing) { sound.pause(); } }); return group; } function resume() { sounds.forEach(function (sound) { if (sound.paused) { sound.play(); } }); return group; } function stop() { sounds.forEach(function (sound) { return sound.stop(); }); return group; } function seek(percent) { sounds.forEach(function (sound) { return sound.seek(percent); }); return group; } function mute() { preMuteVolume = group.volume; group.volume = 0; return group; } function unMute() { group.volume = preMuteVolume || 1; return group; } function setVolume(value) { group.volume = value; return group; } function fade(volume, duration) { if (context) { var param = gain.gain; var time = context.currentTime; param.cancelScheduledValues(time); param.setValueAtTime(param.value, time); // param.setValueAtTime(volume, time + duration); param.linearRampToValueAtTime(volume, time + duration); // param.setTargetAtTime(volume, time, duration); // param.exponentialRampToValueAtTime(Math.max(volume, 0.0001), time + duration); } else { sounds.forEach(function (sound) { return sound.fade(volume, duration); }); } return group; } /* * Load */ function load() { sounds.forEach(function (sound) { return sound.load(); }); } /* * Unload */ function unload() { sounds.forEach(function (sound) { return sound.unload(); }); } /* * Destroy */ function destroy() { while (sounds.length) { sounds.pop().destroy(); } } /* * Api */ group = { add: add, find: find, remove: remove, play: play, pause: pause, resume: resume, stop: stop, seek: seek, setVolume: setVolume, mute: mute, unMute: unMute, fade: fade, load: load, unload: unload, destroy: destroy, gain: gain, get effects() { return effects._nodes; }, set effects(value) { effects.removeAll().add(value); }, get fx() { return this.effects; }, set fx(value) { this.effects = value; }, get sounds() { return sounds; }, get volume() { return gain.gain.value; }, set volume(value) { if (isNaN(value)) { return; } value = Math.min(Math.max(value, 0), 1); if (context) { gain.gain.cancelScheduledValues(context.currentTime); gain.gain.value = value; gain.gain.setValueAtTime(value, context.currentTime); } else { gain.gain.value = value; } sounds.forEach(function (sound) { if (!sound.context) { sound.groupVolume = value; } }); } }; return group; } Group.Effects = Effects; // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. function EventEmitter$1() { this._events = this._events || {}; this._maxListeners = this._maxListeners || undefined; } var events = EventEmitter$1; // Backwards-compat with node 0.10.x EventEmitter$1.EventEmitter = EventEmitter$1; EventEmitter$1.prototype._events = undefined; EventEmitter$1.prototype._maxListeners = undefined; // By default EventEmitters will print a warning if more than 10 listeners are // added to it. This is a useful default which helps finding memory leaks. EventEmitter$1.defaultMaxListeners = 10; // Obviously not all Emitters should be limited to 10. This function allows // that to be increased. Set to zero for unlimited. EventEmitter$1.prototype.setMaxListeners = function(n) { if (!isNumber(n) || n < 0 || isNaN(n)) throw TypeError('n must be a positive number'); this._maxListeners = n; return this; }; EventEmitter$1.prototype.emit = function(type) { var er, handler, len, args, i, listeners; if (!this._events) this._events = {}; // If there is no 'error' event listener then throw. if (type === 'error') { if (!this._events.error || (isObject$1(this._events.error) && !this._events.error.length)) { er = arguments[1]; if (er instanceof Error) { throw er; // Unhandled 'error' event } else { // At least give some kind of context to the user var err = new Error('Uncaught, unspecified "error" event. (' + er + ')'); err.context = er; throw err; } } } handler = this._events[type]; if (isUndefined(handler)) return false; if (isFunction(handler)) { switch (arguments.length) { // fast cases case 1: handler.call(this); break; case 2: handler.call(this, arguments[1]); break; case 3: handler.call(this, arguments[1], arguments[2]); break; // slower default: args = Array.prototype.slice.call(arguments, 1); handler.apply(this, args); } } else if (isObject$1(handler)) { args = Array.prototype.slice.call(arguments, 1); listeners = handler.slice(); len = listeners.length; for (i = 0; i < len; i++) listeners[i].apply(this, args); } return true; }; EventEmitter$1.prototype.addListener = function(type, listener) { var m; if (!isFunction(listener)) throw TypeError('listener must be a function'); if (!this._events) this._events = {}; // To avoid recursion in the case that type === "newListener"! Before // adding it to the listeners, first emit "newListener". if (this._events.newListener) this.emit('newListener', type, isFunction(listener.listener) ? listener.listener : listener); if (!this._events[type]) // Optimize the case of one listener. Don't need the extra array object. this._events[type] = listener; else if (isObject$1(this._events[type])) // If we've already got an array, just append. this._events[type].push(listener); else // Adding the second element, need to change to array. this._events[type] = [this._events[type], listener]; // Check for listener leak if (isObject$1(this._events[type]) && !this._events[type].warned) { if (!isUndefined(this._maxListeners)) { m = this._maxListeners; } else { m = EventEmitter$1.defaultMaxListeners; } if (m && m > 0 && this._events[type].length > m) { this._events[type].warned = true; console.error('(node) warning: possible EventEmitter memory ' + 'leak detected. %d listeners added. ' + 'Use emitter.setMaxListeners() to increase limit.', this._events[type].length); if (typeof console.trace === 'function') { // not supported in IE 10 console.trace(); } } } return this; }; EventEmitter$1.prototype.on = EventEmitter$1.prototype.addListener; EventEmitter$1.prototype.once = function(type, listener) { if (!isFunction(listener)) throw TypeError('listener must be a function'); var fired = false; function g() { this.removeListener(type, g); if (!fired) { fired = true; listener.apply(this, arguments); } } g.listener = listener; this.on(type, g); return this; }; // emits a 'removeListener' event iff the listener was removed EventEmitter$1.prototype.removeListener = function(type, listener) { var list, position, length, i; if (!isFunction(listener)) throw TypeError('listener must be a function'); if (!this._events || !this._events[type]) return this; list = this._events[type]; length = list.length; position = -1; if (list === listener || (isFunction(list.listener) && list.listener === listener)) { delete this._events[type]; if (this._events.removeListener) this.emit('removeListener', type, listener); } else if (isObject$1(list)) { for (i = length; i-- > 0;) { if (list[i] === listener || (list[i].listener && list[i].listener === listener)) { position = i; break; } } if (position < 0) return this; if (list.length === 1) { list.length = 0; delete this._events[type]; } else { list.splice(position, 1); } if (this._events.removeListener) this.emit('removeListener', type, listener); } return this; }; EventEmitter$1.prototype.removeAllListeners = function(type) { var key, listeners; if (!this._events) return this; // not listening for removeListener, no need to emit if (!this._events.removeListener) { if (arguments.length === 0) this._events = {}; else if (this._events[type]) delete this._events[type]; return this; } // emit removeListener for all listeners on all events if (arguments.length === 0) { for (key in this._events) { if (key === 'removeListener') continue; this.removeAllListeners(key); } this.removeAllListeners('removeListener'); this._events = {}; return this; } listeners = this._events[type]; if (isFunction(listeners)) { this.removeListener(type, listeners); } else if (listeners) { // LIFO order while (listeners.length) this.removeListener(type, listeners[listeners.length - 1]); } delete this._events[type]; return this; }; EventEmitter$1.prototype.listeners = function(type) { var ret; if (!this._events || !this._events[type]) ret = []; else if (isFunction(this._events[type])) ret = [this._events[type]]; else ret = this._events[type].slice(); return ret; }; EventEmitter$1.prototype.listenerCount = function(type) { if (this._events) { var evlistener = this._events[type]; if (isFunction(evlistener)) return 1; else if (evlistener) return evlistener.length; } return 0; }; EventEmitter$1.listenerCount = function(emitter, type) { return emitter.listenerCount(type); }; function isFunction(arg) { return typeof arg === 'function'; } function isNumber(arg) { return typeof arg === 'number'; } function isObject$1(arg) { return typeof arg === 'object' && arg !== null; } function isUndefined(arg) { return arg === void 0; } var EventEmitter = events.EventEmitter; var Emitter = function (_EventEmitter) { inherits(Emitter, _EventEmitter); function Emitter() { classCallCheck(this, Emitter); return possibleConstructorReturn(this, _EventEmitter.call(this)); } Emitter.prototype.off = function off(type, listener) { if (listener) { return this.removeListener(type, listener); } if (type) { return this.removeAllListeners(type); } return this.removeAllListeners(); }; return Emitter; }(EventEmitter); var ERROR_STATE = ['', 'ABORTED', 'NETWORK', 'DECODE', 'SRC_NOT_SUPPORTED']; function Loader(url, deferLoad) { var emitter = new Emitter(); var audioContext = null; var data = null; var isTouchLocked = false; var progress = 0; var request = null; var timeout = null; function removeListeners() { emitter.off(); if (data && typeof data.removeEventListener === 'function') { data.removeEventListener('load', readyHandler); data.removeEventListener('canplaythrough', readyHandler); data.removeEventListener('error', errorHandler); data.onerror = null; } if (request) { request.removeEventListener('progress', progressHandler); request.removeEventListener('load', loadHandler); request.removeEventListener('error', errorHandler); } } function dispatchComplete(buffer) { emitter.emit('progress', 1); emitter.emit('loaded', buffer); emitter.emit('complete', buffer); removeListeners(); } function progressHandler(event) { if (event.lengthComputable) { progress = event.loaded / event.total; emitter.emit('progress', progress); } } function errorHandler() { cancelTimeout(); var status = ''; if (request) { status = request.status + ' ' + request.statusText; } else if (data && data.error) { status = ERROR_STATE[data.error.code]; } if (emitter.listenerCount('error')) { emitter.emit('error', new Error('Load Error: ' + status + ' ' + url)); } removeListeners(); } function cancelTimeout() { window.clearTimeout(timeout); } function decodeArrayBuffer(arraybuffer) { audioContext.decodeAudioData(arraybuffer, function (buffer) { data = buffer; request = null; progress = 1; dispatchComplete(buffer); }, errorHandler); } function loadHandler() { if (request.status >= 400) { errorHandler(); return; } decodeArrayBuffer(request.response); } function readyHandler() { cancelTimeout(); if (!data) { return; } if (!data.readyState) { errorHandler(); return; } progress = 1; dispatchComplete(data); } function cancel() { cancelTimeout(); removeListeners(); if (request && request.readyState !== 4) { request.abort(); } request = null; } function destroy() { cancel(); request = null; data = null; audioContext = null; } // audio buffer function loadArrayBuffer() { if (url instanceof window.ArrayBuffer) { decodeArrayBuffer(url); return; } request = new XMLHttpRequest(); request.open('GET', url, true); request.responseType = 'arraybuffer'; request.addEventListener('progress', progressHandler); request.addEventListener('load', loadHandler); request.addEventListener('error', errorHandler); request.send(); } // audio element function loadAudioElement() { if (!data || !data.tagName) { data = document.createElement('audio'); } if (!isTouchLocked) { cancelTimeout(); timeout = window.setTimeout(readyHandler, 3000); data.addEventListener('canplaythrough', readyHandler, false); data.addEventListener('load', readyHandler, false); } data.addEventListener('error', errorHandler, false); data.preload = 'auto'; data.onerror = errorHandler; data.src = url; data.load(); if (isTouchLocked) { dispatchComplete(data); } } function start() { var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; if (!url || deferLoad && !force) { return; } if (audioContext) { loadArrayBuffer(); } else { loadAudioElement(); } } // reload function load(newUrl) { url = newUrl; start(); } var api = { on: emitter.on.bind(emitter), once: emitter.once.bind(emitter), off: emitter.off.bind(emitter), load: load, start: start, cancel: cancel, destroy: destroy }; Object.defineProperties(api, { data: { get: function get() { return data; } }, progress: { get: function get() { return progress; } }, audioContext: { set: function set(value) { audioContext = value; } }, isTouchLocked: { set: function set(value) { isTouchLocked = value; } }, url: { get: function get() { return url; } } }); return Object.freeze(api); } Loader.Group = function () { var emitter = new Emitter(); var queue = []; var numLoaded = 0; var numTotal = 0; var currentLoader = null; function progressHandler(progress) { var loaded = numLoaded + progress; emitter.emit('progress', loaded / numTotal); } function completeHandler() { numLoaded++; removeListeners(); emitter.emit('progress', numLoaded / numTotal); next(); } function errorHandler(e) { removeListeners(); if (emitter.listenerCount('error')) { emitter.emit('error', e); } next(); } function next() { if (queue.length === 0) { currentLoader = null; emitter.emit('complete'); return; } currentLoader = queue.pop(); currentLoader.on('progress', progressHandler); currentLoader.once('loaded', completeHandler); currentLoader.once('error', errorHandler); currentLoader.start(); } function removeListeners() { currentLoader.off('progress', progressHandler); currentLoader.off('loaded', completeHandler); currentLoader.off('error', errorHandler); } function add(loader) { queue.push(loader); numTotal++; return loader; } function start() { numTotal = queue.length; next(); } return Object.freeze({ on: emitter.on.bind(emitter), once: emitter.once.bind(emitter), off: emitter.off.bind(emitter), add: add, start: start }); }; function log(api) { var title = 'sono ' + api.VERSION, info = 'Supported:' + api.isSupported + ' WebAudioAPI:' + api.hasWebAudio + ' TouchLocked:' + api.isTouchLocked + ' State:' + (api.context && api.context.state) + ' Extensions:' + api.file.extensions; if (navigator.userAgent.indexOf('Chrome') > -1) { var args = ['%c ♫ ' + title + ' ♫ %c ' + info + ' ', 'color: #FFFFFF; background: #379F7A', 'color: #1F1C0D; background: #E0FBAC']; console.log.apply(console, args); } else if (window.console && window.console.log.call) { console.log.call(console, title + ' ' + info); } } function pageVisibility(onHidden, onShown) { var enabled = false; var hidden = null; var visibilityChange = null; if (typeof document.hidden !== 'undefined') { hidden = 'hidden'; visibilityChange = 'visibilitychange'; } else if (typeof document.mozHidden !== 'undefined') { hidden = 'mozHidden'; visibilityChange = 'mozvisibilitychange'; } else if (typeof document.msHidden !== 'undefined') { hidden = 'msHidden'; visibilityChange = 'msvisibilitychange'; } else if (typeof document.webkitHidden !== 'undefined') { hidden = 'webkitHidden'; visibilityChange = 'webkitvisibilitychange'; } function onChange() { if (document[hidden]) { onHidden(); } else { onShown(); } } function enable(value) { enabled = value; if (enabled) { document.addEventListener(visibilityChange, onChange, false); } else { document.removeEventListener(visibilityChange, onChange); } } if (typeof visibilityChange !== 'undefined') { enable(true); } return { get enabled() { return enabled; }, set enabled(value) { enable(value); } }; } function BufferSource(buffer, context, endedCallback) { var api = {}; var ended = false; var loop = false; var paused = false; var cuedAt = 0; var playbackRate = 1; var playing = false; var sourceNode = null; var startedAt = 0; function createSourceNode() { if (!sourceNode && context) { sourceNode = context.createBufferSource(); sourceNode.buffer = buffer; } return sourceNode; } /* * Controls */ function stop() { if (sourceNode) { sourceNode.onended = null; try { sourceNode.disconnect(); sourceNode.stop(0); } catch (e) {} sourceNode = null; } paused = false; cuedAt = 0; playing = false; startedAt = 0; } function pause() { var elapsed = context.currentTime - startedAt; stop(); cuedAt = elapsed; playing = false; paused = true; } function endedHandler() { stop(); ended = true; if (typeof endedCallback === 'function') { endedCallback(api); } } function play() { var delay = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; if (playing) { return; } delay = delay ? context.currentTime + delay : 0; if (offset) { cuedAt = 0; } if (cuedAt) { offset = cuedAt; } while (offset > api.duration) { offset = offset % api.duration; } createSourceNode(); sourceNode.onended = endedHandler; source