aria_kit/synthesis/
stream.rs1use std::collections::VecDeque;
9use std::sync::{Arc, Condvar, Mutex, MutexGuard};
10
11use super::SynthesisError;
12
13const BUFFER_SECONDS: u32 = 10;
16
17pub fn stream(sample_rate: u32) -> (SpeechWriter, SpeechStream) {
20 let shared = Arc::new(Shared {
21 state: Mutex::new(StreamState::default()),
22 changed: Condvar::new(),
23 sample_rate,
24 capacity: sample_rate as usize * BUFFER_SECONDS as usize,
25 });
26 (
27 SpeechWriter {
28 shared: Arc::clone(&shared),
29 },
30 SpeechStream {
31 shared,
32 failed: false,
33 },
34 )
35}
36
37#[derive(Debug, thiserror::Error)]
40#[error("the speech stream is closed")]
41pub struct StreamClosed;
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum TryRead {
46 Samples(usize),
48 Starved,
50 Ended,
52}
53
54struct Shared {
55 state: Mutex<StreamState>,
56 changed: Condvar,
57 sample_rate: u32,
58 capacity: usize,
59}
60
61#[derive(Default)]
62struct StreamState {
63 pending: VecDeque<f32>,
64 reader_gone: bool,
65 ended: Option<Result<(), SynthesisError>>,
66}
67
68impl Shared {
69 fn lock(&self) -> MutexGuard<'_, StreamState> {
70 self.state
71 .lock()
72 .unwrap_or_else(|poisoned| poisoned.into_inner())
73 }
74
75 fn close(&self, outcome: Result<(), SynthesisError>) {
78 let mut state = self.lock();
79 if state.ended.is_none() {
80 state.ended = Some(outcome);
81 }
82 drop(state);
83 self.changed.notify_all();
84 }
85}
86
87pub struct SpeechWriter {
92 shared: Arc<Shared>,
93}
94
95impl SpeechWriter {
96 pub fn write(&mut self, samples: &[f32]) -> Result<(), StreamClosed> {
99 let mut remaining = samples;
100 while !remaining.is_empty() {
101 let mut state = self.shared.lock();
102 loop {
103 if state.reader_gone || state.ended.is_some() {
104 return Err(StreamClosed);
105 }
106 if state.pending.len() < self.shared.capacity {
107 break;
108 }
109 state = self
110 .shared
111 .changed
112 .wait(state)
113 .unwrap_or_else(|poisoned| poisoned.into_inner());
114 }
115 let available = self.shared.capacity - state.pending.len();
116 let appended = available.min(remaining.len());
117 state.pending.extend(&remaining[..appended]);
118 remaining = &remaining[appended..];
119 drop(state);
120 self.shared.changed.notify_all();
121 }
122 Ok(())
123 }
124
125 pub fn finish(self) {
128 self.shared.close(Ok(()));
129 }
130
131 pub fn fail(self, error: SynthesisError) {
134 self.shared.close(Err(error));
135 }
136}
137
138impl Drop for SpeechWriter {
139 fn drop(&mut self) {
140 self.shared.close(Err(SynthesisError::SourceGone));
141 }
142}
143
144pub struct SpeechStream {
146 shared: Arc<Shared>,
147 failed: bool,
148}
149
150impl SpeechStream {
151 pub fn sample_rate(&self) -> u32 {
152 self.shared.sample_rate
153 }
154
155 pub fn read(&mut self, out: &mut [f32]) -> Result<usize, SynthesisError> {
159 if out.is_empty() {
160 return Err(SynthesisError::EmptyReadBuffer);
161 }
162 let mut state = self.shared.lock();
163 while state.pending.is_empty() && state.ended.is_none() {
164 state = self
165 .shared
166 .changed
167 .wait(state)
168 .unwrap_or_else(|poisoned| poisoned.into_inner());
169 }
170 let copied = Self::drain(&mut state, out);
171 if copied > 0 {
172 drop(state);
173 self.shared.changed.notify_all();
174 return Ok(copied);
175 }
176 let outcome = state
177 .ended
178 .as_mut()
179 .expect("the wait only ends with samples or an outcome");
180 match std::mem::replace(outcome, Ok(())) {
181 Ok(()) if self.failed => Err(SynthesisError::AlreadyFailed),
182 Ok(()) => Ok(0),
183 Err(error) => {
184 self.failed = true;
185 Err(error)
186 }
187 }
188 }
189
190 pub fn try_read(&mut self, out: &mut [f32]) -> TryRead {
193 let mut state = self.shared.lock();
194 let copied = Self::drain(&mut state, out);
195 if copied > 0 {
196 drop(state);
197 self.shared.changed.notify_all();
198 return TryRead::Samples(copied);
199 }
200 if state.ended.is_some() {
201 TryRead::Ended
202 } else {
203 TryRead::Starved
204 }
205 }
206
207 fn drain(state: &mut StreamState, out: &mut [f32]) -> usize {
208 let wanted = out.len().min(state.pending.len());
209 for slot in out.iter_mut().take(wanted) {
210 *slot = state
211 .pending
212 .pop_front()
213 .expect("the drain count is bounded by the buffer length");
214 }
215 wanted
216 }
217}
218
219impl Drop for SpeechStream {
220 fn drop(&mut self) {
221 let mut state = self.shared.lock();
222 state.reader_gone = true;
223 state.pending.clear();
224 drop(state);
225 self.shared.changed.notify_all();
226 }
227}
228
229#[cfg(test)]
230mod tests {
231 use super::*;
232
233 const RATE: u32 = 24_000;
234
235 #[test]
236 fn a_reader_hears_what_the_writer_wrote() {
237 let (mut writer, mut speech) = stream(RATE);
238 writer.write(&[0.1, 0.2]).expect("the reader is listening");
239 writer.write(&[0.3]).expect("the reader is listening");
240 writer.finish();
241
242 let mut heard = Vec::new();
243 let mut buffer = [0.0; 2];
244 loop {
245 match speech.read(&mut buffer).expect("the utterance succeeded") {
246 0 => break,
247 copied => heard.extend_from_slice(&buffer[..copied]),
248 }
249 }
250
251 assert_eq!(heard, [0.1, 0.2, 0.3]);
252 }
253
254 #[test]
255 fn a_reader_waits_for_samples_synthesis_has_not_written_yet() {
256 let (mut writer, mut speech) = stream(RATE);
257 std::thread::spawn(move || {
258 writer.write(&[0.5]).expect("the reader is listening");
259 writer.finish();
260 });
261
262 let mut buffer = [0.0; 4];
263 let copied = speech.read(&mut buffer).expect("a sample arrives");
264
265 assert_eq!(&buffer[..copied], [0.5]);
266 }
267
268 #[test]
269 fn a_full_buffer_blocks_the_writer_until_playback_drains_it() {
270 let (mut writer, mut speech) = stream(1);
271 let chunk = vec![0.0; BUFFER_SECONDS as usize];
272 writer.write(&chunk).expect("the buffer has room");
273
274 let blocked = std::thread::spawn(move || {
275 writer.write(&[1.0]).expect("the reader drains the buffer");
276 writer.finish();
277 });
278 let mut buffer = vec![0.0; BUFFER_SECONDS as usize];
279 while !matches!(speech.read(&mut buffer), Ok(0) | Err(_)) {}
280
281 blocked.join().expect("the writer unblocks");
282 }
283
284 #[test]
285 fn one_large_write_never_overfills_the_buffer() {
286 let (mut writer, mut speech) = stream(1);
287 let samples = vec![0.5; BUFFER_SECONDS as usize * 2];
288 let finished = std::thread::spawn(move || {
289 writer
290 .write(&samples)
291 .expect("the reader drains the buffer");
292 writer.finish();
293 });
294
295 let mut received = Vec::new();
296 let mut buffer = [0.0; BUFFER_SECONDS as usize];
297 loop {
298 let copied = speech.read(&mut buffer).expect("the utterance succeeds");
299 if copied == 0 {
300 break;
301 }
302 assert!(copied <= BUFFER_SECONDS as usize);
303 received.extend_from_slice(&buffer[..copied]);
304 }
305
306 finished.join().expect("the writer finishes");
307 assert_eq!(received.len(), BUFFER_SECONDS as usize * 2);
308 }
309
310 #[test]
311 fn dropping_the_reader_stops_the_writer() {
312 let (mut writer, speech) = stream(RATE);
313 drop(speech);
314
315 writer
316 .write(&[0.1])
317 .expect_err("nobody is left to hear the samples");
318 }
319
320 #[test]
321 fn dropping_the_writer_unfinished_fails_the_utterance() {
322 let (writer, mut speech) = stream(RATE);
323 drop(writer);
324
325 let error = speech
326 .read(&mut [0.0; 4])
327 .expect_err("a vanished synthesizer is a failure");
328
329 assert!(matches!(error, SynthesisError::SourceGone));
330 }
331
332 #[test]
333 fn a_failed_utterance_stays_failed_for_every_later_read() {
334 let (writer, mut speech) = stream(RATE);
335 writer.fail(SynthesisError::Cancelled);
336 let first = speech
337 .read(&mut [0.0; 4])
338 .expect_err("the first read reports the cause");
339
340 let again = speech
341 .read(&mut [0.0; 4])
342 .expect_err("so does the next one");
343
344 assert!(matches!(first, SynthesisError::Cancelled));
345 assert!(matches!(again, SynthesisError::AlreadyFailed));
346 }
347
348 #[test]
349 fn the_first_outcome_is_the_one_the_reader_learns() {
350 let (writer, mut speech) = stream(RATE);
351 let shared = Arc::clone(&writer.shared);
352 writer.fail(SynthesisError::Cancelled);
353 shared.close(Err(SynthesisError::WorkerPanicked { backend: "test" }));
354
355 let error = speech
356 .read(&mut [0.0; 4])
357 .expect_err("the utterance failed");
358
359 assert!(matches!(error, SynthesisError::Cancelled));
360 }
361
362 #[test]
363 fn an_empty_read_buffer_is_rejected_while_samples_wait() {
364 let (mut writer, mut speech) = stream(RATE);
365 writer.write(&[0.1]).expect("the reader is listening");
366
367 assert!(matches!(
368 speech.read(&mut []),
369 Err(SynthesisError::EmptyReadBuffer)
370 ));
371 }
372
373 #[test]
374 fn try_read_never_waits() {
375 let (mut writer, mut speech) = stream(RATE);
376
377 assert_eq!(speech.try_read(&mut [0.0; 4]), TryRead::Starved);
378
379 writer.write(&[0.7]).expect("the reader is listening");
380 let mut buffer = [0.0; 4];
381 assert_eq!(speech.try_read(&mut buffer), TryRead::Samples(1));
382 assert_eq!(buffer[0], 0.7);
383
384 writer.finish();
385 assert_eq!(speech.try_read(&mut [0.0; 4]), TryRead::Ended);
386 }
387}