use std::collections::VecDeque;
use std::sync::{Arc, Condvar, Mutex, MutexGuard};
use super::SynthesisError;
const BUFFER_SECONDS: u32 = 10;
pub fn stream(sample_rate: u32) -> (SpeechWriter, SpeechStream) {
let shared = Arc::new(Shared {
state: Mutex::new(StreamState::default()),
changed: Condvar::new(),
sample_rate,
capacity: sample_rate as usize * BUFFER_SECONDS as usize,
});
(
SpeechWriter {
shared: Arc::clone(&shared),
},
SpeechStream {
shared,
failed: false,
},
)
}
#[derive(Debug, thiserror::Error)]
#[error("the speech stream is closed")]
pub struct StreamClosed;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TryRead {
Samples(usize),
Starved,
Ended,
}
struct Shared {
state: Mutex<StreamState>,
changed: Condvar,
sample_rate: u32,
capacity: usize,
}
#[derive(Default)]
struct StreamState {
pending: VecDeque<f32>,
reader_gone: bool,
ended: Option<Result<(), SynthesisError>>,
}
impl Shared {
fn lock(&self) -> MutexGuard<'_, StreamState> {
self.state
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
}
fn close(&self, outcome: Result<(), SynthesisError>) {
let mut state = self.lock();
if state.ended.is_none() {
state.ended = Some(outcome);
}
drop(state);
self.changed.notify_all();
}
}
pub struct SpeechWriter {
shared: Arc<Shared>,
}
impl SpeechWriter {
pub fn write(&mut self, samples: &[f32]) -> Result<(), StreamClosed> {
let mut remaining = samples;
while !remaining.is_empty() {
let mut state = self.shared.lock();
loop {
if state.reader_gone || state.ended.is_some() {
return Err(StreamClosed);
}
if state.pending.len() < self.shared.capacity {
break;
}
state = self
.shared
.changed
.wait(state)
.unwrap_or_else(|poisoned| poisoned.into_inner());
}
let available = self.shared.capacity - state.pending.len();
let appended = available.min(remaining.len());
state.pending.extend(&remaining[..appended]);
remaining = &remaining[appended..];
drop(state);
self.shared.changed.notify_all();
}
Ok(())
}
pub fn finish(self) {
self.shared.close(Ok(()));
}
pub fn fail(self, error: SynthesisError) {
self.shared.close(Err(error));
}
}
impl Drop for SpeechWriter {
fn drop(&mut self) {
self.shared.close(Err(SynthesisError::SourceGone));
}
}
pub struct SpeechStream {
shared: Arc<Shared>,
failed: bool,
}
impl SpeechStream {
pub fn sample_rate(&self) -> u32 {
self.shared.sample_rate
}
pub fn read(&mut self, out: &mut [f32]) -> Result<usize, SynthesisError> {
if out.is_empty() {
return Err(SynthesisError::EmptyReadBuffer);
}
let mut state = self.shared.lock();
while state.pending.is_empty() && state.ended.is_none() {
state = self
.shared
.changed
.wait(state)
.unwrap_or_else(|poisoned| poisoned.into_inner());
}
let copied = Self::drain(&mut state, out);
if copied > 0 {
drop(state);
self.shared.changed.notify_all();
return Ok(copied);
}
let outcome = state
.ended
.as_mut()
.expect("the wait only ends with samples or an outcome");
match std::mem::replace(outcome, Ok(())) {
Ok(()) if self.failed => Err(SynthesisError::AlreadyFailed),
Ok(()) => Ok(0),
Err(error) => {
self.failed = true;
Err(error)
}
}
}
pub fn try_read(&mut self, out: &mut [f32]) -> TryRead {
let mut state = self.shared.lock();
let copied = Self::drain(&mut state, out);
if copied > 0 {
drop(state);
self.shared.changed.notify_all();
return TryRead::Samples(copied);
}
if state.ended.is_some() {
TryRead::Ended
} else {
TryRead::Starved
}
}
fn drain(state: &mut StreamState, out: &mut [f32]) -> usize {
let wanted = out.len().min(state.pending.len());
for slot in out.iter_mut().take(wanted) {
*slot = state
.pending
.pop_front()
.expect("the drain count is bounded by the buffer length");
}
wanted
}
}
impl Drop for SpeechStream {
fn drop(&mut self) {
let mut state = self.shared.lock();
state.reader_gone = true;
state.pending.clear();
drop(state);
self.shared.changed.notify_all();
}
}
#[cfg(test)]
mod tests {
use super::*;
const RATE: u32 = 24_000;
#[test]
fn a_reader_hears_what_the_writer_wrote() {
let (mut writer, mut speech) = stream(RATE);
writer.write(&[0.1, 0.2]).expect("the reader is listening");
writer.write(&[0.3]).expect("the reader is listening");
writer.finish();
let mut heard = Vec::new();
let mut buffer = [0.0; 2];
loop {
match speech.read(&mut buffer).expect("the utterance succeeded") {
0 => break,
copied => heard.extend_from_slice(&buffer[..copied]),
}
}
assert_eq!(heard, [0.1, 0.2, 0.3]);
}
#[test]
fn a_reader_waits_for_samples_synthesis_has_not_written_yet() {
let (mut writer, mut speech) = stream(RATE);
std::thread::spawn(move || {
writer.write(&[0.5]).expect("the reader is listening");
writer.finish();
});
let mut buffer = [0.0; 4];
let copied = speech.read(&mut buffer).expect("a sample arrives");
assert_eq!(&buffer[..copied], [0.5]);
}
#[test]
fn a_full_buffer_blocks_the_writer_until_playback_drains_it() {
let (mut writer, mut speech) = stream(1);
let chunk = vec![0.0; BUFFER_SECONDS as usize];
writer.write(&chunk).expect("the buffer has room");
let blocked = std::thread::spawn(move || {
writer.write(&[1.0]).expect("the reader drains the buffer");
writer.finish();
});
let mut buffer = vec![0.0; BUFFER_SECONDS as usize];
while !matches!(speech.read(&mut buffer), Ok(0) | Err(_)) {}
blocked.join().expect("the writer unblocks");
}
#[test]
fn one_large_write_never_overfills_the_buffer() {
let (mut writer, mut speech) = stream(1);
let samples = vec![0.5; BUFFER_SECONDS as usize * 2];
let finished = std::thread::spawn(move || {
writer
.write(&samples)
.expect("the reader drains the buffer");
writer.finish();
});
let mut received = Vec::new();
let mut buffer = [0.0; BUFFER_SECONDS as usize];
loop {
let copied = speech.read(&mut buffer).expect("the utterance succeeds");
if copied == 0 {
break;
}
assert!(copied <= BUFFER_SECONDS as usize);
received.extend_from_slice(&buffer[..copied]);
}
finished.join().expect("the writer finishes");
assert_eq!(received.len(), BUFFER_SECONDS as usize * 2);
}
#[test]
fn dropping_the_reader_stops_the_writer() {
let (mut writer, speech) = stream(RATE);
drop(speech);
writer
.write(&[0.1])
.expect_err("nobody is left to hear the samples");
}
#[test]
fn dropping_the_writer_unfinished_fails_the_utterance() {
let (writer, mut speech) = stream(RATE);
drop(writer);
let error = speech
.read(&mut [0.0; 4])
.expect_err("a vanished synthesizer is a failure");
assert!(matches!(error, SynthesisError::SourceGone));
}
#[test]
fn a_failed_utterance_stays_failed_for_every_later_read() {
let (writer, mut speech) = stream(RATE);
writer.fail(SynthesisError::Cancelled);
let first = speech
.read(&mut [0.0; 4])
.expect_err("the first read reports the cause");
let again = speech
.read(&mut [0.0; 4])
.expect_err("so does the next one");
assert!(matches!(first, SynthesisError::Cancelled));
assert!(matches!(again, SynthesisError::AlreadyFailed));
}
#[test]
fn the_first_outcome_is_the_one_the_reader_learns() {
let (writer, mut speech) = stream(RATE);
let shared = Arc::clone(&writer.shared);
writer.fail(SynthesisError::Cancelled);
shared.close(Err(SynthesisError::WorkerPanicked { backend: "test" }));
let error = speech
.read(&mut [0.0; 4])
.expect_err("the utterance failed");
assert!(matches!(error, SynthesisError::Cancelled));
}
#[test]
fn an_empty_read_buffer_is_rejected_while_samples_wait() {
let (mut writer, mut speech) = stream(RATE);
writer.write(&[0.1]).expect("the reader is listening");
assert!(matches!(
speech.read(&mut []),
Err(SynthesisError::EmptyReadBuffer)
));
}
#[test]
fn try_read_never_waits() {
let (mut writer, mut speech) = stream(RATE);
assert_eq!(speech.try_read(&mut [0.0; 4]), TryRead::Starved);
writer.write(&[0.7]).expect("the reader is listening");
let mut buffer = [0.0; 4];
assert_eq!(speech.try_read(&mut buffer), TryRead::Samples(1));
assert_eq!(buffer[0], 0.7);
writer.finish();
assert_eq!(speech.try_read(&mut [0.0; 4]), TryRead::Ended);
}
}