html5的标签不支持混音或声道操作,必须使用web audio api:统一时间轴、手动处理pcm数据、显式控制信号流;多音源混音需预加载、解码为audiobuffer并在同一audiocontext中节点连接;立体声转单声道须用channelsplitternode拆分、gainnode缩放后合并;实时声道分离需调用getchanneldata()提取左右声道数据;导出音频需offlineaudiocontext渲染并封装wav头;移动端需用户手势触发audiocontext.resume()。

HTML5 的 <audio></audio> 标签本身不支持混音或声道操作——它只是播放器外壳。真正实现多音源混音与声道切换,必须用 Web Audio API,核心是统一时间轴、手动处理 PCM 数据、显式控制信号流。
多音源混音:不能靠多个 audio 标签并发
直接给多个 <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 实现——它只拼接通道,不执行计算。
必须组合使用:
-
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() 必须由用户手势(如点击)触发,否则静音。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











