Tone.js/examples/scripts/StartAudioContext.js
2016-10-05 11:14:30 -04:00

185 lines
No EOL
4.7 KiB
JavaScript

/**
* 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
}))