{"version":3,"file":"pixi-sound.js","sources":["../node_modules/promise-polyfill/src/index.js","../node_modules/promise-polyfill/src/finally.js","../src/Filterable.ts","../src/filters/Filter.ts","../src/instance.ts","../src/webaudio/WebAudioUtils.ts","../src/webaudio/WebAudioInstance.ts","../src/webaudio/WebAudioNodes.ts","../src/webaudio/WebAudioMedia.ts","../src/webaudio/WebAudioContext.ts","../src/filters/EqualizerFilter.ts","../src/filters/DistortionFilter.ts","../src/filters/StereoFilter.ts","../src/filters/ReverbFilter.ts","../src/filters/MonoFilter.ts","../src/filters/TelephoneFilter.ts","../src/htmlaudio/HTMLAudioInstance.ts","../src/htmlaudio/HTMLAudioMedia.ts","../src/htmlaudio/HTMLAudioContext.ts","../src/utils/supported.ts","../src/utils/resolveUrl.ts","../src/loader/LoaderMiddleware.ts","../src/loader/Loader.ts","../src/sprites/SoundSprite.ts","../src/Sound.ts","../src/SoundLibrary.ts","../src/utils/playOnce.ts","../src/utils/render.ts","../src/utils/sineTone.ts","../src/index.ts"],"sourcesContent":["import promiseFinally from './finally';\n\n// Store setTimeout reference so promise-polyfill will be unaffected by\n// other code modifying setTimeout (like sinon.useFakeTimers())\nvar setTimeoutFunc = setTimeout;\n\nfunction noop() {}\n\n// Polyfill for Function.prototype.bind\nfunction bind(fn, thisArg) {\n return function() {\n fn.apply(thisArg, arguments);\n };\n}\n\n/**\n * @constructor\n * @param {Function} fn\n */\nfunction Promise(fn) {\n if (!(this instanceof Promise))\n throw new TypeError('Promises must be constructed via new');\n if (typeof fn !== 'function') throw new TypeError('not a function');\n /** @type {!number} */\n this._state = 0;\n /** @type {!boolean} */\n this._handled = false;\n /** @type {Promise|undefined} */\n this._value = undefined;\n /** @type {!Array} */\n this._deferreds = [];\n\n doResolve(fn, this);\n}\n\nfunction handle(self, deferred) {\n while (self._state === 3) {\n self = self._value;\n }\n if (self._state === 0) {\n self._deferreds.push(deferred);\n return;\n }\n self._handled = true;\n Promise._immediateFn(function() {\n var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;\n if (cb === null) {\n (self._state === 1 ? resolve : reject)(deferred.promise, self._value);\n return;\n }\n var ret;\n try {\n ret = cb(self._value);\n } catch (e) {\n reject(deferred.promise, e);\n return;\n }\n resolve(deferred.promise, ret);\n });\n}\n\nfunction resolve(self, newValue) {\n try {\n // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure\n if (newValue === self)\n throw new TypeError('A promise cannot be resolved with itself.');\n if (\n newValue &&\n (typeof newValue === 'object' || typeof newValue === 'function')\n ) {\n var then = newValue.then;\n if (newValue instanceof Promise) {\n self._state = 3;\n self._value = newValue;\n finale(self);\n return;\n } else if (typeof then === 'function') {\n doResolve(bind(then, newValue), self);\n return;\n }\n }\n self._state = 1;\n self._value = newValue;\n finale(self);\n } catch (e) {\n reject(self, e);\n }\n}\n\nfunction reject(self, newValue) {\n self._state = 2;\n self._value = newValue;\n finale(self);\n}\n\nfunction finale(self) {\n if (self._state === 2 && self._deferreds.length === 0) {\n Promise._immediateFn(function() {\n if (!self._handled) {\n Promise._unhandledRejectionFn(self._value);\n }\n });\n }\n\n for (var i = 0, len = self._deferreds.length; i < len; i++) {\n handle(self, self._deferreds[i]);\n }\n self._deferreds = null;\n}\n\n/**\n * @constructor\n */\nfunction Handler(onFulfilled, onRejected, promise) {\n this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;\n this.onRejected = typeof onRejected === 'function' ? onRejected : null;\n this.promise = promise;\n}\n\n/**\n * Take a potentially misbehaving resolver function and make sure\n * onFulfilled and onRejected are only called once.\n *\n * Makes no guarantees about asynchrony.\n */\nfunction doResolve(fn, self) {\n var done = false;\n try {\n fn(\n function(value) {\n if (done) return;\n done = true;\n resolve(self, value);\n },\n function(reason) {\n if (done) return;\n done = true;\n reject(self, reason);\n }\n );\n } catch (ex) {\n if (done) return;\n done = true;\n reject(self, ex);\n }\n}\n\nPromise.prototype['catch'] = function(onRejected) {\n return this.then(null, onRejected);\n};\n\nPromise.prototype.then = function(onFulfilled, onRejected) {\n // @ts-ignore\n var prom = new this.constructor(noop);\n\n handle(this, new Handler(onFulfilled, onRejected, prom));\n return prom;\n};\n\nPromise.prototype['finally'] = promiseFinally;\n\nPromise.all = function(arr) {\n return new Promise(function(resolve, reject) {\n if (!arr || typeof arr.length === 'undefined')\n throw new TypeError('Promise.all accepts an array');\n var args = Array.prototype.slice.call(arr);\n if (args.length === 0) return resolve([]);\n var remaining = args.length;\n\n function res(i, val) {\n try {\n if (val && (typeof val === 'object' || typeof val === 'function')) {\n var then = val.then;\n if (typeof then === 'function') {\n then.call(\n val,\n function(val) {\n res(i, val);\n },\n reject\n );\n return;\n }\n }\n args[i] = val;\n if (--remaining === 0) {\n resolve(args);\n }\n } catch (ex) {\n reject(ex);\n }\n }\n\n for (var i = 0; i < args.length; i++) {\n res(i, args[i]);\n }\n });\n};\n\nPromise.resolve = function(value) {\n if (value && typeof value === 'object' && value.constructor === Promise) {\n return value;\n }\n\n return new Promise(function(resolve) {\n resolve(value);\n });\n};\n\nPromise.reject = function(value) {\n return new Promise(function(resolve, reject) {\n reject(value);\n });\n};\n\nPromise.race = function(values) {\n return new Promise(function(resolve, reject) {\n for (var i = 0, len = values.length; i < len; i++) {\n values[i].then(resolve, reject);\n }\n });\n};\n\n// Use polyfill for setImmediate for performance gains\nPromise._immediateFn =\n (typeof setImmediate === 'function' &&\n function(fn) {\n setImmediate(fn);\n }) ||\n function(fn) {\n setTimeoutFunc(fn, 0);\n };\n\nPromise._unhandledRejectionFn = function _unhandledRejectionFn(err) {\n if (typeof console !== 'undefined' && console) {\n console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console\n }\n};\n\nexport default Promise;\n","/**\n * @this {Promise}\n */\nfunction finallyConstructor(callback) {\n var constructor = this.constructor;\n return this.then(\n function(value) {\n return constructor.resolve(callback()).then(function() {\n return value;\n });\n },\n function(reason) {\n return constructor.resolve(callback()).then(function() {\n return constructor.reject(reason);\n });\n }\n );\n}\n\nexport default finallyConstructor;\n","import { Filter } from \"./filters/Filter\";\n\n/**\n * Abstract class which SoundNodes and SoundContext\n * both extend. This provides the functionality for adding\n * dynamic filters.\n * @class Filterable\n * @memberof PIXI.sound\n * @param {AudioNode} source The source audio node\n * @param {AudioNode} destination The output audio node\n * @private\n */\nexport class Filterable\n{\n /**\n * Get the gain node\n * @name PIXI.sound.Filterable#_input\n * @type {AudioNode}\n * @private\n */\n private _input: AudioNode;\n\n /**\n * The destination output audio node\n * @name PIXI.sound.Filterable#_output\n * @type {AudioNode}\n * @private\n */\n private _output: AudioNode;\n\n /**\n * Collection of filters.\n * @name PIXI.sound.Filterable#_filters\n * @type {PIXI.sound.filters.Filter[]}\n * @private\n */\n private _filters: Filter[];\n\n constructor(input: AudioNode, output: AudioNode)\n {\n this._output = output;\n this._input = input;\n }\n\n /**\n * The destination output audio node\n * @name PIXI.sound.Filterable#destination\n * @type {AudioNode}\n * @readonly\n */\n get destination(): AudioNode\n {\n return this._input;\n }\n\n /**\n * The collection of filters\n * @name PIXI.sound.Filterable#filters\n * @type {PIXI.sound.filters.Filter[]}\n */\n get filters(): Filter[]\n {\n return this._filters;\n }\n set filters(filters: Filter[])\n {\n if (this._filters)\n {\n this._filters.forEach((filter: Filter) => {\n if (filter)\n {\n filter.disconnect();\n }\n });\n this._filters = null;\n // Reconnect direct path\n this._input.connect(this._output);\n }\n\n if (filters && filters.length)\n {\n this._filters = filters.slice(0);\n\n // Disconnect direct path before inserting filters\n this._input.disconnect();\n\n // Connect each filter\n let prevFilter: Filter = null;\n filters.forEach((filter: Filter) => {\n if (prevFilter === null)\n {\n // first filter is the destination\n // for the analyser\n this._input.connect(filter.destination);\n }\n else\n {\n prevFilter.connect(filter.destination);\n }\n prevFilter = filter;\n });\n prevFilter.connect(this._output);\n }\n }\n\n /**\n * Cleans up.\n * @method PIXI.sound.Filterable#destroy\n */\n public destroy(): void\n {\n this.filters = null;\n this._input = null;\n this._output = null;\n }\n}\n","/**\n * Represents a single sound element. Can be used to play, pause, etc. sound instances.\n *\n * @class Filter\n * @memberof PIXI.sound.filters\n * @param {AudioNode} destination The audio node to use as the destination for the input AudioNode\n * @param {AudioNode} [source] Optional output node, defaults to destination node. This is useful\n * when creating filters which contains multiple AudioNode elements chained together.\n */\nexport class Filter\n{\n /**\n * The node to connect for the filter to the previous filter.\n * @name PIXI.sound.filters.Filter#destination\n * @type {AudioNode}\n */\n public destination: AudioNode;\n\n /**\n * The node to connect for the filter to the previous filter.\n * @name PIXI.sound.filters.Filter#source\n * @type {AudioNode}\n */\n public source: AudioNode;\n\n constructor(destination: AudioNode, source?: AudioNode)\n {\n this.init(destination, source);\n }\n\n /**\n * Reinitialize\n * @method PIXI.sound.filters.Filter#init\n * @private\n */\n protected init(destination: AudioNode, source?: AudioNode)\n {\n this.destination = destination;\n this.source = source || destination;\n }\n\n /**\n * Connect to the destination.\n * @method PIXI.sound.filters.Filter#connect\n * @param {AudioNode} destination The destination node to connect the output to\n */\n public connect(destination: AudioNode): void\n {\n this.source.connect(destination);\n }\n\n /**\n * Completely disconnect filter from destination and source nodes.\n * @method PIXI.sound.filters.Filter#disconnect\n */\n public disconnect(): void\n {\n this.source.disconnect();\n }\n\n /**\n * Destroy the filter and don't use after this.\n * @method PIXI.sound.filters.Filter#destroy\n */\n public destroy(): void\n {\n this.disconnect();\n this.destination = null;\n this.source = null;\n }\n}\n","import { SoundLibrary } from \"./SoundLibrary\";\n\n/**\n * Singletone instance of the SoundLibrary\n * @private\n */\nexport let instance: SoundLibrary;\n\n/**\n * Internal set function for the singleton instance.\n * @private\n * @param {PIXI.sound} sound - Sound library instance\n * @return {PIXI.sound}\n */\nexport function setInstance(sound: SoundLibrary) {\n instance = sound;\n return sound;\n}\n\n/**\n * Internal get function for the singleton instance.\n * @private\n * @return {PIXI.sound}\n */\nexport function getInstance(): SoundLibrary {\n return instance;\n}\n","import { getInstance } from \"../instance\";\nimport { WebAudioContext } from \"./WebAudioContext\";\n\n/**\n * Internal class for Web Audio abstractions and convenience methods.\n * @private\n * @class WebAudioUtils\n * @memberof PIXI.sound.webaudio\n */\nexport class WebAudioUtils\n{\n /**\n * Dezippering is removed in the future Web Audio API, instead\n * we use the `setValueAtTime` method, however, this is not available\n * in all environments (e.g., Android webview), so we fallback to the `value` setter.\n * @method PIXI.sound.webaudio.WebAudioUtils.setParamValue\n * @private\n * @param {AudioParam} param - AudioNode parameter object\n * @param {number} value - Value to set\n * @return {number} The value set\n */\n public static setParamValue(param: AudioParam, value: number): number\n {\n if (param.setValueAtTime)\n {\n const context = getInstance().context as WebAudioContext;\n param.setValueAtTime(value, context.audioContext.currentTime);\n }\n else\n {\n param.value = value;\n }\n return value;\n }\n}\n","import { IMediaInstance } from \"../interfaces\";\nimport { PlayOptions } from \"../Sound\";\nimport { WebAudioMedia } from \"./WebAudioMedia\";\nimport { WebAudioUtils } from \"./WebAudioUtils\";\n\nlet id = 0;\n\n/**\n * A single play instance that handles the AudioBufferSourceNode.\n * @private\n * @class WebAudioInstance\n * @memberof PIXI.sound.webaudio\n * @param {SoundNodes} source Reference to the SoundNodes.\n */\nexport class WebAudioInstance extends PIXI.utils.EventEmitter implements IMediaInstance\n{\n /**\n * The current unique ID for this instance.\n * @name PIXI.sound.webaudio.WebAudioInstance#id\n * @readonly\n */\n public id: number;\n\n /**\n * The source Sound.\n * @type {PIXI.sound.webaudio.WebAudioMedia}\n * @name PIXI.sound.webaudio.WebAudioInstance#_media\n * @private\n */\n private _media: WebAudioMedia;\n\n /**\n * true if paused.\n * @type {boolean}\n * @name PIXI.sound.webaudio.WebAudioInstance#_paused\n * @private\n */\n private _paused: boolean;\n\n /**\n * true if muted.\n * @type {boolean}\n * @name PIXI.sound.webaudio.WebAudioInstance#_muted\n * @private\n */\n private _muted: boolean;\n\n /**\n * true if paused.\n * @type {boolean}\n * @name PIXI.sound.webaudio.WebAudioInstance#_pausedReal\n * @private\n */\n private _pausedReal: boolean;\n\n /**\n * The instance volume\n * @type {number}\n * @name PIXI.sound.webaudio.WebAudioInstance#_volume\n * @private\n */\n private _volume: number;\n\n /**\n * Last update frame number.\n * @type {number}\n * @name PIXI.sound.webaudio.WebAudioInstance#_lastUpdate\n * @private\n */\n private _lastUpdate: number;\n\n /**\n * The total number of seconds elapsed in playback.\n * @type {number}\n * @name PIXI.sound.webaudio.WebAudioInstance#_elapsed\n * @private\n */\n private _elapsed: number;\n\n /**\n * Playback rate, where 1 is 100%.\n * @type {number}\n * @name PIXI.sound.webaudio.WebAudioInstance#_speed\n * @private\n */\n private _speed: number;\n\n /**\n * Playback rate, where 1 is 100%.\n * @type {number}\n * @name PIXI.sound.webaudio.WebAudioInstance#_end\n * @private\n */\n private _end: number;\n\n /**\n * The number of seconds to wait before starting playback\n * @type {number}\n * @name PIXI.sound.webaudio.WebAudioInstance#_wait\n * @private\n */\n private _wait: number;\n\n /**\n * `true` if should be looping.\n * @type {boolean}\n * @name PIXI.sound.webaudio.WebAudioInstance#_loop\n * @private\n */\n private _loop: boolean;\n\n /**\n * Gain node for controlling volume of instance\n * @type {GainNode}\n * @name PIXI.sound.webaudio.WebAudioInstance#_gain\n * @private\n */\n private _gain: GainNode;\n\n /**\n * Length of the sound in seconds.\n * @type {number}\n * @name PIXI.sound.webaudio.WebAudioInstance#_duration\n * @private\n */\n private _duration: number;\n\n /**\n * The progress of the sound from 0 to 1.\n * @type {number}\n * @name PIXI.sound.webaudio.WebAudioInstance#_progress\n * @private\n */\n private _progress: number;\n\n /**\n * Audio buffer source clone from Sound object.\n * @type {AudioBufferSourceNode}\n * @name PIXI.sound.webaudio.WebAudioInstance#_source\n * @private\n */\n private _source: AudioBufferSourceNode;\n\n constructor(media: WebAudioMedia)\n {\n super();\n\n this.id = id++;\n this._media = null;\n this._paused = false;\n this._muted = false;\n this._elapsed = 0;\n\n // Initialize\n this.init(media);\n }\n\n /**\n * Stops the instance, don't use after this.\n * @method PIXI.sound.webaudio.WebAudioInstance#stop\n */\n public stop(): void\n {\n if (this._source)\n {\n this._internalStop();\n\n /**\n * The sound is stopped. Don't use after this is called.\n * @event PIXI.sound.webaudio.WebAudioInstance#stop\n */\n this.emit(\"stop\");\n }\n }\n\n /**\n * Set the instance speed from 0 to 1\n * @member {number} PIXI.sound.htmlaudio.HTMLAudioInstance#speed\n */\n public get speed(): number\n {\n return this._speed;\n }\n public set speed(speed: number)\n {\n this._speed = speed;\n this.refresh();\n this._update(true); // update progress\n }\n\n /**\n * Get the set the volume for this instance from 0 to 1\n * @member {number} PIXI.sound.htmlaudio.HTMLAudioInstance#volume\n */\n public get volume(): number\n {\n return this._volume;\n }\n public set volume(volume: number)\n {\n this._volume = volume;\n this.refresh();\n }\n\n /**\n * `true` if the sound is muted\n * @member {boolean} PIXI.sound.htmlaudio.HTMLAudioInstance#muted\n */\n public get muted(): boolean\n {\n return this._muted;\n }\n public set muted(muted: boolean)\n {\n this._muted = muted;\n this.refresh();\n }\n\n /**\n * If the sound instance should loop playback\n * @member {boolean} PIXI.sound.htmlaudio.HTMLAudioInstance#loop\n */\n public get loop(): boolean\n {\n return this._loop;\n }\n public set loop(loop: boolean)\n {\n this._loop = loop;\n this.refresh();\n }\n\n /**\n * Refresh loop, volume and speed based on changes to parent\n * @method PIXI.sound.webaudio.WebAudioInstance#refresh\n */\n public refresh(): void\n {\n // Sound could be paused\n if (!this._source) {\n return;\n }\n const global = this._media.context;\n const sound = this._media.parent;\n\n // Updating looping\n this._source.loop = this._loop || sound.loop;\n\n // Update the volume\n const globalVolume = global.volume * (global.muted ? 0 : 1);\n const soundVolume = sound.volume * (sound.muted ? 0 : 1);\n const instanceVolume = this._volume * (this._muted ? 0 : 1);\n WebAudioUtils.setParamValue(this._gain.gain, instanceVolume * soundVolume * globalVolume);\n\n // Update the speed\n WebAudioUtils.setParamValue(this._source.playbackRate, this._speed * sound.speed * global.speed);\n }\n\n /**\n * Handle changes in paused state, either globally or sound or instance\n * @method PIXI.sound.webaudio.WebAudioInstance#refreshPaused\n */\n public refreshPaused(): void\n {\n const global = this._media.context;\n const sound = this._media.parent;\n\n // Consider global and sound paused\n const pausedReal = this._paused || sound.paused || global.paused;\n\n if (pausedReal !== this._pausedReal)\n {\n this._pausedReal = pausedReal;\n\n if (pausedReal)\n {\n // pause the sounds\n this._internalStop();\n\n /**\n * The sound is paused.\n * @event PIXI.sound.webaudio.WebAudioInstance#paused\n */\n this.emit(\"paused\");\n }\n else\n {\n /**\n * The sound is unpaused.\n * @event PIXI.sound.webaudio.WebAudioInstance#resumed\n */\n this.emit(\"resumed\");\n\n // resume the playing with offset\n this.play({\n wait: this._elapsed < this._wait ? this._wait - this._elapsed : 0,\n start: Math.max(this._elapsed - this._wait, 0) % this._duration,\n end: this._end,\n speed: this._speed,\n loop: this._loop,\n volume: this._volume,\n });\n }\n\n /**\n * The sound is paused or unpaused.\n * @event PIXI.sound.webaudio.WebAudioInstance#pause\n * @property {boolean} paused If the instance was paused or not.\n */\n this.emit(\"pause\", pausedReal);\n }\n }\n\n /**\n * Plays the sound.\n * @method PIXI.sound.webaudio.WebAudioInstance#play\n * @param {Object} options Play options\n * @param {number} options.start The position to start playing, in seconds.\n * @param {number} options.end The ending position in seconds.\n * @param {number} options.speed Speed for the instance\n * @param {boolean} options.loop If the instance is looping, defaults to sound loop\n * @param {number} options.volume Volume of the instance\n * @param {boolean} options.muted Muted state of instance\n * @param {number} options.wait Delay in seconds before starting playback\n */\n public play(options: PlayOptions): void\n {\n const {start, end, speed, loop, volume, muted, wait} = options;\n\n if (end)\n {\n console.assert(end > start, \"End time is before start time\");\n }\n this._paused = false;\n const {source, gain} = this._media.nodes.cloneBufferSource();\n\n this._source = source;\n this._gain = gain;\n this._speed = speed;\n this._volume = volume;\n this._loop = !!loop;\n this._muted = muted;\n this._wait = wait || 0;\n this.refresh();\n\n const duration: number = this._source.buffer.duration;\n this._duration = duration;\n this._end = end;\n this._lastUpdate = this._now();\n this._elapsed = start;\n this._source.onended = this._onComplete.bind(this);\n const when = wait ? this._now() + wait : 0;\n\n if (this._loop)\n {\n this._source.loopEnd = end;\n this._source.loopStart = start;\n this._source.start(when, start);\n }\n else if (end)\n {\n this._source.start(when, start, end - start);\n }\n else\n {\n this._source.start(when, start);\n }\n\n /**\n * The sound is started.\n * @event PIXI.sound.webaudio.WebAudioInstance#start\n */\n this.emit(\"start\");\n\n // Do an update for the initial progress\n this._update(true);\n\n // Start handling internal ticks\n this._enabled = true;\n }\n\n /**\n * Utility to convert time in millseconds or seconds\n * @method PIXI.sound.webaudio.WebAudioInstance#_toSec\n * @private\n * @param {number} [time] Time in either ms or sec\n * @return {number} Time in seconds\n */\n private _toSec(time?: number): number\n {\n if (time > 10)\n {\n time /= 1000;\n }\n return time || 0;\n }\n\n /**\n * Start the update progress.\n * @name PIXI.sound.webaudio.WebAudioInstance#_enabled\n * @type {boolean}\n * @private\n */\n private set _enabled(enabled: boolean)\n {\n PIXI.ticker.shared.remove(this._updateListener, this);\n if (enabled)\n {\n PIXI.ticker.shared.add(this._updateListener, this);\n }\n }\n\n /**\n * The current playback progress from 0 to 1.\n * @type {number}\n * @name PIXI.sound.webaudio.WebAudioInstance#progress\n */\n public get progress(): number\n {\n return this._progress;\n }\n\n /**\n * Pauses the sound.\n * @type {boolean}\n * @name PIXI.sound.webaudio.WebAudioInstance#paused\n */\n public get paused(): boolean\n {\n return this._paused;\n }\n\n public set paused(paused: boolean)\n {\n this._paused = paused;\n this.refreshPaused();\n }\n\n /**\n * Don't use after this.\n * @method PIXI.sound.webaudio.WebAudioInstance#destroy\n */\n public destroy(): void\n {\n this.removeAllListeners();\n this._internalStop();\n if (this._source)\n {\n this._source.disconnect();\n this._source = null;\n }\n if (this._gain)\n {\n this._gain.disconnect();\n this._gain = null;\n }\n if (this._media)\n {\n this._media.context.events.off(\"refresh\", this.refresh, this);\n this._media.context.events.off(\"refreshPaused\", this.refreshPaused, this);\n this._media = null;\n }\n this._end = null;\n this._speed = 1;\n this._volume = 1;\n this._loop = false;\n this._elapsed = 0;\n this._duration = 0;\n this._wait = 0;\n this._paused = false;\n this._muted = false;\n this._pausedReal = false;\n }\n\n /**\n * To string method for instance.\n * @method PIXI.sound.webaudio.WebAudioInstance#toString\n * @return {string} The string representation of instance.\n * @private\n */\n public toString(): string\n {\n return \"[WebAudioInstance id=\" + this.id + \"]\";\n }\n\n /**\n * Get the current time in seconds.\n * @method PIXI.sound.webaudio.WebAudioInstance#_now\n * @private\n * @return {number} Seconds since start of context\n */\n private _now(): number\n {\n return this._media.context.audioContext.currentTime;\n }\n\n /**\n * Callback for update listener\n * @type {Function}\n * @name PIXI.sound.webaudio.WebAudioInstance#_updateListener\n * @private\n */\n private _updateListener() {\n this._update();\n }\n\n /**\n * Internal update the progress.\n * @method PIXI.sound.webaudio.WebAudioInstance#_update\n * @private\n */\n private _update(force: boolean = false): void\n {\n if (this._source)\n {\n const now: number = this._now();\n const delta: number = now - this._lastUpdate;\n\n if (delta > 0 || force)\n {\n const speed: number = this._source.playbackRate.value;\n this._elapsed += delta * speed;\n this._lastUpdate = now;\n const duration: number = this._duration;\n let progress: number;\n if (this._source.loopStart)\n {\n const soundLength = this._source.loopEnd - this._source.loopStart;\n progress = (this._source.loopStart + this._elapsed % soundLength) / duration;\n }\n else\n {\n progress = (this._elapsed % duration) / duration;\n }\n\n // Update the progress\n this._progress = progress;\n\n /**\n * The sound progress is updated.\n * @event PIXI.sound.webaudio.WebAudioInstance#progress\n * @property {number} progress Amount progressed from 0 to 1\n * @property {number} duration The total playback in seconds\n */\n this.emit(\"progress\", this._progress, duration);\n }\n }\n }\n\n /**\n * Initializes the instance.\n * @method PIXI.sound.webaudio.WebAudioInstance#init\n */\n public init(media: WebAudioMedia): void\n {\n this._media = media;\n media.context.events.on(\"refresh\", this.refresh, this);\n media.context.events.on(\"refreshPaused\", this.refreshPaused, this);\n }\n\n /**\n * Stops the instance.\n * @method PIXI.sound.webaudio.WebAudioInstance#_internalStop\n * @private\n */\n private _internalStop(): void\n {\n if (this._source)\n {\n this._enabled = false;\n this._source.onended = null;\n this._source.stop(0); // param needed for iOS 8 bug\n this._source.disconnect();\n this._source.buffer = null;\n this._source = null;\n }\n }\n\n /**\n * Callback when completed.\n * @method PIXI.sound.webaudio.WebAudioInstance#_onComplete\n * @private\n */\n private _onComplete(): void\n {\n if (this._source)\n {\n this._enabled = false;\n this._source.onended = null;\n this._source.disconnect();\n this._source.buffer = null;\n }\n this._source = null;\n this._progress = 1;\n this.emit(\"progress\", 1, this._duration);\n /**\n * The sound ends, don't use after this\n * @event PIXI.sound.webaudio.WebAudioInstance#end\n */\n this.emit(\"end\", this);\n }\n}\n","import { Filterable } from \"../Filterable\";\nimport { Filter } from \"../filters/Filter\";\nimport { WebAudioContext } from \"./WebAudioContext\";\nimport { WebAudioUtils } from \"./WebAudioUtils\";\n\n/**\n * Output for cloneing node\n * @interface PIXI.sound.SoundNodes~SourceClone\n * @property {AudioBufferSourceNode} source Cloned audio buffer source\n * @property {GainNode} gain Independent volume control\n */\nexport interface SourceClone {\n source: AudioBufferSourceNode;\n gain: GainNode;\n}\n\n/**\n * @private\n * @class WebAudioNodes\n * @extends PIXI.sound.Filterable\n * @private\n * @memberof PIXI.sound.webaudio\n * @param {PIXI.sound.webaudio.WebAudioContext} audioContext The audio context.\n */\nexport class WebAudioNodes extends Filterable\n{\n /**\n * The buffer size for script processor, default is `0` which auto-detects. If you plan to use\n * script node on iOS, you'll need to provide a non-zero amount.\n * @name PIXI.sound.SoundNodes.BUFFER_SIZE\n * @type {number}\n * @default 0\n */\n public static BUFFER_SIZE: number = 0;\n\n /**\n * Get the buffer source node\n * @name PIXI.sound.SoundNodes#bufferSource\n * @type {AudioBufferSourceNode}\n * @readonly\n */\n public bufferSource: AudioBufferSourceNode;\n\n /**\n * Get the gain node\n * @name PIXI.sound.SoundNodes#gain\n * @type {GainNode}\n * @readonly\n */\n public gain: GainNode;\n\n /**\n * Get the analyser node\n * @name PIXI.sound.SoundNodes#analyser\n * @type {AnalyserNode}\n * @readonly\n */\n public analyser: AnalyserNode;\n\n /**\n * Reference to the SoundContext\n * @name PIXI.sound.SoundNodes#context\n * @type {PIXI.sound.webaudio.WebAudioContext}\n * @readonly\n */\n public context: WebAudioContext;\n\n /**\n * Private reference to the script processor node.\n * @name PIXI.sound.SoundNodes#_script\n * @type {ScriptProcessorNode}\n */\n private _script: ScriptProcessorNode;\n\n constructor(context: WebAudioContext)\n {\n const audioContext: AudioContext = context.audioContext;\n\n const bufferSource: AudioBufferSourceNode = audioContext.createBufferSource();\n const gain: GainNode = audioContext.createGain();\n const analyser: AnalyserNode = audioContext.createAnalyser();\n\n bufferSource.connect(analyser);\n analyser.connect(gain);\n gain.connect(context.destination);\n\n super(analyser, gain);\n\n this.context = context;\n this.bufferSource = bufferSource;\n this.gain = gain;\n this.analyser = analyser;\n }\n\n /**\n * Get the script processor node.\n * @name PIXI.sound.SoundNodes#script\n * @type {ScriptProcessorNode}\n * @readonly\n */\n public get script()\n {\n if (!this._script)\n {\n this._script = this.context.audioContext.createScriptProcessor(WebAudioNodes.BUFFER_SIZE);\n this._script.connect(this.context.destination);\n }\n return this._script;\n }\n\n /**\n * Cleans up.\n * @method PIXI.sound.SoundNodes#destroy\n */\n public destroy(): void\n {\n super.destroy();\n\n this.bufferSource.disconnect();\n if (this._script)\n {\n this._script.disconnect();\n }\n this.gain.disconnect();\n this.analyser.disconnect();\n\n this.bufferSource = null;\n this._script = null;\n this.gain = null;\n this.analyser = null;\n\n this.context = null;\n }\n\n /**\n * Clones the bufferSource. Used just before playing a sound.\n * @method PIXI.sound.SoundNodes#cloneBufferSource\n * @returns {PIXI.sound.SoundNodes~SourceClone} The clone AudioBufferSourceNode.\n */\n public cloneBufferSource(): SourceClone\n {\n const orig: AudioBufferSourceNode = this.bufferSource;\n const source: AudioBufferSourceNode = this.context.audioContext.createBufferSource();\n source.buffer = orig.buffer;\n WebAudioUtils.setParamValue(source.playbackRate, orig.playbackRate.value);\n source.loop = orig.loop;\n\n const gain: GainNode = this.context.audioContext.createGain();\n source.connect(gain);\n gain.connect(this.destination);\n return { source, gain };\n }\n\n /**\n * Get buffer size of `ScriptProcessorNode`.\n * @type {number}\n * @readonly\n */\n get bufferSize(): number\n {\n return this.script.bufferSize;\n }\n}\n","import * as path from \"path\";\nimport { Filter } from \"../filters\";\nimport { IMedia } from \"../interfaces\";\nimport { CompleteCallback, LoadedCallback, Options, PlayOptions, Sound } from \"../Sound\";\nimport { SoundSprite, SoundSpriteData, SoundSprites } from \"../sprites\";\nimport { WebAudioContext } from \"./WebAudioContext\";\nimport { WebAudioInstance } from \"./WebAudioInstance\";\nimport { WebAudioNodes } from \"./WebAudioNodes\";\n\n/**\n * Represents a single sound element. Can be used to play, pause, etc. sound instances.\n * @private\n * @class WebAudioMedia\n * @memberof PIXI.sound.webaudio\n * @param {PIXI.sound.Sound} parent - Instance of parent Sound container\n */\nexport class WebAudioMedia implements IMedia\n{\n /**\n * Reference to the parent Sound container.\n * @name PIXI.sound.webaudio.WebAudioMedia#parent\n * @type {PIXI.sound.Sound}\n * @readonly\n */\n public parent: Sound;\n\n /**\n * The file buffer to load.\n * @name PIXI.sound.webaudio.WebAudioMedia#source\n * @type {ArrayBuffer}\n * @readonly\n */\n public source: ArrayBuffer;\n\n /**\n * Instance of the chain builder.\n * @name PIXI.sound.webaudio.WebAudioMedia#_nodes\n * @type {PIXI.sound.webaudio.WebAudioNodes}\n * @private\n */\n private _nodes: WebAudioNodes;\n\n /**\n * Instance of the source node.\n * @name PIXI.sound.webaudio.WebAudioMedia#_source\n * @type {AudioBufferSourceNode}\n * @private\n */\n private _source: AudioBufferSourceNode;\n\n public init(parent: Sound): void\n {\n this.parent = parent;\n this._nodes = new WebAudioNodes(this.context);\n this._source = this._nodes.bufferSource;\n this.source = parent.options.source as ArrayBuffer;\n }\n\n /**\n * Destructor, safer to use `SoundLibrary.remove(alias)` to remove this sound.\n * @private\n * @method PIXI.sound.webaudio.WebAudioMedia#destroy\n */\n public destroy(): void\n {\n this.parent = null;\n this._nodes.destroy();\n this._nodes = null;\n this._source.buffer = null;\n this._source = null;\n this.source = null;\n }\n\n // Implement create\n public create(): WebAudioInstance\n {\n return new WebAudioInstance(this);\n }\n\n // Implement context\n public get context(): WebAudioContext\n {\n return this.parent.context as WebAudioContext;\n }\n\n // Implement isPlayable\n public get isPlayable(): boolean\n {\n return !!this._source && !!this._source.buffer;\n }\n\n // Implement filters\n public get filters(): Filter[]\n {\n return this._nodes.filters;\n }\n public set filters(filters: Filter[])\n {\n this._nodes.filters = filters;\n }\n\n // Implements duration\n public get duration(): number\n {\n console.assert(this.isPlayable, \"Sound not yet playable, no duration\");\n return this._source.buffer.duration;\n }\n\n /**\n * Gets and sets the buffer.\n * @name PIXI.sound.webaudio.WebAudioMedia#buffer\n * @type {AudioBuffer}\n */\n public get buffer(): AudioBuffer\n {\n return this._source.buffer;\n }\n public set buffer(buffer: AudioBuffer)\n {\n this._source.buffer = buffer;\n }\n\n /**\n * Get the current chained nodes object\n * @private\n * @name PIXI.sound.webaudio.WebAudioMedia#nodes\n * @type {PIXI.sound.webaudio.WebAudioNodes}\n */\n public get nodes(): WebAudioNodes\n {\n return this._nodes;\n }\n\n // Implements load\n public load(callback?: LoadedCallback): void\n {\n // Load from the arraybuffer, incase it was loaded outside\n if (this.source)\n {\n this._decode(this.source, callback);\n }\n // Load from the file path\n else if (this.parent.url)\n {\n this._loadUrl(callback);\n }\n else if (callback)\n {\n callback(new Error(\"sound.url or sound.source must be set\"));\n }\n else\n {\n console.error(\"sound.url or sound.source must be set\");\n }\n }\n\n /**\n * Loads a sound using XHMLHttpRequest object.\n * @method PIXI.sound.webaudio.WebAudioMedia#_loadUrl\n * @private\n */\n private _loadUrl(callback?: LoadedCallback): void\n {\n const request = new XMLHttpRequest();\n const url: string = this.parent.url;\n request.open(\"GET\", url, true);\n request.responseType = \"arraybuffer\";\n\n // Decode asynchronously\n request.onload = () => {\n this.source = request.response as ArrayBuffer;\n this._decode(request.response, callback);\n };\n\n // actually start the request\n request.send();\n }\n\n /**\n * Decodes the array buffer.\n * @method PIXI.sound.webaudio.WebAudioMedia#decode\n * @param {ArrayBuffer} arrayBuffer From load.\n * @private\n */\n private _decode(arrayBuffer: ArrayBuffer, callback?: LoadedCallback): void\n {\n const context = this.parent.context as WebAudioContext;\n context.decode(arrayBuffer, (err: Error, buffer: AudioBuffer) =>\n {\n if (err)\n {\n if (callback)\n {\n callback(err);\n }\n }\n else\n {\n this.parent.isLoaded = true;\n this.buffer = buffer;\n const instance = this.parent.autoPlayStart();\n if (callback)\n {\n callback(null, this.parent, instance);\n }\n }\n });\n }\n}\n","import { Filterable } from \"../Filterable\";\nimport { IMediaContext } from \"../interfaces\";\n\n/**\n * Main class to handle WebAudio API. There's a simple chain\n * of AudioNode elements: analyser > compressor > context.destination.\n * any filters that are added are inserted between the analyser and compressor nodes\n * @private\n * @class WebAudioContext\n * @extends PIXI.sound.Filterable\n * @memberof PIXI.sound.webaudio\n */\nexport class WebAudioContext extends Filterable implements IMediaContext\n{\n /**\n * Context Compressor node\n * @name PIXI.sound.webaudio.WebAudioContext#compressor\n * @type {DynamicsCompressorNode}\n * @readonly\n */\n public compressor: DynamicsCompressorNode;\n\n /**\n * Context Analyser node\n * @name PIXI.sound.webaudio.WebAudioContext#analyser\n * @type {AnalyserNode}\n * @readonly\n */\n public analyser: AnalyserNode;\n\n /**\n * Global speed of all sounds\n * @name PIXI.sound.webaudio.WebAudioContext#speed\n * @type {number}\n * @readonly\n */\n public speed: number;\n\n /**\n * Sets the muted state.\n * @type {boolean}\n * @name PIXI.sound.webaudio.WebAudioContext#muted\n * @default false\n */\n public muted: boolean;\n\n /**\n * Sets the volume from 0 to 1.\n * @type {number}\n * @name PIXI.sound.webaudio.WebAudioContext#volume\n * @default 1\n */\n public volume: number;\n\n /**\n * Handle global events\n * @type {PIXI.utils.EventEmitter}\n * @name PIXI.sound.webaudio.WebAudioContext#events\n * @default 1\n */\n public events: PIXI.utils.EventEmitter;\n\n /**\n * The instance of the AudioContext for WebAudio API.\n * @name PIXI.sound.webaudio.WebAudioContext#_ctx\n * @type {AudioContext}\n * @private\n */\n private _ctx: AudioContext;\n\n /**\n * The instance of the OfflineAudioContext for fast decoding audio.\n * @name PIXI.sound.webaudio.WebAudioContext#_offlineCtx\n * @type {OfflineAudioContext}\n * @private\n */\n private _offlineCtx: OfflineAudioContext;\n\n /**\n * Current paused status\n * @name PIXI.sound.webaudio.WebAudioContext#_paused\n * @type {boolean}\n * @private\n * @default false\n */\n private _paused: boolean;\n\n /**\n * Indicated whether audio on iOS has been unlocked, which requires a touchend/mousedown event that plays an\n * empty sound.\n * @name PIXI.sound.webaudio.WebAudioContext#_unlocked\n * @type {boolean}\n * @private\n */\n private _unlocked: boolean;\n\n constructor()\n {\n const win: any = window as any;\n const ctx = new WebAudioContext.AudioContext();\n const compressor: DynamicsCompressorNode = ctx.createDynamicsCompressor();\n const analyser: AnalyserNode = ctx.createAnalyser();\n\n // setup the end of the node chain\n analyser.connect(compressor);\n compressor.connect(ctx.destination);\n\n super(analyser, compressor);\n\n this._ctx = ctx;\n // ios11 safari's webkitOfflineAudioContext allows only 44100 Hz sample rate\n this._offlineCtx = new WebAudioContext.OfflineAudioContext(1, 2,\n (win.OfflineAudioContext) ? ctx.sampleRate : 44100);\n this._unlocked = false;\n\n this.compressor = compressor;\n this.analyser = analyser;\n this.events = new PIXI.utils.EventEmitter();\n\n // Set the defaults\n this.volume = 1;\n this.speed = 1;\n this.muted = false;\n this.paused = false;\n\n // Listen for document level clicks to unlock WebAudio. See the _unlock method.\n if (ctx.state !== \"running\")\n {\n this._unlock(); // When played inside of a touch event, this will enable audio on iOS immediately.\n this._unlock = this._unlock.bind(this);\n document.addEventListener(\"mousedown\", this._unlock, true);\n document.addEventListener(\"touchstart\", this._unlock, true);\n document.addEventListener(\"touchend\", this._unlock, true);\n }\n }\n\n /**\n * Try to unlock audio on iOS. This is triggered from either WebAudio plugin setup (which will work if inside of\n * a `mousedown` or `touchend` event stack), or the first document touchend/mousedown event. If it fails (touchend\n * will fail if the user presses for too long, indicating a scroll event instead of a click event.\n *\n * Note that earlier versions of iOS supported `touchstart` for this, but iOS9 removed this functionality. Adding\n * a `touchstart` event to support older platforms may preclude a `mousedown` even from getting fired on iOS9, so we\n * stick with `mousedown` and `touchend`.\n * @method PIXI.sound.webaudio.WebAudioContext#_unlock\n * @private\n */\n private _unlock(): void\n {\n if (this._unlocked)\n {\n return;\n }\n this.playEmptySound();\n if (this._ctx.state === \"running\")\n {\n document.removeEventListener(\"mousedown\", this._unlock, true);\n document.removeEventListener(\"touchend\", this._unlock, true);\n document.removeEventListener(\"touchstart\", this._unlock, true);\n this._unlocked = true;\n }\n }\n\n /**\n * Plays an empty sound in the web audio context. This is used to enable web audio on iOS devices, as they\n * require the first sound to be played inside of a user initiated event (touch/click).\n * @method PIXI.sound.webaudio.WebAudioContext#playEmptySound\n */\n public playEmptySound(): void\n {\n const source = this._ctx.createBufferSource();\n source.buffer = this._ctx.createBuffer(1, 1, 22050);\n source.connect(this._ctx.destination);\n source.start(0, 0, 0);\n if (source.context.state === \"suspended\")\n {\n source.context.resume();\n }\n }\n\n /**\n * Get AudioContext class, if not supported returns `null`\n * @name PIXI.sound.webaudio.WebAudioContext.AudioContext\n * @type {Function}\n * @static\n */\n public static get AudioContext(): typeof AudioContext\n {\n const win: any = window as any;\n return (\n win.AudioContext ||\n win.webkitAudioContext ||\n null\n );\n }\n\n /**\n * Get OfflineAudioContext class, if not supported returns `null`\n * @name PIXI.sound.webaudio.WebAudioContext.OfflineAudioContext\n * @type {Function}\n * @static\n */\n public static get OfflineAudioContext(): typeof OfflineAudioContext\n {\n const win: any = window as any;\n return (\n win.OfflineAudioContext ||\n win.webkitOfflineAudioContext ||\n null\n );\n }\n\n /**\n * Destroy this context.\n * @method PIXI.sound.webaudio.WebAudioContext#destroy\n */\n public destroy()\n {\n super.destroy();\n\n const ctx: any = this._ctx as any;\n // check if browser supports AudioContext.close()\n if (typeof ctx.close !== \"undefined\")\n {\n ctx.close();\n }\n this.events.removeAllListeners();\n this.analyser.disconnect();\n this.compressor.disconnect();\n this.analyser = null;\n this.compressor = null;\n this.events = null;\n this._offlineCtx = null;\n this._ctx = null;\n }\n\n /**\n * The WebAudio API AudioContext object.\n * @name PIXI.sound.webaudio.WebAudioContext#audioContext\n * @type {AudioContext}\n * @readonly\n */\n public get audioContext(): AudioContext\n {\n return this._ctx;\n }\n\n /**\n * The WebAudio API OfflineAudioContext object.\n * @name PIXI.sound.webaudio.WebAudioContext#offlineContext\n * @type {OfflineAudioContext}\n * @readonly\n */\n public get offlineContext(): OfflineAudioContext\n {\n return this._offlineCtx;\n }\n\n /**\n * Pauses all sounds, even though we handle this at the instance\n * level, we'll also pause the audioContext so that the\n * time used to compute progress isn't messed up.\n * @type {boolean}\n * @name PIXI.sound.webaudio.WebAudioContext#paused\n * @default false\n */\n public set paused(paused: boolean)\n {\n if (paused && this._ctx.state === \"running\")\n {\n (this._ctx as any).suspend();\n }\n else if (!paused && this._ctx.state === \"suspended\")\n {\n (this._ctx as any).resume();\n }\n this._paused = paused;\n }\n public get paused(): boolean\n {\n return this._paused;\n }\n\n /**\n * Emit event when muted, volume or speed changes\n * @method PIXI.sound.webaudio.WebAudioContext#refresh\n * @private\n */\n public refresh(): void\n {\n this.events.emit(\"refresh\");\n }\n\n /**\n * Emit event when muted, volume or speed changes\n * @method PIXI.sound.webaudio.WebAudioContext#refreshPaused\n * @private\n */\n public refreshPaused(): void\n {\n this.events.emit(\"refreshPaused\");\n }\n\n /**\n * Toggles the muted state.\n * @method PIXI.sound.webaudio.WebAudioContext#toggleMute\n * @return {boolean} The current muted state.\n */\n public toggleMute(): boolean\n {\n this.muted = !this.muted;\n this.refresh();\n return this.muted;\n }\n\n /**\n * Toggles the paused state.\n * @method PIXI.sound.webaudio.WebAudioContext#togglePause\n * @return {boolean} The current muted state.\n */\n public togglePause(): boolean\n {\n this.paused = !this.paused;\n this.refreshPaused();\n return this._paused;\n }\n\n /**\n * Decode the audio data\n * @method PIXI.sound.webaudio.WebAudioContext#decode\n * @param {ArrayBuffer} arrayBuffer Buffer from loader\n * @param {Function} callback When completed, error and audioBuffer are parameters.\n */\n public decode(arrayBuffer: ArrayBuffer, callback: (err?: Error, buffer?: AudioBuffer) => void): void\n {\n this._offlineCtx.decodeAudioData(\n arrayBuffer, (buffer: AudioBuffer) => {\n callback(null, buffer);\n },\n (err) => {\n callback(new Error(err.message || \"Unable to decode file\"));\n },\n );\n }\n}\n","import { getInstance } from \"../instance\";\nimport { WebAudioUtils } from \"../webaudio\";\nimport { Filter } from \"./Filter\";\n\ninterface Band {\n f: number;\n type: string;\n gain: number;\n}\n\n/**\n * Filter for adding equalizer bands.\n *\n * @class EqualizerFilter\n * @memberof PIXI.sound.filters\n * @param {number} [f32=0] Default gain for 32 Hz\n * @param {number} [f64=0] Default gain for 64 Hz\n * @param {number} [f125=0] Default gain for 125 Hz\n * @param {number} [f250=0] Default gain for 250 Hz\n * @param {number} [f500=0] Default gain for 500 Hz\n * @param {number} [f1k=0] Default gain for 1000 Hz\n * @param {number} [f2k=0] Default gain for 2000 Hz\n * @param {number} [f4k=0] Default gain for 4000 Hz\n * @param {number} [f8k=0] Default gain for 8000 Hz\n * @param {number} [f16k=0] Default gain for 16000 Hz\n */\nexport class EqualizerFilter extends Filter\n{\n /**\n * Band at 32 Hz\n * @name PIXI.sound.filters.EqualizerFilter.F32\n * @type {number}\n * @readonly\n */\n public static F32: number = 32;\n\n /**\n * Band at 64 Hz\n * @name PIXI.sound.filters.EqualizerFilter.F64\n * @type {number}\n * @readonly\n */\n public static F64: number = 64;\n\n /**\n * Band at 125 Hz\n * @name PIXI.sound.filters.EqualizerFilter.F125\n * @type {number}\n * @readonly\n */\n public static F125: number = 125;\n\n /**\n * Band at 250 Hz\n * @name PIXI.sound.filters.EqualizerFilter.F250\n * @type {number}\n * @readonly\n */\n public static F250: number = 250;\n\n /**\n * Band at 500 Hz\n * @name PIXI.sound.filters.EqualizerFilter.F500\n * @type {number}\n * @readonly\n */\n public static F500: number = 500;\n\n /**\n * Band at 1000 Hz\n * @name PIXI.sound.filters.EqualizerFilter.F1K\n * @type {number}\n * @readonly\n */\n public static F1K: number = 1000;\n\n /**\n * Band at 2000 Hz\n * @name PIXI.sound.filters.EqualizerFilter.F2K\n * @type {number}\n * @readonly\n */\n public static F2K: number = 2000;\n\n /**\n * Band at 4000 Hz\n * @name PIXI.sound.filters.EqualizerFilter.F4K\n * @type {number}\n * @readonly\n */\n public static F4K: number = 4000;\n\n /**\n * Band at 8000 Hz\n * @name PIXI.sound.filters.EqualizerFilter.F8K\n * @type {number}\n * @readonly\n */\n public static F8K: number = 8000;\n\n /**\n * Band at 16000 Hz\n * @name PIXI.sound.filters.EqualizerFilter.F16K\n * @type {number}\n * @readonly\n */\n public static F16K: number = 16000;\n\n /**\n * The list of bands\n * @name PIXI.sounds.filters.EqualizerFilter#bands\n * @type {BiquadFilterNode[]}\n * @readonly\n */\n public bands: BiquadFilterNode[];\n\n /**\n * The map of bands to frequency\n * @name PIXI.sounds.filters.EqualizerFilter#bandsMap\n * @type {Object}\n * @readonly\n */\n public bandsMap: {[id: number]: BiquadFilterNode};\n\n constructor(f32: number = 0, f64: number = 0, f125: number = 0, f250: number = 0, f500: number = 0,\n f1k: number = 0, f2k: number = 0, f4k: number = 0, f8k: number = 0, f16k: number = 0)\n {\n if (getInstance().useLegacy)\n {\n super(null);\n return;\n }\n\n const equalizerBands: Band[] = [\n {\n f: EqualizerFilter.F32,\n type: \"lowshelf\",\n gain: f32,\n },\n {\n f: EqualizerFilter.F64,\n type: \"peaking\",\n gain: f64,\n },\n {\n f: EqualizerFilter.F125,\n type: \"peaking\",\n gain: f125,\n },\n {\n f: EqualizerFilter.F250,\n type: \"peaking\",\n gain: f250,\n },\n {\n f: EqualizerFilter.F500,\n type: \"peaking\",\n gain: f500,\n },\n {\n f: EqualizerFilter.F1K,\n type: \"peaking\",\n gain: f1k,\n },\n {\n f: EqualizerFilter.F2K,\n type: \"peaking\",\n gain: f2k,\n },\n {\n f: EqualizerFilter.F4K,\n type: \"peaking\",\n gain: f4k,\n },\n {\n f: EqualizerFilter.F8K,\n type: \"peaking\",\n gain: f8k,\n },\n {\n f: EqualizerFilter.F16K,\n type: \"highshelf\",\n gain: f16k,\n },\n ];\n\n const bands: BiquadFilterNode[] = equalizerBands.map((band: Band) =>\n {\n const node: BiquadFilterNode = getInstance().context.audioContext.createBiquadFilter();\n node.type = band.type as BiquadFilterType;\n WebAudioUtils.setParamValue(node.Q, 1);\n node.frequency.value = band.f; // WebAudioUtils.setParamValue(filter.frequency, band.f);\n WebAudioUtils.setParamValue(node.gain, band.gain);\n return node;\n });\n\n // Setup the constructor AudioNode, where first is the input, and last is the output\n super(bands[0], bands[bands.length - 1]);\n\n // Manipulate the bands\n this.bands = bands;\n\n // Create a map\n this.bandsMap = {};\n\n for (let i = 0; i < this.bands.length; i++)\n {\n const node: BiquadFilterNode = this.bands[i];\n\n // Connect the previous band to the current one\n if (i > 0)\n {\n this.bands[i - 1].connect(node);\n }\n this.bandsMap[node.frequency.value] = node;\n }\n }\n\n /**\n * Set gain on a specific frequency.\n * @method PIXI.sound.filters.EqualizerFilter#setGain\n * @param {number} frequency The frequency, see EqualizerFilter.F* for bands\n * @param {number} [gain=0] Recommended -40 to 40.\n */\n public setGain(frequency: number, gain: number = 0): void\n {\n if (!this.bandsMap[frequency])\n {\n throw new Error(\"No band found for frequency \" + frequency);\n }\n WebAudioUtils.setParamValue(this.bandsMap[frequency].gain, gain);\n }\n\n /**\n * Get gain amount on a specific frequency.\n * @method PIXI.sound.filters.EqualizerFilter#getGain\n * @return {number} The amount of gain set.\n */\n public getGain(frequency: number): number\n {\n if (!this.bandsMap[frequency])\n {\n throw new Error(\"No band found for frequency \" + frequency);\n }\n return this.bandsMap[frequency].gain.value;\n }\n\n /**\n * Gain at 32 Hz frequencey.\n * @name PIXI.sound.filters.EqualizerFilter#f32\n * @type {number}\n * @default 0\n */\n public set f32(value: number)\n {\n this.setGain(EqualizerFilter.F32, value);\n }\n public get f32(): number\n {\n return this.getGain(EqualizerFilter.F32);\n }\n\n /**\n * Gain at 64 Hz frequencey.\n * @name PIXI.sound.filters.EqualizerFilter#f64\n * @type {number}\n * @default 0\n */\n public set f64(value: number)\n {\n this.setGain(EqualizerFilter.F64, value);\n }\n public get f64(): number\n {\n return this.getGain(EqualizerFilter.F64);\n }\n\n /**\n * Gain at 125 Hz frequencey.\n * @name PIXI.sound.filters.EqualizerFilter#f125\n * @type {number}\n * @default 0\n */\n public set f125(value: number)\n {\n this.setGain(EqualizerFilter.F125, value);\n }\n public get f125(): number\n {\n return this.getGain(EqualizerFilter.F125);\n }\n\n /**\n * Gain at 250 Hz frequencey.\n * @name PIXI.sound.filters.EqualizerFilter#f250\n * @type {number}\n * @default 0\n */\n public set f250(value: number)\n {\n this.setGain(EqualizerFilter.F250, value);\n }\n public get f250(): number\n {\n return this.getGain(EqualizerFilter.F250);\n }\n\n /**\n * Gain at 500 Hz frequencey.\n * @name PIXI.sound.filters.EqualizerFilter#f500\n * @type {number}\n * @default 0\n */\n public set f500(value: number)\n {\n this.setGain(EqualizerFilter.F500, value);\n }\n public get f500(): number\n {\n return this.getGain(EqualizerFilter.F500);\n }\n\n /**\n * Gain at 1 KHz frequencey.\n * @name PIXI.sound.filters.EqualizerFilter#f1k\n * @type {number}\n * @default 0\n */\n public set f1k(value: number)\n {\n this.setGain(EqualizerFilter.F1K, value);\n }\n public get f1k(): number\n {\n return this.getGain(EqualizerFilter.F1K);\n }\n\n /**\n * Gain at 2 KHz frequencey.\n * @name PIXI.sound.filters.EqualizerFilter#f2k\n * @type {number}\n * @default 0\n */\n public set f2k(value: number)\n {\n this.setGain(EqualizerFilter.F2K, value);\n }\n public get f2k(): number\n {\n return this.getGain(EqualizerFilter.F2K);\n }\n\n /**\n * Gain at 4 KHz frequencey.\n * @name PIXI.sound.filters.EqualizerFilter#f4k\n * @type {number}\n * @default 0\n */\n public set f4k(value: number)\n {\n this.setGain(EqualizerFilter.F4K, value);\n }\n public get f4k(): number\n {\n return this.getGain(EqualizerFilter.F4K);\n }\n\n /**\n * Gain at 8 KHz frequencey.\n * @name PIXI.sound.filters.EqualizerFilter#f8k\n * @type {number}\n * @default 0\n */\n public set f8k(value: number)\n {\n this.setGain(EqualizerFilter.F8K, value);\n }\n public get f8k(): number\n {\n return this.getGain(EqualizerFilter.F8K);\n }\n\n /**\n * Gain at 16 KHz frequencey.\n * @name PIXI.sound.filters.EqualizerFilter#f16k\n * @type {number}\n * @default 0\n */\n public set f16k(value: number)\n {\n this.setGain(EqualizerFilter.F16K, value);\n }\n public get f16k(): number\n {\n return this.getGain(EqualizerFilter.F16K);\n }\n\n /**\n * Reset all frequency bands to have gain of 0\n * @method PIXI.sound.filters.EqualizerFilter#reset\n */\n public reset(): void\n {\n this.bands.forEach((band: BiquadFilterNode) => {\n WebAudioUtils.setParamValue(band.gain, 0);\n });\n }\n\n public destroy(): void\n {\n this.bands.forEach((band: BiquadFilterNode) => {\n band.disconnect();\n });\n this.bands = null;\n this.bandsMap = null;\n }\n}\n","import { getInstance } from \"../instance\";\nimport { Filter } from \"./Filter\";\n\n/**\n * Filter for adding adding delaynode.\n *\n * @class DistortionFilter\n * @memberof PIXI.sound.filters\n * @param {number} [amount=0] The amount of distoration from 0 to 1.\n */\nexport class DistortionFilter extends Filter\n{\n /**\n * The Wave shape node use to distort\n * @name PIXI.sound.filters.DistortionFilter#_distortion\n * @type {WaveShaperNode}\n * @private\n */\n private _distortion: WaveShaperNode;\n\n /**\n * The amount of distoration\n * @name PIXI.sound.filters.DistortionFilter#_amount\n * @type {number}\n * @private\n */\n private _amount: number;\n\n constructor(amount: number = 0)\n {\n if (getInstance().useLegacy)\n {\n super(null);\n return;\n }\n\n const {context} = getInstance();\n const distortion: WaveShaperNode = context.audioContext.createWaveShaper();\n\n super(distortion);\n\n this._distortion = distortion;\n\n this.amount = amount;\n }\n\n /**\n * @name PIXI.sound.filters.Distoration#amount\n * @type {number}\n */\n set amount(value: number)\n {\n value *= 1000;\n this._amount = value;\n const samples: number = 44100;\n const curve: Float32Array = new Float32Array(samples);\n const deg: number = Math.PI / 180;\n\n let i: number = 0;\n let x: number;\n\n for (; i < samples; ++i)\n {\n x = i * 2 / samples - 1;\n curve[i] = (3 + value) * x * 20 * deg / (Math.PI + value * Math.abs(x));\n }\n this._distortion.curve = curve;\n this._distortion.oversample = \"4x\";\n }\n get amount(): number\n {\n return this._amount;\n }\n\n public destroy(): void\n {\n this._distortion = null;\n super.destroy();\n }\n}\n","import { getInstance } from \"../instance\";\nimport { WebAudioUtils } from \"../webaudio\";\nimport { Filter } from \"./Filter\";\n\n/**\n * Filter for adding Stereo panning.\n *\n * @class StereoFilter\n * @memberof PIXI.sound.filters\n * @param {number} [pan=0] The amount of panning, -1 is left, 1 is right, 0 is centered.\n */\nexport class StereoFilter extends Filter\n{\n /**\n * The stereo panning node\n * @name PIXI.sound.filters.StereoFilter#_stereo\n * @type {StereoPannerNode}\n * @private\n */\n private _stereo: StereoPannerNode;\n\n /**\n * The stereo panning node\n * @name PIXI.sound.filters.StereoFilter#_panner\n * @type {PannerNode}\n * @private\n */\n private _panner: PannerNode;\n\n /**\n * The amount of panning, -1 is left, 1 is right, 0 is centered\n * @name PIXI.sound.filters.StereoFilter#_pan\n * @type {number}\n * @private\n */\n private _pan: number;\n\n constructor(pan: number = 0)\n {\n if (getInstance().useLegacy)\n {\n super(null);\n return;\n }\n\n let stereo: StereoPannerNode;\n let panner: PannerNode;\n let destination: AudioNode;\n const {audioContext} = getInstance().context;\n\n if (audioContext.createStereoPanner)\n {\n stereo = audioContext.createStereoPanner();\n destination = stereo;\n }\n else\n {\n panner = audioContext.createPanner();\n panner.panningModel = \"equalpower\";\n destination = panner;\n }\n\n super(destination);\n\n this._stereo = stereo;\n this._panner = panner;\n\n this.pan = pan;\n }\n\n /**\n * Set the amount of panning, where -1 is left, 1 is right, and 0 is centered\n * @name PIXI.sound.filters.StereoFilter#pan\n * @type {number}\n */\n set pan(value: number)\n {\n this._pan = value;\n if (this._stereo)\n {\n WebAudioUtils.setParamValue(this._stereo.pan, value);\n }\n else\n {\n this._panner.setPosition(value, 0, 1 - Math.abs(value));\n }\n }\n get pan(): number\n {\n return this._pan;\n }\n\n public destroy(): void\n {\n super.destroy();\n this._stereo = null;\n this._panner = null;\n }\n}\n","import { getInstance } from \"../instance\";\nimport { Filter } from \"./Filter\";\n\n/**\n * Filter for adding reverb. Refactored from\n * https://github.com/web-audio-components/simple-reverb/\n *\n * @class ReverbFilter\n * @memberof PIXI.sound.filters\n * @param {number} [seconds=3] Seconds for reverb\n * @param {number} [decay=2] The decay length\n * @param {boolean} [reverse=false] Reverse reverb\n */\nexport class ReverbFilter extends Filter\n{\n /**\n * @name PIXI.sound.filters.ReverbFilter#_seconds\n * @type {number}\n * @private\n */\n private _seconds: number;\n\n /**\n * @name PIXI.sound.filters.ReverbFilter#_decay\n * @type {number}\n * @private\n */\n private _decay: number;\n\n /**\n * @name PIXI.sound.filters.ReverbFilter#_reverse\n * @type {number}\n * @private\n */\n private _reverse: boolean;\n\n constructor(seconds: number = 3, decay: number = 2, reverse: boolean = false)\n {\n if (getInstance().useLegacy)\n {\n super(null);\n return;\n }\n\n super(null);\n\n this._seconds = this._clamp(seconds, 1, 50);\n this._decay = this._clamp(decay, 0, 100);\n this._reverse = reverse;\n this._rebuild();\n }\n\n /**\n * Clamp a value\n * @method PIXI.sound.filters.ReverbFilter#_clamp\n * @private\n * @param {number} value\n * @param {number} min Minimum value\n * @param {number} max Maximum value\n * @return {number} Clamped number\n */\n private _clamp(value: number, min: number, max: number): number\n {\n return Math.min(max, Math.max(min, value));\n }\n\n /**\n * Length of reverb in seconds from 1 to 50\n * @name PIXI.sound.filters.ReverbFilter#decay\n * @type {number}\n * @default 3\n */\n get seconds(): number\n {\n return this._seconds;\n }\n set seconds(seconds: number)\n {\n this._seconds = this._clamp(seconds, 1, 50);\n this._rebuild();\n }\n\n /**\n * Decay value from 0 to 100\n * @name PIXI.sound.filters.ReverbFilter#decay\n * @type {number}\n * @default 2\n */\n get decay(): number\n {\n return this._decay;\n }\n set decay(decay: number)\n {\n this._decay = this._clamp(decay, 0, 100);\n this._rebuild();\n }\n\n /**\n * Reverse value from 0 to 1\n * @name PIXI.sound.filters.ReverbFilter#reverse\n * @type {boolean}\n * @default false\n */\n get reverse(): boolean\n {\n return this._reverse;\n }\n set reverse(reverse: boolean)\n {\n this._reverse = reverse;\n this._rebuild();\n }\n\n /**\n * Utility function for building an impulse response\n * from the module parameters.\n * @method PIXI.sound.filters.ReverbFilter#_rebuild\n * @private\n */\n private _rebuild(): void\n {\n const context = getInstance().context.audioContext;\n const rate: number = context.sampleRate;\n const length: number = rate * this._seconds;\n const impulse: AudioBuffer = context.createBuffer(2, length, rate);\n const impulseL: Float32Array = impulse.getChannelData(0);\n const impulseR: Float32Array = impulse.getChannelData(1);\n let n: number;\n\n for (let i: number = 0; i < length; i++)\n {\n n = this._reverse ? length - i : i;\n impulseL[i] = (Math.random() * 2 - 1) * Math.pow(1 - n / length, this._decay);\n impulseR[i] = (Math.random() * 2 - 1) * Math.pow(1 - n / length, this._decay);\n }\n const convolver = getInstance().context.audioContext.createConvolver();\n convolver.buffer = impulse;\n this.init(convolver);\n }\n}\n","import { getInstance } from \"../instance\";\nimport { Filter } from \"./Filter\";\n\n/**\n * Combine all channels into mono channel.\n *\n * @class MonoFilter\n * @memberof PIXI.sound.filters\n */\nexport class MonoFilter extends Filter\n{\n /**\n * Merger node\n * @name PIXI.sound.filters.MonoFilter#_merge\n * @type {ChannelMergerNode}\n * @private\n */\n private _merger: ChannelMergerNode;\n\n constructor()\n {\n if (getInstance().useLegacy)\n {\n super(null);\n return;\n }\n const audioContext: AudioContext = getInstance().context.audioContext;\n const splitter: ChannelSplitterNode = audioContext.createChannelSplitter();\n const merger: ChannelMergerNode = audioContext.createChannelMerger();\n merger.connect(splitter);\n super(merger, splitter);\n this._merger = merger;\n }\n\n public destroy(): void\n {\n this._merger.disconnect();\n this._merger = null;\n super.destroy();\n }\n}\n","import { getInstance } from \"../instance\";\nimport { WebAudioUtils } from \"../webaudio\";\nimport { Filter } from \"./Filter\";\n\n/**\n * Creates a telephone-sound filter.\n *\n * @class TelephoneFilter\n * @memberof PIXI.sound.filters\n */\nexport class TelephoneFilter extends Filter\n{\n constructor()\n {\n if (getInstance().useLegacy)\n {\n super(null);\n return;\n }\n\n const {audioContext} = getInstance().context;\n const lpf1 = audioContext.createBiquadFilter();\n const lpf2 = audioContext.createBiquadFilter();\n const hpf1 = audioContext.createBiquadFilter();\n const hpf2 = audioContext.createBiquadFilter();\n\n lpf1.type = \"lowpass\";\n WebAudioUtils.setParamValue(lpf1.frequency, 2000.0);\n\n lpf2.type = \"lowpass\";\n WebAudioUtils.setParamValue(lpf2.frequency, 2000.0);\n\n hpf1.type = \"highpass\";\n WebAudioUtils.setParamValue(hpf1.frequency, 500.0);\n\n hpf2.type = \"highpass\";\n WebAudioUtils.setParamValue(hpf2.frequency, 500.0);\n\n lpf1.connect(lpf2);\n lpf2.connect(hpf1);\n hpf1.connect(hpf2);\n\n super(lpf1, hpf2);\n }\n}\n","import { IMediaInstance } from \"../interfaces/IMediaInstance\";\nimport { PlayOptions } from \"../Sound\";\nimport { HTMLAudioMedia } from \"./HTMLAudioMedia\";\n\nlet id = 0;\n\n/**\n * Instance which wraps the `