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 输出。


性能注意事项

  1. AudioContext 是单例:一个页面只需一个 AudioContext
  2. 大文件分块解码:超过 100MB 的音频先分块再 decodeAudioData
  3. 及时 disconnect:不用的节点调用 node.disconnect() 释放资源
  4. 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#浏览器