This is the 2nd post in our new recording audio in HTML5 series. In the 1st we’ve discussed Recorder.js.
In this blog post, we’ll cover using WebAudioRecorder.js to record mp3, vorbis and wav audio on your website.
We’ll be building a simple demo to help you get started with the library. There are 2 demos available for WebAudioRecorder
(the main one linked from GitHub that’s not working and a functioning one) but they’re fairly complex implementations and thus hard to get started with.
The project is available on GitHub, link at the bottom, live demo here.
Intro to Web Audio Recorder
WebAudioRecorder.js
is a JavaScript library written in 2015 by higuma that can record audio and encode to common formats directly in the browser.
When used in conjunction with getUserMedia()
it can record the audio from the user’s microphone or webcam.
It supports 3 encoding formats:
- Uncompressed waveform audio (.wav)
- Vorbis audio in ogg container (.ogg)
- MP3 (MPEG-1 Audio Layer III) (.mp3)
Libraries for encoding audio
WebAudioRecorder.js
uses external JavaScript libraries to convert the raw audio to mp3 and Vorbis. These libraries are Java Script versions of the popular LAME mp3 encoder and libogg/libvorbis encoders obtained by compiling the original C code using Empscripten as (the asm.js subset of) JavaScript. Since JavaScript is slower than native code you should expect your encoding times to be higher.
These libraries are loaded and used as Web Workers which prevents the browser tab from becoming unresponsive while the audio encoding is underway.
Even though the main WebAudioRecorder.min.js
JS file comes in at just 3.6KB when minified (and 1.21 KB when gzipped) unless you’re recording to wav, you do have to make extra HTTP requests and load the external libraries, and these libraries are quite big:
For encoding MP3
WebAudioRecorderMp3.min.js
386 KB minified and 114 KB gzippedMp3LameEncoder.min.js.mem
96 KB minified and 12 KB gzipped
That’s 2 HTTP requests and 126KB in total when gzipped.
The MP3 encoder is locked at recording 2 channels but you can configure the bitrate from 64 to 320. The current implementation supports LAME CBR encoding only, no VBR (variable bit rate).
For encoding Vorbis in ogg container
WebAudioRecorderOgg.min.js
311 KB minified and 95 KB gzippedOggVorbisEncoder.min.js.mem
553 KB minified and 114KB gzipped
That’s 2 HTTP requests and 209KB in total when gzipped.
The Vorbis encoder can encode mono or stereo sound and you can configure the bitrate from 45kb/s to 500kb/s. Also, keep in mind that Vorbis is mostly aimed at compressing music and audio in general, it’s not aimed at compressing speech like the way Speex is.
Uncompressed wav sound
The small code for capturing data as uncompressed wav is located separately in WebAudioRecorderWav.min.js
which comes in at just 2.6 KB minified and 1 KB gzipped. No need for large libraries here.
When recording to wav, audio data is recorded as 2 channel 16bit audio (CD quality) and thus will be exactly 10.582MB/minute at 44.1kHz but you can lower the number of channels from the WebAudioRecorder
constructor to halve that size.
Sampling rate
Regardless of the library, the sample rate used will be the one set in your OS for your playback device (as per the spec). In practice, you’ll mostly see sample rates of 44100 (44.1kHz) and 48000 (48kHz).
Project Folder
To use the library you must 1st download the latest release (0.1.1 from 2015) from GitHub and set up your project folder. Here’s how I set up mine:
I’m using index.html
for a simple record/stop UI and app.js
to host the code for the interface.
HTML File
In index.html
we need a select for the type of encoding, 2 buttons: start and stop, a list for showing the recorded files and a visible log too keep track of what’s happening. Here’s how my index.html
looks:
Convert audio to:
<select id="encodingTypeSelect">
<option value="wav">Waveform Audio (.wav)</option>
<option value="mp3">MP3 (MPEG-1 Audio Layer III) (.mp3)</option>
<option value="ogg">Ogg Vorbis (.ogg)</option>
</select>
<button id="recordButton">Record</button>
<button id="stopButton" disabled>Stop</button>
<h3>Log</h3>
<pre id="log"></pre>
<h3>Recordings</h3>
<ol id="recordingsList"></ol>
<!-- inserting these scripts at the end to be able to use all the elements in the DOM -->
<script src="js/WebAudioRecorder.min.js"></script>
<script src="js/app.js"></script>
We’re insertingWebAudioRecorder.min.js
andapp.js
at the end to make sure they have access to all the DOM elements when run.
JavaScript File
Now let’s move on to app.js
and build our web based audio recorder.
We’ll start out by defining a few variables and setting up references to those DOM UI elements. The comments describe each variable in detail:
var gumStream;
//stream from getUserMedia()
var recorder;
//WebAudioRecorder object
var input;
//MediaStreamAudioSourceNode we'll be recording var encodingType;
//holds selected encoding for resulting audio (file)
var encodeAfterRecord = true;
// when to encode
var audioContext = new AudioContext;
//new audio context to help us record
var encodingTypeSelect = document.getElementById("encodingTypeSelect");
var recordButton = document.getElementById("recordButton");
var stopButton = document.getElementById("stopButton");
With our record & stop buttons referenced in JS we can add event listeners for when they’re clicked:
recordButton.addEventListener("click", startRecording); stopButton.addEventListener("click", stopRecording);
Code for starting a recording
The startRecording()
function will do most of the heavy lifting in this demo.
In it we first:
- set up the constraints object for audio only (see our audio constraints article for toggling advanced options like noise reduction and echo cancellation)
- call the promise based
navigator.mediaDevices.getUserMedia()
Only if getUserMedia()
succeeds (user grants microphone access) we trigger the rest of the code.
var constraints = {
audio: true,
video: false
}
navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
//most of the code
}).catch(function(err) {});
In getUserMedia
‘s success function we’ll start by booting up a new WebAudioRecorder
object taking care to provide the function for the onEncoderLoading
event directly in its constructor. onEncoderLoading
is the only event to be fired during construction process so to catch the first event correctly, it should be set from constructor parameter.
The JS worker files for converting audio to mp3 and Vorbis are loaded when creating a recorder object or when changing encoding with setEncoding()
. To correctly load those worker files we must set the worker directory from the constructor object using workerDir
(“/js” in our case because that’s where we’ve put all the WebAudioRecorder
files).
The last property we’ll be setting in the constructor object is the encoding
property (mp3, wav or ogg). We’ll use the value from our encodingTypeSelect
drop down menu.
//assign to gumStream for later use
gumStream = stream;
/* use the stream */
input = audioContext.createMediaStreamSource(stream);
//stop the input from playing back through the speakers
input.connect(audioContext.destination) //get the encoding
encodingType = encodingTypeSelect.options[encodingTypeSelect.selectedIndex].value;
//disable the encoding selector
encodingTypeSelect.disabled = true;
recorder = new WebAudioRecorder(input, {
workerDir: "js/",
encoding: encodingType,
onEncoderLoading: function(recorder, encoding) {
// show "loading encoder..." display
__log("Loading " + encoding + " encoder...");
},
onEncoderLoaded: function(recorder, encoding) {
// hide "loading encoder..." display
__log(encoding + " encoder loaded");
}
});
With the new recorder
object initialised we can use the onComplete
event to trigger what happens when the encoding is done – basically we just pass the blob to the createDownloadLink(blob,encoding)
function but more on that later. We could have set up this event in the constructor but for this demo we’ll just add it after:
recorder.onComplete = function(recorder, blob) {
__log("Encoding complete");
createDownloadLink(blob, recorder.encoding);
encodingTypeSelect.disabled = false;
}
Before starting the recording we configure the recorder
object to:
- record for maximum 120 seconds
- encode the audio data AFTER the recording process is stopped
- we set the quality for Vorbis recodings to 0.5 which means abut 160kbps
- we set the bitrate for mp3 encodings to 160 (kbps)
recorder.setOptions({
timeLimit: 120,
encodeAfterRecord: encodeAfterRecord,
ogg: {
quality: 0.5
},
mp3: {
bitRate: 160
}
});
For the mp3 bitRate
you can use values from 64 to 320 while for the Vorbis quality
you can use values from -0.1 to 1.0.
This is the average bitrate each Vorbis quality value corresponds to:
We’re now ready to start the recording process:
//start the recording process
recorder.startRecording();
Here’s how the entire startRecording()
function looks from start to end:
function startRecording() {
console.log("startRecording() called");
}