How to encode PCM data to MP3 in JavaScript?
Solution 1
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.
Solution 2
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
Solution 3
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 ;)
RajV
Updated on June 04, 2022Comments
-
RajV about 2 years
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
andbufferR
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.
-
RajV over 10 yearsnread is -1. Sorry, I should have said that in the post. Editing it now.
-
RajV over 10 yearsI went through lame C source code and tracked the problem down to indeed a very small buffer size sent to lame_encode_buffer_ieee_float. I fixed the problem by setting BUFSIZE to channel_l.length * 4. Now, encoding works for short audio clips. But, fails again for larger clips.
-
Sean Bannister about 10 yearsHow did you end up going with this as I'm looking into recording to MP3 as well. I was thinking your failure for larger clips might be due to libmp3lame.js needing to be compiled with a larger buffer size with emscripten .
-
RajV almost 10 yearsThis will definitely be the ideal approach. However, due to the complexity of the problem, can I ask you to post a fully working example in GitHub? Need the 2 JS files and a HTML. Thank you!
-
mido almost 10 yearshere is the github link, github.com/Mido22/mp3Recorder also updating the answer with it.