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.
JavaScript
x
12
12
1
let rec = new Recorder(async(chunks) => {
2
var blob = new Blob(chunks, {
3
type: 'audio/mp3'
4
});
5
var arrayBuffer = await blob.arrayBuffer();
6
const audioContext = new AudioContext()
7
await audioContext.decodeAudioData(arrayBuffer, (audioBuffer) => {
8
9
// How to I now convert the AudioBuffer into an ArrayBuffer => Blob ?
10
11
}
12
Advertisement
Answer
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.
JavaScript
1
23
23
1
// Float32Array samples
2
const [left, right] = [audioBuffer.getChannelData(0), audioBuffer.getChannelData(1)]
3
4
// interleaved
5
const interleaved = new Float32Array(left.length + right.length)
6
for (let src=0, dst=0; src < left.length; src++, dst+=2) {
7
interleaved[dst] = left[src]
8
interleaved[dst+1] = right[src]
9
}
10
11
// get WAV file bytes and audio params of your audio source
12
const wavBytes = getWavBytes(interleaved.buffer, {
13
isFloat: true, // floating point or 16-bit integer
14
numChannels: 2,
15
sampleRate: 48000,
16
})
17
const wav = new Blob([wavBytes], { type: 'audio/wav' })
18
19
// create download link and append to Dom
20
const downloadLink = document.createElement('a')
21
downloadLink.href = URL.createObjectURL(wav)
22
downloadLink.setAttribute('download', 'my-audio.wav') // name file
23
supporting functions below:
JavaScript
1
67
67
1
// Returns Uint8Array of WAV bytes
2
function getWavBytes(buffer, options) {
3
const type = options.isFloat ? Float32Array : Uint16Array
4
const numFrames = buffer.byteLength / type.BYTES_PER_ELEMENT
5
6
const headerBytes = getWavHeader(Object.assign({}, options, { numFrames }))
7
const wavBytes = new Uint8Array(headerBytes.length + buffer.byteLength);
8
9
// prepend header, then add pcmBytes
10
wavBytes.set(headerBytes, 0)
11
wavBytes.set(new Uint8Array(buffer), headerBytes.length)
12
13
return wavBytes
14
}
15
16
// adapted from https://gist.github.com/also/900023
17
// returns Uint8Array of WAV header bytes
18
function getWavHeader(options) {
19
const numFrames = options.numFrames
20
const numChannels = options.numChannels || 2
21
const sampleRate = options.sampleRate || 44100
22
const bytesPerSample = options.isFloat? 4 : 2
23
const format = options.isFloat? 3 : 1
24
25
const blockAlign = numChannels * bytesPerSample
26
const byteRate = sampleRate * blockAlign
27
const dataSize = numFrames * blockAlign
28
29
const buffer = new ArrayBuffer(44)
30
const dv = new DataView(buffer)
31
32
let p = 0
33
34
function writeString(s) {
35
for (let i = 0; i < s.length; i++) {
36
dv.setUint8(p + i, s.charCodeAt(i))
37
}
38
p += s.length
39
}
40
41
function writeUint32(d) {
42
dv.setUint32(p, d, true)
43
p += 4
44
}
45
46
function writeUint16(d) {
47
dv.setUint16(p, d, true)
48
p += 2
49
}
50
51
writeString('RIFF') // ChunkID
52
writeUint32(dataSize + 36) // ChunkSize
53
writeString('WAVE') // Format
54
writeString('fmt ') // Subchunk1ID
55
writeUint32(16) // Subchunk1Size
56
writeUint16(format) // AudioFormat https://i.stack.imgur.com/BuSmb.png
57
writeUint16(numChannels) // NumChannels
58
writeUint32(sampleRate) // SampleRate
59
writeUint32(byteRate) // ByteRate
60
writeUint16(blockAlign) // BlockAlign
61
writeUint16(bytesPerSample * 8) // BitsPerSample
62
writeString('data') // Subchunk2ID
63
writeUint32(dataSize) // Subchunk2Size
64
65
return new Uint8Array(buffer)
66
}
67