使用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
相关产品推荐
相关产品推荐

