You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Web Audio API与ScriptProcessorNode录制的WAV音频无法播放,需如何修正?

修正方案

1. 修复WAV文件头的格式错误

WAV文件头必须严格遵循PCM规范,常见问题包括字段不匹配、大小计算错误:

  • 确保正确设置声道数、采样率、位深度(通常为16位)的对应字段
  • 总文件大小需计算为 44(头大小) + 音频数据字节数
  • 必须写入正确的RIFF、WAVE、fmt、data标识

示例正确的WAV头生成代码(16位单声道,44100采样率):

function createWavHeader(sampleRate, numChannels, numSamples) {
  const bytesPerSample = 2;
  const byteRate = sampleRate * numChannels * bytesPerSample;
  const blockAlign = numChannels * bytesPerSample;
  const dataSize = numSamples * bytesPerSample * numChannels;
  const buffer = new ArrayBuffer(44);
  const view = new DataView(buffer);

  // RIFF标识
  view.setUint8(0, 82); view.setUint8(1, 73); view.setUint8(2, 70); view.setUint8(3, 70);
  // 文件总大小
  view.setUint32(4, 36 + dataSize, true);
  // WAVE标识
  view.setUint8(8, 87); view.setUint8(9, 65); view.setUint8(10, 86); view.setUint8(11, 69);
  // fmt子块标识
  view.setUint8(12, 102); view.setUint8(13, 109); view.setUint8(14, 116); view.setUint8(15, 32);
  // fmt子块大小(PCM固定为16)
  view.setUint32(16, 16, true);
  // 音频格式(PCM固定为1)
  view.setUint16(20, 1, true);
  // 声道数
  view.setUint16(22, numChannels, true);
  // 采样率
  view.setUint32(24, sampleRate, true);
  // 字节率
  view.setUint32(28, byteRate, true);
  // 块对齐
  view.setUint16(32, blockAlign, true);
  // 位深度
  view.setUint16(34, bytesPerSample * 8, true);
  // data子块标识
  view.setUint8(36, 100); view.setUint8(37, 97); view.setUint8(38, 116); view.setUint8(39, 97);
  // data子块大小
  view.setUint32(40, dataSize, true);

  return buffer;
}

2. 完成音频数据格式转换

ScriptProcessorNode输出的是32位浮点型数据(范围-1到1),而WAV需要16位有符号整型数据(范围-32768到32767),必须做格式转换:

function convertFloat32ToInt16(buffer) {
  const output = new Int16Array(buffer.length);
  for (let i = 0; i < buffer.length; i++) {
    const sample = Math.max(-1, Math.min(1, buffer[i])); // 限制浮点值范围
    output[i] = sample < 0 ? sample * 32768 : sample * 32767;
  }
  return output;
}

3. 匹配采样率设置

确保AudioContext的采样率与WAV头中设置的采样率一致,避免播放速度异常:

  • 创建AudioContext时可显式指定采样率,如new AudioContext({ sampleRate: 44100 })
  • 若使用浏览器默认采样率,需在生成WAV头时传入实际采样率(audioContext.sampleRate)

4. 正确采集缓冲区数据

  • 在onaudioprocess事件中,完整读取输入缓冲区的所有样本,避免丢失或重复数据
  • 多声道场景下,需按交替顺序合并左右声道样本(如双声道先存左声道样本,再存右声道样本)

5. 正确拼接头与数据

生成最终Blob时,需将WAV头的ArrayBuffer与转换后的音频数据合并为完整的ArrayBuffer:

// 假设audioData是存储所有转换后Int16样本的数组
const header = createWavHeader(audioContext.sampleRate, 1, audioData.length);
const wavBuffer = new ArrayBuffer(header.byteLength + audioData.byteLength);
// 写入头数据
new Uint8Array(wavBuffer).set(new Uint8Array(header), 0);
// 写入音频数据(每个Int16占2字节,所以偏移量为头长度/2)
new Int16Array(wavBuffer).set(audioData, header.byteLength / 2);

const blob = new Blob([wavBuffer], { type: 'audio/wav' });

内容的提问来源于stack exchange,提问作者Alok

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 19:18:17