簡體   English   中英

如何在 Javascript 中將音頻緩沖區轉換為 MP3?

[英]How to convert Audio buffer to MP3 in Javascript?

我在 ReactJS 中使用 MediaRecorder 來錄制來自麥克風的音頻並存儲到 MIME 類型為“audio/mp3”的 blob 中。 我想將此 blob 轉換為 MP3 並將其上傳到 S3 存儲桶中。

我可以通過使用 audioContext、decodeAudioData 和 audioBufferToWav 函數將其轉換為 WAV,但是 WAV 的大小非常大。 由於 MP3 文件的大小相對非常小,所以我希望它將我的 blob 轉換為 MP3。 有什么幫助嗎?

我用於錄制和轉換為 wav 的代碼:

getUserMedia({ audio: true })
      .then(stream => {
        this.stream = stream;
        const mimeType = 'audio/mp3';
        this.mediaRecorder = new MediaRecorder(stream);
        this.mediaRecorder.start();
        const audioChunks = [];
        this.mediaRecorder.addEventListener('dataavailable', event => {
          audioChunks.push(event.data);
        });

        this.mediaRecorder.addEventListener('stop', () => {
          const audioBlob = new Blob(audioChunks, {
            type: mimeType});

        });
      }).catch(error => { });

將上面創建的 blob 轉換為 WAV:

const reader = new window.FileReader();
    reader.readAsDataURL(audioBlob);
    reader.onloadend = () => {
      let base64 = reader.result + '';
      base64 = base64.split(',')[1];
      const ab = new ArrayBuffer(base64.length);
      const buff = new Buffer.from(base64, 'base64');
      const view = new Uint8Array(ab);
      for (let i = 0; i < buff.length; ++i) {
        view[i] = buff[i];
      }
      const context = new AudioContext();
      context.decodeAudioData(ab, (buffer) => {
      const wavFile = toWav(buffer);
}

我將wavFile存儲到 S3 中。 我想要MP3,請幫忙?

我沒有使用 ReactJS MediaRecorder,也沒有完全按照您的具體示例中發生的情況進行操作,但我有一個將 AudioBuffer 轉換為 mp3 的解決方案。

第一個 function 基於russellgood.com/how-to-convert-audiobuffer-to-audio-file 第二個是基於lamejs

首先,將AudioBuffer轉換為wave blob

function audioBufferToWav(aBuffer) {
    let numOfChan = aBuffer.numberOfChannels,
        btwLength = aBuffer.length * numOfChan * 2 + 44,
        btwArrBuff = new ArrayBuffer(btwLength),
        btwView = new DataView(btwArrBuff),
        btwChnls = [],
        btwIndex,
        btwSample,
        btwOffset = 0,
        btwPos = 0;
    setUint32(0x46464952); // "RIFF"
    setUint32(btwLength - 8); // file length - 8
    setUint32(0x45564157); // "WAVE"
    setUint32(0x20746d66); // "fmt " chunk
    setUint32(16); // length = 16
    setUint16(1); // PCM (uncompressed)
    setUint16(numOfChan);
    setUint32(aBuffer.sampleRate);
    setUint32(aBuffer.sampleRate * 2 * numOfChan); // avg. bytes/sec
    setUint16(numOfChan * 2); // block-align
    setUint16(16); // 16-bit
    setUint32(0x61746164); // "data" - chunk
    setUint32(btwLength - btwPos - 4); // chunk length

    for (btwIndex = 0; btwIndex < aBuffer.numberOfChannels; btwIndex++)
        btwChnls.push(aBuffer.getChannelData(btwIndex));

    while (btwPos < btwLength) {
        for (btwIndex = 0; btwIndex < numOfChan; btwIndex++) {
            // interleave btwChnls
            btwSample = Math.max(-1, Math.min(1, btwChnls[btwIndex][btwOffset])); // clamp
            btwSample = (0.5 + btwSample < 0 ? btwSample * 32768 : btwSample * 32767) | 0; // scale to 16-bit signed int
            btwView.setInt16(btwPos, btwSample, true); // write 16-bit sample
            btwPos += 2;
        }
        btwOffset++; // next source sample
    }

    let wavHdr = lamejs.WavHeader.readHeader(new DataView(btwArrBuff));
    let wavSamples = new Int16Array(btwArrBuff, wavHdr.dataOffset, wavHdr.dataLen / 2);

    wavToMp3(wavHdr.channels, wavHdr.sampleRate, wavSamples);

    function setUint16(data) {
        btwView.setUint16(btwPos, data, true);
        btwPos += 2;
    }

    function setUint32(data) {
        btwView.setUint32(btwPos, data, true);
        btwPos += 4;
    }
}

二、將wave轉換成mp3

function wavToMp3(channels, sampleRate, samples) {
    var buffer = [];
    var mp3enc = new lamejs.Mp3Encoder(channels, sampleRate, 128);
    var remaining = samples.length;
    var samplesPerFrame = 1152;
    for (var i = 0; remaining >= samplesPerFrame; i += samplesPerFrame) {
        var mono = samples.subarray(i, i + samplesPerFrame);
        var mp3buf = mp3enc.encodeBuffer(mono);
        if (mp3buf.length > 0) {
            buffer.push(new Int8Array(mp3buf));
        }
        remaining -= samplesPerFrame;
    }
    var d = mp3enc.flush();
    if(d.length > 0){
        buffer.push(new Int8Array(d));
    }

    var mp3Blob = new Blob(buffer, {type: 'audio/mp3'});
    var bUrl = window.URL.createObjectURL(mp3Blob);

    // send the download link to the console
    console.log('mp3 download:', bUrl);

}

希望這可以幫助!

這就是我根據很好的 CuriousChad 答案結合MediaRecorder和格式轉換器的方式。 只需將 MP3 編碼器視為立體聲即可工作。

首先,將 AudioFormat 設置為 'WEBM' (Chrome)、'MP3' 或 'WAV':


                    this.mediaRecorder.onstop = e => {
                    if (AudioFormat === 'MP3' || AudioFormat === 'WAV')
                    {
                        var data = this.chunks[0];
                        var blob = new Blob(this.chunks, { type : 'video/webm' });
    
                        const audioContext = new AudioContext();
                        const fileReader = new FileReader();
    
                        // Set up file reader on loaded end event
                        fileReader.onloadend = () => {
                              const arrayBuffer = fileReader.result;// as ArrayBuffer;
    
                              // Convert array buffer into audio buffer
                              audioContext.decodeAudioData(arrayBuffer, (audioBuffer) => {
    
                                // Do something with audioBuffer
                                console.log(audioBuffer)
                                var MP3Blob = this.audioBufferToWav(audioBuffer);
                                onStop(MP3Blob,audioBuffer);
   
                             })    
                        }
    
                        //Load blob
                        fileReader.readAsArrayBuffer(blob)
                    }
                    else {
                          var data = this.chunks[0];
                          var blob = new Blob(this.chunks, { type : 'audio/mpeg' });
                          onStop(blob,data)
                    }
                    this.chunks = [];
                    };

二、將Buffer轉Wav:

     audioBufferToWav(aBuffer) {
        let numOfChan = aBuffer.numberOfChannels,
            btwLength = aBuffer.length * numOfChan * 2 + 44,
            btwArrBuff = new ArrayBuffer(btwLength),
            btwView = new DataView(btwArrBuff),
            btwChnls = [],
            btwIndex,
            btwSample,
            btwOffset = 0,
            btwPos = 0;
        setUint32(0x46464952); // "RIFF"
        setUint32(btwLength - 8); // file length - 8
        setUint32(0x45564157); // "WAVE"
        setUint32(0x20746d66); // "fmt " chunk
        setUint32(16); // length = 16
        setUint16(1); // PCM (uncompressed)
        setUint16(numOfChan);
        setUint32(aBuffer.sampleRate);
        setUint32(aBuffer.sampleRate * 2 * numOfChan); // avg. bytes/sec
        setUint16(numOfChan * 2); // block-align
        setUint16(16); // 16-bit
        setUint32(0x61746164); // "data" - chunk
        setUint32(btwLength - btwPos - 4); // chunk length

        for (btwIndex = 0; btwIndex < aBuffer.numberOfChannels; btwIndex++)
            btwChnls.push(aBuffer.getChannelData(btwIndex));

        while (btwPos < btwLength) {
            for (btwIndex = 0; btwIndex < numOfChan; btwIndex++) {
                // interleave btwChnls
                btwSample = Math.max(-1, Math.min(1, btwChnls[btwIndex][btwOffset])); // clamp
                btwSample = (0.5 + btwSample < 0 ? btwSample * 32768 : btwSample * 32767) | 0; // scale to 16-bit signed int
                btwView.setInt16(btwPos, btwSample, true); // write 16-bit sample
                btwPos += 2;
            }
            btwOffset++; // next source sample
        }

        let wavHdr = lamejs.WavHeader.readHeader(new DataView(btwArrBuff));

        //Stereo
        let data = new Int16Array(btwArrBuff, wavHdr.dataOffset, wavHdr.dataLen / 2);
        let leftData = [];
        let rightData = [];
        for (let i = 0; i < data.length; i += 2) {
                     leftData.push(data[i]);
                     rightData.push(data[i + 1]);
        }
        var left = new Int16Array(leftData);
        var right = new Int16Array(rightData);


        if (AudioFormat === 'MP3')
        {
            //STEREO
            if (wavHdr.channels===2)
                return this.wavToMp3Stereo(wavHdr.channels, wavHdr.sampleRate,  left,right);
            //MONO
            else if (wavHdr.channels===1)
                return this.wavToMp3Mono(wavHdr.channels, wavHdr.sampleRate,  data);
        }
        else
           return new Blob([btwArrBuff], {type: "audio/wav"});

        function setUint16(data) {
            btwView.setUint16(btwPos, data, true);
            btwPos += 2;
        }

        function setUint32(data) {
            btwView.setUint32(btwPos, data, true);
            btwPos += 4;
        }
    }

三、WAV轉MP3:

我不得不切換到立體聲,因為我有 2 個 MP3Encoder 左右聲道。

wavToMp3(channels, sampleRate, left, right = null) {
 var buffer = [];
 var mp3enc = new lamejs.Mp3Encoder(channels, sampleRate, 128);
 var remaining = left.length;
 var samplesPerFrame = 1152;


 for (var i = 0; remaining >= samplesPerFrame; i += samplesPerFrame) {

     if (!right)
     {
         var mono = left.subarray(i, i + samplesPerFrame);
         var mp3buf = mp3enc.encodeBuffer(mono);
     }
     else {
         var leftChunk = left.subarray(i, i + samplesPerFrame);
         var rightChunk = right.subarray(i, i + samplesPerFrame);
         var mp3buf = mp3enc.encodeBuffer(leftChunk,rightChunk);
     }
         if (mp3buf.length > 0) {
                 buffer.push(mp3buf);//new Int8Array(mp3buf));
         }
         remaining -= samplesPerFrame;
 }
 var d = mp3enc.flush();
 if(d.length > 0){
         buffer.push(new Int8Array(d));
 }

 var mp3Blob = new Blob(buffer, {type: 'audio/mp3'});
 //var bUrl = window.URL.createObjectURL(mp3Blob);

 // send the download link to the console
 //console.log('mp3 download:', bUrl);
 return mp3Blob;

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM