gemini_genai_rs/buffer/
jitter.rs1use std::collections::VecDeque;
9use std::time::Instant;
10
11#[derive(Debug, Clone)]
13pub struct JitterConfig {
14 pub sample_rate: u32,
16 pub min_depth_samples: usize,
18 pub max_depth_samples: usize,
20 pub jitter_alpha: f64,
23 pub target_jitter_multiple: f64,
25}
26
27impl Default for JitterConfig {
28 fn default() -> Self {
29 Self {
30 sample_rate: 24000,
31 min_depth_samples: 24000 / 5, max_depth_samples: 24000 * 2, jitter_alpha: 0.125, target_jitter_multiple: 2.0,
35 }
36 }
37}
38
39impl JitterConfig {
40 pub fn for_sample_rate(sample_rate: u32) -> Self {
42 Self {
43 sample_rate,
44 min_depth_samples: sample_rate as usize / 5,
45 max_depth_samples: sample_rate as usize * 2,
46 ..Default::default()
47 }
48 }
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub enum BufferState {
54 Filling,
56 Playing,
58 Underrun,
60}
61
62pub struct AudioJitterBuffer {
64 config: JitterConfig,
65 queue: VecDeque<i16>,
66 state: BufferState,
67 jitter_estimate_us: f64,
69 last_arrival: Option<Instant>,
71 underrun_count: u64,
73}
74
75impl AudioJitterBuffer {
76 pub fn new(config: JitterConfig) -> Self {
78 let initial_capacity = config.max_depth_samples;
79 Self {
80 config,
81 queue: VecDeque::with_capacity(initial_capacity),
82 state: BufferState::Filling,
83 jitter_estimate_us: 0.0,
84 last_arrival: None,
85 underrun_count: 0,
86 }
87 }
88
89 pub fn state(&self) -> BufferState {
91 self.state
92 }
93
94 pub fn depth(&self) -> usize {
96 self.queue.len()
97 }
98
99 pub fn depth_ms(&self) -> f64 {
101 self.queue.len() as f64 / self.config.sample_rate as f64 * 1000.0
102 }
103
104 pub fn underrun_count(&self) -> u64 {
106 self.underrun_count
107 }
108
109 pub fn jitter_estimate_us(&self) -> f64 {
111 self.jitter_estimate_us
112 }
113
114 fn adaptive_min_depth(&self) -> usize {
116 let jitter_samples = (self.jitter_estimate_us / 1_000_000.0
117 * self.config.sample_rate as f64
118 * self.config.target_jitter_multiple) as usize;
119 jitter_samples.max(self.config.min_depth_samples)
120 }
121
122 pub fn push(&mut self, samples: &[i16]) {
124 let now = Instant::now();
126 if let Some(last) = self.last_arrival {
127 let interval_us = now.duration_since(last).as_micros() as f64;
128 let deviation = (interval_us - self.jitter_estimate_us).abs();
130 self.jitter_estimate_us = self.jitter_estimate_us * (1.0 - self.config.jitter_alpha)
131 + deviation * self.config.jitter_alpha;
132 }
133 self.last_arrival = Some(now);
134
135 let total_after = self.queue.len() + samples.len();
137 if total_after > self.config.max_depth_samples {
138 let to_drop = total_after - self.config.max_depth_samples;
139 self.queue.drain(..to_drop.min(self.queue.len()));
140 }
141
142 self.queue.extend(samples.iter());
143
144 if (self.state == BufferState::Filling || self.state == BufferState::Underrun)
146 && self.queue.len() >= self.adaptive_min_depth()
147 {
148 self.state = BufferState::Playing;
149 }
150 }
151
152 pub fn pull(&mut self, out: &mut [i16]) -> usize {
159 match self.state {
160 BufferState::Filling => {
161 out.fill(0);
163 0
164 }
165 BufferState::Playing | BufferState::Underrun => {
166 let available = self.queue.len().min(out.len());
167 for (i, sample) in self.queue.drain(..available).enumerate() {
168 out[i] = sample;
169 }
170
171 if available < out.len() {
173 out[available..].fill(0);
174 if self.state == BufferState::Playing {
175 self.state = BufferState::Underrun;
176 self.underrun_count += 1;
177 }
178 } else if self.state == BufferState::Underrun
179 && self.queue.len() >= self.adaptive_min_depth()
180 {
181 self.state = BufferState::Playing;
182 }
183
184 available
185 }
186 }
187 }
188
189 pub fn flush(&mut self) {
194 self.queue.clear();
195 self.state = BufferState::Filling;
196 self.last_arrival = None;
197 }
198
199 pub fn reset(&mut self) {
201 self.flush();
202 self.jitter_estimate_us = 0.0;
203 self.underrun_count = 0;
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 fn make_buffer() -> AudioJitterBuffer {
212 AudioJitterBuffer::new(JitterConfig {
213 sample_rate: 16000,
214 min_depth_samples: 1600, max_depth_samples: 16000,
216 jitter_alpha: 0.125,
217 target_jitter_multiple: 2.0,
218 })
219 }
220
221 #[test]
222 fn starts_in_filling_state() {
223 let buf = make_buffer();
224 assert_eq!(buf.state(), BufferState::Filling);
225 assert_eq!(buf.depth(), 0);
226 }
227
228 #[test]
229 fn filling_produces_silence() {
230 let mut buf = make_buffer();
231 buf.push(&vec![42i16; 800]); let mut out = [0i16; 160];
234 let real = buf.pull(&mut out);
235 assert_eq!(real, 0);
236 assert!(out.iter().all(|&s| s == 0));
237 }
238
239 #[test]
240 fn transitions_to_playing() {
241 let mut buf = make_buffer();
242 buf.push(&vec![100i16; 1600]); assert_eq!(buf.state(), BufferState::Playing);
245
246 let mut out = [0i16; 160];
247 let real = buf.pull(&mut out);
248 assert_eq!(real, 160);
249 assert!(out.iter().all(|&s| s == 100));
250 }
251
252 #[test]
253 fn underrun_fills_silence() {
254 let mut buf = make_buffer();
255 buf.push(&vec![99i16; 1600]);
256 assert_eq!(buf.state(), BufferState::Playing);
257
258 let mut out = [0i16; 1600];
260 buf.pull(&mut out);
261
262 let mut out2 = [0i16; 160];
264 let real = buf.pull(&mut out2);
265 assert_eq!(real, 0);
266 assert_eq!(buf.state(), BufferState::Underrun);
267 assert_eq!(buf.underrun_count(), 1);
268 }
269
270 #[test]
271 fn flush_clears_and_resets() {
272 let mut buf = make_buffer();
273 buf.push(&vec![42i16; 3200]);
274 assert_eq!(buf.state(), BufferState::Playing);
275
276 buf.flush();
277 assert_eq!(buf.state(), BufferState::Filling);
278 assert_eq!(buf.depth(), 0);
279 }
280
281 #[test]
282 fn overflow_drops_oldest() {
283 let mut buf = AudioJitterBuffer::new(JitterConfig {
284 sample_rate: 16000,
285 min_depth_samples: 100,
286 max_depth_samples: 500,
287 ..Default::default()
288 });
289
290 buf.push(&vec![1i16; 400]);
291 buf.push(&vec![2i16; 200]); assert!(buf.depth() <= 500);
294
295 let mut out = [0i16; 500];
297 buf.pull(&mut out);
298 assert!(out[300..].iter().all(|&s| s == 2));
300 }
301
302 #[test]
303 fn depth_ms_calculation() {
304 let mut buf = make_buffer();
305 buf.push(&vec![0i16; 1600]); assert!((buf.depth_ms() - 100.0).abs() < 0.01);
307 }
308}