voice模块
Voice模块
语音识别(ASR):将语音转为文本,支持流式和非流式。
语音合成(TTS):将文本转为语音。
VAD(语音活动检测):检测音频中是否有人声。
流式识别,确认哪个地方需要加逗号终止截断。
bool is_endpoint = recognizer.IsEndpoint(s.get()); // 是否检测到端点
端点检测配置:
struct EndpointConfig {// For default setting,// rule1 times out after 2.4 seconds of silence, even if we decoded nothing.// rule2 times out after 1.2 seconds of silence after decoding something.// rule3 times out after the utterance is 20 seconds long, regardless of// anything else.EndpointRule rule1;EndpointRule rule2;EndpointRule rule3;void Register(ParseOptions *po);EndpointConfig(): rule1{false, 2.4, 0}, rule2{true, 0.4, 0}, rule3{false, 0, 20} {}EndpointConfig(const EndpointRule &rule1, const EndpointRule &rule2,const EndpointRule &rule3): rule1(rule1), rule2(rule2), rule3(rule3) {}std::string ToString() const;
};
1. EndpointRule 判定逻辑
每个EndpointRule的核心字段有:
- must_decoded_something(是否必须识别出内容)
- min_trailing_silence(静音时长阈值,单位:秒)
- min_utterance_length(最短语音段时长,单位:秒)
判定流程如下:
- must_decoded_something = false
- 只要静音时长 ≥ min_trailing_silence,无论是否识别出内容,都可以触发端点。
- must_decoded_something = true
- 必须已经识别出内容(即ASR输出过文本),且静音时长 ≥ min_trailing_silence,才会触发端点。
- min_utterance_length > 0
- 只要当前语音段长度 ≥ min_utterance_length,无论其它条件,直接触发端点。
实际判定时,三条规则只要有一条满足就会触发端点(即“或”关系)。
2. 与VAD的关系
- VAD(语音活动检测)负责判断每一帧音频是“说话”还是“静音”。
- 端点检测(Endpointing)依赖VAD的结果,统计“静音”持续时长。
- 只有VAD判定为“静音”时,才会累计静音时长,进而判断是否满足min_trailing_silence。
简言之:
- VAD是底层的“静音/有声”判定器。
- EndpointRule是基于VAD结果的“分句/分段”策略。
多规则联合决策的端点检测机制,通过三个并行规则覆盖不同场景:
规则1:长时间静音强制截断(防漏检)-2.4s
规则2:内容后静音检测(自然停顿)-0.4s
规则3:最大时长限制(系统保护)-20s
static int32_t RecordCallback(const void *input_buffer,void * /*output_buffer*/,unsigned long frames_per_buffer, // NOLINTconst PaStreamCallbackTimeInfo * /*time_info*/,PaStreamCallbackFlags /*status_flags*/,void *user_data) {//wait 标志位实现音频采集与TTS播放的互斥,// 当 wait=false 时(TTS已经播报完毕),将音频数据送入识别流水线if (!wait) {//PortAudio 回调要求 void* 通用指针,因此需要强制转换为语音识别流水线专用的 OnlineStream 对象指针auto stream = reinterpret_cast<sherpa_onnx::OnlineStream *>(user_data);// 音频数据注入:stream->AcceptWaveform(mic_sample_rate,reinterpret_cast<const float *>(input_buffer),frames_per_buffer);}return stop ? paComplete : paContinue;
}
识别到一句话后,文本通过ZeroMQ发送给LLM,随后等待TTS播放完毕再继续采集。
tts在播报的时候要避免voice获取,这样会乱的,因此要有wait标志位
- 函数作用
- 这是 PortAudio 的音频采集回调函数,每采集到一帧音频数据就会被自动调用一次。
- 其主要任务是:将采集到的音频数据送入流式ASR识别流水线。
- wait 互斥机制
- wait 是一个全局标志位,用于控制音频采集与TTS播放的互斥。
- 当 wait == true 时,说明TTS正在播报,暂停音频采集,防止ASR和TTS同时占用音频资源。
- 当 wait == false 时,说明TTS已播报完毕,允许采集音频并送入ASR。
- user_data 强制类型转换
- PortAudio 的回调接口只能传递 void* 指针。
- 这里将其强制转换为sherpa_onnx::OnlineStream*,即ASR识别流水线的流对象指针。
- 音频数据注入
- stream->AcceptWaveform(...) 将采集到的音频帧(以 float 格式)送入ASR流。
- 参数说明:
- mic_sample_rate:采样率(如16000Hz)
- reinterpret_cast<const float *>(input_buffer):将原始音频缓冲区转换为 float 指针
- frames_per_buffer:本次采集的帧数
- 回调返回值
- 如果 stop == true(如收到 Ctrl+C),返回 paComplete,通知 PortAudio 停止采集。
- 否则返回 paContinue,继续采集下一帧。