I've been trying to get the data from the media stream of a microphone when a user speaks into it. My goal is to continually send that data over to a web server as until the user closes the mic by a button. I've tried a lot like this from mdn unfortunately its in javascript and I try to rewrite it in typescript which I think is where my problem arises. At the moment this is what I have
mediaRecorder?: MediaRecorder
chunks: any = []
dest?: MediaStreamAudioDestinationNode
constraints = { audio:true, video: false }
async record_audio() {
if (navigator.mediaDevices){
var $this = this;
navigator.mediaDevices.getUserMedia(this.constraints).then((stream)=>{
this.mediaRecorder = new MediaRecorder(stream)
this.mediaRecorder.start();
//this.mediaRecorder.ondataavailable = this.pushChunks;
this.mediaRecorder.ondataavailable = function (e) {
console.log("pushing chunk")
$this.chunks.push(e.data);
};
console.log('starting recorder...')
})
} else { alert('getUserMedia not supported.'); }
}
The method is called but the mediaRecorder.ondataavailable
is never called. I don't know if the method gets destroyed after it exits or something.
How do I continually get the data in this stream so I can upload it to a web server?
Addition
I worked around to write this methods. The AudioWorkletNode
fires it process
method continually but unfortunately again, the worklet has to be implemented in a different class and file which I cannot utilize the resources in the current class component
I chipped in the commented code into the block to see if it will fire but it doesn't
async record_audio() {
console.log("audio is starting up ...");
if (await navigator.mediaDevices.getUserMedia({audio:true, video: false})){
this.start_microphone(await navigator.mediaDevices.getUserMedia({audio:true, video: false}))
} else { alert('getUserMedia not supported in this browser.'); }
};
async start_microphone(stream:MediaStream){
// this.mediaRecorder = new MediaRecorder(stream)
// console.log('starting recorder...')
// this.mediaRecorder.start();
// this.mediaRecorder.ondataavailable = this.pushChunks;
// this.mediaRecorder.ondataavailable = function (e) {
// console.log("pushing chunk")
// this.chunks.push(e.data);
// };
this.gainNode = this.audioCtx!.createGain();
//gain_node.connect( $this.audioCtx!.destination );
this.microphone_stream = this.audioCtx!.createMediaStreamSource(stream);
this.microphone_stream.connect(this.gainNode);
await this.audioCtx!.audioWorklet.addModule("/assets/audio-processor.js");
this.processorNode = new AudioWorkletNode(this.audioCtx!, "audio-processor");
this.microphone_stream.connect(this.processorNode);
}
You can try this approach to resolve the issue:
start function
navigator.mediaDevices.getUserMedia({ audio: true, video: false })
.then(stream => {
if (!this.audioContext) {
this.audioContext = new AudioContext();
this.gainNode = this.audioContext.createGain();
}
const source = this.audioContext.createMediaStreamSource(stream);
source.connect(this.gainNode);
this.mediaRecorder = new MediaRecorder(stream);
this.mediaRecorder.start(1000);
this.mediaRecorder.ondataavailable = (e: BlobEvent) => {
this.chunks.push(e.data);
};
})
.catch(error => {
console.error('Error accessing microphone:', error);
});
where
audioContext = new AudioContext();
gainNode = audioContext.createGain();