Source: AudioEngine.js

const DEBUG = false;

/**
 * AudioEngine class.
 * Creates an WebAudio audio context and manages a Switchboard AudioWorkletNode.
 */
class AudioEngine {

    /**
     * AudioEngine constructor.
     */
    constructor() {
        if (DEBUG) console.log("[SB] [AudioEngine] constructor");
        this._audioContext = new AudioContext();
        this._workletNode = null;
    }

    /**
     * Initializes the audio engine with a worklet node.
     * @param {AudioWorkletNode} workletNode The audio worklet node that does the audio processing.
     */
    initialize(workletNode) {
        if (DEBUG) console.log("[SB] [AudioEngine] initialize", workletNode);
        if (workletNode.numberOfOutputs > 0) {
            workletNode.connect(this._audioContext.destination);
        }
        this._audioContext.suspend();
        this._workletNode = workletNode;
    }

    /**
     * Loads and decodes audio from a URL.
     * @param {String} url The URL of the audio to load.
     * @returns {Promise} A promise for the loading and decoding process that provides an audio buffer.
     */
    loadAudio(url) {
        if (DEBUG) console.log("[SB] [AudioEngine] loadAudio", url);
        let audioEngine = this;
        return new Promise((resolve, reject) => {
            var request = new XMLHttpRequest();
            request.open('GET', url, true);
            request.responseType = 'arraybuffer';
            request.onload = function() {
                audioEngine.getAudioContext().decodeAudioData(
                    request.response,
                    function(buffer) {
                        resolve(buffer);
                    },
                    function() {
                        console.error("[SB] [AudioEngine] Could not load " + url);
                        reject();
                    }
                );
            }
            request.send();
        })
    }

    /**
     * Gets the user audio stream
     * @param {Object} constraints Constraints for the user media.
     * @returns The user media.
     */
    async getUserAudioStream(constraints) {
        if (DEBUG) console.log("[SB] [AudioEngine] getUserAudioStream", constraints);
        const defaultContraints = {
            audio: {
                echoCancellation: false,
                autoGainControl: false,
                noiseSuppression: false
            }
        }
        return await navigator.mediaDevices.getUserMedia(constraints ? constraints : defaultContraints);
    }

    /**
     * Gets the WebAudio audio context.
     * @returns {AudioContext} The WebAudio audio context.
     */
    getAudioContext() {
        return this._audioContext;
    }

    /**
     * Gets the AudioWorkletNode instance.
     * @returns {AudioWorkletNode} The AudioWorkletNode instance.
     */
    getAudioWorkletNode() {
        return this._workletNode;
    }

    /**
     * Starts the audio engine. Resumes the audio context.
     */
    start() {
        this._audioContext.resume();
    }

    /**
     * Stops the audio engine. Suspends the audio context.
     */
    stop() {
        this._audioContext.suspend();
    }

    /**
     * Posts a message to the worklet node's port.
     * @param {Object} message The message to send to the worklet node.
     */
    postMessage(message) {
        this._workletNode.port.postMessage(message)
    }
}

export default AudioEngine;