Record audio with JavaScript

 
 
  • Gérald Barré
 

Browsers provide more and more APIs which allows creating applications with more and more features. In this post, we will see how to record the sound of the microphone.

The first thing is to ask the user's consent to use the microphone:

JavaScript
navigator.getUserMedia = navigator.getUserMedia ||
    navigator.webkitGetUserMedia ||
    navigator.mozGetUserMedia ||
    navigator.msGetUserMedia;

navigator.getUserMedia({
        audio: true
    },
    function (e) {
        // success
    },
    function (e) {
        // error
        console.error(e);
    });

This should display this popup:

Once accepted, you can create the code to read the audio stream:

JavaScript
navigator.getUserMedia({ audio: true },
    function (e) {
        // creates the audio context
        window.AudioContext = window.AudioContext || window.webkitAudioContext;
        context = new AudioContext();

        // creates an audio node from the microphone incoming stream
        mediaStream = context.createMediaStreamSource(e);

        // https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/createScriptProcessor
        var bufferSize = 2048;
        var numberOfInputChannels = 2;
        var numberOfOutputChannels = 2;
        if (context.createScriptProcessor) {
            recorder = context.createScriptProcessor(bufferSize, numberOfInputChannels, numberOfOutputChannels);
        } else {
            recorder = context.createJavaScriptNode(bufferSize, numberOfInputChannels, numberOfOutputChannels);
        }

        recorder.onaudioprocess = function (e) {
            console.log("on audio progress");
        }

        // we connect the recorder with the input stream
        mediaStream.connect(recorder);
        recorder.connect(context.destination);
    }

In the onaudioprogress event you can store the read content in an array:

JavaScript
var leftchannel = [];
var rightchannel = [];
var recordingLength = 0;

recorder.onaudioprocess = function (e) {
    leftchannel.push(new Float32Array(e.inputBuffer.getChannelData(0)));
    rightchannel.push(new Float32Array(e.inputBuffer.getChannelData(1)));
    recordingLength += bufferSize;
}

Once the recording is finished (for example by clicking on a button), you can create a WAV audio file. This format is very simple hence the choice. It consists of a header followed by data.

JavaScript
// stop recording
recorder.disconnect(context.destination);
mediaStream.disconnect(recorder);

// we flat the left and right channels down
// Float32Array[] => Float32Array
var leftBuffer = flattenArray(leftchannel, recordingLength); // flattenArray is on GitHub (see below)
var rightBuffer = flattenArray(rightchannel, recordingLength);

// we interleave both channels together
// [left[0],right[0],left[1],right[1],...]
var interleaved = interleave(leftBuffer, rightBuffer); // interleave is on GitHub (see below)

// we create our wav file
var buffer = new ArrayBuffer(44 + interleaved.length * 2);
var view = new DataView(buffer);

// RIFF chunk descriptor
writeUTFBytes(view, 0, 'RIFF');
view.setUint32(4, 44 + interleaved.length * 2, true);
writeUTFBytes(view, 8, 'WAVE');

// FMT sub-chunk
writeUTFBytes(view, 12, 'fmt ');
view.setUint32(16, 16, true);             // chunkSize
view.setUint16(20, 1, true);              // wFormatTag
view.setUint16(22, 2, true);              // wChannels: stereo (2 channels)
view.setUint32(24, sampleRate, true);     // dwSamplesPerSec
view.setUint32(28, sampleRate * 4, true); // dwAvgBytesPerSec
view.setUint16(32, 4, true);              // wBlockAlign
view.setUint16(34, 16, true);             // wBitsPerSample

// data sub-chunk
writeUTFBytes(view, 36, 'data');
view.setUint32(40, interleaved.length * 2, true);

// write the PCM samples
var index = 44;
var volume = 1;
for (var i = 0; i < interleaved.length; i++) {
    view.setInt16(index, interleaved[i] * (0x7FFF * volume), true);
    index += 2;
}

// our final blob
var blob = new Blob([view], { type: 'audio/wav' });

There you go. It is then possible to play the sound directly in the browser:

JavaScript
var url = window.URL.createObjectURL(blob);
var audio = new Audio(url);
audio.play();

Or download the file:

JavaScript
var url = URL.createObjectURL(blob);
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
a.href = url;
a.download = "sample.wav";
a.click();
window.URL.revokeObjectURL(url);

You can find the working example on GitHub: https://gist.github.com/meziantou/edb7217fddfbb70e899e.

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?Buy Me A Coffee💖 Sponsor on GitHub