
sherpa-onnx Dart 实战Silero VAD 端点检测 非流式 ASR 完整识别方案【免费下载链接】sherpa-onnxSpeech-to-text, text-to-speech, speaker diarization, speech enhancement, source separation, and VAD using next-gen Kaldi with onnxruntime without Internet connection. Support embedded systems, Android, iOS, HarmonyOS, Raspberry Pi, RISC-V, RK NPU, Axera NPU, Ascend NPU, x86_64 servers, websocket server/client, support 12 programming languages项目地址: https://gitcode.com/GitHub_Trending/sh/sherpa-onnx本文围绕 sherpa-onnx 的 Dart API 示例dart-api-examples/vad-with-non-streaming-asr/展开讲解如何用 Silero VAD 模型对整段录音做端点检测Voice Activity Detection再将检测出的每段语音分别送入 Paraformer、SenseVoice、Whisper、Zipformer Transducer 等非流式offline识别模型转写成文本。读完后你将掌握VoiceActivityDetector与OfflineRecognizer的标准协作流程、Silero VAD 各参数的含义与默认值、8 种模型类型各自的命令行参数与运行脚本用法以及示例中逐窗喂入音频windowSize切片、flush()收尾、显式释放资源等关键细节。一、这个示例解决什么问题对于一段较长的离线录音会议录音、采访音频等直接整段丢给非流式 ASR 模型有两个问题一是多数模型对输入时长有限制二是无法得到“每句话出现在哪个时间段”的时间戳。sherpa-onnx 给出的标准解法是先 VAD 分句、再逐句识别Silero VAD 按固定窗口默认 512 个采样点逐帧检测语音活动输出一个个带起始时间戳的语音片段每个片段交给一个OfflineRecognizer流创建、喂入、解码得到文本按起始时间 -- 结束时间 : 文本的格式打印带时间戳的结果。目录 dart-api-examples/vad-with-non-streaming-asr/README.md 中列出的示例及对应脚本如下bin/下实际还有 README 未收录的moonshine.dart、zipformer-ctc.dart、dolphin-ctc.dart等下文一并覆盖Dart 程序使用的模型运行脚本bin/paraformer.dartParaformerrun-paraformer.shbin/sense-voice.dartSenseVoice CTCrun-sense-voice-zh.sh、run-sense-voice-en.shbin/sense-voice-2.dartSenseVoice另一实现入口run-sense-voice-zh-2.shbin/telespeech-ctc.dartTeleSpeech CTCrun-telespeech-ctc.shbin/whisper.dartWhisperrun-whisper.shbin/zipformer-transducer.dartZipformer Transducerrun-zipformer-transducer.shbin/zipformer-ctc.dartZipformer CTCrun-zipformer-ctc.shbin/moonshine.dartMoonshinerun-moonshine.shbin/dolphin-ctc.dartDolphin CTCrun-dolphin-ctc.sh二、环境依赖与前置条件由 pubspec.yaml 可见示例的依赖声明environment: sdk: 3.2.0 4.0.0 dependencies: sherpa_onnx: 1.13.7 path: ^1.9.0 args: ^2.5.0需要 Dart SDK 3.2.0 及以上版本依赖sherpa_onnx包示例锁定版本 1.13.7它是仓库中 flutter/sherpa_onnx 包的纯 Dart 封装通过 FFI 调用底层 C API无需原生插件即可在桌面/服务器端运行args用于解析命令行参数path用于路径处理。每个模型的运行脚本都遵循同一套流程先执行dart pub get安装依赖再按需下载三样东西——模型压缩包、Silero VAD 模型silero_vad.onnx、一条 16 kHz 的测试音频中文用lei-jun-test.wav英文用Obama.wav最后以dart run启动对应的 Dart 程序。这些资源均来自项目自身的 Releases 发布渠道脚本内已写好完整的下载与解压命令可直接运行bash run-paraformer.sh之类的脚本。三、核心流程拆解以 paraformer.dart 为例所有 8 个示例的结构几乎一致以 bin/paraformer.dart 为主线逐步说明。1. 初始化绑定与命令行参数void main(ListString arguments) async { await sherpa_onnx.initBindingsAsync(); ... }initBindingsAsync()负责加载底层动态库是所有 sherpa_onnx Dart 调用的前置步骤。随后用ArgParser定义 4 个必选参数--silero-vad、--model、--tokens、--input-wav缺任何一项都会打印 usage 并退出。2. 配置并创建 Silero VADfinal sileroVadConfig sherpa_onnx.SileroVadModelConfig( model: sileroVad, minSilenceDuration: 0.25, minSpeechDuration: 0.5, maxSpeechDuration: 5.0, ); final vadConfig sherpa_onnx.VadModelConfig( sileroVad: sileroVadConfig, numThreads: 1, debug: true, ); final vad sherpa_onnx.VoiceActivityDetector( config: vadConfig, bufferSizeInSeconds: 10);各参数的含义默认值来自 flutter/sherpa_onnx/lib/src/vad_config.dart参数示例取值默认值含义model./silero_vad.onnx必填Silero VAD 模型路径threshold未显式设置0.5语音概率判定阈值越高越严格minSilenceDuration0.250.5段内连续静音超过该秒数则视为一句话结束minSpeechDuration0.5—语音段最短时长短于此值的片段被丢弃maxSpeechDuration5.0—语音段最长时长秒防止长段不切分windowSize未显式设置512Silero VAD 单帧采样点数对应 32 ms 16 kHz也是喂入 VAD 的步长numThreads11VAD 推理线程数bufferSizeInSeconds1010检测器内部缓存缓冲单位秒从源码结构看VoiceActivityDetector构造时会把SileroVadModelConfig的字段逐一映射到 C 层结构体见 flutter/sherpa_onnx/lib/src/vad.dart 中minSilenceDuration、windowSize等赋值因此示例中的取值最终作用于底层 Silero 模型推理逻辑。3. 配置并创建非流式识别器final paraformer sherpa_onnx.OfflineParaformerModelConfig(model: model); final modelConfig sherpa_onnx.OfflineModelConfig( paraformer: paraformer, tokens: tokens, debug: true, numThreads: 1, modelType: paraformer, ); final config sherpa_onnx.OfflineRecognizerConfig(model: modelConfig); final recognizer sherpa_onnx.OfflineRecognizer(config);关键点非流式识别器通过OfflineModelConfig中的字段组合来区分模型类型——Paraformer 填paraformer字段并设modelType: paraformerWhisper 填whisper字段内含encoder/decoder两个路径Zipformer Transducer 填zipformer2字段内含encoder/decoder/joiner三个路径SenseVoice 填senseVoice字段。tokens指向词表文件numThreads控制 ONNX Runtime 推理线程数。4. 逐窗喂入 VAD边检测边解码final waveData sherpa_onnx.readWave(inputWav); if (waveData.sampleRate ! 16000) { print(Only 16000 Hz is supported. Given: ${waveData.sampleRate}); exit(1); } int numSamples waveData.samples.length; int numIter numSamples ~/ vadConfig.sileroVad.windowSize; for (int i 0; i ! numIter; i) { int start i * vadConfig.sileroVad.windowSize; vad.acceptWaveform(Float32List.sublistView( waveData.samples, start, start vadConfig.sileroVad.windowSize)); while (!vad.isEmpty()) { final samples vad.front().samples; final startTime vad.front().start.toDouble() / waveData.sampleRate; final endTime startTime samples.length.toDouble() / waveData.sampleRate; final stream recognizer.createStream(); stream.acceptWaveform(samples: samples, sampleRate: waveData.sampleRate); recognizer.decode(stream); final result recognizer.getResult(stream); stream.free(); print( ${startTime.toStringAsPrecision(5)} -- ${endTime.toStringAsPrecision(5)} : ${result.text}); vad.pop(); } } vad.flush();这段主循环体现了 VAD ASR 协作的核心模式采样率限制示例只接受 16 kHz 单声道 WAV其他采样率直接退出各模型运行脚本提供的测试音频均为 16 kHz。切片步长 windowSizevad.acceptWaveform每次喂入windowSize默认 512个采样点即模拟 32 ms 一帧的准实时输入这也是 VAD 模型设计的帧长用Float32List.sublistView做零拷贝切片。isEmpty/front/pop队列语义VAD 内部维护一个语音段队列。每当检测到一段完整语音满足minSilenceDuration/maxSpeechDuration切分条件该片段就进入队列主循环用while (!vad.isEmpty())依次取出front()片段、解码、打印再pop()移除。时间戳计算vad.front().start是片段起始采样点序号除以采样率得起始秒数start samples.length / sampleRate得结束秒数。vad.flush()收尾音频喂完后调用flush()把 VAD 内部残留的语音结尾没有跟随足够静音的段强制吐出队列再用同样的 while 循环解码避免丢失结尾的语音。每次解码都是“一次一片段”的独立生命周期createStream() → acceptWaveform → decode → getResult → stream.free()片段之间互不依赖这也意味着片段级错误不会相互污染。5. 资源释放程序末尾显式调用vad.free()和recognizer.free()。这两个对象持有底层 C 实例与 ONNX Runtime 会话示例的写法明确了 Dart 侧所有 native 资源都遵循“用完即释放”的约定。四、各模型的命令行参数与运行方式Paraformer中文paraformer.dartdart run \ ./bin/paraformer.dart \ --silero-vad ./silero_vad.onnx \ --model ./sherpa-onnx-paraformer-zh-2023-09-14/model.int8.onnx \ --tokens ./sherpa-onnx-paraformer-zh-2023-09-14/tokens.txt \ --input-wav ./lei-jun-test.wav对应 run-paraformer.sh使用官方发布的 Paraformer 中文模型 int8 量化版。SenseVoice多语言sense-voice.dart/sense-voice-2.dartSenseVoice 示例比 Paraformer 多两个可选参数见 bin/sense-voice.dartdart run \ ./bin/sense-voice.dart \ --silero-vad ./silero_vad.onnx \ --model ./sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17/model.onnx \ --tokens ./sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17/tokens.txt \ --use-itn true \ --input-wav ./lei-jun-test.wav--language取值为auto、zh、en、ja、ko、yue留空则自动检测默认自动--use-itntrue时启用逆文本规范化inverse text normalization把“二零二五年”这类读法还原为数字等书面形式。中文、英文两个脚本run-sense-voice-zh.sh、run-sense-voice-en.sh均传--use-itn true分别配合lei-jun-test.wav中文与Obama.wav英文演示。Whisperwhisper.dartWhisper 需要 encoder 与 decoder 两个模型文件因此多一个--decoder参数见 bin/whisper.dartdart run \ ./bin/whisper.dart \ --silero-vad ./silero_vad.onnx \ --encoder ./sherpa-onnx-whisper-tiny.en/tiny.en-encoder.int8.onnx \ --decoder ./sherpa-onnx-whisper-tiny.en/tiny.en-decoder.int8.onnx \ --tokens ./sherpa-onnx-whisper-tiny.en/tiny.en-tokens.txt \ --input-wav ./Obama.wavZipformer Transducerzipformer-transducer.dartTransducer 架构还需要 joiner 模型是三件套参数最多的一类dart run \ ./bin/zipformer-transducer.dart \ --silero-vad ./silero_vad.onnx \ --encoder ./sherpa-onnx-zipformer-gigaspeech-2023-12-12/encoder-epoch-30-avg-1.int8.onnx \ --decoder ./sherpa-onnx-zipformer-gigaspeech-2023-12-12/decoder-epoch-30-avg-1.onnx \ --joiner ./sherpa-onnx-zipformer-gigaspeech-2023-12-12/joiner-epoch-30-avg-1.int8.onnx \ --tokens ./sherpa-onnx-zipformer-gigaspeech-2023-12-12/tokens.txt \ --input-wav ./Obama.wav单文件 CTC / 其他模型telespeech-ctc.dart、zipformer-ctc.dart、dolphin-ctc.dart均只需--model单文件加--tokens与 Paraformer 的参数形态一致例如 run-telespeech-ctc.shdart run \ ./bin/telespeech-ctc.dart \ --silero-vad ./silero_vad.onnx \ --model ./sherpa-onnx-telespeech-ctc-int8-zh-2024-06-04/model.int8.onnx \ --tokens ./sherpa-onnx-telespeech-ctc-int8-zh-2024-06-04/tokens.txt \ --input-wav ./lei-jun-test.wavmoonshine.dart参数最多需要--preprocessor、--encoder、--uncached-decoder、--cached-decoder四个模型文件加--tokens完整命令见 run-moonshine.sh。五、输出格式与结果解读所有示例的输出统一为如下格式时间单位秒保留 5 位有效数字0.40000 -- 2.32000 : 你好这里是 sherpa-onnx 5.10000 -- 7.00000 : 第二句话的内容每一行对应 VAD 切出的一个语音段左端是片段起始时间右端是结束时间冒号后是非流式识别器输出的文本。由于 VAD 参数minSilenceDuration、maxSpeechDuration等决定切分粒度调整这些值会直接影响行数与每行文本的长短maxSpeechDuration调小可得到更细的时间戳minSilenceDuration调大则更不容易把一句话截断。六、小结与扩展方向dart-api-examples/vad-with-non-streaming-asr/演示的是 sherpa-onnx 处理离线长音频的标准范式Silero VAD 做端点检测 → 每段独立创建 stream 送入 OfflineRecognizer → flush 收尾 → 显式释放资源。这套模式在仓库的其他语言示例如 python-api-examples/vad-with-non-streaming-asr.py中同样成立Dart 版本可直接用于命令行批处理、桌面应用与 Webflutter/sherpa_onnx包同时提供 FFI 与 Web 两套实现。实际集成时可以按需调整的点切分策略修改SileroVadModelConfig的minSilenceDuration/minSpeechDuration/maxSpeechDuration与threshold平衡句级时间戳精度与切分稳定性性能调大VadModelConfig.numThreads与OfflineModelConfig.numThreads示例为保持跨平台结果一致固定为 1模型替换只需替换OfflineModelConfig中对应模型类型的子配置如换成 Whisper 或 Zipformer CTCVAD 部分与主循环代码完全复用实时场景本示例按文件整段处理若要从麦克风准实时处理思路不变只是把“逐窗读文件”换成“逐窗读音频输入设备”仓库中其他 Dart 示例如 dart-api-examples/streaming-asr/展示了流式侧的写法。【免费下载链接】sherpa-onnxSpeech-to-text, text-to-speech, speaker diarization, speech enhancement, source separation, and VAD using next-gen Kaldi with onnxruntime without Internet connection. Support embedded systems, Android, iOS, HarmonyOS, Raspberry Pi, RISC-V, RK NPU, Axera NPU, Ascend NPU, x86_64 servers, websocket server/client, support 12 programming languages项目地址: https://gitcode.com/GitHub_Trending/sh/sherpa-onnx创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考