Skip to content

Commit 66c7225

Browse files
authored
Add more examples for offline ASR models with Rust API. (k2-fsa#3209)
This PR adds additional Rust API examples for offline ASR (Automatic Speech Recognition) models, expanding coverage to include Nemo Parakeet, Zipformer variants for different languages (Vietnamese, Chinese-English, English), and updates configuration options.
1 parent 5570bac commit 66c7225

9 files changed

Lines changed: 217 additions & 2 deletions

File tree

.github/scripts/test-rust.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ cd rust-api-examples
66

77
./run-version.sh
88

9+
./run-nemo-parakeet-en.sh
10+
./run-zipformer-vi.sh
11+
./run-zipformer-zh-en.sh
12+
./run-zipformer-en.sh
13+
914
./run-sense-voice.sh
1015

1116
./run-streaming-zipformer-en.sh
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Copyright (c) 2026 Xiaomi Corporation
2+
//
3+
// This file demonstrates how to use Nemo Parakeet with sherpa-onnx's Rust API
4+
// for offline speech recognition.
5+
//
6+
// See ../README.md for how to run it.
7+
8+
use clap::Parser;
9+
use sherpa_onnx::{OfflineRecognizer, OfflineRecognizerConfig, OfflineTransducerModelConfig, Wave};
10+
use std::time::Instant;
11+
12+
/// Nemo Parakeet offline example
13+
#[derive(Parser, Debug)]
14+
#[command(author, version, about, long_about = None)]
15+
struct Args {
16+
/// Path to WAV file
17+
#[arg(long)]
18+
wav: String,
19+
20+
/// Path to encoder ONNX model
21+
#[arg(long)]
22+
encoder: String,
23+
24+
/// Path to decoder ONNX model
25+
#[arg(long)]
26+
decoder: String,
27+
28+
/// Path to joiner ONNX model
29+
#[arg(long)]
30+
joiner: String,
31+
32+
/// Path to tokens file
33+
#[arg(long)]
34+
tokens: String,
35+
36+
/// Provider (default: cpu)
37+
#[arg(long, default_value = "cpu")]
38+
provider: String,
39+
40+
/// Enable debug logs
41+
#[arg(long, default_value_t = false)]
42+
debug: bool,
43+
44+
/// Number of threads
45+
#[arg(long, default_value_t = 2)]
46+
num_threads: i32,
47+
}
48+
49+
fn main() {
50+
let args = Args::parse();
51+
52+
let wave = Wave::read(&args.wav).expect("Failed to read WAV file");
53+
let audio_duration = wave.samples().len() as f64 / wave.sample_rate() as f64;
54+
55+
// Create default recognizer config
56+
let mut recognizer_config = OfflineRecognizerConfig::default();
57+
58+
// Set the transducer model
59+
recognizer_config.model_config.transducer = OfflineTransducerModelConfig {
60+
encoder: Some(args.encoder.clone()),
61+
decoder: Some(args.decoder.clone()),
62+
joiner: Some(args.joiner.clone()),
63+
};
64+
65+
recognizer_config.model_config.tokens = Some(args.tokens.clone());
66+
recognizer_config.model_config.provider = Some(args.provider.clone());
67+
recognizer_config.model_config.debug = args.debug;
68+
recognizer_config.model_config.num_threads = args.num_threads;
69+
70+
// Measure recognizer creation time
71+
println!("Creating recognizer ...");
72+
let start_creation = Instant::now();
73+
let recognizer =
74+
OfflineRecognizer::create(&recognizer_config).expect("Failed to create OfflineRecognizer");
75+
let creation_elapsed = start_creation.elapsed().as_secs_f64();
76+
println!("Recognizer created in {:.3} seconds.", creation_elapsed);
77+
78+
let stream = recognizer.create_stream();
79+
80+
// Measure recognition time
81+
let start_recognition = Instant::now();
82+
stream.accept_waveform(wave.sample_rate(), wave.samples());
83+
recognizer.decode(&stream);
84+
let recognition_elapsed = start_recognition.elapsed().as_secs_f64();
85+
86+
// Get recognition result
87+
if let Some(result) = stream.get_result() {
88+
println!("Decoded text: {}", result.text);
89+
90+
let total_elapsed = creation_elapsed + recognition_elapsed;
91+
let rtf = recognition_elapsed / audio_duration;
92+
93+
println!("\n=== Performance Summary ===");
94+
println!("Audio duration : {:.3} seconds", audio_duration);
95+
println!("Recognizer creation time: {:.3} seconds", creation_elapsed);
96+
println!(
97+
"Recognition time : {:.3} seconds",
98+
recognition_elapsed
99+
);
100+
println!("Total elapsed time : {:.3} seconds", total_elapsed);
101+
102+
println!(
103+
"Real-Time Factor (RTF) : {:.3} (recognition_elapsed / audio_duration = {:.3} / {:.3})",
104+
rtf, recognition_elapsed, audio_duration
105+
);
106+
107+
println!(
108+
"Number of threads : {}",
109+
recognizer_config.model_config.num_threads
110+
);
111+
} else {
112+
eprintln!("Failed to get recognition result");
113+
}
114+
}

rust-api-examples/examples/sense_voice.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ struct Args {
4040
/// Enable inverse text normalization
4141
#[arg(long, default_value_t = true)]
4242
use_itn: bool,
43+
44+
/// Number of threads
45+
#[arg(long, default_value_t = 2)]
46+
num_threads: i32,
4347
}
4448

4549
fn main() {
@@ -59,7 +63,7 @@ fn main() {
5963
recognizer_config.model_config.tokens = Some(args.tokens.clone());
6064
recognizer_config.model_config.provider = Some(args.provider.clone());
6165
recognizer_config.model_config.debug = args.debug;
62-
recognizer_config.model_config.num_threads = 2;
66+
recognizer_config.model_config.num_threads = args.num_threads;
6367

6468
// Measure recognizer creation time
6569
println!("Creating recognizer ...");
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
nemo_parakeet.rs
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/env bash
2+
set -ex
3+
4+
# See also
5+
# https://k2-fsa.github.io/sherpa/onnx/pretrained_models/offline-transducer/nemo-transducer-models.html#sherpa-onnx-nemo-parakeet-tdt-0-6b-v2-int8-english
6+
if [ ! -f "./sherpa-onnx-nemo-parakeet-tdt-0.6b-v2-int8/encoder.int8.onnx" ]; then
7+
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-nemo-parakeet-tdt-0.6b-v2-int8.tar.bz2
8+
tar xvf sherpa-onnx-nemo-parakeet-tdt-0.6b-v2-int8.tar.bz2
9+
rm sherpa-onnx-nemo-parakeet-tdt-0.6b-v2-int8.tar.bz2
10+
ls -lh sherpa-onnx-nemo-parakeet-tdt-0.6b-v2-int8
11+
fi
12+
13+
# Run Rust Nemo Parakeet example
14+
cargo run --example nemo_parakeet -- \
15+
--wav "./sherpa-onnx-nemo-parakeet-tdt-0.6b-v2-int8/test_wavs/0.wav" \
16+
--encoder "./sherpa-onnx-nemo-parakeet-tdt-0.6b-v2-int8/encoder.int8.onnx" \
17+
--decoder "./sherpa-onnx-nemo-parakeet-tdt-0.6b-v2-int8/decoder.int8.onnx" \
18+
--joiner "./sherpa-onnx-nemo-parakeet-tdt-0.6b-v2-int8/joiner.int8.onnx" \
19+
--tokens "./sherpa-onnx-nemo-parakeet-tdt-0.6b-v2-int8/tokens.txt" \
20+
--provider cpu \
21+
--num-threads 2 \
22+
--debug

rust-api-examples/run-sense-voice.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,6 @@ fi
1414
cargo run --example sense_voice -- \
1515
--wav ./sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17/test_wavs/en.wav \
1616
--model ./sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17/model.int8.onnx \
17-
--tokens ./sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17/tokens.txt
17+
--tokens ./sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2024-07-17/tokens.txt \
18+
--num-threads 2 \
19+
--debug
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env bash
2+
set -ex
3+
4+
# see also
5+
# https://k2-fsa.github.io/sherpa/onnx/pretrained_models/offline-transducer/zipformer-transducer-models.html#icefall-asr-multidataset-pruned-transducer-stateless7-2023-05-04-english
6+
if [ ! -f "./icefall-asr-multidataset-pruned_transducer_stateless7-2023-05-04/data/lang_bpe_500/tokens.txt" ]; then
7+
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/icefall-asr-multidataset-pruned_transducer_stateless7-2023-05-04.tar.bz2
8+
9+
tar xvf icefall-asr-multidataset-pruned_transducer_stateless7-2023-05-04.tar.bz2
10+
rm icefall-asr-multidataset-pruned_transducer_stateless7-2023-05-04.tar.bz2
11+
ls -lh icefall-asr-multidataset-pruned_transducer_stateless7-2023-05-04
12+
fi
13+
14+
# Run Zipformer transducer
15+
cargo run --example zipformer -- \
16+
--wav "./icefall-asr-multidataset-pruned_transducer_stateless7-2023-05-04/test_wavs/1089-134686-0001.wav" \
17+
--tokens=./icefall-asr-multidataset-pruned_transducer_stateless7-2023-05-04/data/lang_bpe_500/tokens.txt \
18+
--encoder=./icefall-asr-multidataset-pruned_transducer_stateless7-2023-05-04/exp/encoder-epoch-30-avg-4.int8.onnx \
19+
--decoder=./icefall-asr-multidataset-pruned_transducer_stateless7-2023-05-04/exp/decoder-epoch-30-avg-4.onnx \
20+
--joiner=./icefall-asr-multidataset-pruned_transducer_stateless7-2023-05-04/exp/joiner-epoch-30-avg-4.int8.onnx \
21+
--provider cpu \
22+
--num-threads 2 \
23+
--debug
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/env bash
2+
set -ex
3+
4+
# see also
5+
# https://k2-fsa.github.io/sherpa/onnx/pretrained_models/offline-transducer/zipformer-transducer-models.html#sherpa-onnx-zipformer-vi-30m-int8-2026-02-09-vietnamese
6+
if [ ! -f "./sherpa-onnx-zipformer-vi-30M-int8-2026-02-09/encoder.int8.onnx" ]; then
7+
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-zipformer-vi-30M-int8-2026-02-09.tar.bz2
8+
tar xvf sherpa-onnx-zipformer-vi-30M-int8-2026-02-09.tar.bz2
9+
rm sherpa-onnx-zipformer-vi-30M-int8-2026-02-09.tar.bz2
10+
ls -lh sherpa-onnx-zipformer-vi-30M-int8-2026-02-09
11+
fi
12+
13+
# Run Zipformer transducer
14+
cargo run --example zipformer -- \
15+
--wav "./sherpa-onnx-zipformer-vi-30M-int8-2026-02-09/test_wavs/0.wav" \
16+
--encoder "./sherpa-onnx-zipformer-vi-30M-int8-2026-02-09/encoder.int8.onnx" \
17+
--decoder "./sherpa-onnx-zipformer-vi-30M-int8-2026-02-09/decoder.onnx" \
18+
--joiner "./sherpa-onnx-zipformer-vi-30M-int8-2026-02-09/joiner.int8.onnx" \
19+
--tokens "./sherpa-onnx-zipformer-vi-30M-int8-2026-02-09/tokens.txt" \
20+
--provider cpu \
21+
--num-threads 2 \
22+
--debug
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/env bash
2+
set -ex
3+
4+
# see also
5+
# https://k2-fsa.github.io/sherpa/onnx/pretrained_models/offline-transducer/zipformer-transducer-models.html#sherpa-onnx-zipformer-zh-en-2023-11-22-chinese-english
6+
if [ ! -f "./sherpa-onnx-zipformer-zh-en-2023-11-22/encoder-epoch-34-avg-19.int8.onnx" ]; then
7+
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-zipformer-zh-en-2023-11-22.tar.bz2
8+
tar xvf sherpa-onnx-zipformer-zh-en-2023-11-22.tar.bz2
9+
rm sherpa-onnx-zipformer-zh-en-2023-11-22.tar.bz2
10+
ls -lh sherpa-onnx-zipformer-zh-en-2023-11-22
11+
fi
12+
13+
# Run Zipformer transducer
14+
cargo run --example zipformer -- \
15+
--wav "./sherpa-onnx-zipformer-zh-en-2023-11-22/test_wavs/0.wav" \
16+
--encoder "./sherpa-onnx-zipformer-zh-en-2023-11-22/encoder-epoch-34-avg-19.int8.onnx" \
17+
--decoder "./sherpa-onnx-zipformer-zh-en-2023-11-22/decoder-epoch-34-avg-19.onnx" \
18+
--joiner "./sherpa-onnx-zipformer-zh-en-2023-11-22/joiner-epoch-34-avg-19.int8.onnx" \
19+
--tokens "./sherpa-onnx-zipformer-zh-en-2023-11-22/tokens.txt" \
20+
--provider cpu \
21+
--num-threads 2 \
22+
--debug

0 commit comments

Comments
 (0)