sono
Version:
A simple yet powerful JavaScript library for working with Web Audio
1,904 lines (1,590 loc) • 166 kB
JavaScript
(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