use std::path::Path;
use anyhow::{Context, Result};
use parking_lot::Mutex;
use crate::runtime::{
factory::RuntimeFactory,
session::RuntimeSession,
tensor::{Shape, Tensor, TensorData},
};
use super::{VAD_FRAME_SAMPLES, VAD_SAMPLE_RATE, VAD_STATE_LEN, VadConfig, regions_from_probs};
pub struct SileroVad {
session: Mutex<Box<dyn RuntimeSession>>,
input_tensors: Mutex<Vec<Tensor>>,
}
impl SileroVad {
pub fn load(model_path: &Path) -> Result<Self> {
let factory = crate::runtime::cpu_factory();
Self::load_with_factory(model_path, factory.as_ref())
}
pub fn load_with_factory(model_path: &Path, factory: &dyn RuntimeFactory) -> Result<Self> {
tracing::debug!("Loading VAD model from {}", model_path.display());
let runtime = factory
.cpu_fallback()
.create(1)
.map_err(|e| anyhow::anyhow!(e))
.context("Failed to create runtime for VAD model")?;
let session = runtime
.load_session(model_path, false)
.map_err(|e| anyhow::anyhow!(e))
.context("Failed to load VAD model")?;
tracing::info!("VAD model loaded from {}", model_path.display());
Ok(Self {
session: Mutex::new(session),
input_tensors: Mutex::new(vec![
Tensor::new_checked(
Shape::new(vec![1, VAD_FRAME_SAMPLES]),
TensorData::F32(vec![0.0; VAD_FRAME_SAMPLES]),
),
Tensor::new_checked(
Shape::new(vec![2, 1, 128]),
TensorData::F32(vec![0.0; VAD_STATE_LEN]),
),
Tensor::new_checked(Shape::new(vec![1]), TensorData::I64(vec![VAD_SAMPLE_RATE])),
]),
})
}
pub(crate) fn run_frame(&self, frame: &[f32], state: &mut [f32; VAD_STATE_LEN]) -> Result<f32> {
let mut input = [0.0f32; VAD_FRAME_SAMPLES];
let n = frame.len().min(VAD_FRAME_SAMPLES);
input[..n].copy_from_slice(&frame[..n]);
let outputs = {
let mut inputs = self.input_tensors.lock();
inputs[0]
.as_f32_mut()
.context("VAD frame tensor is not f32")?
.copy_from_slice(&input);
inputs[1]
.as_f32_mut()
.context("VAD state tensor is not f32")?
.copy_from_slice(state);
let session = self.session.lock();
session.run(&inputs).context("VAD model inference failed")?
};
let mut prob = 0.0f32;
let mut new_state = [0.0f32; VAD_STATE_LEN];
for output in outputs {
let view = output.view();
if let Some(data) = view.data().as_f32() {
if data.len() == VAD_STATE_LEN {
new_state.copy_from_slice(data);
} else if data.len() == 1 {
prob = data[0];
}
}
}
state.copy_from_slice(&new_state);
Ok(prob)
}
pub fn frame_probs(&self, samples: &[f32]) -> Result<Vec<f32>> {
self.frame_probs_with_abort(samples, None)
}
pub(crate) fn frame_probs_with_abort(
&self,
samples: &[f32],
abort: Option<&dyn Fn() -> bool>,
) -> Result<Vec<f32>> {
let mut state = [0.0f32; VAD_STATE_LEN];
let mut probs = Vec::with_capacity(samples.len() / VAD_FRAME_SAMPLES + 1);
let mut i = 0;
let mut since_check = 0usize;
while i < samples.len() {
if let Some(abort) = abort {
since_check += 1;
if since_check >= 64 {
since_check = 0;
if abort() {
anyhow::bail!("cancelled");
}
}
}
let end = (i + VAD_FRAME_SAMPLES).min(samples.len());
probs.push(self.run_frame(&samples[i..end], &mut state)?);
i = end;
}
Ok(probs)
}
pub fn speech_regions(&self, samples: &[f32], cfg: &VadConfig) -> Result<Vec<(usize, usize)>> {
self.speech_regions_with_abort(samples, cfg, None)
}
pub(crate) fn speech_regions_with_abort(
&self,
samples: &[f32],
cfg: &VadConfig,
abort: Option<&dyn Fn() -> bool>,
) -> Result<Vec<(usize, usize)>> {
let probs = self.frame_probs_with_abort(samples, abort)?;
Ok(regions_from_probs(
&probs,
VAD_FRAME_SAMPLES,
samples.len(),
cfg,
))
}
}