/** * StartAudioContext.js * @author Yotam Mann * @license http://opensource.org/licenses/MIT MIT License * @copyright 2016 Yotam Mann */ (function (root, factory) { if (typeof define === "function" && define.amd) { define([], factory) } else if (typeof module === "object" && module.exports) { module.exports = factory() } else { root.StartAudioContext = factory() } }(this, function () { //TAP LISTENER///////////////////////////////////////////////////////////// /** * @class Listens for non-dragging tap ends on the given element * @param {Element} element * @internal */ var TapListener = function(element, context){ this._dragged = false this._element = element this._bindedMove = this._moved.bind(this) this._bindedEnd = this._ended.bind(this, context) element.addEventListener("touchmove", this._bindedMove) element.addEventListener("touchend", this._bindedEnd) element.addEventListener("mouseup", this._bindedEnd) } /** * drag move event */ TapListener.prototype._moved = function(e){ this._dragged = true }; /** * tap ended listener */ TapListener.prototype._ended = function(context){ if (!this._dragged){ startContext(context) } this._dragged = false }; /** * remove all the bound events */ TapListener.prototype.dispose = function(){ this._element.removeEventListener("touchmove", this._bindedMove) this._element.removeEventListener("touchend", this._bindedEnd) this._element.removeEventListener("mouseup", this._bindedEnd) this._bindedMove = null this._bindedEnd = null this._element = null }; //END TAP LISTENER///////////////////////////////////////////////////////// /** * Plays a silent sound and also invoke the "resume" method * @param {AudioContext} context * @private */ function startContext(context){ // this accomplishes the iOS specific requirement var buffer = context.createBuffer(1, 1, context.sampleRate) var source = context.createBufferSource() source.buffer = buffer source.connect(context.destination) source.start(0) // resume the audio context if (context.resume){ context.resume() } } /** * Returns true if the audio context is started * @param {AudioContext} context * @return {Boolean} * @private */ function isStarted(context){ return context.state === "running" } /** * Invokes the callback as soon as the AudioContext * is started * @param {AudioContext} context * @param {Function} callback */ function onStarted(context, callback){ function checkLoop(){ if (isStarted(context)){ callback() } else { requestAnimationFrame(checkLoop) if (context.resume){ context.resume() } } } if (isStarted(context)){ callback() } else { checkLoop() } } /** * Add a tap listener to the audio context * @param {Array|Element|String|jQuery} element * @param {Array} tapListeners */ function bindTapListener(element, tapListeners, context){ if (Array.isArray(element) || (NodeList && element instanceof NodeList)){ for (var i = 0; i < element.length; i++){ bindTapListener(element[i], tapListeners, context) } } else if (typeof element === "string"){ bindTapListener(document.querySelectorAll(element), tapListeners, context) } else if (element.jquery && typeof element.toArray === "function"){ bindTapListener(element.toArray(), tapListeners, context) } else if (Element && element instanceof Element){ //if it's an element, create a TapListener var tap = new TapListener(element, context) tapListeners.push(tap) } } /** * @param {AudioContext} context The AudioContext to start. * @param {Array|String|Element|jQuery} elements For iOS, the list of elements * to bind tap event listeners * which will start the AudioContext. * @param {Function=} callback The callback to invoke when the AudioContext is started. * @return {Promise} The promise is invoked when the AudioContext * is started. */ function StartAudioContext(context, elements, callback){ //the promise is invoked when the AudioContext is started var promise = new Promise(function(success) { onStarted(context, success) }) // The TapListeners bound to the elements var tapListeners = [] // add all the tap listeners if (elements){ bindTapListener(elements, tapListeners, context) } //dispose all these tap listeners when the context is started promise.then(function(){ for (var i = 0; i < tapListeners.length; i++){ tapListeners[i].dispose() } tapListeners = null if (callback){ callback() } }) return promise } return StartAudioContext }))