#[derive(Debug, Clone, PartialEq)]
pub struct VadConfig {
pub threshold: f32,
pub neg_threshold: Option<f32>,
pub min_speech_ms: u32,
pub min_silence_ms: u32,
pub speech_pad_ms: u32,
pub max_speech_ms: u32,
pub sample_rate: u32,
}
impl Default for VadConfig {
fn default() -> Self {
VadConfig {
threshold: 0.5,
neg_threshold: None,
min_speech_ms: 250,
min_silence_ms: 100,
speech_pad_ms: 30,
max_speech_ms: 0,
sample_rate: 16000,
}
}
}
impl VadConfig {
pub fn aggressive() -> Self {
VadConfig {
threshold: 0.35,
neg_threshold: None,
min_speech_ms: 200,
min_silence_ms: 150,
speech_pad_ms: 50,
max_speech_ms: 0,
sample_rate: 16000,
}
}
pub fn balanced() -> Self {
VadConfig::default()
}
pub fn conservative() -> Self {
VadConfig {
threshold: 0.6,
neg_threshold: None,
min_speech_ms: 300,
min_silence_ms: 300,
speech_pad_ms: 30,
max_speech_ms: 0,
sample_rate: 16000,
}
}
pub fn resolved_neg_threshold(&self) -> f32 {
match self.neg_threshold {
Some(v) => v,
None => (self.threshold - 0.15).max(0.01),
}
}
pub(crate) fn frame_size(&self) -> usize {
if self.sample_rate == 8000 {
256
} else {
512
}
}
pub(crate) fn context_size(&self) -> usize {
if self.sample_rate == 8000 {
32
} else {
64
}
}
pub(crate) fn ms_to_samples(&self, ms: u32) -> u64 {
(u64::from(ms) * u64::from(self.sample_rate)) / 1000
}
pub(crate) fn validate(&self) -> Result<(), String> {
if self.sample_rate != 8000 && self.sample_rate != 16000 {
return Err(format!(
"sample_rate must be 8000 or 16000, got {}",
self.sample_rate
));
}
if !(0.0..=1.0).contains(&self.threshold) {
return Err(format!(
"threshold must be within [0.0, 1.0], got {}",
self.threshold
));
}
if let Some(nt) = self.neg_threshold {
if !(0.0..=1.0).contains(&nt) {
return Err(format!("neg_threshold must be within [0.0, 1.0], got {nt}"));
}
}
Ok(())
}
}