Skip to main content

native_whisperx/config/
vad.rs

1//! VAD configuration for energy, Silero, pyannote, and delegated WhisperX paths.
2
3use std::path::PathBuf;
4
5use serde::{Deserialize, Serialize};
6
7use super::defaults::{
8    default_vad_enabled, default_vad_frame_seconds, default_vad_hop_seconds,
9    default_vad_max_chunk_seconds, default_vad_merge_gap_seconds, default_vad_min_speech_seconds,
10    default_vad_padding_seconds, default_vad_rms_threshold,
11};
12use super::ConfigSelection;
13
14#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
15#[serde(rename_all = "camelCase")]
16pub struct VadConfig {
17    #[serde(default = "default_vad_enabled")]
18    pub enabled: bool,
19    #[serde(default)]
20    pub method: VadMethod,
21    #[serde(default, skip_serializing_if = "ConfigSelection::is_explicit")]
22    pub selection: ConfigSelection,
23    #[serde(default)]
24    pub onset: Option<f32>,
25    #[serde(default)]
26    pub offset: Option<f32>,
27    #[serde(default)]
28    pub chunk_size: Option<f64>,
29    #[serde(default = "default_vad_rms_threshold")]
30    pub rms_threshold: f32,
31    #[serde(default = "default_vad_frame_seconds")]
32    pub frame_seconds: f64,
33    #[serde(default = "default_vad_hop_seconds")]
34    pub hop_seconds: f64,
35    #[serde(default = "default_vad_min_speech_seconds")]
36    pub min_speech_seconds: f64,
37    #[serde(default = "default_vad_padding_seconds")]
38    pub padding_seconds: f64,
39    #[serde(default = "default_vad_merge_gap_seconds")]
40    pub merge_gap_seconds: f64,
41    #[serde(default = "default_vad_max_chunk_seconds")]
42    pub max_chunk_seconds: f64,
43    #[serde(default)]
44    pub model_bundle: Option<PathBuf>,
45    #[serde(default)]
46    pub model_file: Option<String>,
47    #[serde(default)]
48    pub input_name: Option<String>,
49    #[serde(default)]
50    pub output_name: Option<String>,
51}
52
53impl Default for VadConfig {
54    fn default() -> Self {
55        Self {
56            enabled: true,
57            method: VadMethod::Energy,
58            selection: ConfigSelection::Explicit,
59            onset: None,
60            offset: None,
61            chunk_size: None,
62            rms_threshold: 0.01,
63            frame_seconds: 0.03,
64            hop_seconds: 0.01,
65            min_speech_seconds: 0.08,
66            padding_seconds: 0.02,
67            merge_gap_seconds: 0.05,
68            max_chunk_seconds: 30.0,
69            model_bundle: None,
70            model_file: None,
71            input_name: None,
72            output_name: None,
73        }
74    }
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
78#[serde(rename_all = "lowercase")]
79pub enum VadMethod {
80    #[default]
81    Energy,
82    Pyannote,
83    Silero,
84}
85
86impl VadMethod {
87    pub fn as_whisperx_arg(self) -> &'static str {
88        match self {
89            Self::Energy => "energy",
90            Self::Pyannote => "pyannote",
91            Self::Silero => "silero",
92        }
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99    use crate::ConfigSelection;
100
101    #[test]
102    fn vad_config_serializes_automatic_selection_separately_from_explicit_method() {
103        let automatic = VadConfig {
104            selection: ConfigSelection::Automatic,
105            method: VadMethod::Pyannote,
106            ..VadConfig::default()
107        };
108
109        let json = serde_json::to_value(&automatic).expect("serialize VAD config");
110
111        assert_eq!(json["selection"], "automatic");
112        assert_eq!(json["method"], "pyannote");
113
114        let explicit = VadConfig {
115            method: VadMethod::Pyannote,
116            ..VadConfig::default()
117        };
118        let json = serde_json::to_value(&explicit).expect("serialize explicit VAD config");
119
120        assert!(json.get("selection").is_none());
121        assert_eq!(json["method"], "pyannote");
122
123        let decoded: VadConfig = serde_json::from_value(serde_json::json!({
124            "method": "silero"
125        }))
126        .expect("deserialize existing VAD config shape");
127        assert_eq!(decoded.selection, ConfigSelection::Explicit);
128        assert_eq!(decoded.method, VadMethod::Silero);
129
130        let decoded: VadConfig = serde_json::from_value(serde_json::json!({
131            "selection": "automatic",
132            "method": "energy"
133        }))
134        .expect("deserialize automatic VAD config");
135        assert_eq!(decoded.selection, ConfigSelection::Automatic);
136        assert_eq!(decoded.method, VadMethod::Energy);
137    }
138}