HTML5的<audio>标签不支持混音或声道操作,必须使用Web Audio API:统一时间轴、手动处理PCM数据、显式控制信号流;多音源混音需预加载、解码为AudioBuffer并在同一AudioContext中节点连接;立体声转单声道须用ChannelSplitterNode拆分、GainNode缩放后合并;实时声道分离需调用getChannelData()提取左右声道数据;导出音频需OfflineAudioContext渲染并封装WAV头;移动端需用户手势触发audioContext.resume()。

HTML5 的 <audio> 标签本身不支持混音或声道操作——它只是播放器外壳。真正实现多音源混音与声道切换,必须用 Web Audio API,核心是统一时间轴、手动处理 PCM 数据、显式控制信号流。
多音源混音:不能靠多个 audio 标签并发
直接给多个 <audio> 元素调用 .play() 看似“同时播放”,实则存在严重问题:
- 各音频解码独立,起始时刻误差常达 ±100ms,几秒后明显不同步
- 浏览器不提供原始音频数据访问权限,无法做加法运算
- 音量叠加无缩放控制,两路满幅信号相加必触发削波(clipping),出现爆音
正确做法是:所有音轨预加载 → 统一解码为 AudioBuffer → 在同一个 AudioContext 中用节点连接混音。
- 用
fetch().then(r => r.arrayBuffer())获取二进制数据,再调context.decodeAudioData() - 缓存解码后的
buffer,后续复用,避免重复解码阻塞主线程 - 每轨使用
context.createBufferSource(),启动时传入精确时间戳:source.start(context.currentTime)
立体声转单声道:不是丢掉一个声道,而是下混
把左右声道合并为一路单声道,工程上标准做法是等权平均:mono[i] = (left[i] + right[i]) / 2。Web Audio 中不能靠 ChannelMergerNode 实现——它只拼接通道,不执行计算。
立即学习“前端免费学习笔记(深入)”;
必须组合使用:
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
-
ChannelSplitterNode:将立体声输入拆成两个单声道输出(索引 0 是左,1 是右) - 两个
GainNode:分别设gain.value = 0.5,实现除以 2 - 把两个增益节点都连到同一目标(如
context.destination):Web Audio 自动对多路输入做无缩放相加
这样就完成了安全、可预测的下混,避免幅度越界。
实时声道分离:提取左/右声道单独处理
若需监听左声道、静音右声道,或给左右分别加不同效果,需从 AudioBuffer 提取原始数据:
- 立体声音频的
buffer.numberOfChannels === 2 -
buffer.getChannelData(0)返回左声道Float32Array,buffer.getChannelData(1)返回右声道 - 可将任一通道写入新
AudioBuffer(单声道),再用AudioBufferSourceNode播放 - 也可送入不同处理链:比如左声道走延迟,右声道走均衡器,最后再合并
注意:此操作需离线完成(如剪辑导出),或用 AudioWorklet 实现实时低延迟处理——ScriptProcessorNode 已废弃。
导出与兼容性要点
处理完的音频若需保存为文件:
- 用
OfflineAudioContext渲染,采样率和时长需预先设定 - 渲染完成后,调
audioBuffer.copyFromChannel()提取 PCM 数据 - 手动封装 WAV 头部(浏览器原生不支持 MP3 编码,需引入
lamejs等库)
移动端注意:iOS Safari 和微信内置浏览器要求首次 audioContext.resume() 必须由用户手势(如点击)触发,否则静音。


















