Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert AudioBuffer to ArrayBuffer / Blob for WAV Download

I'd like to convert an AudioBuffer to a Blob so that I can create an ObjectURL from it and then download the audio file.

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 ?

}
like image 349
talha khalid Avatar asked Jun 03 '20 12:06

talha khalid


People also ask

How do I create a WAV Blob from an arraybuffer?

After writing the WAVE header, we write the buffer to the ArrayBuffer sample by sample. The finished ArrayBuffer is then passed into the Blob constructor with the WAV audio file type. The function returns this new Blob. Notice the line with the comment that reads ‘write 16-bit sample’. This line writes an audio sample to the new ArrayBuffer.

How do I get an audiobuffer from an audio file?

If you’re not sure how to get an AudioBuffer from an audio file, check out my previous blog post about how to process an uploaded file with the Web Audio API. If you already have an AudioBuffer, you can skip to rendering it as a WAV file. The AudioBuffer could come from audio recorded clientside, uploaded clientside, or from the server.

What is audiobuffer to arraybuffer conversion?

Understanding AudioBuffer to ArrayBuffer conversion An AudioBuffer contains non-interleaved Float32Array PCM samples for each decoded audio channel. For a stereo AudioBuffer, it will contain 2 channels.

What is an audiobuffer in Java?

The AudioBuffer interface represents a short audio asset residing in memory, created from an audio file using the AudioContext.decodeAudioData () method, or from raw data using AudioContext.createBuffer (). Once put into an AudioBuffer, the audio can then be played by being passed into an AudioBufferSourceNode.


1 Answers

An AudioBuffer contains non-interleaved Float32Array PCM samples for each decoded audio channel. For a stereo AudioBuffer, it will contain 2 channels. Those channels need to be interleaved first, and then the interleaved PCM must have a WAV header appended to it so you can download and play as a 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

supporting functions below:

// 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 https://i.stack.imgur.com/BuSmb.png
  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)
}
like image 89
AnthumChris Avatar answered Oct 18 '22 23:10

AnthumChris