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();
}
}