Add ten-vad Rust API example to remove silences from a file (#3778)

This commit is contained in:
Santiago Pablo Ortiz authored and GitHub committed 2026-07-23 14:15:48 +08:00
1 parent f7982bd7dd
commit 64cc667a8a
4 files changed
+147

No files matched your search

+2
View File
@@ -81,6 +81,8 @@ rm -rf sherpa-onnx-punct-ct-transformer-zh-en-vocab272727-2024-04-12-int8
./run-silero-vad-remove-silence.sh
./run-ten-vad-remove-silence.sh
./run-nemo-parakeet-en.sh
./run-zipformer-vi.sh
./run-zipformer-zh-en.sh
+7
View File
@@ -91,6 +91,7 @@ in your own Cargo project, see
| 46 | [zipformer_transducer_simulate_streaming_microphone](#example-46-simulated-streaming-asr-with-zipformer-transducer-japanese-and-vad-from-microphone) | Simulated streaming ASR with Zipformer transducer (Japanese) and VAD from microphone |
| 47 | [qwen3_asr_simulate_streaming_microphone](#example-47-simulated-streaming-asr-with-qwen3-asr-and-vad-from-microphone) | Simulated streaming ASR with Qwen3 ASR and VAD from microphone |
| 48 | [whisper](#example-48-asr-with-non-streaming-whisper) | Non-streaming ASR with Whisper (multilingual) |
| 49 | [ten_vad_remove_silence](#example-49-remove-silences-from-a-file-using-ten-vad) | Remove silences from an audio file using ten-vad |
## Run it
@@ -422,3 +423,9 @@ Qwen3 ASR recognizer on each detected segment.
```bash
./run-whisper.sh
```
### Example 49: Remove silences from a file using ten-vad
```bash
./run-ten-vad-remove-silence.sh
```
@@ -0,0 +1,122 @@
// Copyright (c) 2026 Xiaomi Corporation
//
// This file demonstrates how to use ten-vad with sherpa-onnx's
// Rust API to remove non-speech segments and save speech-only audio.
//
// See ../README.md for how to run it
use clap::Parser;
use sherpa_onnx::{self, TenVadModelConfig, VadModelConfig, VoiceActivityDetector, Wave};
/// Simple VAD example: remove non-speech segments from a WAV file using ten-vad
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
/// Path to input WAV file
#[arg(long)]
input: String,
/// Path to output WAV file
#[arg(long)]
output: String,
/// Path to ten-vad ONNX model
#[arg(long)]
ten_vad_model: String,
}
fn main() -> anyhow::Result<()> {
let args = Args::parse();
// Read WAV file
let wave = Wave::read(&args.input)
.ok_or_else(|| anyhow::anyhow!("Failed to read WAV file: {}", &args.input))?;
let sample_rate = wave.sample_rate();
let input_num_samples = wave.num_samples();
let input_duration = input_num_samples as f32 / sample_rate as f32;
println!(
"Input WAV: sample rate: {}, num samples: {}, duration: {:.2}s",
sample_rate, input_num_samples, input_duration
);
if sample_rate != 16000 {
anyhow::bail!(
"ten-vad expects a sample rate of 16000 Hz, but the input file has {} Hz",
sample_rate
);
}
// Configure VAD
let mut ten_vad_config = TenVadModelConfig::default();
ten_vad_config.model = Some(args.ten_vad_model);
// ten-vad expects a window size of 256 samples at 16 kHz.
// Please don't change it unless you know what you are doing.
ten_vad_config.window_size = 256;
// You can tune the values below
ten_vad_config.threshold = 0.5;
ten_vad_config.min_silence_duration = 0.25;
ten_vad_config.min_speech_duration = 0.5;
ten_vad_config.max_speech_duration = 5.0;
let window_size = ten_vad_config.window_size as usize;
let vad_config = VadModelConfig {
silero_vad: Default::default(),
ten_vad: ten_vad_config,
sample_rate,
num_threads: 1,
provider: Some("cpu".to_string()),
debug: false,
};
let vad = VoiceActivityDetector::create(&vad_config, 30.0)
.ok_or_else(|| anyhow::anyhow!("Failed to create VoiceActivityDetector"))?;
let mut speech_samples = Vec::with_capacity(input_num_samples as usize);
for chunk in wave.samples().chunks(window_size) {
vad.accept_waveform(chunk);
while let Some(seg) = vad.front() {
speech_samples.extend_from_slice(seg.samples());
vad.pop();
}
}
vad.flush();
while let Some(seg) = vad.front() {
speech_samples.extend_from_slice(seg.samples());
vad.pop();
}
// Write speech-only samples to output WAV
let ok = sherpa_onnx::write(&args.output, &speech_samples, sample_rate);
if !ok {
anyhow::bail!("Failed to save speech-only audio to {}", args.output);
}
println!("Saved speech-only audio to {}", args.output);
// Summary
let output_num_samples = speech_samples.len();
let output_duration = output_num_samples as f32 / sample_rate as f32;
println!("\n=== Summary ===");
println!(
"Input: sample rate = {}, samples = {}, duration = {:.2}s",
sample_rate, input_num_samples, input_duration
);
println!(
"Output: sample rate = {}, samples = {}, duration = {:.2}s",
sample_rate, output_num_samples, output_duration
);
if input_duration > 0.0 {
println!(
"Removed non-speech: {:.2}% of input removed",
100.0 * (1.0 - output_duration / input_duration)
);
}
Ok(())
}
+16
View File
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -ex
# https://k2-fsa.github.io/sherpa/onnx/vad/index.html
if [ ! -f "./ten-vad.onnx" ]; then
curl -SLf -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/ten-vad.onnx
fi
if [ ! -f ./lei-jun-test.wav ]; then
curl -SLf -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/lei-jun-test.wav
fi
cargo run --example ten_vad_remove_silence -- \
--input ./lei-jun-test.wav \
--output ./no-silence-ten-vad.wav \
--ten-vad-model ./ten-vad.onnx