src/WebAudio/AudioContext.js
import debug from 'debug';
/**
* Recorder
* Simple Audio Recorder based on WebAudio technology
*/
export default class AudioContext {
/**
* Private object to hold AudioContext node.
* @type {AudioContext}
* @inherit
*/
audioContext = null;
/**
* This will be the log function.
* Will output to stdout.
* @type {Function}
* @inherit
*/
log = null;
/**
* This will be the error function.
* Will output to stderr.
* @type {Function}
* @inherit
*/
error = null;
/**
* Keep a list of all registered event listeners.
* This way, we could conveniently remove all the listeners at once.
* @type {Array}
* @private
*/
eventListeners = [];
/**
* Construct the Player.
*
* @param {Object} audioContext - Allow to provide custom/own audioContext object.
* @param {string} debugNameSpace - Name to be used for debugging.
*/
constructor(audioContext = null, debugNameSpace = 'AudioContext') {
// Provide AudioContext object. If not passed the
// default window.AudioContext will be used.
if (audioContext) {
this.audioContext = audioContext;
} else {
this.audioContext = this.createAudioContext();
}
// Prepare the log and error function to be used!
this.log = debug(`its-sdk:${debugNameSpace}`);
this.error = debug(`its-sdk:${debugNameSpace}`);
// Bind log to stdout in stead of stderr
this.log.log = console.log.bind(console);
this.suspendAudioContext = this.suspendAudioContext.bind(this);
}
/**
* Wraps the addEventListener which is available on the AudioContext node.
* Note that it is requierd to pass a named function to actually be able
* to remove an event listeners. This is just how the EventTarget.removeEventListener
* works. There are no extra checks for.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/attachEvent
* @param {...*} args - Array with passed arguments.
*/
addEventListener(...args) {
// First, add the event listener to our local list.
this.eventListeners.push(args);
// Now, actually add the event listener!
this.audioContext.addEventListener(...args);
}
/**
* Wraps the removeEventListener which is available on the AudioContext node.
* Make sure to call with the same arguments as the addEventListner.
*
* If you didn't call the addEventListener with a named function, please note
* that you won't be able to remove the eventListener.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener
* @param {...*} args - Array with passed arguments.
*/
removeEventListener(...args) {
// First, try to find the event in our list.
const itemIndex = this.eventListeners.findIndex(item => {
// If the count of items doesn't equal, it's definitely not the
// listener we want to remove.
if (item.length !== args.length) {
return false;
}
// Now we're gonna loop through the items to check if we can
// find an item which does not compare equal. If we find one
// 'some' will immediately return true and the party is over!
// Wubba lubba dub dub!
return !args.some((arg, index) => item[index] !== arg);
});
// Remove that item, and only that one, from the list
if (itemIndex) {
this.eventListeners.splice(itemIndex, 1);
}
// Remove the event handler!
this.audioContext.removeEventListener(...args);
}
removeAllEventListeners() {
this.eventListeners.forEach(listener => {
this.removeEventListener(...listener);
});
this.eventListeners = [];
}
/**
* Use this method to conveniently fire an event.
* We could, if we wanted, add some data.
*
* @param {string} eventName - Event to fire.
* @param {Object} data - Data to pass as detail.
* @inherit
*/
fireEvent(eventName, data = null) {
if (!eventName) {
return;
}
const customEvent = new CustomEvent(eventName, {detail: data});
this.audioContext.dispatchEvent(customEvent);
}
/**
* Get the audio context or create one.
*
* @return {AudioContext} The AudioContext created will be returned
*/
createAudioContext() {
if (!window.ItslAudioContext) {
window.AudioContext =
window.AudioContext || window.webkitAudioContext;
window.ItslAudioContext = new window.AudioContext();
}
return window.ItslAudioContext;
}
/**
* Suspend the AudioContext to preserve power and such.
*/
suspendAudioContext() {
this.audioContext.suspend();
}
/**
* Resume a suspended AudioContext.
*/
resumeAudioContext() {
if (this.audioContext.state === 'suspended') {
this.audioContext.resume();
}
}
}