11、语音识别集成:Web Speech API基础、Deepgram/Whisper实时转写、流式ASR与WebRTC音频桥接
语音识别,说白了就是把麦克风里收进来的声音,变成屏幕上能看到的文字。这个能力放在WebRTC里,能玩出很多花样——实时字幕、会议纪要、语音指令,甚至AI助手的对话入口。
我最早接触这块是在做一个跨国会议系统的时候。客户要求所有发言必须实时转成文字,而且延迟不能超过500毫秒。那时候我才发现,浏览器自带的语音识别和第三方服务之间,差距还真不小。今天咱们就把这条路走一遍。
核心脉络:从浏览器原生API起步,再到专业级ASR引擎,最后把音频流从WebRTC管道里“接”出来喂给识别器。这是三条递进的路线。
11.1 浏览器自带的语音识别:Web Speech API
Web Speech API 是W3C的标准接口。Chrome和Safari支持得不错,Firefox嘛……嗯,你懂的。它分两块:语音合成(SpeechSynthesis)和语音识别(SpeechRecognition)。我们今天只聊后者。
它的用法其实挺简单。创建一个识别器实例,配一下语言,然后监听结果事件就行。
// 创建识别器
const recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
// 基本配置
recognition.lang = 'zh-CN';
recognition.continuous = true; // 持续识别
recognition.interimResults = true; // 返回中间结果
// 监听结果
recognition.onresult = (event) => {
let transcript = '';
for (let i = event.resultIndex; i < event.results.length; i++) {
transcript += event.results[i][0].transcript;
}
console.log('识别结果:', transcript);
};
// 启动
recognition.start();
这里有个坑。我曾经在项目里直接用这个API做实时字幕,结果发现它每隔几十秒就会自动断开。后来查文档才知道,Chrome为了省电,非活动页面会暂停识别。解决办法是监听 onend 事件,断开了就重新 start()。
注意:Web Speech API 在HTTPS环境下才能用。本地开发可以用 localhost,但生产环境必须配证书。另外,它依赖Google的云端服务(Chrome),网络不通的时候会直接报错。
这个API的优点是零配置、开箱即用。缺点是精度一般,而且你没法控制音频数据——它直接从麦克风取流,你拿不到原始PCM。这就引出了下一个话题。
11.2 专业级ASR引擎:Deepgram与Whisper
当浏览器自带的识别满足不了你的时候,就该上专业引擎了。我个人的经验是:通用场景用Deepgram,需要离线或定制模型用Whisper。
| 特性 | Web Speech API | Deepgram | Whisper (本地部署) |
|---|---|---|---|
| 精度 | 中等 | 高 | 极高 |
| 延迟 | 200-500ms | 300-800ms | 500ms-2s |
| 流式支持 | 原生支持 | WebSocket流式 | 需自行实现 |
| 离线能力 | 否 | 否 | 是 |
| 成本 | 免费 | 按量计费 | 服务器成本 |
Deepgram 的流式接口走WebSocket。你只需要把音频数据切成小块,通过socket发过去,它就会实时返回识别文本。它的API设计得很干净,我特别喜欢它的 utterance_end 事件——能告诉你一句话说完了,方便做断句。
// Deepgram WebSocket 流式识别示例
const socket = new WebSocket('wss://api.deepgram.com/v1/listen', [
'token', 'YOUR_DEEPGRAM_API_KEY'
]);
socket.onopen = () => {
// 发送配置
socket.send(JSON.stringify({
type: 'Configure',
config: {
encoding: 'linear16',
sample_rate: 16000,
channels: 1,
interim_results: true,
language: 'zh-CN'
}
}));
};
// 发送音频数据块(从WebRTC获取)
function sendAudioChunk(pcmData) {
if (socket.readyState === WebSocket.OPEN) {
socket.send(pcmData);
}
}
socket.onmessage = (msg) => {
const result = JSON.parse(msg.data);
if (result.type === 'Results') {
console.log('Deepgram识别:', result.channel.alternatives[0].transcript);
}
};
Whisper 这边,我建议用 whisper.cpp 或者 faster-whisper。它们比OpenAI官方的Python实现快得多,而且能跑在CPU上。不过Whisper原生不支持流式,你需要自己维护一个滑动窗口——每收集到2秒音频就送进去识别一次,然后和前面的结果做合并。
我的经验:Whisper对中文的识别精度确实高,但实时性是个问题。如果你做的是会议纪要这种非实时场景,Whisper是首选。如果是实时字幕,Deepgram更靠谱。
11.3 流式ASR的核心:音频数据怎么喂
不管是Deepgram还是Whisper,它们要的都是裸PCM数据。而WebRTC里走的是编码后的Opus包。所以中间需要一道“桥接”——把WebRTC的音频流解码成PCM,再喂给ASR引擎。
这个桥接过程,我画了一张图帮你理解。
具体怎么做呢?核心是 AudioWorklet。它运行在独立的音频线程里,能拿到解码后的PCM数据。你可以在AudioWorkletProcessor里把数据post到主线程,然后通过WebSocket发给Deepgram,或者喂给本地的Whisper进程。
// AudioWorkletProcessor 示例
class AudioCaptureProcessor extends AudioWorkletProcessor {
constructor() {
super();
this.port.onmessage = (event) => {
// 接收主线程的配置
};
}
process(inputs, outputs, parameters) {
const input = inputs[0];
if (input && input.length > 0) {
const channelData = input[0]; // Float32Array
// 转成16位PCM
const pcm16 = new Int16Array(channelData.length);
for (let i = 0; i < channelData.length; i++) {
pcm16[i] = Math.max(-32768, Math.min(32767, channelData[i] * 32768));
}
// 发送到主线程
this.port.postMessage(pcm16.buffer, [pcm16.buffer]);
}
return true;
}
}
registerProcessor('audio-capture', AudioCaptureProcessor);
重要提醒:AudioWorklet里拿到的数据是Float32,范围在-1到1之间。ASR引擎通常要16位有符号整数PCM,采样率16kHz。别忘了做格式转换和重采样。我见过有人直接拿Float32喂给Deepgram,结果识别出来全是乱码。
11.4 实战中的避坑指南
这条路我走过不少弯路。挑几个典型的说说。
- 采样率不匹配:WebRTC默认用48kHz,但大多数ASR引擎要16kHz。不做重采样的话,识别精度会断崖式下降。我习惯用
libsamplerate或者浏览器自带的AudioContext做离线重采样。 - 静音检测:别把静音段也送去识别,浪费带宽还容易出幻觉。加一个VAD(语音活动检测)模块,检测到有人说话再开始送数据。Deepgram自带VAD,Whisper需要自己实现。
- 缓冲区管理:网络抖动会导致音频数据到达不均匀。用环形缓冲区做平滑,我一般设500ms的缓冲深度。太深了延迟大,太浅了容易断。
- 多路音频:如果会议里有好几个人同时说话,你需要为每个
RTCRtpReceiver单独开一个ASR连接。别混流,混了就没法区分谁说了什么。
一个小技巧:在调试阶段,把PCM数据同时写到一个WAV文件里。这样如果识别结果不对,你可以回放原始音频,确认是ASR的问题还是音频采集的问题。这个习惯帮我省了不少排查时间。
11.5 总结一下
语音识别集成这件事,说白了就是三选一:
- 简单场景用Web Speech API,省事但不可控
- 专业场景上Deepgram,流式支持好,延迟低
- 离线或高精度场景用Whisper,但要自己处理流式逻辑
而不管选哪个,核心都是把WebRTC的音频流通过AudioWorklet解码成PCM,再喂给ASR引擎。这个桥接做好了,后面的应用层——实时字幕、语音搜索、AI对话——就都水到渠成了。
我个人的建议是:先拿Web Speech API快速验证产品逻辑,等需要上量的时候再切到Deepgram。至于Whisper,适合做离线处理或者对数据隐私有要求的场景。别一上来就上最重的方案,容易把自己绕进去。
公众号:蓝海资料掘金营,微信deep3321