sound/index.js

import Manager from '../manager';
import { getAudioContext } from './audioContext';

/**
 * @class
 */
class AudioManager extends Manager {
    /**
     * @constructor
     * @param {{onError: function}} options
     */
    constructor(options = {}) {
        super(options);
    }

    cleanup() {
        this.audioChangeCallback = null;
        this.audioContext?.close();
    }

    getAudioContext() {
        if (this.audioContext) {
            return this.audioContext;
        }

        this.audioContext = getAudioContext();
        return this.audioContext;
    }

    /**
     * Assign a function as a callback for when audio data changes for audio
     * being played in a graphics program.
     * @param {object} mediaElement - Audio element playing sound to analyze
     * @param {function} fn - A callback to be triggered on audio data change.
     */
    audioChangeMethod(mediaElement, fn) {
        const audioContext = this.getAudioContext();
        if (!audioContext) {
            return;
        }
        const analyser = audioContext.createAnalyser();
        analyser.fftSize = 128;
        const source = audioContext.createMediaElementSource(mediaElement);
        source.crossOrigin = 'anonymous';
        source.connect(analyser);
        const gainNode = audioContext.createGain();
        source.connect(gainNode);
        gainNode.connect(audioContext.destination);
        const bufferLength = analyser.frequencyBinCount;
        const dataArray = new Uint8Array(bufferLength);
        const audioChangeCallback = this.withErrorHandler(fn);
        this.setTimer(() => {
            analyser.getByteFrequencyData(dataArray);
            audioChangeCallback(dataArray);
        });
    }
}

export default AudioManager;