Web Audio API 实战:浏览器端音频处理架构
技术架构
Web Audio API 架构
Web Audio API 基于音频图 (Audio Graph) 模型——音频数据流经一系列节点,每个节点执行特定处理:
AudioBufferSource → GainNode → AnalyserNode → AudioDestination
(音源) (音量) (分析/可视化) (扬声器)
与 HTML5 <audio> 元素不同,Web Audio API 提供底层、精确的音频控制能力。
核心对象
| 对象 | 作用 | 类比 |
|---|---|---|
AudioContext |
音频处理的入口和时钟 | 音频引擎 |
AudioBuffer |
内存中的音频数据 | 音频文件 |
AudioNode |
处理单元 | 效果器/调音台 |
AudioParam |
可动画的参数 | 音量旋钮 |
创建 AudioContext
const ctx = new AudioContext();
// 浏览器策略:需要用户交互后才能启动
button.addEventListener('click', () => ctx.resume());
音频剪辑实现
工具库 音频剪辑工具 的处理流程:
async function trimAudio(file: File, startSec: number, endSec: number) {
const arrayBuffer = await file.arrayBuffer();
const audioBuffer = await ctx.decodeAudioData(arrayBuffer);
const duration = endSec - startSec;
const sampleRate = audioBuffer.sampleRate;
const startSample = Math.floor(startSec * sampleRate);
const endSample = Math.floor(endSec * sampleRate);
const length = endSample - startSample;
// 创建裁剪后的 buffer
const trimmed = ctx.createBuffer(
audioBuffer.numberOfChannels, length, sampleRate
);
for (let ch = 0; ch < audioBuffer.numberOfChannels; ch++) {
const sourceData = audioBuffer.getChannelData(ch);
const destData = trimmed.getChannelData(ch);
destData.set(sourceData.subarray(startSample, endSample));
}
return trimmed;
}
波形可视化
AnalyserNode 提供实时频域/时域数据:
const analyser = ctx.createAnalyser();
analyser.fftSize = 2048;
source.connect(analyser);
analyser.connect(ctx.destination);
const dataArray = new Uint8Array(analyser.frequencyBinCount);
function drawWaveform() {
analyser.getByteTimeDomainData(dataArray);
// 在 Canvas 上绘制波形
requestAnimationFrame(drawWaveform);
}
音频格式与编码
| 格式 | 浏览器解码 | 浏览器编码 | 质量 |
|---|---|---|---|
| WAV | ✅ | ✅ (AudioContext) | 无损 |
| MP3 | ✅ | ❌ | 有损 |
| OGG | ✅ | ❌ | 有损 |
| AAC | ✅ (Safari) | ❌ | 有损 |
| WebM/Opus | ✅ | ✅ (MediaRecorder) | 有损 |
浏览器端编码能力有限。工具库音频工具主要处理 WAV 格式,视频工具的 音频提取 则借助 ffmpeg.wasm 支持 MP3/OGG 输出。
性能注意事项
- AudioContext 是单例:一个页面只需一个 AudioContext
- 大文件分块解码:超过 100MB 的音频先分块再 decodeAudioData
- 及时 disconnect:不用的节点调用
node.disconnect()释放资源 - OfflineAudioContext:离线渲染不需要实时播放,性能更好
AudioWorklet:自定义音频处理器
ScriptProcessorNode 已废弃,现代方案是 AudioWorklet——在独立线程中运行自定义音频处理代码:
// 注册 Worklet 处理器
await ctx.audioWorklet.addModule('audio-processor.js');
// audio-processor.js
class GainProcessor extends AudioWorkletProcessor {
static get parameterDescriptors() {
return [{ name: 'gain', defaultValue: 1, minValue: 0, maxValue: 2 }];
}
process(inputs, outputs, parameters) {
const input = inputs[0];
const output = outputs[0];
const gain = parameters.gain[0];
for (let channel = 0; channel < output.length; channel++) {
const inputChannel = input[channel];
const outputChannel = output[channel];
for (let i = 0; i < outputChannel.length; i++) {
outputChannel[i] = inputChannel[i] * gain;
}
}
return true; // 保持活跃
}
}
registerProcessor('gain-processor', GainProcessor);
使用 Worklet 节点:
const gainNode = new AudioWorkletNode(ctx, 'gain-processor');
const gainParam = gainNode.parameters.get('gain');
source.connect(gainNode).connect(ctx.destination);
gainParam.linearRampToValueAtTime(0.5, ctx.currentTime + 1); // 1 秒内渐弱
音频效果链
将多个 AudioNode 串联形成效果链,模拟调音台流程:
音源 → CompressorNode → EQ(BiquadFilterNode) → DistortionNode → 输出
// 构建压缩 → 均衡 → 失真 效果链
const compressor = ctx.createDynamicsCompressor();
compressor.threshold.value = -24;
compressor.ratio.value = 12;
const eq = ctx.createBiquadFilter();
eq.type = 'lowpass';
eq.frequency.value = 1000;
const distortion = ctx.createWaveShaper();
distortion.curve = makeDistortionCurve(400);
source
.connect(compressor)
.connect(eq)
.connect(distortion)
.connect(ctx.destination);
function makeDistortionCurve(amount: number): Float32Array {
const samples = 44100;
const curve = new Float32Array(samples);
for (let i = 0; i < samples; i++) {
const x = (i * 2) / samples - 1;
curve[i] = ((Math.PI + amount) * x) / (Math.PI + amount * Math.abs(x));
}
return curve;
}
OfflineAudioContext:无头离线渲染
对于不需要实时播放的处理(如格式转换、批量剪辑),OfflineAudioContext 速度显著更快:
async function renderOffline(buffer: AudioBuffer, duration: number): Promise<AudioBuffer> {
const offlineCtx = new OfflineAudioContext(
buffer.numberOfChannels,
buffer.length,
buffer.sampleRate
);
const source = offlineCtx.createBufferSource();
source.buffer = buffer;
// 添加效果节点(与实时相同)
const gain = offlineCtx.createGain();
gain.gain.value = 0.5;
source.connect(gain).connect(offlineCtx.destination);
source.start(0);
return await offlineCtx.startRendering(); // 尽快渲染完成
}
性能对比:1 分钟 44100Hz 立体声 WAV 文件 → 实时播放需 60 秒 → OfflineAudioContext 离线渲染约 2-3 秒(不受实时间束缚)。
3D 空间音效
Web Audio API 通过 PannerNode 原生支持 3D 空间定位音频:
const panner = ctx.createPanner();
panner.panningModel = 'HRTF'; // 头部传递函数(Head-Related Transfer Function)
// 设置听者位置
ctx.listener.positionX.value = 0;
ctx.listener.positionY.value = 0;
ctx.listener.positionZ.value = 0;
// 设置音源为右上后方
panner.positionX.value = 5; // 右侧 5 米
panner.positionY.value = 3; // 上方 3 米
panner.positionZ.value = -4; // 后方 4 米
// 距离衰减模型
panner.distanceModel = 'inverse';
panner.refDistance = 1;
panner.maxDistance = 100;
适用于 VR/AR 体验或 3D 游戏中的沉浸式音频效果,无需任何第三方库。
总结
Web Audio API 让浏览器具备了专业级音频处理能力。AudioContext 音频图、AudioBuffer 精确裁剪、AnalyserNode 实时分析——这些原语组合起来,可以实现从简单剪辑到复杂 DSP 的全谱系音频工具。
本站提供浏览器本地工具,免注册即可试用 →
#Web Audio API#音频处理#AudioContext#DSP#浏览器