javascriptreactjsmp3lame

Lamejs exports mp3 compressed audio file with no sound


I have to export an AudioBuffer object to mp3 compressed file, in javascript, but the output has no sound.

lamejs is not outputting any audible sound: this is a duplicate, but no answer and I can't add a comment for ask if it's solved (need 50 reputation).

I use the same library, but I found a new maintained version, that fixes some bugs: https://github.com/shijinyu/lamejs

I can export the AudioBuffer in wav file. It works very well.

https://github.com/zhuker/lamejs/issues/68: here he says that encoder accepts PCM audio only. So I call getChannelData function (Web Audio API) from the AudioBuffer source.

// 2 channels
async encodeAudioBufferLame(audioBuffer) {

    var mp3encoder = new Mp3Encoder(2, 44100, 128);

    var mp3Data = [];

    const [left, right] =  [audioBuffer.getChannelData(0), audioBuffer.getChannelData(1)];

    const sampleBlockSize = 1152; //can be anything but make it a multiple of 576 to make encoders life easier

    for (var i = 0; i < left.length; i += sampleBlockSize) {
        const leftChunk = left.subarray(i, i + sampleBlockSize);
        const rightChunk = right.subarray(i, i + sampleBlockSize);

        var mp3buf = mp3encoder.encodeBuffer(leftChunk, rightChunk);

        if (mp3buf.length > 0) {
            mp3Data.push(mp3buf);
        }
    }
    var mp3buf = mp3encoder.flush();   //finish writing mp3

    if (mp3buf.length > 0) {
            mp3Data.push(mp3buf);
    }

    return mp3Data;

}

And then:

const mp3Data = await encodeAudioBufferLame(audioBuffer);
const blob = new Blob(mp3Data, {type: 'audio/mp3'});
const url = window.URL.createObjectURL(blob);


const downloadLink = document.createElement('a');
downloadLink.href = url;
downloadLink.setAttribute('download', `myAudio.mp3`);
downloadLink.click();

The output result has same original duration/length, but no sound. My sound card is good and the audio desktop is on.

I tried other libraries too, but I encountered other bugs.

All help is really appreciated!!


Solution

  • Great!! Thanks to James, who found a link with solution (see comment).

    The solution:

    async encodeAudioBufferLame(audioBuffer) {
    
            var mp3encoder = new Mp3Encoder(2, audioBuffer.sampleRate, 128); // 44100 is replaced. It needs the real AudioBuffer sample rate. In my test case 48000.
    
            var mp3Data = [];
    
            const [left, right] =  [audioBuffer.getChannelData(0), audioBuffer.getChannelData(1)];
    
            // The transformed data, this is what you will pass to lame instead
            // If you are sure to use a Float32Array you can skip this and use [left, right] const.
            const l = new Float32Array(left.length); 
            const r = new Float32Array(right.length);
    
            //Convert to required format
            for(var i=0;i<left.length;i++) {
                    l[i] = left[i]*32767.5;
                    r[i] = right[i]*32767.5;
            }
    
            const sampleBlockSize = 1152; //can be anything but make it a multiple of 576 to make encoders life easier
    
            for (var i = 0; i < l.length; i += sampleBlockSize) {
                const leftChunk = l.subarray(i, i + sampleBlockSize);
                const rightChunk = r.subarray(i, i + sampleBlockSize);
    
                var mp3buf = mp3encoder.encodeBuffer(leftChunk, rightChunk);
    
                if (mp3buf.length > 0) {
                    mp3Data.push(mp3buf);
                }
            }
            var mp3buf = mp3encoder.flush();   //finish writing mp3
    
            if (mp3buf.length > 0) {
                    mp3Data.push(mp3buf);
            }
    
            return mp3Data;
    
        }
    

    Thanks a lot James!!

    I can't vote your comment, maybe for my poor reputation.