diff --git a/text/src/tts.rs b/text/src/tts.rs index e1119b8..4ff3efd 100644 --- a/text/src/tts.rs +++ b/text/src/tts.rs @@ -1,13 +1,7 @@ use crate::TtsString; use common::config::{parse, parse_bool}; use ini::Ini; -use lazy_static::lazy_static; -use parking_lot::Mutex; -use tts::{Gender, Tts, UtteranceId, Voice}; - -lazy_static! { - static ref UTTERANCE_ID: Mutex> = Mutex::new(None); -} +use tts::{Gender, Tts, Voice}; /// Text-to-speech. pub struct TTS { @@ -17,8 +11,8 @@ pub struct TTS { speech: Vec, /// If true, return subtitle text. pub show_subtitles: bool, - /// If true, callbacks are supported. - callbacks: bool, + /// If true, Casey is speaking. + pub speaking: bool, } impl TTS { @@ -27,15 +21,8 @@ impl TTS { // Get subtitles. let show_subtitles = parse_bool(section, "subtitles"); // Try to load the text-to-speech engine. - let (tts, callbacks) = match Tts::default() { + let tts = match Tts::default() { Ok(mut tts) => { - let callbacks = - tts.supported_features().utterance_callbacks && !cfg!(target_os = "macos"); - if callbacks { - let _ = tts.on_utterance_begin(Some(Box::new(on_utterance_begin))); - let _ = tts.on_utterance_end(Some(Box::new(on_utterance_end))); - let _ = tts.on_utterance_stop(Some(Box::new(on_utterance_end))); - } // Try to set the voice. if let Ok(voices) = tts.voices() { // Try to parse the voice ID as an index. @@ -95,34 +82,27 @@ impl TTS { "rate_linux" }; let _ = tts.set_rate(parse(section, rate_key)); - (Some(tts), callbacks) + Some(tts) } Err(error) => { println!("{}", error); - (None, false) + None } }; Self { show_subtitles, tts, speech: vec![], - callbacks, + speaking: false, } } /// Stop speaking. pub fn stop(&mut self) { - if self.is_speaking() { + if self.speaking { if let Some(tts) = &mut self.tts { let _ = tts.stop(); - - // This fixes a race condition. - // Speech Dispatcher won't immediately stop. - if cfg!(unix) { - if let Some(mut u) = UTTERANCE_ID.try_lock() { - *u = None; - } - } + self.speaking = false; } self.speech.clear(); } @@ -131,7 +111,7 @@ impl TTS { /// Update the subtitle state. pub fn update(&mut self) { // We're done speaking but we have more to say. - if !self.speech.is_empty() && !self.is_speaking() { + if !self.speech.is_empty() && !self.speaking { // Remove the first element. self.speech.remove(0); // Start speaking the next element. @@ -150,49 +130,20 @@ impl TTS { } } - /// Returns true if Casey is speaking. - fn is_speaking(&self) -> bool { - match &self.tts { - Some(tts) => { - if self.callbacks { - match UTTERANCE_ID.try_lock() { - Some(id) => id.is_some(), - None => false, - } - } else { - tts.is_speaking().unwrap_or(false) - } - } - None => false, - } - } - /// Say something and show subtitles. fn say(&mut self, text: &str) { if let Some(tts) = &mut self.tts { - if let Ok(utterance) = tts.speak(text, true) { - self.on_utter(utterance); + if tts.speak(text, true).is_ok() { + self.speaking = true; } } } - - /// Don't use utterances because we can't clone them. - #[cfg(target_os = "macos")] - fn on_utter(&self, _: Option) {} - - /// Store the utterance. - #[cfg(not(target_os = "macos"))] - fn on_utter(&self, utterance: Option) { - if self.callbacks { - *UTTERANCE_ID.lock() = utterance; - } - } } impl Enqueable for TTS { fn enqueue(&mut self, text: TtsString) { // Start speaking the first element. - if !self.is_speaking() { + if !self.speaking { self.say(&text.spoken) } // Push this element. We need it for subtitles. @@ -203,7 +154,7 @@ impl Enqueable for TTS { impl Enqueable<&TtsString> for TTS { fn enqueue(&mut self, text: &TtsString) { // Start speaking the first element. - if !self.is_speaking() { + if !self.speaking { self.say(&text.spoken) } // Push this element. We need it for subtitles. @@ -229,7 +180,7 @@ impl Enqueable> for TTS { return; } // Start speaking the first element. - if !self.is_speaking() { + if !self.speaking { self.say(&text[0].spoken) } self.speech.extend(text); @@ -242,7 +193,7 @@ impl Enqueable> for TTS { return; } // Start speaking the first element. - if !self.is_speaking() { + if !self.speaking { self.say(&text[0].spoken) } self.speech @@ -256,27 +207,8 @@ pub trait Enqueable { fn enqueue(&mut self, text: T); } -/// Invoked when an utterance begins. -fn on_utterance_begin(utterance: UtteranceId) { - // Use try_lock to handle situations in which Cacophony shuts down and drops tts. - if let Some(mut u) = UTTERANCE_ID.try_lock() { - *u = Some(utterance); - } -} - -/// Invoked when an utterance ends. -fn on_utterance_end(_: UtteranceId) { - // Use try_lock to handle situations in which Cacophony shuts down and drops tts. - if let Some(mut u) = UTTERANCE_ID.try_lock() { - *u = None; - } -} - #[cfg(test)] mod tests { - #[cfg(unix)] - use std::{thread::sleep, time::Duration}; - use crate::Enqueable; use common::get_test_config; @@ -294,15 +226,9 @@ mod tests { assert!(tts.show_subtitles); assert_eq!(tts.get_subtitles().unwrap(), TTS_STRING); tts.update(); - assert!(tts.is_speaking()); + assert!(tts.speaking); tts.stop(); tts.update(); - println!("stopped"); - assert!(!tts.is_speaking()); - - // This fixes a PoisonError. - // The race condition is somewhere in speech-dispatcher-sys. - #[cfg(unix)] - sleep(Duration::from_secs(2)); + assert!(!tts.speaking); } }