Record audio with JavaScript

 
 
  • Gérald Barré
 

Browsers are continuously expanding their APIs, enabling richer web applications. In this post, we will see how to record audio from the microphone.

First, request the user's permission to access 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 will display the following permission prompt:

Once the user accepts, you can set up 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 onaudioprocess event, store the incoming audio data in arrays:

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 recording is complete (for example, when the user clicks a stop button), you can build a WAV file. WAV is a straightforward format consisting of a header followed by raw audio 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' });

You can then play the audio 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?