use super::*;
pub(super) struct LinearStitcher {
pub(super) frac: f64,
}
impl LinearStitcher {
pub(super) fn new() -> Self {
Self { frac: 0.0 }
}
pub(super) fn producible(&self, fifo_frames: usize, ratio: f64) -> usize {
if fifo_frames == 0 {
return 0;
}
let span = (fifo_frames - 1) as f64 - self.frac;
if span < 0.0 {
return 0;
}
(span / ratio) as usize + 1
}
pub(super) fn pull(
&mut self,
fifo: &mut Vec<f32>,
ratio: f64,
out_frames: usize,
out: &mut Vec<f32>,
) {
let ch = CHANNELS as usize;
let frames = fifo.len() / ch;
debug_assert!(out_frames <= self.producible(frames, ratio));
for k in 0..out_frames {
let pos = self.frac + k as f64 * ratio;
let left = pos as usize;
let right = (left + 1).min(frames - 1);
let w = (pos - left as f64) as f32;
for c in 0..ch {
let a = fifo[left * ch + c];
let b = fifo[right * ch + c];
out.push(a + w * (b - a));
}
}
let end = self.frac + out_frames as f64 * ratio;
let consumed = (end as usize).min(frames);
self.frac = end - consumed as f64;
fifo.drain(..consumed * ch);
}
pub(super) fn reset(&mut self) {
self.frac = 0.0;
}
}
pub(super) struct DriftController {
pub(super) ratio: f64,
pub(super) ema_diff: f64,
pub(super) pending_samples: usize,
}
impl DriftController {
pub(super) fn new() -> Self {
Self {
ratio: 1.0,
ema_diff: 0.0,
pending_samples: 0,
}
}
pub(super) fn on_output(&mut self, samples: usize, mic_len: usize, system_len: usize) {
self.pending_samples += samples;
if self.pending_samples >= DRIFT_UPDATE_INTERVAL_SAMPLES {
self.pending_samples = 0;
self.update(mic_len, system_len);
}
}
pub(super) fn update(&mut self, mic_len: usize, system_len: usize) {
let diff = mic_len as f64 - system_len as f64;
self.ema_diff += DRIFT_EMA_ALPHA * (diff - self.ema_diff);
let target = (1.0 - DRIFT_GAIN * self.ema_diff)
.clamp(1.0 - DRIFT_RATIO_LIMIT, 1.0 + DRIFT_RATIO_LIMIT);
let step = (target - self.ratio).clamp(-DRIFT_SLEW_PER_UPDATE, DRIFT_SLEW_PER_UPDATE);
self.ratio += step;
}
}
pub(super) struct DriftCorrection {
pub(super) stitcher: LinearStitcher,
pub(super) controller: DriftController,
}
impl DriftCorrection {
pub(super) fn new() -> Self {
Self {
stitcher: LinearStitcher::new(),
controller: DriftController::new(),
}
}
}