繁体   English   中英

将 AudioBuffer 转换为 ArrayBuffer / Blob 以供 WAV 下载

[英]Convert AudioBuffer to ArrayBuffer / Blob for WAV Download

我想将AudioBuffer转换为Blob ,以便可以从中创建 ObjectURL,然后下载音频文件。

let rec = new Recorder(async(chunks) => {
 var blob = new Blob(chunks, {
        type: 'audio/mp3'
      });
var arrayBuffer = await blob.arrayBuffer();
 const audioContext = new AudioContext()
 await audioContext.decodeAudioData(arrayBuffer, (audioBuffer) => {

// How to I now convert the AudioBuffer into an ArrayBuffer => Blob ?

}

AudioBuffer 包含每个解码音频通道的非交错Float32Array PCM 样本。 对于立体声 AudioBuffer,它将包含 2 个通道。 这些通道首先需要交错,然后交错的 PCM 必须附加一个 WAV header,以便您可以下载并作为 WAV 播放。

// Float32Array samples
const [left, right] =  [audioBuffer.getChannelData(0), audioBuffer.getChannelData(1)]

// interleaved
const interleaved = new Float32Array(left.length + right.length)
for (let src=0, dst=0; src < left.length; src++, dst+=2) {
  interleaved[dst] =   left[src]
  interleaved[dst+1] = right[src]
}

// get WAV file bytes and audio params of your audio source
const wavBytes = getWavBytes(interleaved.buffer, {
  isFloat: true,       // floating point or 16-bit integer
  numChannels: 2,
  sampleRate: 48000,
})
const wav = new Blob([wavBytes], { type: 'audio/wav' })

// create download link and append to Dom
const downloadLink = document.createElement('a')
downloadLink.href = URL.createObjectURL(wav)
downloadLink.setAttribute('download', 'my-audio.wav') // name file

支持功能如下:

// Returns Uint8Array of WAV bytes
function getWavBytes(buffer, options) {
  const type = options.isFloat ? Float32Array : Uint16Array
  const numFrames = buffer.byteLength / type.BYTES_PER_ELEMENT

  const headerBytes = getWavHeader(Object.assign({}, options, { numFrames }))
  const wavBytes = new Uint8Array(headerBytes.length + buffer.byteLength);

  // prepend header, then add pcmBytes
  wavBytes.set(headerBytes, 0)
  wavBytes.set(new Uint8Array(buffer), headerBytes.length)

  return wavBytes
}

// adapted from https://gist.github.com/also/900023
// returns Uint8Array of WAV header bytes
function getWavHeader(options) {
  const numFrames =      options.numFrames
  const numChannels =    options.numChannels || 2
  const sampleRate =     options.sampleRate || 44100
  const bytesPerSample = options.isFloat? 4 : 2
  const format =         options.isFloat? 3 : 1

  const blockAlign = numChannels * bytesPerSample
  const byteRate = sampleRate * blockAlign
  const dataSize = numFrames * blockAlign

  const buffer = new ArrayBuffer(44)
  const dv = new DataView(buffer)

  let p = 0

  function writeString(s) {
    for (let i = 0; i < s.length; i++) {
      dv.setUint8(p + i, s.charCodeAt(i))
    }
    p += s.length
  }

  function writeUint32(d) {
    dv.setUint32(p, d, true)
    p += 4
  }

  function writeUint16(d) {
    dv.setUint16(p, d, true)
    p += 2
  }

  writeString('RIFF')              // ChunkID
  writeUint32(dataSize + 36)       // ChunkSize
  writeString('WAVE')              // Format
  writeString('fmt ')              // Subchunk1ID
  writeUint32(16)                  // Subchunk1Size
  writeUint16(format)              // AudioFormat
  writeUint16(numChannels)         // NumChannels
  writeUint32(sampleRate)          // SampleRate
  writeUint32(byteRate)            // ByteRate
  writeUint16(blockAlign)          // BlockAlign
  writeUint16(bytesPerSample * 8)  // BitsPerSample
  writeString('data')              // Subchunk2ID
  writeUint32(dataSize)            // Subchunk2Size

  return new Uint8Array(buffer)
}

对上面 AnthumChri 的回复进行了小幅编辑。 以下 function 添加了对立体声输入的检查。

  convertAudioBufferToBlob(audioBuffer) {

    var channelData = [],
      totalLength = 0,
      channelLength = 0;

    for (var i = 0; i < audioBuffer.numberOfChannels; i++) {
      channelData.push(audioBuffer.getChannelData(i));
      totalLength += channelData[i].length;
      if (i == 0) channelLength = channelData[i].length;
    }

    // interleaved
    const interleaved = new Float32Array(totalLength);

    for (
      let src = 0, dst = 0;
      src < channelLength;
      src++, dst += audioBuffer.numberOfChannels
    ) {
      for (var j = 0; j < audioBuffer.numberOfChannels; j++) {
        interleaved[dst + j] = channelData[j][src];
      }
      //interleaved[dst] = left[src];
      //interleaved[dst + 1] = right[src];
    }

    // get WAV file bytes and audio params of your audio source
    const wavBytes = this.getWavBytes(interleaved.buffer, {
      isFloat: true, // floating point or 16-bit integer
      numChannels: audioBuffer.numberOfChannels,
      sampleRate: 48000,
    });
    const wav = new Blob([wavBytes], { type: "audio/wav" });
    return wav;
  },

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM