2014-02-13 59 views
5

我正在使用Recorder.js錄製來自麥克風的音頻。該庫可以將PCM數據編碼爲WAV,我可以使用<audio>成功播放該數據。但是,由此產生的WAV數據過於龐大(5分鐘記錄約爲38MB)。我嘗試使用從Speech-to-Server可用的libmp3lame.js。如何在PCM中將PCM數據編碼爲MP3?

recorderWorker.js,我導入拉梅腳本:

importScripts("libmp3lame.js"); 

然後,我改變了exportWAV()功能編碼PCM緩衝區到MP3,而不是WAV。在bufferL

Uncaught RangeError: Invalid array buffer length 

這裏PCM數據和bufferR是Float32Array:

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方法是拋出這個錯誤。我無法找到Lame.encode_buffer_ieee_float預期輸入的內容。

的違規行Lame.encode_buffer_ieee_float所引發錯誤是:

var arraybuf = new ArrayBuffer(nread); 

我把一個斷點,並檢查了nread值。它是-1。

所以,我的問題是如何在這種情況下使用Lame MP3 JavaScript庫?謝謝。

回答

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,所以我不認爲你發送錯誤類型的參數。

+0

NREAD是-1。對不起,我應該在帖子中說過。現在編輯它。 – RajV

+2

我通過了蹩腳的C源代碼,並將問題追蹤到發送到lame_encode_buffer_ieee_float的緩衝區大小非常小。我通過將BUFSIZE設置爲channel_l.length * 4來解決問題。現在,編碼適用於短音頻剪輯。但是,對於較大的剪輯再次失敗。 – RajV

+0

你是怎麼結束了這個,因爲我正在考慮錄製MP3以及。我認爲你的更大剪輯失敗可能是由於libmp3lame.js需要使用emscripten以更大的緩衝區大小進行編譯。 –

1

我也有同樣的錯誤

但我解決了做這一步: 支持MP3網絡工作者mp3worker.init()執行初始化時,您要開始一個新的記錄。 這是由於mp3codec值是零,因爲你執行sendMessage(command: 'end'); 該解決方案可以解決你的問題。)

0

我建立凱文·恩尼斯的回答,解決從wav文件持續時間較長而產生的問題,我們必須做而不是等待所有數據準備好,只要您從onaudioprocess方法收到數據就對數據進行編碼,結果是編碼變得閃電般快。


我已經在這裏結合Recorderjsspeech-to-server

代碼放在github

我的修改,

在recorder.js

var bufferLen = 16384 // line no. 7

在libmp3lame.js

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 ]); // line no. 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=[]; 
} 

(PS:如果你要, 您可以將recordWorker.js和encoder.js合併到一個文件中)

+0

這絕對是理想的方法。但是,由於問題的複雜性,我可以要求您在GitHub中發佈一個完整的示例嗎?需要2個JS文件和一個HTML。謝謝! – RajV

+0

這裏是github鏈接, https://github.com/Mido22/mp3Recorder 也用它更新了答案。 – mido

0

由於上述答案已清楚表明,庫中的lame_encode_buffer_ieee_float模塊調用返回的nread值爲-1,表示編碼失敗。

它看起來像BUFSIZE變量被傳遞給兩個分配outbuf緩衝區,然後傳遞給編碼功能被設置爲8192個樣本,或緩衝區大小爲2048個樣本。這對於即將使用的編碼來說可以做得很好,但是如果你通過一個比2048個樣本更大的緩衝區,這將會糟糕透頂。

我解決這個問題的辦法是由outbuf中的malloc調用之前加入這一行的代碼:

​​

結果BUFSIZE將被傳遞給lame_encode_buffer_ieee_float以及和NREAD將持有數據的數量字節在最終的編碼緩衝區中。

希望這可能會有助於未來解決此問題的其他人!

5

有一個用純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 
+0

感謝這個圖書館,它真的有效! – dimaninc

0

解決通過改變的startRecording之後加入的代碼:

function startRecording() { 

    // this code 
    var worker = new Worker('js/mp3Worker.js'); 
    worker.postMessage({ 
     command: 'init' 
    }); 
    //  

    }