libsherpacore - 基于 sherpa-onnx 的 C++ 语音交互静态库

基于 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) 流式语音识别,支持增量解码
KWS KeywordSpotter 关键词唤醒,持续检测
VAD VoiceActivityDetector (silero) 人声检测,实时判断是否有人说话
TTS OfflineTts (kokoro/matcha/zipvoice) 语音合成 + miniaudio 播放,三种模型可选

ASR 模式

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

模式 值 引擎 模型 说明
Offline 1 OfflineRecognizer SenseVoice 非流式,累积音频后一次性解码,准确率高(默认)
Online 2 OnlineRecognizer Zipformer 流式,一次性送入 + InputFinished + while Decode

唤醒模式

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

模式 值 唤醒方式 流程
Kws 1 关键词唤醒 检测到关键词 → 等待静音+预缓冲 → 累积音频 → 静默2秒 → 送ASR
Vad 2 人声检测唤醒(默认) 预缓冲2秒音频 → VAD实时检测到人声 → 唤醒并继续累积 → 静默2秒 → 送ASR
Tts 3 纯语音输出 不监听麦克风,仅做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 / tts vad
--tts <model> / -tts <model> TTS 模型:kokoro / matcha / zipvoice zipvoice
--provider <p> / -provider <p> 推理后端:auto / cpu / cuda auto

示例:

复制代码
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 退出程序

信号列表

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

结果过滤

通过 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)

参数 值 说明
threshold 0.3 检测阈值(越低越敏感)
min_silence_duration 0.5s 判定语音结束的静音时长
min_speech_duration 0.25s 最短语音时长
max_speech_duration 15s 最大语音时长(超时强制切段)
window_size 512 处理窗口大小

KWS(关键词唤醒)

参数 值 说明
max_active_paths 8 搜索路径数(越大越灵活)
num_trailing_blanks 3 关键词后 trailing blank 帧数
keywords_score 2.0 关键词加分(越高越易匹配)
keywords_threshold 0.1 匹配阈值(越低越易触发,快语速友好)
num_threads 2 推理线程数

注: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(语音合成)四大功能,提供统一的唤醒→识别→合成交互流程。

产品案例

阿福老年陪护机器人

相关推荐
xiaoye-duck1 小时前
《C++ 项目:手写大模型接入 SDK》ChatGPT接口设计(中):全量响应实现和测试
网络·c++·语言模型·chatgpt
東隅已逝,桑榆非晚2 小时前
算法3.双指针法-快乐数
c++·笔记·学习·算法
eggwyw2 小时前
C++相交链表和反转链表详解
开发语言·c++
杜 硕2 小时前
从零开始的C++生活——第六讲——02
c++·算法·生活
小超人诞生日记2 小时前
C++ STL 反向迭代器:从源码框架到适配器实现
c++·算法
青少儿编程课堂3 小时前
蓝桥杯青少组图形化 + 全国青少年信息素养大赛 + 电子学会Scratch考级:校园成语接龙大挑战解析
c++·python·算法·bfs·信息学竞赛
小贾要学习3 小时前
【C++后端开发项目】Reactor 模型 / C++ 仿写 muduo HTTP 服务器:让底层网络库真正跑起网页
linux·服务器·c++·http·网络编程·muduo·reactor模型
A7bert7774 小时前
【MobileSAM部署至RK3588】环境适配→转换RKNN→开发板部署
c++·人工智能·深度学习·目标检测·ubuntu
青少儿编程课堂4 小时前
蓝桥杯·全国青少年信息素养大赛·电子学会C/C++考级:分层图最短路解析
c++·python·算法·bfs·信息学竞赛