/**
* 把AudioBuffer转换为WAVE音频资源
* @param {AudioBuffer} abuffer - 音频数据
* @param {number} len - 音频数据的总样本数 = 每秒钟的采样次数(audioBuffer.sampleRate) * 音频数据的总时长(audioBuffer.duration)
* @returns {Blob} WAVE音频资源
*/
function bufferToWave(abuffer, len) {
let numOfChan = abuffer.numberOfChannels,
length = len * numOfChan * 2 + 44,
buffer = new ArrayBuffer(length),
view = new DataView(buffer),
channels = [],
i,
sample,
offset = 0,
pos = 0;
// write WAVE header
// "RIFF"
setUint32(0x46464952);
// file length - 8
setUint32(length - 8);
// "WAVE"
setUint32(0x45564157);
// "fmt " chunk
setUint32(0x20746d66);
// length = 16
setUint32(16);
// PCM (uncompressed)
setUint16(1);
setUint16(numOfChan);
setUint32(abuffer.sampleRate);
// avg. bytes/sec
setUint32(abuffer.sampleRate * 2 * numOfChan);
// block-align
setUint16(numOfChan * 2);
// 16-bit (hardcoded in this demo)
setUint16(16);
// "data" - chunk
setUint32(0x61746164);
// chunk length
setUint32(length - pos - 4);
// write interleaved data
for (i = 0; i < abuffer.numberOfChannels; i++) {
channels.push(abuffer.getChannelData(i));
}
while (pos < length) {
// interleave channels
for (i = 0; i < numOfChan; i++) {
// clamp
sample = Math.max(-1, Math.min(1, channels[i][offset]));
// scale to 16-bit signed int
sample = (0.5 + sample < 0 ? sample * 32768 : sample * 32767) | 0;
// write 16-bit sample
view.setInt16(pos, sample, true);
pos += 2;
}
// next source sample
offset++;
}
// create Blob
return new Blob([buffer], { type: 'audio/wav' });
function setUint16(data) {
view.setUint16(pos, data, true);
pos += 2;
}
function setUint32(data) {
view.setUint32(pos, data, true);
pos += 4;
}
}