feat: promote linux native audio path
This commit is contained in:
@@ -33,12 +33,16 @@
|
||||
//! ## Fallback
|
||||
//!
|
||||
//! `try_new` returns `None` when the model file is missing, the ONNX
|
||||
//! Runtime is unavailable, or the platform is not iOS/macOS. The caller
|
||||
//! Runtime is unavailable, or the platform cannot load ONNX Runtime. The caller
|
||||
//! falls back to `WebRtcFallbackVad`.
|
||||
|
||||
use super::{VadOutput, VoiceActivityDetector};
|
||||
#[cfg(target_os = "linux")]
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
#[cfg(target_os = "linux")]
|
||||
use std::sync::OnceLock;
|
||||
use std::thread::JoinHandle;
|
||||
|
||||
/// 16 kHz frame size for Silero VAD v6 (32 ms).
|
||||
@@ -88,7 +92,7 @@ impl SileroOnnxVad {
|
||||
/// Attempt to load the Silero v6 ONNX model from `model_path`.
|
||||
///
|
||||
/// Returns `None` when the model file is missing, the ONNX Runtime
|
||||
/// is unavailable, or the platform does not support ONNX.
|
||||
/// is unavailable, or the platform cannot initialize ONNX Runtime.
|
||||
pub fn try_new(model_path: &str) -> Option<Self> {
|
||||
Self::try_new_onnx(model_path)
|
||||
}
|
||||
@@ -105,6 +109,17 @@ impl SileroOnnxVad {
|
||||
return None;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
if let Err(err) = ensure_linux_onnxruntime_loaded() {
|
||||
error!(
|
||||
target: "chanora_audio",
|
||||
path = model_path,
|
||||
error = %err,
|
||||
"SileroOnnxVad: ONNX Runtime is unavailable on Linux; falling back to WebRtcFallbackVad"
|
||||
);
|
||||
return None;
|
||||
}
|
||||
|
||||
let session_result = std::panic::catch_unwind(|| {
|
||||
ort::session::Session::builder().and_then(|mut b| b.commit_from_file(model_path))
|
||||
});
|
||||
@@ -301,6 +316,111 @@ impl VoiceActivityDetector for SileroOnnxVad {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn ensure_linux_onnxruntime_loaded() -> Result<(), String> {
|
||||
static ORT_INIT: OnceLock<Result<(), String>> = OnceLock::new();
|
||||
|
||||
ORT_INIT
|
||||
.get_or_init(|| {
|
||||
let dylib_path = linux_onnxruntime_dylib_path();
|
||||
match dylib_path.as_ref() {
|
||||
Some(path) => {
|
||||
tracing::info!(
|
||||
target: "chanora_audio",
|
||||
path = %path.display(),
|
||||
"SileroOnnxVad: loading Linux ONNX Runtime from discovered shared library"
|
||||
);
|
||||
ort::init_from(path)
|
||||
.map_err(|e| format!("init_from({}): {e}", path.display()))?
|
||||
.with_name("chanora_audio")
|
||||
.commit()
|
||||
.then_some(())
|
||||
.ok_or_else(|| {
|
||||
format!("commit(init_from {}): environment already initialized", path.display())
|
||||
})
|
||||
}
|
||||
None => {
|
||||
tracing::warn!(
|
||||
target: "chanora_audio",
|
||||
"SileroOnnxVad: no explicit Linux ONNX Runtime shared library path found; trying loader default"
|
||||
);
|
||||
ort::init()
|
||||
.with_name("chanora_audio")
|
||||
.commit()
|
||||
.then_some(())
|
||||
.ok_or_else(|| "commit(init): environment already initialized".to_string())
|
||||
}
|
||||
}
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn linux_onnxruntime_dylib_path() -> Option<PathBuf> {
|
||||
if let Some(path) = existing_env_file("ORT_DYLIB_PATH") {
|
||||
return Some(path);
|
||||
}
|
||||
|
||||
let mut candidates = Vec::new();
|
||||
|
||||
if let Ok(exe) = std::env::current_exe() {
|
||||
if let Some(exe_dir) = exe.parent() {
|
||||
candidates.push(exe_dir.join("lib").join("libonnxruntime.so"));
|
||||
candidates.push(exe_dir.join("libonnxruntime.so"));
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(cwd) = std::env::current_dir() {
|
||||
candidates.push(cwd.join("libonnxruntime.so"));
|
||||
}
|
||||
|
||||
candidates.extend([
|
||||
PathBuf::from("/usr/lib/libonnxruntime.so"),
|
||||
PathBuf::from("/usr/lib64/libonnxruntime.so"),
|
||||
PathBuf::from("/usr/local/lib/libonnxruntime.so"),
|
||||
PathBuf::from("/lib/x86_64-linux-gnu/libonnxruntime.so"),
|
||||
PathBuf::from("/usr/lib/x86_64-linux-gnu/libonnxruntime.so"),
|
||||
PathBuf::from("/lib/aarch64-linux-gnu/libonnxruntime.so"),
|
||||
PathBuf::from("/usr/lib/aarch64-linux-gnu/libonnxruntime.so"),
|
||||
]);
|
||||
|
||||
for candidate in candidates {
|
||||
if candidate.is_file() {
|
||||
return Some(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
first_matching_dir_entry("/usr/lib", "libonnxruntime.so")
|
||||
.or_else(|| first_matching_dir_entry("/usr/lib64", "libonnxruntime.so"))
|
||||
.or_else(|| first_matching_dir_entry("/usr/local/lib", "libonnxruntime.so"))
|
||||
.or_else(|| first_matching_dir_entry("/usr/lib/x86_64-linux-gnu", "libonnxruntime.so"))
|
||||
.or_else(|| first_matching_dir_entry("/usr/lib/aarch64-linux-gnu", "libonnxruntime.so"))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn existing_env_file(name: &str) -> Option<PathBuf> {
|
||||
let path = std::env::var_os(name).map(PathBuf::from)?;
|
||||
path.is_file().then_some(path)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn first_matching_dir_entry(dir: &str, prefix: &str) -> Option<PathBuf> {
|
||||
let mut matches: Vec<PathBuf> = std::fs::read_dir(dir)
|
||||
.ok()?
|
||||
.filter_map(|entry| entry.ok())
|
||||
.map(|entry| entry.path())
|
||||
.filter(|path| {
|
||||
path.is_file()
|
||||
&& path
|
||||
.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
.is_some_and(|name| name.starts_with(prefix))
|
||||
})
|
||||
.collect();
|
||||
matches.sort();
|
||||
matches.into_iter().next()
|
||||
}
|
||||
|
||||
// SAFETY: ONNX Runtime sessions are thread-safe for inference.
|
||||
// State arrays are owned by this struct and accessed only from
|
||||
// the single capture callback thread.
|
||||
@@ -401,6 +521,8 @@ impl Drop for SileroOnnxVadWorker {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[cfg(target_os = "linux")]
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn make_stub_vad() -> SileroOnnxVad {
|
||||
SileroOnnxVad {
|
||||
@@ -496,4 +618,17 @@ mod tests {
|
||||
&completed_frame[SILERO_FRAME_16K - SILERO_CONTEXT_16K..]
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn existing_env_file_ignores_missing_paths() {
|
||||
let var_name = format!("CHANORA_TEST_ORT_{}", std::process::id());
|
||||
std::env::remove_var(&var_name);
|
||||
assert!(existing_env_file(&var_name).is_none());
|
||||
|
||||
let missing = PathBuf::from("/definitely/missing/libonnxruntime.so");
|
||||
std::env::set_var(&var_name, &missing);
|
||||
assert!(existing_env_file(&var_name).is_none());
|
||||
std::env::remove_var(&var_name);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user