mirror of
https://github.com/k2-fsa/sherpa-onnx.git
synced 2026-11-10 13:04:05 +08:00
Add Rust API for Moonshine v2 (#3247)
This pull request integrates the latest Moonshine v2 model architecture into the sherpa-onnx Rust API, enhancing its capabilities for offline speech recognition. The changes include extending the model configuration to support Moonshine v2's merged decoder, providing a new example to showcase its usage, and updating related scripts and crate versions. This allows Rust developers to leverage the improved performance and features of Moonshine v2 models.
This commit is contained in:
1 parent
13ab9970b5
commit
a579b72bb6
9 files changed
+143
-10
No files matched your search
@@ -6,6 +6,8 @@ cd rust-api-examples
|
||||
|
||||
./run-version.sh
|
||||
|
||||
./run-moonshine-v2.sh
|
||||
|
||||
./run-fire-red-asr-ctc.sh
|
||||
|
||||
./run-silero-vad-remove-silence.sh
|
||||
|
||||
Generated
+5
-5
@@ -553,7 +553,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-api-examples"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -621,9 +621,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sherpa-onnx"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5bc662a40347268c9088f8e52f47e39268c0c5da80c4d30c38e40464e6f6fef"
|
||||
checksum = "2cb3cd528fb1ab43135fd7a68d45720910da8d6c036b97808125a456f1b86ffb"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -632,9 +632,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sherpa-onnx-sys"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a5d16f9c5d442c96ca2fdfc5f5344fd58c93da7b9182707e5885f98a67f3682"
|
||||
checksum = "50748be67c68ff33d4c21c934f8d7c7067bb4ca766583864f05b03219a55d4e9"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
[package]
|
||||
name = "rust-api-examples"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
sherpa-onnx = "0.1.8"
|
||||
sherpa-onnx = "0.1.9"
|
||||
# sherpa-onnx = { path = "../sherpa-onnx/rust/sherpa-onnx" }
|
||||
|
||||
cpal = { version = "0.16", optional = true } # cross-platform audio I/O
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
// Copyright (c) 2026 Xiaomi Corporation
|
||||
//
|
||||
// This file demonstrates how to use a Moonshine v2 model with sherpa-onnx's Rust API
|
||||
// for offline speech recognition.
|
||||
//
|
||||
// See ../README.md for how to run it.
|
||||
|
||||
use clap::Parser;
|
||||
use sherpa_onnx::{OfflineRecognizer, OfflineRecognizerConfig, Wave};
|
||||
use std::time::Instant;
|
||||
|
||||
/// Moonshine v2 offline example
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct Args {
|
||||
/// Path to WAV file
|
||||
#[arg(long)]
|
||||
wav: String,
|
||||
|
||||
/// Path to the encoder model
|
||||
#[arg(long)]
|
||||
encoder: String,
|
||||
|
||||
/// Path to the decoder model
|
||||
#[arg(long)]
|
||||
decoder: String,
|
||||
|
||||
/// Path to tokens file
|
||||
#[arg(long)]
|
||||
tokens: String,
|
||||
|
||||
/// Provider (default: cpu)
|
||||
#[arg(long, default_value = "cpu")]
|
||||
provider: String,
|
||||
|
||||
/// Enable debug logs
|
||||
#[arg(long, default_value_t = false)]
|
||||
debug: bool,
|
||||
|
||||
/// Number of threads
|
||||
#[arg(long, default_value_t = 2)]
|
||||
num_threads: i32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
|
||||
let wave = Wave::read(&args.wav).expect("Failed to read WAV file");
|
||||
let audio_duration = wave.samples().len() as f64 / wave.sample_rate() as f64;
|
||||
|
||||
let mut recognizer_config = OfflineRecognizerConfig::default();
|
||||
|
||||
recognizer_config.model_config.moonshine.encoder = Some(args.encoder.clone());
|
||||
recognizer_config.model_config.moonshine.merged_decoder = Some(args.decoder.clone());
|
||||
|
||||
recognizer_config.model_config.tokens = Some(args.tokens.clone());
|
||||
recognizer_config.model_config.provider = Some(args.provider.clone());
|
||||
recognizer_config.model_config.debug = args.debug;
|
||||
recognizer_config.model_config.num_threads = args.num_threads;
|
||||
|
||||
// Measure recognizer creation time
|
||||
println!("Creating recognizer ...");
|
||||
let start_creation = Instant::now();
|
||||
let recognizer =
|
||||
OfflineRecognizer::create(&recognizer_config).expect("Failed to create OfflineRecognizer");
|
||||
let creation_elapsed = start_creation.elapsed().as_secs_f64();
|
||||
println!("Recognizer created in {:.3} seconds.", creation_elapsed);
|
||||
|
||||
let stream = recognizer.create_stream();
|
||||
|
||||
// Measure recognition time
|
||||
let start_recognition = Instant::now();
|
||||
stream.accept_waveform(wave.sample_rate(), wave.samples());
|
||||
recognizer.decode(&stream);
|
||||
let recognition_elapsed = start_recognition.elapsed().as_secs_f64();
|
||||
|
||||
// Get recognition result
|
||||
if let Some(result) = stream.get_result() {
|
||||
println!("Decoded text: {}", result.text);
|
||||
|
||||
let total_time = creation_elapsed + recognition_elapsed;
|
||||
let rtf = recognition_elapsed / audio_duration;
|
||||
|
||||
println!("\n=== Performance Summary ===");
|
||||
println!("Audio duration : {:.3} seconds", audio_duration);
|
||||
println!("Recognizer creation time: {:.3} seconds", creation_elapsed);
|
||||
println!(
|
||||
"Recognition time : {:.3} seconds",
|
||||
recognition_elapsed
|
||||
);
|
||||
println!("Total elapsed time : {:.3} seconds", total_time);
|
||||
|
||||
// Detailed RTF computation log
|
||||
println!(
|
||||
"Real-Time Factor (RTF) : {:.3} (recognition_elapsed / audio_duration = {:.3} / {:.3})",
|
||||
rtf, recognition_elapsed, audio_duration
|
||||
);
|
||||
|
||||
println!(
|
||||
"Number of threads : {}",
|
||||
recognizer_config.model_config.num_threads
|
||||
);
|
||||
} else {
|
||||
eprintln!("Failed to get recognition result");
|
||||
}
|
||||
}
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
set -ex
|
||||
|
||||
# see
|
||||
# https://k2-fsa.github.io/sherpa/onnx/moonshine
|
||||
if [ ! -f ./sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27/encoder_model.ort ]; then
|
||||
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27.tar.bz2
|
||||
tar xvf sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27.tar.bz2
|
||||
rm sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27.tar.bz2
|
||||
fi
|
||||
|
||||
cargo run --example moonshine_v2 -- \
|
||||
--wav ./sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27/test_wavs/0.wav \
|
||||
--encoder ./sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27/encoder_model.ort \
|
||||
--decoder ./sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27/decoder_model_merged.ort \
|
||||
--tokens ./sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27/tokens.txt \
|
||||
--num-threads 2
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "sherpa-onnx-sys"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
edition = "2021"
|
||||
description = "Raw FFI bindings to the sherpa-onnx C API"
|
||||
license = "Apache-2.0"
|
||||
|
||||
@@ -60,6 +60,7 @@ pub struct OfflineMoonshineModelConfig {
|
||||
pub encoder: *const c_char,
|
||||
pub uncached_decoder: *const c_char,
|
||||
pub cached_decoder: *const c_char,
|
||||
pub merged_decoder: *const c_char,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "sherpa-onnx"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
edition = "2021"
|
||||
description = "Safe Rust wrapper for sherpa-onnx speech recognition toolkit"
|
||||
license = "Apache-2.0"
|
||||
@@ -20,6 +20,6 @@ include = [
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
sherpa-onnx-sys = { path = "../sherpa-onnx-sys", version = "0.1.8" }
|
||||
sherpa-onnx-sys = { path = "../sherpa-onnx-sys", version = "0.1.9" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
@@ -107,12 +107,18 @@ impl OfflineFireRedAsrModelConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// For Moonshine v1, you need 4 models:
|
||||
/// - preprocessor, encoder, uncached_decoder, cached_decoder
|
||||
///
|
||||
/// For Moonshine v2, you need 2 models:
|
||||
/// - encoder, merged_decoder
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct OfflineMoonshineModelConfig {
|
||||
pub preprocessor: Option<String>,
|
||||
pub encoder: Option<String>,
|
||||
pub uncached_decoder: Option<String>,
|
||||
pub cached_decoder: Option<String>,
|
||||
pub merged_decoder: Option<String>,
|
||||
}
|
||||
|
||||
impl OfflineMoonshineModelConfig {
|
||||
@@ -122,6 +128,7 @@ impl OfflineMoonshineModelConfig {
|
||||
encoder: to_c_ptr(&self.encoder, cstrings),
|
||||
uncached_decoder: to_c_ptr(&self.uncached_decoder, cstrings),
|
||||
cached_decoder: to_c_ptr(&self.cached_decoder, cstrings),
|
||||
merged_decoder: to_c_ptr(&self.merged_decoder, cstrings),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user