JavaScript AudioBuffer转换成WAV格式

1. 使用npm已有包

安装包:npm install audiobuffer-to-wav --save
用例:

var toWav = require('audiobuffer-to-wav')
var xhr = require('xhr')
var context = new AudioContext()

// request the MP3 as binary arraybuffer
xhr({
  uri: 'audio/track.mp3',
  responseType: 'arraybuffer'
}, function (err, body, resp) {
  if (err) throw err
  // decode the MP3 arraybuffer into an AudioBuffer
  audioContext.decodeAudioData(resp, function (buffer) {
    // encode AudioBuffer to WAV ArrayBuffer
    var wav = toWav(buffer)//实际上是在audio前加wav的头
    
    // do something with the WAV ArrayBuffer ...
  })
})

引用:
audiobuffer-to-wav

2. 自己实现

export function audioBufferToWav(buffer: AudioBuffer, opt?: any) {
  opt = opt || {};
  const numChannels = buffer.numberOfChannels;
  const sampleRate = opt.sampleRate || buffer.sampleRate;
  const format = opt.float32 ? 3 : 1;
  const bitDepth = format === 3 ? 32 : 16;
  let result;
  if (numChannels === 2) {
    result = interleave(buffer.getChannelData(0), buffer.getChannelData(1));
  } else {
    result = buffer.getChannelData(0);
  }

  return encodeWAV(result, format, sampleRate, numChannels, bitDepth);
}

function encodeWAV(samples: Float32Array, format: number, sampleRate: number, numChannels: number, bitDepth: number) {
  const bytesPerSample = bitDepth / 8;
  const blockAlign = numChannels * bytesPerSample;

  let buffer = new ArrayBuffer(44 + samples.length * bytesPerSample);
  let view = new DataView(buffer);

  writeString(view, 0, "RIFF");
  view.setUint32(4, 36 + samples.length * bytesPerSample, true);
  writeString(view, 8, "WAVE");
  writeString(view, 12, "fmt ");
  view.setUint32(16, 16, true);
  view.setUint16(20, format, true);
  view.setUint16(22, numChannels, true);
  view.setUint32(24, sampleRate, true);
  view.setUint32(28, sampleRate * blockAlign, true);
  view.setUint16(32, blockAlign, true);
  view.setUint16(34, bitDepth, true);
  writeString(view, 36, "data");
  view.setUint32(40, samples.length * bytesPerSample, true);
  if (format === 1) {
    floatTo16BitPCM(view, 44, samples);
  } else {
    writeFloat32(view, 44, samples);
  }

  return buffer;
}

function interleave(inputL: Float32Array, inputR: Float32Array) {
  let length = inputL.length + inputR.length;
  let result = new Float32Array(length);

  let index = 0;
  let inputIndex = 0;

  while (index < length) {
    result[index++] = inputL[inputIndex];
    result[index++] = inputR[inputIndex];
    inputIndex++;
  }
  return result;
}

function writeFloat32(output: DataView, offset: number, input: Float32Array) {
  for (let i = 0; i < input.length; i++, offset += 4) {
    output.setFloat32(offset, input[i], true);
  }
}

function floatTo16BitPCM(output: DataView, offset: number, input: Float32Array) {
  for (let i = 0; i < input.length; i++, offset += 2) {
    let s = Math.max(-1, Math.min(1, input[i]));
    output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);
  }
}

function writeString(view: DataView, offset: number, string: string) {
  for (let i = 0; i < string.length; i++) {
    view.setUint8(offset + i, string.charCodeAt(i));
  }
}

但直接用这种方法重采样效果不佳。

原文地址:https://www.cnblogs.com/xym4869/p/13532015.html