javascriptangulartypescriptweb-audio-api

MediaRecorder not firing event


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

Solution

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