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引擎。

这个桥接过程,我画了一张图帮你理解。

WebRTC音频桥接流式ASR架构 WebRTC 音频流 (Opus) Opus解码器 → PCM 16kHz 16bit 环形缓冲区 滑动窗口管理 RTCPeerConnection AudioWorklet ASR引擎 Deepgram / Whisper 定时取帧 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