基于 sherpa-onnx 的 C++ 语音交互静态库,封装 ASR(语音识别)、KWS(关键词唤醒)、VAD(人声检测)、TTS(语音合成)四大功能,提供统一的唤醒→识别→合成交互流程。

系统架构

┌─────────────────────────────────────────────────┐
│                  应用层(demo / 用户代码)          │
│          connect 信号 → 回调处理结果               │
├─────────────────────────────────────────────────┤
│              SherPaOnnxManager(公共接口)          │
│  loadModels / start / stop / speak / setWakeMode  │
├──────────────┬──────────────┬────────────────────┤
│  mic 回调线程  │  AsrWorker   │   TtsWorker 线程    │
│  VAD+KWS 检测 │  超时+ASR解码 │  合成+播放           │
│  音频累积     │              │                    │
├──────────────┴──────────────┴────────────────────┤
│          sherpa-onnx 引擎(OfflineRecognizer /     │
│    OnlineRecognizer / KeywordSpotter / VAD / TTS) │
├───────────────────────────────────────────────────┤
│          miniaudio(音频采集/播放,header-only)     │
├───────────────────────────────────────────────────┤
│          sigslot(信号槽 + EventLoop 跨线程投递)    │
└─────────────────────────────────────────────────┘

设计要点

  • 纯 C++17,无 Qt 等第三方框架依赖,跨平台(Windows / Linux)
  • PIMPL 模式:公共头文件不暴露 sherpa-onnx / miniaudio 类型
  • 静态库:无符号导出宏,直接链接
  • sigslot 信号槽 + EventLoop:替代 Qt 信号槽 + QMetaObject::invokeMethod(Qt::QueuedConnection),任意线程 postToMainThread → 主线程 processAll 执行
  • 线程模型:mic 回调线程(miniaudio 内部)+ AsrWorker 线程(超时检测+ASR解码)+ TtsWorker 线程(合成+播放)+ 主线程(事件循环)

核心功能

功能引擎说明
ASR (Offline)OfflineRecognizer (SenseVoice)非流式语音识别,累积音频后一次性解码(默认)
ASR (Online)OnlineRecognizer (Zipformer)流式语音识别,支持增量解码
KWSKeywordSpotter关键词唤醒,持续检测
VADVoiceActivityDetector (silero)人声检测,实时判断是否有人说话
TTSOfflineTts (kokoro/matcha/zipvoice)语音合成 + miniaudio 播放,三种模型可选

ASR 模式

通过 setAsrMode() 设置,支持两种 ASR 引擎:

模式值引擎模型说明
Offline1OfflineRecognizerSenseVoice非流式,累积音频后一次性解码,准确率高(默认)
Online2OnlineRecognizerZipformer流式,一次性送入 + InputFinished + while Decode

唤醒模式

通过 setWakeMode() 设置,定义 3 种工作模式:

模式值唤醒方式流程
Kws1关键词唤醒检测到关键词 → 等待静音+预缓冲 → 累积音频 → 静默2秒 → 送ASR
Vad2人声检测唤醒(默认)预缓冲2秒音频 → VAD实时检测到人声 → 唤醒并继续累积 → 静默2秒 → 送ASR
Tts3纯语音输出不监听麦克风,仅做TTS语音合成播放

VAD 模式预缓冲机制:未唤醒时也持续累积每帧音频(保留最近 2 秒),VAD 实时检测到人声(vadSpeechNow=true)时立即唤醒,此时缓冲区已包含语音开头,避免丢失前段语音。

KWS 唤醒后音频处理策略:KWS 检测到关键词后,进入"等待静音"状态并同时预缓冲音频:

  • 若检测到静音(用户说完唤醒词后停顿)→ 清空预缓冲(丢弃唤醒词尾音)→ 再等 0.3 秒确保静音稳定 → 开始累积命令音频
  • 若 0.5 秒内未检测到静音(用户说完唤醒词后立刻说命令)→ 保留预缓冲(不丢失命令开头)→ 立即开始累积

KWS 持续运行:KWS 始终处理音频(AcceptWaveform + Decode)保持 stream 状态流动,但仅在睡眠模式下检查 GetResult,避免已唤醒后误触发清空 ASR 缓冲区。

超时语义:唤醒后持续说话时不会超时(lastSpeechTime 每帧刷新),只有静默超过2秒才触发 flush 送 ASR。flush 后重置 VAD 和 KWS 状态,避免长期累积导致检测退化。

目录结构

libsherpacore/
├── includes/                      # 公共头文件
│   ├── sherpa_core_manager.h      # 管理类公共接口
│   ├── signal.hpp                 # sigslot 信号槽 + EventLoop
│   └── ringbuffer.h               # 环形缓冲区(预留)
├── srcs/                          # 内部实现
│   ├── sherpa_core_manager.cpp    # 核心实现
│   ├── sherpa_core_manager_p.h    # PIMPL(含 sherpa-onnx / miniaudio 头)
│   └── miniaudio.h                # header-only 音频库
├── demos/
│   └── main.cpp                   # 测试程序
├── models/                        # 模型文件
│   ├── asr/
│   │   ├── sherpa-onnx-sense-voice-small/   # Offline ASR 模型
│   │   └── sherpa-onnx-streaming-zipformer-zh/  # Online ASR 模型
│   ├── kws/
│   ├── silero_vad.onnx
│   └── tts/                        # TTS 模型
│       ├── sherpa-onnx-zipvoice-distill-int8-zh-en-emilia/  # ZipVoice 中英双语
│       ├── matcha-icefall-zh-baker/    # Matcha 中文 TTS(单音色,默认)
│       └── kokoro-multi-lang-v1_0/     # Kokoro 多语言 TTS(53 音色)
├── third_party/sherpa-onnx/       # sherpa-onnx SDK
├── CMakeLists.txt                 # 构建脚本
└── build.bat                      # Windows 一键构建

模型下载

模型文件未包含在仓库中,请单独下载:

models.7z — 链接: https://pan.baidu.com/s/1-uetgD3w5OJvJ__whK6ISg?pwd=rmqh 提取码: rmqh

下载后解压到项目根目录,确保 models/ 目录结构与上述一致。

构建

依赖

  • CMake ≥ 3.15
  • C++17 编译器(MSVC 2019+ / GCC 9+)
  • sherpa-onnx SDK(已包含在 third_party/)

Windows

# 方式一:一键脚本
build.bat

# 方式二:手动 cmake
cmake -S . -B build -G "Visual Studio 17 2022" -A x64
cmake --build build --config Release

产物:

  • libs/sherpacore.lib — 静态库
  • bin/demo_test.exe — 测试程序

Linux

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build

使用方法

1. 创建管理类并连接信号

#include "sherpa_core_manager.h"

SherpaCoreManager manager;

// 连接信号(继承 sigslot::has_slots<>)
class MyListener : public sigslot::has_slots<> {
public:
    void onWakedUp()        { /* 唤醒回调 */ }
    void onAsrFinal(std::string text) { /* 识别结果 */ }
    void onAsleep()         { /* 超时进入睡眠 */ }
    void onError(std::string msg)     { /* 错误 */ }
};

MyListener listener;
manager.wakedUp.connect(&listener, &MyListener::onWakedUp);
manager.asrFinalResult.connect(&listener, &MyListener::onAsrFinal);
manager.asleep.connect(&listener, &MyListener::onAsleep);
manager.errorOccurred.connect(&listener, &MyListener::onError);

2. 配置模型路径并加载

manager.setAsrModelDir("./models/asr/sherpa-onnx-sense-voice-small");
manager.setKwsModelDir("./models/kws");
manager.setVadModelPath("./models/silero_vad.onnx");
manager.setTtsModelDir("./models/tts/matcha-icefall-zh-baker");  // TTS(Matcha,默认)
manager.setAsrMode(AsrMode::Offline);               // 默认 Offline (SenseVoice)
manager.setWakeMode(WakeMode::Vad);                  // 默认 VAD 模式
manager.setProvider("auto");                         // 推理后端:auto(自动检测CUDA)/cpu/cuda,默认 auto
manager.setFilterResult(true);                       // 过滤无效结果(纯标点/空白/单字),默认开启
manager.setFilteredResults({"我。", "啊。"});          // 自定义过滤:完全匹配则丢弃

if (!manager.loadModels()) {
    // 加载失败处理
}

3. 启动流水线

manager.start();
// mic 开始采集 → VAD/KWS 检测 → 唤醒 → 累积音频 → 静默2秒 → ASR识别 → asleep

4. 主线程事件循环

while (running) {
    sigslot::EventLoop::instance().processAll();  // 处理跨线程事件
    std::this_thread::sleep_for(std::chrono::milliseconds(20));
}

5. TTS 语音合成

manager.speak("你好,今天天气怎么样?", 0, 1.0f);  // text, speakerId, speed
manager.setTtsVolume(2.0f);                          // TTS 播放音量(0.0-2.0,默认 1.0)
manager.pauseTts();   // 暂停播放
manager.resumeTts();  // 恢复播放
manager.stopTts();    // 停止播放

6. 停止

manager.stop();

demo_test 命令行参数

demo_test [选项]
选项说明默认
--online / -online使用流式 Zipformer ASR 模型Offline SenseVoice
--wake <mode> / -wake <mode>唤醒模式:vad / kws / ttsvad
--tts <model> / -tts <model>TTS 模型:kokoro / matcha / zipvoicezipvoice
--provider <p> / -provider <p>推理后端:auto / cpu / cudaauto

示例:

demo_test                           # VAD 模式 + Offline SenseVoice + ZipVoice + auto provider
demo_test --online                  # VAD 模式 + Online Zipformer
demo_test --wake kws                # KWS 唤醒词模式
demo_test --wake tts                # TTS 纯语音输出模式
demo_test --wake vad --online       # VAD 模式 + Online Zipformer
demo_test --tts matcha              # 使用 Matcha TTS 模型
demo_test --provider cpu            # 强制使用 CPU 推理
demo_test --provider cuda           # 强制使用 CUDA 推理(需 GPU 版 sherpa-onnx)

运行时命令(stdin):

命令说明
t <text>TTS 合成并播放文本
p暂停 TTS 播放
r恢复 TTS 播放
s停止 TTS 播放
q退出程序

信号列表

信号参数触发时机执行线程
modelLoadProgressstring模型加载进度loadModels 调用线程
started—流水线启动start 调用线程
stopped—流水线停止stop 调用线程
wakedUp—唤醒(KWS命中/VAD检测到人声)主线程
asleep—静默超时,进入睡眠主线程
voiceDetected—VAD 检测到人声(边沿触发:无人声→有人声),不受唤醒模式限制主线程
asrFinalResultstringASR 识别最终结果主线程
ttsStarted—TTS 播放开始主线程
ttsFinished—TTS 播放完成主线程
ttsStopped—TTS 被停止主线程
errorOccurredstring错误信息主线程或调用线程

结果过滤

通过 setFilterResult(bool) 控制,默认开启。开启后,ASR 识别结果在发送 asrFinalResult 信号前会统计有效内容数量(汉字 U+4E00-U+9FFF、字母 a-z/A-Z、数字 0-9)。若结果有效内容数 ≤ 1(如纯标点 "。"、单字 "我。"),则判定为无效结果,不发送信号。

manager.setFilterResult(true);   // 开启过滤(默认)
manager.setFilterResult(false);  // 关闭过滤,输出原始结果

自定义过滤列表:通过 setFilteredResults() 设置一组字符串,ASR 结果若完全匹配列表中的任一字符串则丢弃:

manager.setFilteredResults({"我。", "啊。", "嗯。"});

该过滤同时适用于 Offline (SenseVoice) 和 Online (Zipformer) 两种 ASR 路径。

模型配置

ASR — Offline (SenseVoice)

models/asr/sherpa-onnx-sense-voice-small/
├── model_q8.onnx      # 量化模型
└── tokens.txt         # 词表

配置参数(代码内固定):

  • language = "zh" — 中文
  • use_itn = true — 逆文本归一化
  • num_threads = 2

ASR — Online (Zipformer)

models/asr/sherpa-onnx-streaming-zipformer-zh/
├── encoder-epoch-99-avg-1.int8.onnx   # encoder(int8 量化)
├── decoder-epoch-99-avg-1.int8.onnx   # decoder(int8 量化)
├── joiner-epoch-99-avg-1.int8.onnx    # joiner(int8 量化)
└── tokens.txt                         # 词表

配置参数(代码内固定):

  • feat_config.sample_rate = 16000
  • feat_config.feature_dim = 80
  • decoding_method = "greedy_search"
  • enable_endpoint = false
  • num_threads = 1

VAD(silero)

参数值说明
threshold0.3检测阈值(越低越敏感)
min_silence_duration0.5s判定语音结束的静音时长
min_speech_duration0.25s最短语音时长
max_speech_duration15s最大语音时长(超时强制切段)
window_size512处理窗口大小

KWS(关键词唤醒)

参数值说明
max_active_paths8搜索路径数(越大越灵活)
num_trailing_blanks3关键词后 trailing blank 帧数
keywords_score2.0关键词加分(越高越易匹配)
keywords_threshold0.1匹配阈值(越低越易触发,快语速友好)
num_threads2推理线程数

注:keywords_threshold 从默认 0.25 降至 0.1,keywords_score 从 1.0 升至 2.0,以支持快语速唤醒。

TTS 模型选择(三选一)

通过 setTtsModelType() 选择 TTS 引擎(需在 loadModels() 前设置):

manager.setTtsModelType(TtsModelType::Kokoro);    // 多语言多音色

manager.setTtsModelType(TtsModelType::Matcha);    // 中文单音色(默认)
manager.setTtsModelType(TtsModelType::Zipvoice);  // 中英双语 flow-matching

demo 运行时通过 --tts <kokoro|matcha|zipvoice> 参数选择(默认 matcha)。

推理后端(Provider)选择

通过 setProvider() 设置推理后端(需在 loadModels() 前设置):

manager.setProvider("auto");   // 自动检测:CUDA 可用则用 CUDA,否则 CPU(默认)
manager.setProvider("cpu");    // 强制 CPU
manager.setProvider("cuda");   // 强制 CUDA
  • auto(默认):通过 onnxruntime C API GetAvailableProviders 查询当前构建是否支持 CUDAExecutionProvider,支持则用 CUDA,否则回退 CPU
  • cpu:强制 CPU 推理
  • cuda:强制 CUDA 推理。需自行替换 GPU 版 sherpa-onnx/onnxruntime 到 third_party/ 目录,并安装 CUDA Toolkit + cuDNN。若 CUDA 运行时缺失,模型加载会失败

demo 运行时通过 --provider <auto|cpu|cuda> 参数选择(默认 auto)。

2. ZipVoice 中英双语蒸馏
models/tts/sherpa-onnx-zipvoice-distill-int8-zh-en-emilia/
├── encoder.int8.onnx   # 编码器
├── decoder.int8.onnx   # 解码器(flow-matching)
├── vocos_24khz.onnx    # 声码器(Vocos 24kHz,需单独下载)
├── tokens.txt          # 词表
├── lexicon.txt         # 词典
├── espeak-ng-data/     # 音素数据
└── test_wavs/          # 参考音频(零样本克隆用)

配置参数(代码内固定):

  • zipvoice.encoder = "encoder.int8.onnx"
  • zipvoice.decoder = "decoder.int8.onnx"
  • zipvoice.vocoder = "vocos_24khz.onnx"
  • zipvoice.tokens = "tokens.txt"
  • zipvoice.lexicon = "lexicon.txt"
  • zipvoice.data_dir = "espeak-ng-data"

零样本语音克隆:ZipVoice 合成时必须提供参考音频 + 参考文本(代码内自动加载):

  • 参考音频:test_wavs/leijun-1.wav(雷军音色,24kHz 单声道 f32)
  • 参考文本:"那还是三十六年前, 一九八七年. 我呢考上了武汉大学的计算机系."
  • num_steps = 4(flow-matching 步数,质量/速度折中)
  • 如需更换音色,替换参考 wav 和对应文本(必须与音频内容完全一致,否则音质明显劣化)
  • vocoder 下载地址:https://github.com/k2-fsa/sherpa-onnx/releases/download/vocoder-models/vocos_24khz.onnx
1. Matcha 中文 Baker(默认)
models/tts/matcha-icefall-zh-baker/
├── model-steps-3.onnx    # 声学模型
├── vocos-22khz-univ.onnx # 声码器(Vocos)
├── tokens.txt            # 词表
├── lexicon.txt           # 中文词典
├── dict/                 # 中文分词词典
├── date.fst              # 日期正则
├── number.fst            # 数字正则
└── phone.fst             # 电话正则

配置参数(代码内固定):

  • matcha.acoustic_model = "model-steps-3.onnx"
  • matcha.vocoder = "vocos-22khz-univ.onnx"
  • matcha.lexicon = "lexicon.txt"
  • matcha.tokens = "tokens.txt"
  • matcha.dict_dir = "dict"
  • rule_fsts = "number.fst,date.fst,phone.fst"(文本规范化:数字/日期/电话→中文读法)
  • num_threads = hardware_concurrency()
  • 单音色(speakerId = 0,DataBaker 中文女声)
3. Kokoro 多语言
models/tts/kokoro-multi-lang-v1_0/
├── model.onnx          # 声学模型
├── voices.bin          # 多音色声音库(53 音色,ID 0-52)
├── tokens.txt          # 词表
├── lexicon-zh.txt      # 中文词典
└── espeak-ng-data/     # 多语言音素数据

配置参数(代码内固定):

  • kokoro.model = "model.onnx"
  • kokoro.voices = "voices.bin"
  • kokoro.tokens = "tokens.txt"
  • kokoro.data_dir = "espeak-ng-data"
  • kokoro.lexicon = "lexicon-zh.txt"
  • 多音色(中文音色 45-52,如 45=小贝、50=云希)

数字读法模式:通过 setTtsNumberMode() 控制(三种模型通用):

manager.setTtsNumberMode(TtsNumberMode::Integer);  // 整数读法:1234 → 一千二百三十四
manager.setTtsNumberMode(TtsNumberMode::Digit);    // 逐位读法(默认):1234 → 一二三四
  • Integer 模式依赖 number.fst 规则文件,按中文整数读法规范化
  • Digit 模式在代码中预处理,将每个数字逐位映射为汉字(适合验证码、电话号码等场景)

音频参数

参数值
采样率16000 Hz
声道数1(单声道)
格式int16 PCM
输入增益1.5x(放大软语音)
唤醒超时2 秒静默
VAD 预缓冲2 秒(VAD 模式)
累积上限30 秒
项目主页

libsherpacorecore: 基于 sherpa-onnx 的 C++ 语音交互静态库,封装 ASR(语音识别)、KWS(关键词唤醒)、VAD(人声检测)、TTS(语音合成)四大功能,提供统一的唤醒→识别→合成交互流程。

产品案例

阿福老年陪护机器人

                                                             阿福老年陪护机器人

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐