07 — STT 模块
源码目录:src/vocal10n/stt/。
职责
- 以 16 kHz 单声道采集麦克风音频。
- 在转写前可选执行 TTS 回放回声消除。
- 以滑动窗口流式方式运行 FasterWhisper。
- 发出 partial(实时)与 confirmed(稳定)文本事件。
- 过滤幻觉文本与明显重复。
- 可选进行轻量说话人标记(diarisation)。
文件
| 文件 | 作用 |
|---|---|
audio_capture.py | sounddevice 输入流、环形缓冲、设备枚举 |
playback_aec.py | PlaybackTimeline + AdaptiveEchoCanceller(NLMS + DTD) |
engine.py | STTEngine(FasterWhisper 封装) |
transcript.py | 片段管理:partial 与 confirmed 切分 |
filters.py | 幻觉过滤、邻接去重、短语重复抑制、音素纠错 |
diarizer.py | 可选说话人标记 |
worker.py | 从环形缓冲拉流并驱动引擎的后台线程 |
controller.py | STT tab 使用的公开接口 |
流式策略
采集层每 0.2 秒写入一帧到环形缓冲。工作线程循环:
- 读取末尾
window_seconds(默认 6.5)的音频。 - 调用
STTEngine.transcribe(),返回SegmentResult[](text、start、end、avg_logprob、no_speech_prob、可用时含词级置信度)。 - 每段先过
filters.HallucinationFilter,再做邻接去重与短语重复抑制,避免 Whisper 在噪声下循环输出。 - 按规则分段:
- Pending:
end位于窗口尾部confirm_threshold秒内。 - Confirmed:早于阈值,或年龄超过
max_segment_age(强制刷新,避免长句不切分导致卡住)。
- Pending:
- 对 pending 尾部发布
STT_PARTIAL,对新稳定片段发布STT_CONFIRMED。
识别上下文(见 14 — 知识库与 RAG)会拼接进 Whisper initial_prompt,并受 initial_prompt_capacity 限制。
幻觉过滤
filters.py 包含多层防护:
- 静态过滤表。 从
config/filters.txt读取常见幻觉短语(如静音时的 “Thank you for watching”)。匹配则丢弃。 - 邻接去重。 与上一条 confirmed 相同或近似时抑制输出。
- 短语重复抑制。 检测短片段连续 N 次重复(Whisper 常见失效模式)。
- 音素索引。 结合
stt_terms/context_gaming.txt等,对领域词进行模糊纠错。
完整过滤表可在知识库页通过 vocal10n.ui.widgets.filter_list_editor 编辑。
回放感知 AEC
启用 TTS 时,合成语音可能被麦克风再次采集并转写,形成回声环。playback_aec.py 的 AEC 层用于阻断:
flowchart LR
Mic([mic]) --> AEC["AdaptiveEchoCanceller<br/>(NLMS, length filter_taps)"]
TTSPlay([TTS playback]) --> Timeline[PlaybackTimeline]
Timeline -->|reference signal| AEC
AEC --> DTD{"Double-talk?<br/>mic > dt_threshold × echo"}
DTD -- yes --> Freeze["Freeze NLMS weights<br/>(still apply filter)"]
DTD -- no --> Adapt[Adapt NLMS weights]
Freeze --> Out([clean mic to Whisper])
Adapt --> Out
PlaybackTimeline是线程安全的回放音频环形缓冲,在AudioPlayer调用sd.play()时打时间戳。AdaptiveEchoCanceller每个 mic chunk 执行:- 从
PlaybackTimeline取参考信号,并通过互相关估计声学延迟(上限aec.max_delay_ms)。 - 执行长度为
aec.filter_taps的块式 NLMS,自适应步长为aec.step_size。 - 运行双讲检测:若 mic 能量超过估计回声能量的
aec.dt_threshold倍,则冻结权重更新,但仍应用当前滤波器,避免说话重叠时模型漂移。
- 从
该方案在提交 f334773 中引入。
说话人标记
diarizer.py 提供轻量级分段说话人标记:当 SystemState.speaker_tagging 开启时,为 confirmed 段加 [S1]、[S2] 等前缀。该实现面向流式实时场景,不是离线高精度分析模型。