use std::time::{Duration, Instant};
pub const DEFAULT_SOFT_TURN_TIMEOUT: Duration = Duration::from_secs(2);
pub struct SoftTurnDetector {
vad_ended_at: Option<Instant>,
timeout: Duration,
}
impl SoftTurnDetector {
pub fn new(timeout: Duration) -> Self {
Self {
vad_ended_at: None,
timeout,
}
}
pub fn on_vad_end(&mut self) {
self.vad_ended_at = Some(Instant::now());
}
pub fn on_model_response(&mut self) {
self.vad_ended_at = None;
}
pub fn check(&self, now: Instant) -> bool {
self.vad_ended_at
.map(|t| now.duration_since(t) >= self.timeout)
.unwrap_or(false)
}
pub fn reset(&mut self) {
self.vad_ended_at = None;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn no_soft_turn_without_vad_end() {
let d = SoftTurnDetector::new(Duration::from_millis(100));
assert!(!d.check(Instant::now()));
}
#[test]
fn soft_turn_after_timeout() {
let mut d = SoftTurnDetector::new(Duration::from_millis(50));
d.on_vad_end();
assert!(!d.check(Instant::now()));
std::thread::sleep(Duration::from_millis(60));
assert!(d.check(Instant::now()));
}
#[test]
fn model_response_cancels_soft_turn() {
let mut d = SoftTurnDetector::new(Duration::from_millis(50));
d.on_vad_end();
d.on_model_response();
std::thread::sleep(Duration::from_millis(60));
assert!(!d.check(Instant::now()));
}
#[test]
fn reset_clears_state() {
let mut d = SoftTurnDetector::new(Duration::from_millis(50));
d.on_vad_end();
std::thread::sleep(Duration::from_millis(60));
assert!(d.check(Instant::now()));
d.reset();
assert!(!d.check(Instant::now()));
}
}