Recording mp3 audio in HTML5 using vmsg - a WebAssembly library based on LAME
This is the 3rd post in our new recording audio in HTML5 series. We’ve previously covered how to use Recorder.js to capture uncompressed pcm audio and WebAudioRecorder.js to capture pcm, mp3 and Vorbis audio in the browser.
We’ll now briefly look at vmsg.
vmsg is very similar to WebAudioRecorder.js in that it uses a compiled version of the LAME mp3 encoding library but they differ in the way that library was compiled:
- Web Audio Recorder used a JavaScript library compiled from LAME’s C code to a subset of JavaScript called asm.js.
- vmsg uses the same C based LAME mp3 encoding library but compiled to a newer WebAssembly library which, in theory, should be faster than the asm.js one and smaller (73KB vs 114 KB gzipped).
- vmsg uses the latest version of the LAME library (3.100 from October 2017)
But encoding mp3 is very fast on modern CPUs – even when using the asm.js version of the LAME mp3 encoder – so choosing between Web Audio Recorder and vmsg comes down to ease of use, browser support, whether or not the library is still maintained and personal preference.
vmsg also differs in that:
- it includes a recording UI/modal with start/stop buttons + gain and pitch sliders
- it handles the
getUserMedia()
code required to access the microphone on desktop and mobile devices
Building a simple demo
Since vmsg’s 3 official demos (live demo, live coding demo, demo folder) are all React based we’re going to set up a simple JS/HTML demo.
You can check out our demo live here, the code is on GitHub.
We’ll start off with a simple index.html
that includes app.js
as an ES6 module and vmsg.css
which contains CSS classes for vmsg’s UI.
<title>Simple vmsg audio recording demo</title>
<link rel="stylesheet" type="text/css" href="vmsg.css">
<script src="app.js" type="module"></script>
<button id="recordButton">Record</button>
Module scripts are deferred so even though app.js
is included at the beginning it will not execute until the document has finished parsing thus the recordButton
element will be available in app.js
.
Here’s the app.js
code:
import {
record
} from "./vmsg.js";
let recordButton = document.getElementById("recordButton");
recordButton.onclick = function() {
record({
wasmURL: "vmsg.wasm"
}).then(blob => {
console.log("Recorded MP3", blob);
var url = URL.createObjectURL(blob);
var preview = document.createElement('audio');
preview.controls = true;
preview.src = url;
document.body.appendChild(preview);
});
};
The app.js
module imports the record function from vmsg.js
and sets up the onclick
event function for the Record button. When the button is clicked the record()
function is executed and the vmsg UI shows up, a modal that greyes out everything else:
The record()
function receives the path to the vmsg.wasm
WebAssembly library through the wasmURL
object property. If omitted the default path is /static/js/vmsg.wasm
. No other properties but the path to a webassembly polyfill (shimURL
) and the pitch (pitch
, values between -1 and 1) can be sent.
For this demo we’ve placed everything in the same folder:
After recording, when you click the top right checkmark, the blob is made available to the app.js code
. In our code, we add it as an <audio>
element in the HTML page for playback but you could do anything with it including POST-ing it to a server side upload script.
To avoid 1 HTTP call we could have placed the code in app.js
inline in index.html
like this:
<script type="module"> //app.js code </script>
Important: Make sure your web server delivers .wasm
files with the MIME type application/wasm
like this:
Otherwise, Chrome will throw an error
> Error: TypeError: Failed to execute ‘compile’ on ‘WebAssembly’: Incorrect response MIME type. Expected ‘application/wasm’.
On Apache you can easily configure the correct MIME type for .wasm files by adding the following line to a .htaccess
file located in the same folder or in the root folder of the web server.
AddType application/wasm .wasm
Since we’re using ES6 modules and WebAssembly this demo and library will only work on the latest browsers:
Desktop:
- Edge 16
- Firefox 60
- Chrome 61
- Safari 11
Mobile:
- Safari 11 on iOS
- Chrome 61 on Android
More details: ES6 modules, WebAssembly.
The vmsg library author also provides a wasm polyfill which will makes the library work on much older desktop browsers.
Quality wise the vmsg library is encoding mono mp3 audio at the sample rate set in your OS for your playback device (as per the spec). In practice you’ll most commonly see 44100 samples/second (44.1kHz) or 48000 samples/second (48kHz)
Personally, I would have loved to see the vmsg wasm API separated from the UI and the getUserMedia()
code.
Further reading: