import MyEventEmitter from '../myEventEmitter';
/**
* @class provides a simple interface for recording audio from a microphone
* using the Media Recorder API.
*/
class AudioRecorder extends MyEventEmitter {
/** @private */
#mediaRecorder;
/** @private */
#recordedChunks = [];
/** @private */
#audioBlob;
/** @private */
#audioContext;
/** @private */
#recordedSignals = [];
/** @private */
sinkSamplingRate;
/**
* Decode the audio data from the recorded audio blob.
* @private
*/
#saveRecording = async () => {
const arrayBuffer = await this.#audioBlob.arrayBuffer();
const audioBuffer = await this.#audioContext.decodeAudioData(arrayBuffer);
const data = audioBuffer.getChannelData(0);
console.log(`Decoded audio buffer with ${data.length} samples`);
this.#recordedSignals.push(Array.from(data));
};
/**
* Event listener triggered when data is available in the media recorder.
* @private
* @param {*} e - The event object.
*/
#onRecorderDataAvailable = e => {
if (e.data && e.data.size > 0) this.#recordedChunks.push(e.data);
};
/**
* Method to create a media recorder object and set up event listeners.
* @private
* @param {MediaStream} stream - The stream of audio from the Listener.
*/
#setMediaRecorder = stream => {
// Create a new MediaRecorder object
this.#mediaRecorder = new MediaRecorder(stream);
// Add event listeners
this.#mediaRecorder.ondataavailable = e => this.#onRecorderDataAvailable(e);
};
#setAudioContext = () => {
this.#audioContext = new (window.AudioContext ||
window.webkitAudioContext ||
window.audioContext)({
sampleRate: this.sinkSamplingRate,
});
};
/**
* Public method to start the recording process.
* @param {MediaStream} stream - The stream of audio from the Listener.
*/
startRecording = async stream => {
// Create a fresh audio context
this.#setAudioContext();
// Set up media recorder if needed
if (!this.#mediaRecorder) this.#setMediaRecorder(stream);
// clear recorded chunks
this.#recordedChunks = [];
// start recording
this.#mediaRecorder.start();
};
/**
* Method to stop the recording process.
* @public
*/
stopRecording = async () => {
// Stop the media recorder, and wait for the data to be available
await new Promise(resolve => {
this.#mediaRecorder.onstop = () => {
// when the stop event is triggered, resolve the promise
this.#audioBlob = new Blob(this.#recordedChunks, {
type: 'audio/wav; codecs=opus',
});
resolve(this.#audioBlob);
};
// call stop
this.#mediaRecorder.stop();
});
// Now that we have data, save it
await this.#saveRecording();
};
/**
* Public method to get the last recorded audio signal
* @returns
*/
getLastRecordedSignal = () => this.#recordedSignals[this.#recordedSignals.length - 1];
/**
* Public method to get all the recorded audio signals
* @returns
*/
getAllRecordedSignals = () => this.#recordedSignals;
/**
* Public method to set the sampling rate used by the capture device
* @param {Number} sinkSamplingRate - The sampling rate of the capture device
*/
setSinkSamplingRate = sinkSamplingRate => {
this.sinkSamplingRate = sinkSamplingRate;
};
}
export default AudioRecorder;