Audio Recording Functionality with two buttons, one to start recording, other to stop recording

Viewed 1839

I am very new to programming in Vue.js and what I want to do is a simple audio recorder that starts recording audio on click of a recording button and that stops recording on click of a stop button. When stopping it should display the audio file in the template and store the audio in a blob which should be later on stored locally.

I have implemented the template as follows:

<template>
    <!-- Voice Record Title + Button -->
    <div class="form-group row">
        <label for="Audio" class="col-2 col-form-label labelTop">Audio</label>
        <div class="col-1">
            <button @click="recordAudio()" type="button" id="button_record" class="btn btn-danger">
            </button>
            <button type="button" id="button_stop" class="btn btn-success">
            </button>
            <div id="audio" class="audio" controls>
            </div>
        </div>
    </div>
</template>

The script contains the following code:

export default {
    methods: {
        recordAudio() {
            var device = navigator.mediaDevices.getUserMedia({ audio: true });
            var items = [];
            device.then((stream) => {
                var recorder = new MediaRecorder(stream);
                recorder.ondataavailable = (e) => {
                    items.push(e.data);
                    if (recorder.state == "inactive") {
                        var blob = new Blob(items, { type: "audio/*" });
                        var audio = document.getElementById("audio");
                        var mainaudio = document.createElement("audio");
                        mainaudio.setAttribute("controls", "controls");
                        audio.appendChild(mainaudio);
                        mainaudio.innerHTML =
                            '<source src="' +
                            URL.createObjectURL(blob) +
                            '" type="audio/*" />';
                    }
                };
                recorder.start();
                // I do not want a timeout to stop the recording but clicking the stop button to do so
                setTimeout(() => {
                    recorder.stop();
                }, 5000);
            });
        },
    },
};

This might sound simple, but the only thing that I want to do now is that it stops recording not using this timeout function after some seconds, but to stop recording on click of button_stop. I tried so many things, e.g. directly setting an event-handler to button_stop with

@click="recorder.stop()" 

or also:

if (document.getElementById("button_stop").clicked == true) {
                recorder.stop();
                }

and also:

document.getElementById("button_stop").addEventListener("click", recorder.stop());
                if (document.getElementById("button_stop").clicked == true) {
                    recorder.stop();
                }

but nothing of that really works.

As I am very new to Vue and Javascript generally, this is really frustrating me. I would really would appreciate any help.

2 Answers

You should be able to achieve this by assigning recorder to a variable in your component, not only in your function. With that, you can later call this.recorder.stop() on click of button.

Try:

data() {
  return {
    recorder: null
  }
},
methods: {
    recordAudio() {
      var device = navigator.mediaDevices.getUserMedia({ audio: true });
      device.then((stream) => {
        // use this!
        this.recorder = new MediaRecorder(stream);
        this.recorder.ondataavailable = (e) => {
           // ....
        };
      });
    },
    // called on button click
    stop() {
      this.recorder.stop()
    }
}

Template:

<button type="button" id="button_stop" class="btn btn-success" @click="stop">

Here my parent component starts the recordAudio and stop methods. After finished the recording the data is emitted to the parent to be send into another child & send from there to a server. A little bit like a whatsapp voice message and the result is psoted into a chat above.

<template>
  <!-- Voice Record -->
  <div>
    <v-btn @click="recordAudio()">
      <v-icon> mdi-microphone </v-icon>
    </v-btn>
    <v-btn @click="stop()">
      <v-icon> mdi-stop-circle-outline </v-icon>
    </v-btn>
  </div>
</template>
<script>
export default {
  data() {
    return {
      recorder: null,
      chunks: [],
      device: null,
      blobObj: null,
    };
  },

  created() {
    this.device = navigator.mediaDevices.getUserMedia({ audio: true });
  },
  methods: {
    
    recordAudio() {
      this.device.then((stream) => {
        this.recorder = new MediaRecorder(stream);
        this.recorder.ondataavailable = (e) => {
          this.chunks.push(e.data);
          if (this.recorder.state === "inactive") {
            let blob = new Blob(this.chunks, { type: "audio/wav" });
            // save to blobObj
            this.blobObj = blob;
            this.chunks = [];
            // emit to parent
            this.$emit("send-audio", this.blobObj);
            this.blobObj = null;
          }
        };
        // start
        this.recorder.start();
      });
    },
    stop() {
      // stop
      this.recorder.stop();
    },
  },
};
</script>
Related