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 ?
}
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
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)
}
a minor edit to AnthumChri's reply above. The following function adds a check for stereo input.
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;
},
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.