/** * 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 () { /** * The StartAudioContext object */ var StartAudioContext = { /** * The audio context passed in by the user * @type {AudioContext} */ context : null, /** * The TapListeners bound to the elements * @type {Array} * @private */ _tapListeners : [], /** * Callbacks to invoke when the audio context is started * @type {Array} * @private */ _onStarted : [], }; /** * Set the context * @param {AudioContext} ctx * @returns {StartAudioContext} */ StartAudioContext.setContext = function(ctx){ StartAudioContext.context = ctx; return StartAudioContext; }; /** * Add a tap listener to the audio context * @param {Array|Element|String|jQuery} element * @returns {StartAudioContext} */ StartAudioContext.on = function(element){ if (Array.isArray(element) || (NodeList && element instanceof NodeList)){ for (var i = 0; i < element.length; i++){ StartAudioContext.on(element[i]); } } else if (typeof element === "string"){ StartAudioContext.on(document.querySelectorAll(element)); } else if (element.jquery && typeof element.toArray === "function"){ StartAudioContext.on(element.toArray()); } else if (Element && element instanceof Element){ //if it's an element, create a TapListener var tap = new TapListener(element, onTap); StartAudioContext._tapListeners.push(tap); } return StartAudioContext; }; /** * Bind a callback to when the audio context is started. * @param {Function} cb * @return {StartAudioContext} */ StartAudioContext.onStarted = function(cb){ //if it's already started, invoke the callback if (StartAudioContext.isStarted()){ cb(); } else { StartAudioContext._onStarted.push(cb); } return StartAudioContext; }; /** * returns true if the context is started * @return {Boolean} */ StartAudioContext.isStarted = function(){ return (StartAudioContext.context !== null && StartAudioContext.context.state === "running"); }; /** * @class Listens for non-dragging tap ends on the given element * @param {Element} element * @internal */ var TapListener = function(element){ this._dragged = false; this._element = element; this._bindedMove = this._moved.bind(this); this._bindedEnd = this._ended.bind(this); 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(e){ if (!this._dragged){ onTap(); } 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; }; /** * Invoked the first time of the elements is tapped. * Creates a silent oscillator when a non-dragging touchend * event has been triggered. */ function onTap(){ //start the audio context with a silent oscillator if (StartAudioContext.context && !StartAudioContext.isStarted()){ var osc = StartAudioContext.context.createOscillator(); var silent = StartAudioContext.context.createGain(); silent.gain.value = 0; osc.connect(silent); silent.connect(StartAudioContext.context.destination); var now = StartAudioContext.context.currentTime; osc.start(now); osc.stop(now+0.5); } //dispose all the tap listeners if (StartAudioContext._tapListeners){ for (var i = 0; i < StartAudioContext._tapListeners.length; i++){ StartAudioContext._tapListeners[i].dispose(); } StartAudioContext._tapListeners = null; } //the onstarted callbacks if (StartAudioContext._onStarted){ for (var j = 0; j < StartAudioContext._onStarted.length; j++){ StartAudioContext._onStarted[j](); } StartAudioContext._onStarted = null; } } return StartAudioContext; }));