Skip to main content

flexaudio_vad/
config.rs

1//! VAD 設定。既定値は silero-VAD 原典 (`get_speech_timestamps`) のデフォルトに揃える。
2
3/// VAD の挙動を制御する設定。
4///
5/// デフォルト値は silero-VAD の `get_speech_timestamps` に揃えてある。
6#[derive(Debug, Clone, PartialEq)]
7pub struct VadConfig {
8    /// 発話開始とみなす確率しきい値 (>=)。既定 0.5。
9    pub threshold: f32,
10    /// 無音開始とみなす負側しきい値 (<)。`None` なら
11    /// `max(threshold - 0.15, 0.01)`(silero 準拠)。
12    pub neg_threshold: Option<f32>,
13    /// 採用する発話の最小長 (ms)。これ未満のセグメントは破棄。既定 250。
14    pub min_speech_ms: u32,
15    /// 発話終了の確定に必要な無音長 (ms)。既定 100(silero)。
16    pub min_silence_ms: u32,
17    /// セグメント境界を前後に広げるパディング (ms)。既定 30(silero)。
18    pub speech_pad_ms: u32,
19    /// 1 セグメントの最大長 (ms)。0 = 無制限。超過時は強制分割。既定 0。
20    pub max_speech_ms: u32,
21    /// サンプルレート。8000 または 16000 のみ。既定 16000。
22    pub sample_rate: u32,
23}
24
25impl Default for VadConfig {
26    /// silero のデフォルト([`VadConfig::balanced`] と同じ)。
27    fn default() -> Self {
28        VadConfig {
29            threshold: 0.5,
30            neg_threshold: None,
31            min_speech_ms: 250,
32            min_silence_ms: 100,
33            speech_pad_ms: 30,
34            max_speech_ms: 0,
35            sample_rate: 16000,
36        }
37    }
38}
39
40impl VadConfig {
41    /// 取りこぼしを減らす感度高めのプリセット。
42    ///
43    /// しきい値を下げ、短い無音でも発話を継続しやすくする。
44    pub fn aggressive() -> Self {
45        VadConfig {
46            threshold: 0.35,
47            neg_threshold: None,
48            min_speech_ms: 200,
49            min_silence_ms: 150,
50            speech_pad_ms: 50,
51            max_speech_ms: 0,
52            sample_rate: 16000,
53        }
54    }
55
56    /// バランス型プリセット。silero のデフォルト([`VadConfig::default`])と同じ。
57    pub fn balanced() -> Self {
58        VadConfig::default()
59    }
60
61    /// 誤検出を減らす保守的なプリセット。
62    ///
63    /// しきい値を上げ、より長い無音で発話を切る。
64    pub fn conservative() -> Self {
65        VadConfig {
66            threshold: 0.6,
67            neg_threshold: None,
68            min_speech_ms: 300,
69            min_silence_ms: 300,
70            speech_pad_ms: 30,
71            max_speech_ms: 0,
72            sample_rate: 16000,
73        }
74    }
75
76    /// 実効的な負側しきい値を返す。
77    ///
78    /// 明示指定があればそれを、なければ `max(threshold - 0.15, 0.01)`(silero 準拠)。
79    pub fn resolved_neg_threshold(&self) -> f32 {
80        match self.neg_threshold {
81            Some(v) => v,
82            None => (self.threshold - 0.15).max(0.01),
83        }
84    }
85
86    /// 16k なら 512、8k なら 256。silero のフレーム長。
87    pub(crate) fn frame_size(&self) -> usize {
88        if self.sample_rate == 8000 {
89            256
90        } else {
91            512
92        }
93    }
94
95    /// 16k なら 64、8k なら 32。silero の前置コンテキスト長。
96    pub(crate) fn context_size(&self) -> usize {
97        if self.sample_rate == 8000 {
98            32
99        } else {
100            64
101        }
102    }
103
104    /// ms をサンプル数に変換 (現在の `sample_rate` 基準)。
105    pub(crate) fn ms_to_samples(&self, ms: u32) -> u64 {
106        (u64::from(ms) * u64::from(self.sample_rate)) / 1000
107    }
108
109    /// 設定の妥当性を検証する。
110    pub(crate) fn validate(&self) -> Result<(), String> {
111        if self.sample_rate != 8000 && self.sample_rate != 16000 {
112            return Err(format!(
113                "sample_rate must be 8000 or 16000, got {}",
114                self.sample_rate
115            ));
116        }
117        if !(0.0..=1.0).contains(&self.threshold) {
118            return Err(format!(
119                "threshold must be within [0.0, 1.0], got {}",
120                self.threshold
121            ));
122        }
123        if let Some(nt) = self.neg_threshold {
124            if !(0.0..=1.0).contains(&nt) {
125                return Err(format!("neg_threshold must be within [0.0, 1.0], got {nt}"));
126            }
127        }
128        Ok(())
129    }
130}