2. VAD基础概念
做 VAD 前端部署,第一步就是选对工具。说白了,你得想清楚:音频从哪来?怎么处理?跑在哪个线程?
我刚开始做这个方向时,也踩过不少坑。今天就把这些经验拆开揉碎了讲给你听。
2.1 音频采集:Web Audio API vs WebRTC
这两个都能拿到麦克风音频流。但它们的定位完全不同。
| 对比维度 | Web Audio API | WebRTC (getUserMedia + MediaStream) |
|---|---|---|
| 核心用途 | 音频处理、合成、分析 | 实时通信、媒体流传输 |
| 获取音频方式 | 通过 AudioContext.createMediaStreamSource() |
通过 getUserMedia() 获取 MediaStream |
| 采样率控制 | 由 AudioContext 的 sampleRate 决定 |
由浏览器和硬件决定,无法直接控制 |
| 延迟特性 | 低延迟,适合实时处理 | 中等延迟,为网络传输优化 |
| VAD 适用性 | ⭐⭐⭐⭐⭐ 首选 | ⭐⭐⭐ 需要额外转换 |
我的建议:VAD 场景下,直接用 Web Audio API。它原生支持音频图(Audio Graph),方便你把麦克风节点、处理节点、分析节点串起来。WebRTC 的 MediaStream 虽然也能用,但多一层转换,没必要。
举个例子。你拿到 MediaStream 后,想喂给 VAD 模型,得先转成 AudioBuffer 或 Float32Array。Web Audio API 直接就能输出 AudioBuffer,省掉中间步骤。
// 推荐方式:Web Audio API
const audioContext = new AudioContext({ sampleRate: 16000 });
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const source = audioContext.createMediaStreamSource(stream);
// source 直接接入 AudioWorklet 或 ScriptProcessor
小技巧:创建 AudioContext 时,显式指定 sampleRate: 16000。VAD 模型通常用 16kHz 采样率,省去后续重采样步骤。我在项目中吃过这个亏——默认 48kHz,还得手动降采样,多了一堆代码。
2.2 音频处理:AudioWorklet vs ScriptProcessor
拿到音频流后,你需要一个「处理节点」来跑 VAD 算法。这里有两个选择。
2.2.1 ScriptProcessor(已废弃,但还能用)
ScriptProcessor 是 Web Audio API 早期的处理方案。它运行在主线程,通过 onaudioprocess 事件回调处理音频。
const processor = audioContext.createScriptProcessor(1024, 1, 1);
processor.onaudioprocess = (event) => {
const inputData = event.inputBuffer.getChannelData(0);
// 在这里跑 VAD 算法
const isSpeech = vadModel.process(inputData);
};
注意:ScriptProcessor 在主线程运行。如果你的 VAD 算法计算量大,会卡 UI。我曾在项目里用 ScriptProcessor 跑一个轻量 VAD,结果页面滚动都掉帧。后来果断换了 AudioWorklet。
2.2.2 AudioWorklet(推荐)
AudioWorklet 是 Web Audio API 的现代方案。它在独立线程(AudioWorkletGlobalScope)中运行,不阻塞主线程。
// 1. 注册 Worklet
await audioContext.audioWorklet.addModule('vad-processor.js');
// 2. 创建节点
const vadNode = new AudioWorkletNode(audioContext, 'vad-processor');
// 3. 连接
source.connect(vadNode);
vadNode.connect(audioContext.destination);
// 4. 接收结果
vadNode.port.onmessage = (event) => {
const { isSpeech, confidence } = event.data;
updateUI(isSpeech, confidence);
};
在 vad-processor.js 中:
class VADProcessor extends AudioWorkletProcessor {
process(inputs, outputs, parameters) {
const input = inputs[0];
if (input.length > 0) {
const channelData = input[0];
const result = runVAD(channelData);
this.port.postMessage(result);
}
return true; // 保持处理器活跃
}
}
registerProcessor('vad-processor', VADProcessor);
| 对比维度 | ScriptProcessor | AudioWorklet |
|---|---|---|
| 运行线程 | 主线程 | 独立音频线程 |
| 延迟 | 较高(至少 2 个 buffer 长度) | 极低(1 个 buffer 长度) |
| 稳定性 | 可能被 UI 任务阻塞 | 稳定,不受主线程影响 |
| 浏览器支持 | 所有浏览器(已标记废弃) | Chrome 66+, Firefox 76+, Safari 14.1+ |
| VAD 推荐度 | ❌ 不推荐 | ✅ 强烈推荐 |
核心结论:新项目直接用 AudioWorklet。ScriptProcessor 已经是历史遗留产物了。如果你需要兼容非常老的浏览器(比如 Chrome 66 之前),才考虑 ScriptProcessor 作为 fallback。
2.3 浏览器兼容性分析
做前端部署,最头疼的就是兼容性。我整理了一份主流浏览器的支持情况。
| 特性 | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
| Web Audio API | ✅ 全支持 | ✅ 全支持 | ✅ 全支持 | ✅ 全支持 |
| getUserMedia | ✅ 全支持 | ✅ 全支持 | ✅ 11+ | ✅ 全支持 |
| AudioWorklet | ✅ 66+ | ✅ 76+ | ✅ 14.1+ | ✅ 79+ |
| ScriptProcessor | ✅ 全支持(废弃) | ✅ 全支持(废弃) | ✅ 全支持(废弃) | ✅ 全支持(废弃) |
| AudioContext.sampleRate | ✅ 全支持 | ✅ 全支持 | ⚠️ 部分限制 | ✅ 全支持 |
避坑指南:我曾经在 Safari 上栽过跟头。Safari 14.1 以下不支持 AudioWorklet,而且 AudioContext 的 sampleRate 不能随意指定——它只支持设备原生采样率。如果你需要 16kHz,得手动重采样。建议在代码里加一个特性检测:
function isAudioWorkletSupported() {
return 'audioWorklet' in AudioContext.prototype;
}
function getSupportedSampleRate() {
const ctx = new AudioContext();
return ctx.sampleRate;
}
2.4 整体技术选型流程图
下面这张图,帮你理清整个选型逻辑。
2.5 我的最终推荐方案
说了这么多,直接给结论吧。
- 音频采集:用
getUserMedia获取MediaStream,然后通过AudioContext.createMediaStreamSource()接入 Web Audio API 的音频图。 - 音频处理:用
AudioWorklet跑 VAD 算法。如果目标浏览器不支持,降级到ScriptProcessor。 - 采样率:创建
AudioContext时指定sampleRate: 16000。如果浏览器不支持(比如 Safari),手动重采样。 - 兼容性检测:运行时检测
audioWorklet和sampleRate支持情况,动态选择方案。
个人经验:我做过一个跨平台 VAD SDK,覆盖 Chrome、Firefox、Safari、Edge 以及内置浏览器。最终方案是:AudioWorklet 为主,ScriptProcessor 为 fallback,Safari 单独处理采样率。这套方案跑了半年,线上崩溃率低于 0.1%。