Skip to content

Latest commit

 

History

History
222 lines (155 loc) · 8.47 KB

File metadata and controls

222 lines (155 loc) · 8.47 KB

TEN-VAD C++ 库接口文档

本项目把 ten-vad-ggml 参考项目完整移植为 C++17、零第三方依赖 的实现,并产出可供其他项目链接使用的 DLL。

文档涵盖:构建方法、C API 参考、如何链接进其他项目、以及性能基准数据(与 Python ONNX 实现对比)。


1. 交付物

产物 说明
ten_vad_ggml.dll 共享库(Release x64),导出 4 个 C 函数
ten_vad_ggml_shared.lib MSVC 导入库,供其他项目链接
ten_vad.h 公共 C 头文件,仅依赖 <stddef.h> / <stdint.h>
ten-vad-ggml.bin GGML 格式模型文件(296 KB,运行时按路径加载,不内嵌)
ten_vad_ggml.lib 可选:静态库(同源码构建的另一个 target)

DLL 内实现完整 VAD 流水线:STFT → Mel 滤波组 → 对数归一化 → LPC 残差自相关基音估计(Viterbi 追踪)→ 3 层可分离卷积 → 2 层 LSTM → 2 层全连接 → Sigmoid。不含 onnxruntime / ggml / FFTW 等任何外部库。


2. 构建

2.1 依赖

  • Visual Studio 2022(MSVC,C++17)
  • CMake ≥ 3.15
  • 无需其他第三方库

2.2 CMake 构建

cmake -S cpp -B cpp/build -G "Visual Studio 17 2022" -A x64
cmake --build cpp/build --config Release

2.3 产物位置与 CMake 目标

cpp/build/Release/ten_vad_ggml_shared.dll    # DLL
cpp/build/Release/ten_vad_ggml_shared.lib    # 导入库
cpp/build/Release/ten_vad_ggml.lib           # 静态库(可选,同源构建)
cpp/build/Release/ten_vad_ggml.exe 等        # CLI / 基准 / 测试工具
CMake 目标 类型 说明
ten_vad_ggml STATIC 静态库,供本仓库 CLI/测试/基准链接
ten_vad_ggml_shared SHARED DLL + 导入库,供外部项目链接
ten_vad_cli EXE 命令行 VAD(WAV → 逐帧概率)
ten_vad_tests EXE 单元测试(4745 项断言,ctest 可跑)
ten_vad_dllexample EXE DLL 消费者示例(验证导出 API)
ten_vad_bench EXE 性能基准(峰值内存 / 帧耗时 / RTF,仅 Windows)

可选 CMake 开关:-DTEN_VAD_BUILD_TESTS=OFF-DTEN_VAD_BUILD_CLI=OFF


3. C API 参考

头文件:ten_vad.h

所有函数以 C 链接(extern "C"),默认 __cdecl 调用约定,可从 C / C++ 直接调用。

3.1 常量

#define TEN_VAD_SAMPLE_RATE  16000   // 采样率 Hz
#define TEN_VAD_HOP_SIZE     256     // 每次 process() 处理的采样数(16 ms)
#define TEN_VAD_FRAME_BYTES  (TEN_VAD_HOP_SIZE * 2)  // 16-bit 采样字节数(512 B)

3.2 类型

typedef struct ten_vad_ctx ten_vad_ctx;  // 不透明句柄

3.3 函数

ten_vad_create

ten_vad_ctx *ten_vad_create(const char *model_path);

加载 GGML 模型并创建 VAD 上下文。失败(文件不存在、magic 错误、超参数不匹配、张量截断)返回 NULL

ten_vad_process

float ten_vad_process(ten_vad_ctx *ctx, const int16_t *samples, size_t n_samples);

处理一个 hop(16 ms)的音频,返回语音概率,范围 [0, 1]

  • samples:16 kHz 的 int16 采样,至少 n_samples 个。
  • n_samples:允许少于 TEN_VAD_HOP_SIZE(不足部分按静音补齐),便于流式收尾。
  • 内部失败返回 0

ten_vad_reset

void ten_vad_reset(ten_vad_ctx *ctx);

复位全部状态:LSTM 隐/细胞状态、特征提取状态、30 秒重置计数器。

ten_vad_destroy

void ten_vad_destroy(ten_vad_ctx *ctx);

释放上下文。ctxNULL 时安全。

3.4 典型用法

#include "ten_vad/ten_vad.h"

ten_vad_ctx *vad = ten_vad_create("ten-vad-ggml.bin");
if (!vad) { /* 处理加载失败 */ }

int16_t buf[TEN_VAD_HOP_SIZE];
while (读取到 TEN_VAD_HOP_SIZE 个采样) {
    float prob = ten_vad_process(vad, buf, TEN_VAD_HOP_SIZE);
    /* prob > 0.5 视为语音 */
}
ten_vad_destroy(vad);

4. 链接进其他项目

4.1 需要的东西

  1. ten_vad_ggml_shared.dll(部署时与可执行文件同目录,或加入 PATH
  2. ten_vad_ggml_shared.lib(链接时使用)
  3. ten_vad.h(编译时使用)
  4. ten-vad-ggml.bin(运行时按路径加载)

4.2 MSVC(cl.exe)直接编译

cl /std:c++17 /EHsc /O2 /I <include目录> main.cpp <build>\Release\ten_vad_ggml_shared.lib

ten_vad.hTEN_VAD_API 宏:定义了 TEN_VAD_SHARED 时按 __declspec(dllimport) 声明,不定义则按普通声明(两种都能链接,建议定义以获得更优的调用代码)。

4.3 CMake 消费者

add_executable(my_app main.cpp)
target_link_libraries(my_app PRIVATE <path-to-ten_vad_ggml_shared.lib>)
target_include_directories(my_app PRIVATE <path-to-include>)
target_compile_definitions(my_app PRIVATE TEN_VAD_SHARED)   # 可选,见上

运行时保证 DLL 可被找到(VS 调试会把 DLL 拷到 exe 目录,或用 $<TARGET_FILE:ten_vad_ggml_shared> 配合 add_custom_command(TARGET ... POST_BUILD) 拷贝)。

4.4 其他语言

导出的是标准 C ABI,可用 ctypes / CFFI / P/Invoke / FFI 直接绑定:

import ctypes
dll = ctypes.CDLL("ten_vad_ggml_shared.dll")
dll.ten_vad_create.restype = ctypes.c_void_p
dll.ten_vad_create.argtypes = [ctypes.c_char_p]
dll.ten_vad_process.restype = ctypes.c_float
dll.ten_vad_process.argtypes = [ctypes.c_void_p,
                                ctypes.POINTER(ctypes.c_int16), ctypes.c_size_t]

5. 性能数据

5.1 测试方法

  • 平台:Windows x64,MSVC Release(/O2),单线程。
  • 输入:30 秒合成类语音信号(220 Hz + 440 Hz 双音,幅度包络调制),16 kHz / int16,共 1875 帧(每帧 16 ms)。
  • C++ 指标QueryPerformanceCounter 计时;峰值工作集(Peak Working Set,psapi)。
  • Python ONNX 指标time.perf_counter 分段计时(特征提取 / onnxruntime 推理);同样用 psapi 测峰值工作集。
  • RTF = 处理耗时 / 音频时长。RTF < 1 表示快于实时。
  • C++ 基准工具:ten_vad_bench.cpp,Python:bench_onnx.py

5.2 结果(30 s 音频,1875 帧)

指标 C++ (DLL/静态同源) Python ONNX 倍率
全流程耗时 334 ms 74 087 ms C++ 快 222×
全流程 RTF 0.0111 2.470
每帧耗时(全流程) 178 µs 39 513 µs C++ 快 222×
推理 RTF(LSTM+Dense,不含特征) 0.00489 0.0288 C++ 快 5.9×
推理每帧 78 µs 461 µs
峰值工作集 5.49 MB 57.14 MB C++ 低 10.4×
会话/模型初始化 一次性开销(仅读 296 KB 模型文件) 133 ms

说明:

  • Python 特征提取为 numpy 参考实现(逐帧 Python 循环,未向量化),是全流程 RTF 2.47 的主因;生产级 Python 实现可显著改善。纯推理环节(同网络)C++ 仍快 5.9×。
  • C++ 侧峰值工作集 5.49 MB 已含模型(296 KB)、全部 DSP 状态与 LSTM 权重。
  • 输出概率与 Python ONNX / 参考项目实现一致(最大偏差 < 0.001,4745 项测试通过)。

5.3 实时性结论

  • 单线程 C++ 实现 RTF ≈ 0.011,即处理 1 秒音频仅需约 11 ms,有约 90× 实时余量,可运行在低端嵌入式/移动 CPU 上。
  • 每帧 178 µs 中,特征提取约占 100 µs、推理约 78 µs;LSTM 门运算为当前主要计算热点。

6. 注意事项

  • 线程安全ten_vad_ctx 持有全部可变状态,单个上下文不可并发调用。多路音频请每路创建独立上下文;多个上下文可在多线程并行使用。
  • 调用约定:C API 为 __cdecl(x64 上统一)。
  • 运行时依赖:DLL 依赖 MSVC 运行库(VCRuntime)。目标机器需装有对应的 VC++ Redistributable(Release 下为 x64)。除此之外无其他动态依赖。
  • 30 秒 LSTM 重置:为复刻原生库行为,每处理 1875 帧(30 s)自动清零 LSTM 状态(内部处理,调用方无需干预);ten_vad_reset() 可随时手动清零。
  • 模型文件:模型为 GGML 格式(magic 0x67676d6c),版本需与本库超参数匹配(2 层 LSTM、隐层 64、40 Mel 带)。
  • 静态链接选项:若不需要 DLL,可直接链接 ten_vad_ggml.lib(静态库)并包含 ten_vad.h(此时不定义 TEN_VAD_SHARED),产物零运行时依赖(除 VC 运行库)。