pub trait TurnTakingPolicy {
fn user_finished(&self, recent_audio: &[f32], silence_ms: u32) -> bool;
}
#[derive(Debug, Clone, Copy)]
pub struct SilenceTurnTakingPolicy {
silence_threshold_ms: u32,
}
impl Default for SilenceTurnTakingPolicy {
fn default() -> Self {
Self {
silence_threshold_ms: 1600,
}
}
}
impl SilenceTurnTakingPolicy {
#[must_use]
pub const fn new(silence_threshold_ms: u32) -> Self {
Self {
silence_threshold_ms,
}
}
#[inline(always)]
#[must_use]
pub const fn silence_threshold_ms(&self) -> u32 {
self.silence_threshold_ms
}
#[must_use]
pub fn with_silence_threshold_ms(self, silence_threshold_ms: u32) -> Self {
Self {
silence_threshold_ms,
}
}
}
impl TurnTakingPolicy for SilenceTurnTakingPolicy {
fn user_finished(&self, _recent_audio: &[f32], silence_ms: u32) -> bool {
silence_ms >= self.silence_threshold_ms
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn silence_above_threshold_finishes_turn() {
let policy = SilenceTurnTakingPolicy::default();
assert!(policy.user_finished(&[], 1600));
assert!(policy.user_finished(&[], 1601));
assert!(policy.user_finished(&[], 99_999));
}
#[test]
fn silence_below_threshold_keeps_turn_open() {
let policy = SilenceTurnTakingPolicy::default();
assert!(!policy.user_finished(&[], 0));
assert!(!policy.user_finished(&[], 100));
assert!(!policy.user_finished(&[], 1599));
}
#[test]
fn custom_threshold_honored() {
let policy = SilenceTurnTakingPolicy::new(500);
assert!(!policy.user_finished(&[], 499));
assert!(policy.user_finished(&[], 500));
}
#[test]
fn recent_audio_content_is_ignored() {
let policy = SilenceTurnTakingPolicy::new(200);
let loud = vec![0.9_f32; 1024];
let quiet = vec![0.0_f32; 1024];
assert!(policy.user_finished(&loud, 250));
assert!(policy.user_finished(&quiet, 250));
assert!(!policy.user_finished(&loud, 100));
assert!(!policy.user_finished(&quiet, 100));
}
}