Repository

js/Mobilizing/renderer/audio/OfflineRenderer.js

import EventEmitter from "../../core/util/EventEmitter";
import Source from "./Source";

/**
* OfflineRenderer is a Web Audio API based renderer for recording sound.
*/

const EVT_COMPLETE = "complete";

export default class OfflineRenderer {
    /**
    * @example
    *     //to do
    * @constructor
    * @extends Component
    * @param {Object} params Parameters object, given by the constructor.
    * @param {Boolean} [params.length=true] Set the length of this renderer restuling audioBuffer in seconds
    */
    constructor({
        length = 20
    } = {}) {

        this.events = new EventEmitter({"scope" : this});

        this.audioContext = undefined;
        this.length = length;

        //Get the main audio context of Web Audio
        if (window.OfflineAudioContext !== undefined) {
            this.audioContext = new window.OfflineAudioContext(2, 44100 * this.length, 44100);
        }
        else if (window.webkitOfflineAudioContext !== undefined) {
            this.audioContext = new window.webkitOfflineAudioContext(2, 44100 * this.length, 44100);
        }

        //hack to reset the SR
        if (this.audioContext) {

            if (this.audioContext.sampleRate !== 44100) {
                const buffer = this.audioContext.createBuffer(1, 1, 44100);
                const dummy = this.audioContext.createBufferSource();
                dummy.buffer = buffer;
                dummy.connect(this.audioContext.destination);
                dummy.start(0);
                dummy.disconnect();
                this.audioContext.close();

                //Get the main audio context of Web Audio
                if (window.OfflineAudioContext !== undefined) {
                    this.audioContext = new window.OfflineAudioContext(2, 44100 * 40, 44100);
                }
                else if (window.webkitOfflineAudioContext !== undefined) {
                    this.audioContext = new window.webkitOfflineAudioContext();
                }
            }

            this.masterGain = this.audioContext.createGain();
            this.masterGain.connect(this.audioContext.destination);
        }

        //keep an array of connected sources for easy duplication for offlineContext
        this.sources = [];

        this.audioContext.addEventListener("complete", (event) => {
            console.log(event, "complete" );
            this.events.trigger(EVT_COMPLETE, event.renderedBuffer);
        });
        console.log("OfflineRenderer", this);
    }

    /**
     * Maintains an array of all the sources attached to this renderer
     * @param {Source} source 
     */
    registerNode(source) {
        this.sources.push(source);
    }

    /**
     * @return {Number} the current time of this audio renderer
     */
    getCurrentTime() {
        return this.audioContext.currentTime;
    }

    /**
     * Set this audioContext's gain volume, use a 1 sec linear ramp to avoid clicks
     * @param {Number} val 
     */
    setMasterGain(val) {
        let value = val;
        if (val === 0) {
            value = 0.001;
        }
        //this.masterGain.gain.value = val;
        this.masterGain.gain.linearRampToValueAtTime(value, this.getCurrentTime() + 1);
    }

    /**
    * Get this audioContext's gain volume
    * @param {Number} val 
    */
    getMasterGain() {
        return this.masterGain.gain.value;
    }

    /**
     * Duplicates all the registered audio sources from the given AudioRenderer.
     * Each source will use the same buffer than its original
     * @param {Rendrer} audioRenderer to take sources form
     */
    duplicateSourcesFromRenderer( renderer ) {

        renderer.sources.forEach((source) => {

            const sourceClone = new Source({
                "renderer": this
            });
            sourceClone.offsetStartTime = source.offsetStartTime;
            sourceClone.scheduleStartTime = source.baseScheduleStartTime;
            sourceClone.playbackRate = source.playbackRate;
            sourceClone.loop = source.loop;
            sourceClone.buffer = source.buffer;

            sourceClone.generateAudioBufferSource();
            this.registerNode(sourceClone);
            console.log("sourceClone",sourceClone);
        });
        //console.log(this.sources);
    }

    beep(frequency) {
        const freq = (frequency) ? frequency : 440;
        const osc = this.audioContext.createOscillator();
        osc.connect(this.masterGain);
        osc.frequency.value = freq;
        osc.start(0);
        osc.stop(this.audioContext.currentTime + 0.2);
    }

    /**
     * Renders this context contents (all the connected sources) to an audio buffer
     */
    startRendering(){
        this.audioContext.startRendering();
    }
}