1use crate::audio::RecordingReader;
5use crate::transcription::{
6 Layer, Operation, Transcriber, TranscriptSegment, TranscriptUpdate, TranscriptionError,
7};
8
9pub fn next_heard<T: Transcriber, L: Layer>(
16 operation: &mut Operation<'_, T, L>,
17 recording: &mut RecordingReader,
18) -> Result<Option<Heard>, TranscriptionError> {
19 loop {
20 match next_heard_update(operation, recording, false)? {
21 Some(HeardUpdate::Finalized(heard)) => return Ok(Some(heard)),
22 Some(HeardUpdate::Provisional { .. }) => {}
23 None => return Ok(None),
24 }
25 }
26}
27
28pub fn next_heard_update<T: Transcriber, L: Layer>(
31 operation: &mut Operation<'_, T, L>,
32 recording: &mut RecordingReader,
33 previews: bool,
34) -> Result<Option<HeardUpdate>, TranscriptionError> {
35 let segment = match operation.next(recording, previews)? {
36 Some(TranscriptUpdate::Finalized(segment)) => segment,
37 Some(TranscriptUpdate::Provisional {
38 text,
39 source_window,
40 }) => {
41 return Ok(Some(HeardUpdate::Provisional {
42 text,
43 source_window,
44 }));
45 }
46 None => return Ok(None),
47 };
48 let span = (segment.word_spans().is_some() || segment.source_window().is_some())
49 .then(|| segment.span())
50 .flatten();
51 let window = span
52 .clone()
53 .or_else(|| segment.source_window())
54 .unwrap_or(0..operation.read_to());
55 recording.release_before(operation.retained_from());
56 Ok(Some(HeardUpdate::Finalized(Heard {
57 segment,
58 span,
59 window,
60 })))
61}
62
63pub enum HeardUpdate {
64 Finalized(Heard),
65 Provisional {
66 text: String,
67 source_window: Option<std::ops::Range<u64>>,
68 },
69}
70
71pub struct Heard {
73 pub segment: TranscriptSegment,
74 pub span: Option<std::ops::Range<u64>>,
78 pub window: std::ops::Range<u64>,
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use crate::audio::CANONICAL_SAMPLE_RATE;
87
88 #[test]
89 fn preview_reads_preserve_finalized_segments_and_completion() {
90 use crate::audio::testing::{Pace, paced};
91 use crate::cancel::CancelToken;
92 use crate::transcription::{
93 Cadence, Hypothesis, Language, TimedWord, Transcription, TranscriptionAudio,
94 };
95 use std::sync::Mutex;
96
97 struct Script {
98 pace: Pace,
99 calls: Mutex<usize>,
100 timed: bool,
101 }
102 impl Transcriber for Script {
103 fn validate_language(
104 &self,
105 _: &Language,
106 _: &CancelToken,
107 ) -> Result<(), TranscriptionError> {
108 Ok(())
109 }
110 fn transcribe_with(
111 &self,
112 audio: TranscriptionAudio<'_>,
113 _: &Language,
114 _: &CancelToken,
115 ) -> Result<Hypothesis, TranscriptionError> {
116 let mut calls = self.calls.lock().expect("no poison");
117 let text = ["one two", "one two three", "three four", "three four five"][*calls];
118 *calls += 1;
119 self.pace.advance();
120 if self.timed {
121 let duration = audio.require_samples("script")?.len() as f32
122 / CANONICAL_SAMPLE_RATE as f32;
123 let words = text.split_whitespace().count();
124 Ok(Hypothesis::with_words(
125 text,
126 (0..words)
127 .map(|index| TimedWord {
128 start: duration * index as f32 / words as f32,
129 end: duration * (index + 1) as f32 / words as f32,
130 })
131 .collect(),
132 ))
133 } else {
134 Ok(Hypothesis::new(text))
135 }
136 }
137 }
138 let run = |previews, timed| {
139 let (_directory, mut recording, pace) = paced(vec![vec![8192_i16; 1600]; 4]);
140 let transcription = Transcription::agreement(
141 Cadence::from_millis(100, 200),
142 Script {
143 pace,
144 calls: Mutex::new(0),
145 timed,
146 },
147 );
148 let mut operation = transcription.start(&Language::Automatic).expect("starts");
149 let mut rows = Vec::new();
150 let mut preview_count = 0;
151 loop {
152 let update = if previews {
153 next_heard_update(&mut operation, &mut recording, true)
154 } else {
155 next_heard(&mut operation, &mut recording)
156 .map(|heard| heard.map(HeardUpdate::Finalized))
157 }
158 .expect("reads");
159 match update {
160 Some(HeardUpdate::Finalized(heard)) => {
161 rows.push((heard.segment.into_text(), heard.window));
162 }
163 Some(HeardUpdate::Provisional { .. }) => preview_count += 1,
164 None => break,
165 }
166 }
167 let final_text = operation
168 .finish(&mut recording)
169 .expect("transcript")
170 .into_text();
171 (rows, final_text, preview_count)
172 };
173 for timed in [false, true] {
174 let (baseline, baseline_text, _) = run(false, timed);
175 let (previewed, preview_text, count) = run(true, timed);
176 assert!(count > 0);
177 assert_eq!(previewed, baseline);
178 assert_eq!(preview_text, baseline_text);
179 }
180 }
181
182 #[test]
183 fn delayed_untimed_words_keep_the_entire_decode_window() {
184 use crate::audio::testing::{Pace, paced};
185 use crate::cancel::CancelToken;
186 use crate::transcription::{
187 Cadence, Hypothesis, Language, Transcription, TranscriptionAudio,
188 };
189 use std::sync::Mutex;
190
191 struct Script {
192 pace: Pace,
193 calls: Mutex<usize>,
194 }
195 impl Transcriber for Script {
196 fn validate_language(
197 &self,
198 _: &Language,
199 _: &CancelToken,
200 ) -> Result<(), TranscriptionError> {
201 Ok(())
202 }
203
204 fn transcribe_with(
205 &self,
206 _: TranscriptionAudio<'_>,
207 _: &Language,
208 _: &CancelToken,
209 ) -> Result<Hypothesis, TranscriptionError> {
210 let mut calls = self.calls.lock().unwrap();
211 let answers = [
212 "one two",
213 "one two",
214 "one two three four",
215 "one two three four",
216 "one two three four five six",
217 "one two three four five six",
218 "one two three four five six guest phrase",
219 "one two three four five six guest phrase",
220 ];
221 let text = answers[*calls];
222 *calls += 1;
223 self.pace.advance();
224 Ok(Hypothesis::new(text))
225 }
226 }
227 let second = CANONICAL_SAMPLE_RATE as usize;
228 let (_directory, mut recording, pace) = paced(vec![vec![8192_i16; second]; 8]);
229 let transcription = Transcription::agreement(
230 Cadence::from_millis(1_000, 20_000),
231 Script {
232 pace,
233 calls: Mutex::new(0),
234 },
235 );
236 let mut operation = transcription.start(&Language::Automatic).unwrap();
237 let mut heard = Vec::new();
238 while let Some(segment) = next_heard(&mut operation, &mut recording).unwrap() {
239 heard.push(segment);
240 }
241 assert_eq!(
242 heard
243 .iter()
244 .map(|heard| heard.segment.as_str())
245 .collect::<Vec<_>>(),
246 ["one two", "three four", "five six", "guest phrase"]
247 );
248 let guest = heard.last().unwrap();
249 assert_eq!(guest.segment.span(), None);
250 assert_eq!(guest.segment.word_spans(), None);
251 assert_eq!(guest.segment.source_window(), Some(0..8 * second as u64));
252 assert_eq!(guest.window, 0..8 * second as u64);
253 }
254}