我正在使用Recorder.js录制来自麦克风的音频。该库可以在WAV中编码PCM数据,我可以使用<audio>
成功播放它。但是,由此产生的WAV数据太大(5分钟录制时约为38MB)。我尝试使用Speech-to-Server提供的libmp3lame.js。
在recorderWorker.js
中,我正在导入Lame脚本:
importScripts("libmp3lame.js");
然后,我更改了exportWAV()
函数,将PCM缓冲区编码为MP3而不是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);
}
但是,Lame.encode_buffer_ieee_float
方法会抛出此错误:
Uncaught RangeError: Invalid array buffer length
此处bufferL
和bufferR
中的PCM数据是Float32Array。我无法确定Lame.encode_buffer_ieee_float到底需要什么作为输入。
Lame.encode_buffer_ieee_float
中引发错误的违规行是:
var arraybuf = new ArrayBuffer(nread);
我放了一个断点并检查了nread
的值。它是-1。
所以,我的问题是如何在这种情况下使用Lame MP3 JavaScript库?谢谢。
答案 0 :(得分:6)
有一个用纯javascript编写的库,叫做lamejs。 它比libmp3lame的emscripten编译快得多。 https://github.com/zhuker/lamejs
使用示例:
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
答案 1 :(得分:2)
我现在没有太多时间,所以我实际上无法下载这些库并进行测试,但我首先要在这一行之后设置一个断点:
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 ])
中的 libmp3lame.js
。
然后为nread
设置监视表达式。你想看看那个值是什么,因为我很确定它是下一行(var arraybuf = new ArrayBuffer(nread);
)抛出的。
这至少可以让你深入了解正在发生的事情。
我还注意到encode_buffer_ieee_float
方法内部假设为BUFSIZE = 8192
。我不完全确定这是否重要,但它让我想知道这种方法是否真的只是作为编码单个Mp3帧而不是任意长度的缓冲区的手段。
无论如何,如果你能看到nread
的价值是什么,那至少应该让你走上正确的轨道去弄清楚发生了什么。但它肯定看起来像第二个和第三个参数是Float32Arrays
,所以我认为你不会发送错误类型的参数。
答案 2 :(得分:1)
我也有同样的错误
但我通过这一步解决了这个问题:
当您想要开始新录制时,执行mp3 web worker mp3worker.init()
的init。
这是由于mp3codec
值为null,因为您执行sendMessage(command: 'end');
此解决方案可以解决您的问题;)
答案 3 :(得分:0)
我正在建立Kevin Ennis的答案,解决因wav文件持续时间较长而引起的问题,我们要做的是,不要等待所有数据准备好,而是在收到数据后立即对数据进行编码onaudioprocess
方法,结果是编码变得闪电般快。
我已将Recorderjs和speech-to-server合并在一起。
放入github的代码。
我的修改,
在recorder.js
中libmp3lame.js中的var bufferLen = 16384 //行号7
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,channel_l.length * 4]); //行号62866
并将recordWorker.js重构为
var WORKER_PATH = 'encoder.js';
var encoder, data=[];
this.onmessage = function(e){
switch(e.data.command){
case 'init':
init(e.data.config);
break;
case 'record':
record(e.data.buffer);
break;
case 'exportMP3':
exportMP3();
break;
case 'clear':
clear();
break;
}
};
function init(config){
sampleRate = config.sampleRate;
encoder = new Worker(WORKER_PATH);
encoder.onmessage = function(e){
switch(e.data.cmd){
case 'data' :
var length = e.data.buf.length;
for(var i=0;i<length;i++)
data.push(e.data.buf[i]);
console.log('data = '+e.data.buf);break;
case 'end' :
var audioBlob = new Blob([new Uint8Array(data)], { type: 'audio/mp3' });
self.postMessage(audioBlob);
break;
}
}
encoder.postMessage({
cmd: 'init',
config: {
samplerate: sampleRate,
channels: 2,
mode: 1, // setting mode as Stereo.
bitrate: 64
}
});
}
function record(inputBuffer){
encoder.postMessage({
cmd: 'encode',
rbuf: inputBuffer[0],
lbuf: inputBuffer[1]
});
}
function exportMP3(){
encoder.postMessage({cmd: 'finish'});
}
function clear(){
data=[];
}
(p.s:如果需要,可以将recordWorker.js和encoder.js合并为单个文件)
答案 4 :(得分:0)
正如上面的答案所表明的那样,显而易见的是,库中lame_encode_buffer_ieee_float的模块调用返回的nread值为-1,表示编码失败。
看起来传递的BUFSIZE变量既分配outbuf缓冲区,又传递给encode函数,设置为8192个样本,或缓冲区大小为2048个样本。对于即插即用编码,这样做会很好,但是如果你传递的缓冲区大于2048个样本,那么这将会废弃。
我解决问题的方法是在outbuf malloc调用之前添加这行代码:
BUFSIZE = channel_l.length * 4; // add this line
var outbuf = _malloc(BUFSIZE);
因此,BUFSIZE也将传递给lame_encode_buffer_ieee_float,而nread将保存生成的编码缓冲区中的数据字节数。
希望这可能有助于将来遇到此问题的其他人!
答案 5 :(得分:0)
通过在startRecording:
之后更改添加代码来解决 function startRecording() {
// this code
var worker = new Worker('js/mp3Worker.js');
worker.postMessage({
command: 'init'
});
//
}