1use std::marker::PhantomData;
6
7use super::{
8 Language, Layer, Mode, Operation, Streaming, Transcriber, Transcript, TranscriptSegment,
9 Transcription, TranscriptionError,
10};
11use crate::audio::RecordingReader;
12
13#[cfg(not(target_family = "wasm"))]
14use crate::audio::Stopper;
15#[cfg(all(feature = "microphone", not(target_family = "wasm")))]
16use crate::audio::{Microphone, RecordingMode};
17
18pub struct Session<'a, T, M: Mode, L = ()> {
24 operation: Operation<'a, T, L>,
25 recording: RecordingReader,
26 #[cfg(all(feature = "microphone", not(target_family = "wasm")))]
28 _microphone: Option<Microphone>,
29 #[cfg(not(target_family = "wasm"))]
30 stopper: Option<Stopper>,
31 mode: PhantomData<M>,
32}
33
34impl<'a, T: Transcriber, M: Mode, L: Layer> Session<'a, T, M, L> {
35 pub fn over(
38 transcription: &'a Transcription<T, M, L>,
39 language: &'a Language,
40 recording: RecordingReader,
41 ) -> Result<Self, TranscriptionError> {
42 Ok(Self {
43 operation: transcription.start(language)?,
44 recording,
45 #[cfg(all(feature = "microphone", not(target_family = "wasm")))]
46 _microphone: None,
47 #[cfg(not(target_family = "wasm"))]
48 stopper: None,
49 mode: PhantomData,
50 })
51 }
52
53 pub fn finish(mut self) -> Result<Transcript, TranscriptionError> {
57 #[cfg(not(target_family = "wasm"))]
58 if let Some(stopper) = &self.stopper {
59 stopper.stop();
60 }
61 self.operation.finish(&mut self.recording)
62 }
63}
64
65#[cfg(all(feature = "microphone", not(target_family = "wasm")))]
66impl<'a, T: Transcriber, M: Mode, L: Layer> Session<'a, T, M, L> {
67 pub fn microphone(
71 transcription: &'a Transcription<T, M, L>,
72 language: &'a Language,
73 ) -> Result<Self, TranscriptionError> {
74 let mut microphone = Microphone::open().map_err(TranscriptionError::Capture)?;
77 let (recording, stopper) = microphone
78 .record(RecordingMode::UntilStopped)
79 .map_err(TranscriptionError::Capture)?;
80 let operation = match transcription.start(language) {
81 Ok(operation) => operation,
82 Err(error) => {
83 stopper.stop();
84 return Err(error);
85 }
86 };
87 Ok(Self {
88 operation,
89 recording,
90 _microphone: Some(microphone),
91 stopper: Some(stopper),
92 mode: PhantomData,
93 })
94 }
95}
96
97impl<'a, T: Transcriber, L: Layer> Session<'a, T, Streaming, L> {
98 pub fn next_finalized(&mut self) -> Result<Option<TranscriptSegment>, TranscriptionError> {
101 self.operation.next_finalized(&mut self.recording)
102 }
103}
104
105#[cfg(not(target_family = "wasm"))]
106impl<T, M: Mode, L> Drop for Session<'_, T, M, L> {
107 fn drop(&mut self) {
108 if let Some(stopper) = &self.stopper {
112 stopper.stop();
113 }
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use std::collections::VecDeque;
120 use std::sync::Mutex;
121
122 use super::*;
123 use crate::audio::CANONICAL_SAMPLE_RATE;
124 use crate::audio::testing::{Pace, paced, recorded};
125 use crate::cancel::CancelToken;
126 use crate::transcription::{Cadence, Hypothesis, TranscriptionAudio};
127
128 const STRIDE_MS: usize = 100;
129 const STRIDE: usize = STRIDE_MS * CANONICAL_SAMPLE_RATE as usize / 1000;
130
131 struct Scripted {
134 answers: Mutex<VecDeque<&'static str>>,
135 pace: Option<Pace>,
136 }
137
138 impl Scripted {
139 fn new(answers: &[&'static str]) -> Self {
140 Self {
141 answers: Mutex::new(answers.iter().copied().collect()),
142 pace: None,
143 }
144 }
145
146 fn paced(answers: &[&'static str], pace: Pace) -> Self {
147 Self {
148 answers: Mutex::new(answers.iter().copied().collect()),
149 pace: Some(pace),
150 }
151 }
152 }
153
154 impl Transcriber for Scripted {
155 fn validate_language(
156 &self,
157 _language: &Language,
158 _cancel: &CancelToken,
159 ) -> Result<(), TranscriptionError> {
160 Ok(())
161 }
162
163 fn transcribe_with(
164 &self,
165 _audio: TranscriptionAudio<'_>,
166 _language: &Language,
167 _cancel: &CancelToken,
168 ) -> Result<Hypothesis, TranscriptionError> {
169 if let Some(pace) = &self.pace {
170 pace.advance();
171 }
172 let answer = self
173 .answers
174 .lock()
175 .unwrap_or_else(|poisoned| poisoned.into_inner())
176 .pop_front()
177 .expect("the script covers every decode");
178 Ok(Hypothesis::new(answer))
179 }
180 }
181
182 fn stride_batch() -> Vec<i16> {
183 vec![8_192; STRIDE]
184 }
185
186 #[test]
187 fn a_streaming_session_commits_words_as_their_agreement_holds() {
188 let (_directory, recording, pace) = paced(vec![stride_batch(); 4]);
189 let cadence = Cadence::from_millis(STRIDE_MS, 20_000);
190 let transcription = Transcription::agreement(
191 cadence,
192 Scripted::paced(&["one", "one two", "one two three", "one two three"], pace),
193 );
194 let language = Language::Automatic;
195 let mut session =
196 Session::over(&transcription, &language, recording).expect("the session starts");
197
198 let mut segments = Vec::new();
199 while let Some(segment) = session.next_finalized().expect("every step succeeds") {
200 segments.push(segment.into_text());
201 }
202 let transcript = session.finish().expect("the transcript completes");
203
204 assert_eq!(segments, ["one", "two", "three"]);
205 assert_eq!(transcript.as_str(), "one two three");
206 }
207
208 #[test]
209 fn a_whole_session_has_only_its_end() {
210 let (_directory, recording) = recorded(&[0; 64]);
211 let transcription = Transcription::complete_samples(Scripted::new(&["hello world"]));
212 let language = Language::Automatic;
213
214 let transcript = Session::over(&transcription, &language, recording)
215 .expect("the session starts")
216 .finish()
217 .expect("the transcript completes");
218
219 assert_eq!(transcript.as_str(), "hello world");
220 }
221}