aria_kit/speaker/turns/
feed.rs1use std::time::Duration;
2
3use super::{SpeakerTracker, Timeline};
4use crate::audio::{CaptureError, RecordingReader, RecordingWaker, canonical_samples};
5use crate::primitives::error_chain;
6use crate::voice_activity::{SpeechDetector, SpeechMap, VoiceActivityDetector};
7
8pub struct TurnFeed<V, K> {
12 audio: RecordingReader,
13 speech: SpeechDetector<V>,
14 spoken_to: u64,
16 tracker: Option<K>,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum Ahead {
22 Heard,
23 Idle,
25 Ended,
27}
28
29impl<V: VoiceActivityDetector, K: SpeakerTracker> TurnFeed<V, K> {
30 pub fn start(
34 recording: &mut RecordingReader,
35 speech: SpeechDetector<V>,
36 tracker: Option<K>,
37 ) -> Self {
38 let audio = recording.fork_window();
39 let from = audio.held_from();
40 debug_assert_eq!(
41 speech.map().reached(),
42 from,
43 "the detector starts at the fork"
44 );
45 debug_assert!(
46 tracker
47 .as_ref()
48 .is_none_or(|tracker| tracker.heard_to() == from),
49 "the tracker starts at the fork"
50 );
51 Self {
52 audio,
53 speech,
54 spoken_to: from,
55 tracker,
56 }
57 }
58
59 pub fn held_from(&self) -> u64 {
62 self.audio.held_from()
63 }
64
65 pub fn speech(&self) -> &SpeechMap {
66 self.speech.map()
67 }
68
69 pub fn timeline(&self) -> Option<&Timeline> {
71 self.tracker.as_ref().map(SpeakerTracker::timeline)
72 }
73
74 pub fn drop_tracker(&mut self) {
77 self.tracker = None;
78 self.release();
79 }
80
81 pub fn hear_through(&mut self, position: u64) -> bool {
86 let filled = self.audio.fill_through(position);
87 let reached = filled.as_ref().is_ok_and(|reached| *reached);
88 self.speak(filled.map(drop), position);
89 self.track_through(position);
90 reached
91 }
92
93 pub fn hear_ahead(&mut self, piece: Duration, max_lag: Option<Duration>) -> Ahead {
97 let before = self.heard_to();
98 let filled = self.audio.fill_available();
99 self.speak(filled, u64::MAX);
100 if let Some(max_lag) = max_lag {
101 self.catch_up(max_lag);
102 }
103 let through = self.tracker.as_ref().map_or(self.spoken_to, |tracker| {
104 tracker.heard_to() + canonical_samples(piece)
105 });
106 self.track_through(through);
107 if self.heard_to() != before {
110 return Ahead::Heard;
111 }
112 if self.audio.ended_at().is_some() {
113 return Ahead::Ended;
114 }
115 Ahead::Idle
116 }
117
118 pub fn wait_for_audio(&self, timeout: Duration, stopped: impl Fn() -> bool) {
122 self.audio.wait_for_audio(timeout, stopped);
123 }
124
125 pub fn waker(&self) -> RecordingWaker {
127 self.audio.waker()
128 }
129
130 fn heard_to(&self) -> (u64, Option<u64>) {
132 let tracked = self.tracker.as_ref().map(SpeakerTracker::heard_to);
133 (self.spoken_to, tracked)
134 }
135
136 pub fn hear_rest(&mut self) {
139 let filled = self.audio.fill();
140 self.speak(filled, u64::MAX);
141 self.speech.finish();
142 self.track_through(u64::MAX);
143 if let Some(tracker) = &mut self.tracker {
144 tracker.finish();
145 }
146 }
147
148 pub fn forget_before(&mut self, at: u64) {
151 let at = match &mut self.tracker {
152 Some(tracker) => tracker.forget_before(at),
153 None => at.min(self.spoken_to),
154 };
155 self.speech.forget_before(at);
156 }
157
158 fn speak(&mut self, filled: Result<(), CaptureError>, position: u64) {
159 if let Err(error) = filled {
161 tracing::debug!(error = %error_chain(&error), "the voice feed heard capture end early");
162 }
163 let end = position.min(self.audio.held_to()).max(self.spoken_to);
164 if !self.speech.map().is_broken()
165 && let Err(error) = self.speech.hear(self.audio.span(self.spoken_to..end))
166 {
167 tracing::warn!(
168 error = %error_chain(&error),
169 "voice-activity inference failed; no more speech can be told apart"
170 );
171 }
172 self.spoken_to = end;
173 }
174
175 fn catch_up(&mut self, max_lag: Duration) {
176 if let Some(tracker) = &mut self.tracker
177 && self.spoken_to.saturating_sub(tracker.heard_to()) > canonical_samples(max_lag)
178 {
179 tracker.skip_to(self.speech.map(), self.spoken_to);
180 }
181 }
182
183 fn track_through(&mut self, position: u64) {
186 if let Some(tracker) = &mut self.tracker {
187 let start = tracker.heard_to();
188 let end = position.min(self.spoken_to).max(start);
189 tracker.hear(self.speech.map(), self.audio.span(start..end));
190 }
191 self.release();
192 }
193
194 fn release(&mut self) {
196 let heard = self
197 .tracker
198 .as_ref()
199 .map_or(self.spoken_to, SpeakerTracker::heard_to);
200 self.audio.release_before(heard.min(self.spoken_to));
201 }
202}