Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to encode PCM data to MP3 in JavaScript?

I am using Recorder.js to record audio from mic. That library can encode the PCM data in WAV and I can successfully play it back using <audio>. But, the resulting WAV data is too huge (~38MB for a 5 minute recording). I tried using libmp3lame.js available from Speech-to-Server.

In recorderWorker.js, I am importing the Lame script:

importScripts("libmp3lame.js");

Then, I changed the exportWAV() function to encode the PCM buffers into MP3 instead of WAV.

function exportWAV(type){
    var bufferL = mergeBuffers(recBuffersL, recLength);
    var bufferR = mergeBuffers(recBuffersR, recLength);
    //var interleaved = interleave(bufferL, bufferR);

    console.log("Start MP3 encoding");
    var mp3codec = Lame.init();
    Lame.set_mode(mp3codec, Lame.JOINT_STEREO);
    Lame.set_num_channels(mp3codec, 2);
    Lame.set_out_samplerate(mp3codec, sampleRate);
    Lame.set_bitrate(mp3codec, 128);
    Lame.init_params(mp3codec);

    var mp3data = Lame.encode_buffer_ieee_float(mp3codec, bufferL, bufferR);
    audioBlob = new Blob([mp3data.data], { type: "audio/mp3" });
    console.log("Done MP3 encoding");

    this.postMessage(audioBlob);
}

But, the Lame.encode_buffer_ieee_float method is throwing this error:

Uncaught RangeError: Invalid array buffer length 

Here PCM data in bufferL and bufferR are Float32Array. I can not find out what exactly Lame.encode_buffer_ieee_float expects as input.

The offending line in Lame.encode_buffer_ieee_float that is throwing the error is:

var arraybuf = new ArrayBuffer(nread);

I put a breakpoint and checked the value of nread. It is -1.

So, my question is how do I use the Lame MP3 JavaScript library in this situation? Thank you.

like image 417
RajV Avatar asked Feb 13 '14 20:02

RajV


3 Answers

There's a library written in pure javascript, called lamejs. It is much faster than emscripten compile of libmp3lame. https://github.com/zhuker/lamejs

Example usage:

lib = new lamejs();
mp3encoder = new lib.Mp3Encoder(1, 44100, 128); //mono 44.1khz encode to 128kbps
samples = new Int16Array(44100); //one second of silence
var mp3 = mp3encoder.encodeBuffer(samples); //encode mp3
like image 91
Alex Zhukov Avatar answered Nov 03 '22 10:11

Alex Zhukov


I don't have a ton of time right now, so I can't actually download the libraries and put together a test, but I'd start by putting a breakpoint after this line:

var nread = Module.ccall('lame_encode_buffer_ieee_float', 'number', [ 'number', 'number', 'number', 'number', 'number', 'number' ], [ handle, inbuf_l, inbuf_r, channel_l.length, outbuf, BUFSIZE ]) in libmp3lame.js.

Then set up a watch expression for nread. You want to see what that value is, because I'm pretty sure it's the next line (var arraybuf = new ArrayBuffer(nread);) that's throwing.

That might at least give you some insight into what's going on.

I also noticed that the encode_buffer_ieee_float method internally assumes BUFSIZE = 8192. I'm not entirely sure if that's significant or not, but it has me wondering whether or not this method might actually be intended only as a means for encoding individual Mp3 frames rather than buffers of arbitrary length.

Anyway, if you can see what the value of nread is, that should at least get you on the right track toward figuring out what's going on. But it definitely looks like the second and third parameters are intended to be Float32Arrays, so I don't think you're sending the wrong type of arguments.

like image 38
Kevin Ennis Avatar answered Nov 03 '22 11:11

Kevin Ennis


I have also the same error

But i solve that by doing this step: Execute init for mp3 web worker mp3worker.init() when you want to start a new recording. It's due to mp3codec value which was null, because you execute sendMessage(command: 'end'); This solution solves your problem ;)

like image 1
princeOfLightness Avatar answered Nov 03 '22 10:11

princeOfLightness