node.jsxmlhttprequestfetchrecordingmediastream

How to record mic from client then send to server


I would like to record microphone input from the client and then when he stops, send the data to the server and then output the recorded audio to a specific folder.

So far I have for the recording on the client I have followed this

mediaRecorder.onstop = function(e) {
   console.log("recorder stopped");

   const blob = new Blob(chunks, { 'type' : 'audio/ogg; codecs=opus' });

   chunks = [];

   const formData = new FormData();
   formData.append('audio-file', blob);

   return fetch('http://localhost:3000/notes', {
      method: 'POST',
      body: formData
   });

}

console.log(blob) on the client returns an object

Blob { size: 35412, type: "audio/ogg; codecs=opus" }

On the server side I use Node.js

app.post("/notes",function(req,res){
  console.log(req);
});

The server receives formData but the body is empty {}

I have also tried XMLHttpRequest with the same result.


Solution

  • I've played about with this type of project before. I created a simple form that allows you to record from the microphone, then upload to the server.

    Sound files will be saved in ./sound_files

    Just run the node server like

    node server.js

    And go to localhost:3000 to view the page.

    Node code (server.js)

    const express = require('express');
    const multer = require('multer');
        
    const storage = multer.diskStorage(
        {
            destination: './sound_files/',
            filename: function (req, file, cb ) {
                cb( null, file.originalname);
            }
        }
    );
    
    const upload = multer( { storage: storage } );
    
    const app = express();
    const port = 3000;
    
    app.use(express.static('./'));
    
    app.post("/notes", upload.single("audio_data"), function(req,res){
        res.status(200).send("ok");
    });
    
    app.listen(port, () => {
        console.log(`Express server listening on port: ${port}...`);
    });
    

    index.html

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>Speech to text test</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link rel="stylesheet" type="text/css" href="https://bootswatch.com/4/cerulean/bootstrap.min.css">
    </head>
    <body style="padding:50px;">
        <h1>Speech to text test</h1>
        <div id="controls">
        <button id="recordButton">Record</button>
        <button id="transcribeButton" disabled>Stop and upload to server</button>
        </div>
        <div id="output"></div>
        <script src="https://cdn.rawgit.com/mattdiamond/Recorderjs/08e7abd9/dist/recorder.js"></script>
        <script>
                    
            let rec = null;
            let audioStream = null;
    
            const recordButton = document.getElementById("recordButton");
            const transcribeButton = document.getElementById("transcribeButton");
    
            recordButton.addEventListener("click", startRecording);
            transcribeButton.addEventListener("click", transcribeText);
    
            function startRecording() {
    
                let constraints = { audio: true, video:false }
    
                recordButton.disabled = true;
                transcribeButton.disabled = false;
    
                navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
                    const audioContext = new window.AudioContext();
                    audioStream = stream;
                    const input = audioContext.createMediaStreamSource(stream);
                    rec = new Recorder(input, { numChannels: 1 })
                    rec.record()
                    document.getElementById("output").innerHTML = "Recording started..."
                }).catch(function(err) {
                    recordButton.disabled = false;
                    transcribeButton.disabled = true;
                });
            }
    
            function transcribeText() {
                document.getElementById("output").innerHTML = "Converting audio to text..."
                transcribeButton.disabled = true;
                recordButton.disabled = false;
                rec.stop();
                audioStream.getAudioTracks()[0].stop();
                rec.exportWAV(uploadSoundData);
            }
    
            function uploadSoundData(blob) {
                const filename = "sound-file-" + new Date().getTime() + ".wav";
                const formData = new FormData();
                formData.append("audio_data", blob, filename);
                
                fetch('http://localhost:3000/notes', {
                    method: 'POST',
                    body: formData
                }).then(async result => { 
                    document.getElementById("output").innerHTML = await result.text();
                }).catch(error => { 
                    document.getElementById("output").innerHTML = "An error occurred: " + error;
                })
            }
    
        </script>
    </body>
    </html>