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

问题描述 投票:2回答:1

我想对AudioBuffer进行切片,所以我首先从blob创建了ArrayBuffer,因为无法将blob直接转换为audioBuffer,但是,很多时间试图将AudioBuffer转换回ArrayBuffer但没有用...我首先将音频Blob转换为Arraybuffer,然后将其转换为AudioBuffer,但无法将其转换回ArrayBuffer。我想要ArrayBuffer,因为我无法从audioBuffer生成Blob,因此首先需要ArrayBuffer,可以将其转换为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) => {

//but now can't convert audioBuffer back to Arraybuffer

}
javascript web-audio-api arraybuffer typed-arrays
1个回答
1
投票

一个AudioBuffer包含每个解码音频通道的非交织Float32Array PCM样本。对于立体声AudioBuffer,它将包含2个通道。这些通道需要首先进行交错,然后交错的PCM必须附加一个WAV标头,以便您可以下载并作为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]
}

// create WAV file
const wavBytes = getWavBytes(interleaved, {
  numFrames: interleaved.length, // you may need to divide by 2 here
  numChannels: 2,
  sampleRate: 44100, // you may need to adjust to 48000
  isFloat: true
})
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

以下支持功能:

function getWavBytes(pcmSamples, options) {
  const headerBytes = getWavHeader(options)
  const wavBytes = new Uint8Array(headerBytes.length + pcmSamples.length);

  // prepend header, then add pcmBytes
  wavBytes.set(headerBytes, 0)
  wavBytes.set(pcmSamples, headerBytes.length)

  return wavBytes
}


// adapted from https://gist.github.com/also/900023
// Returns Uint8Array of WAV 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)
}
© www.soinside.com 2019 - 2024. All rights reserved.