From 4816ec575c3ff9d49c923bb6a90ab24f4f172999 Mon Sep 17 00:00:00 2001 From: Nolan Darilek Date: Tue, 22 Sep 2020 12:40:03 -0500 Subject: [PATCH] Make speak calls return an utterance ID, where possible. --- src/backends/speech_dispatcher.rs | 12 ++++++++---- src/backends/tolk.rs | 6 +++--- src/backends/web.rs | 17 ++++++++++++++--- src/backends/winrt.rs | 19 ++++++++++++++++--- src/lib.rs | 21 +++++++++++++++++---- 5 files changed, 58 insertions(+), 17 deletions(-) diff --git a/src/backends/speech_dispatcher.rs b/src/backends/speech_dispatcher.rs index 009a8fb..5717c61 100644 --- a/src/backends/speech_dispatcher.rs +++ b/src/backends/speech_dispatcher.rs @@ -6,7 +6,7 @@ use lazy_static::*; use log::{info, trace}; use speech_dispatcher::*; -use crate::{Backend, Error, Features}; +use crate::{Backend, Error, Features, UtteranceId}; pub struct SpeechDispatcher(Connection); @@ -59,7 +59,7 @@ impl Backend for SpeechDispatcher { } } - fn speak(&mut self, text: &str, interrupt: bool) -> Result<(), Error> { + fn speak(&mut self, text: &str, interrupt: bool) -> Result, Error> { trace!("speak({}, {})", text, interrupt); if interrupt { self.stop()?; @@ -68,11 +68,15 @@ impl Backend for SpeechDispatcher { if single_char { self.0.set_punctuation(Punctuation::All); } - self.0.say(Priority::Important, text); + let id = self.0.say(Priority::Important, text); if single_char { self.0.set_punctuation(Punctuation::None); } - Ok(()) + if let Some(id) = id { + Ok(Some(UtteranceId::SpeechDispatcher(id))) + } else { + Err(Error::NoneError) + } } fn stop(&mut self) -> Result<(), Error> { diff --git a/src/backends/tolk.rs b/src/backends/tolk.rs index 370da65..28c3f42 100644 --- a/src/backends/tolk.rs +++ b/src/backends/tolk.rs @@ -2,7 +2,7 @@ use log::{info, trace}; use tolk::Tolk as TolkPtr; -use crate::{Backend, Error, Features}; +use crate::{Backend, Error, Features, UtteranceId}; pub struct Tolk(TolkPtr); @@ -26,10 +26,10 @@ impl Backend for Tolk { } } - fn speak(&mut self, text: &str, interrupt: bool) -> Result<(), Error> { + fn speak(&mut self, text: &str, interrupt: bool) -> Result, Error> { trace!("speak({}, {})", text, interrupt); self.0.speak(text, interrupt); - Ok(()) + Ok(None) } fn stop(&mut self) -> Result<(), Error> { diff --git a/src/backends/web.rs b/src/backends/web.rs index 664793c..89db8a9 100644 --- a/src/backends/web.rs +++ b/src/backends/web.rs @@ -1,8 +1,11 @@ #[cfg(target_arch = "wasm32")] +use std::sync::Mutex; + +use lazy_static::lazy_static; use log::{info, trace}; use web_sys::SpeechSynthesisUtterance; -use crate::{Backend, Error, Features}; +use crate::{Backend, Error, Features, UtteranceId}; pub struct Web { rate: f32, @@ -10,6 +13,10 @@ pub struct Web { volume: f32, } +lazy_static! { + static ref NEXT_UTTERANCE_ID: Mutex = Mutex::new(0); +} + impl Web { pub fn new() -> Result { info!("Initializing Web backend"); @@ -32,7 +39,7 @@ impl Backend for Web { } } - fn speak(&mut self, text: &str, interrupt: bool) -> Result<(), Error> { + fn speak(&mut self, text: &str, interrupt: bool) -> Result, Error> { trace!("speak({}, {})", text, interrupt); let utterance = SpeechSynthesisUtterance::new_with_text(text).unwrap(); utterance.set_rate(self.rate); @@ -44,8 +51,12 @@ impl Backend for Web { if let Some(window) = web_sys::window() { let speech_synthesis = window.speech_synthesis().unwrap(); speech_synthesis.speak(&utterance); + let mut utterance_id = NEXT_UTTERANCE_ID.lock().unwrap(); + *utterance_id += 1; + Ok(Some(UtteranceId::Web(*utterance_id))) + } else { + Err(Error::NoneError) } - Ok(()) } fn stop(&mut self) -> Result<(), Error> { diff --git a/src/backends/winrt.rs b/src/backends/winrt.rs index 7464f58..32fb219 100644 --- a/src/backends/winrt.rs +++ b/src/backends/winrt.rs @@ -1,4 +1,7 @@ #[cfg(windows)] +use std::sync::Mutex; + +use lazy_static::lazy_static; use log::{info, trace}; use tts_winrt_bindings::windows::media::core::MediaSource; @@ -7,7 +10,7 @@ use tts_winrt_bindings::windows::media::playback::{ }; use tts_winrt_bindings::windows::media::speech_synthesis::SpeechSynthesizer; -use crate::{Backend, Error, Features}; +use crate::{Backend, Error, Features, UtteranceId}; impl From for Error { fn from(e: winrt::Error) -> Self { @@ -21,6 +24,10 @@ pub struct WinRT { playback_list: MediaPlaybackList, } +lazy_static! { + static ref NEXT_UTTERANCE_ID: Mutex = Mutex::new(0); +} + impl WinRT { pub fn new() -> std::result::Result { info!("Initializing WinRT backend"); @@ -55,7 +62,11 @@ impl Backend for WinRT { } } - fn speak(&mut self, text: &str, interrupt: bool) -> std::result::Result<(), Error> { + fn speak( + &mut self, + text: &str, + interrupt: bool, + ) -> std::result::Result, Error> { trace!("speak({}, {})", text, interrupt); if interrupt { self.stop()?; @@ -76,7 +87,9 @@ impl Backend for WinRT { if !self.is_speaking()? { self.player.play()?; } - Ok(()) + let mut utterance_id = NEXT_UTTERANCE_ID.lock().unwrap(); + *utterance_id += 1; + Ok(Some(UtteranceId::WinRT(*utterance_id))) } fn stop(&mut self) -> std::result::Result<(), Error> { diff --git a/src/lib.rs b/src/lib.rs index 56b9f32..2d494cf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,6 +40,16 @@ pub enum Backends { AvFoundation, } +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum UtteranceId { + #[cfg(target_os = "linux")] + SpeechDispatcher(i32), + #[cfg(target_arch = "wasm32")] + Web(u64), + #[cfg(windows)] + WinRT(u64), +} + pub struct Features { pub stop: bool, pub rate: bool, @@ -80,7 +90,7 @@ pub enum Error { pub trait Backend { fn supported_features(&self) -> Features; - fn speak(&mut self, text: &str, interrupt: bool) -> Result<(), Error>; + fn speak(&mut self, text: &str, interrupt: bool) -> Result, Error>; fn stop(&mut self) -> Result<(), Error>; fn min_rate(&self) -> f32; fn max_rate(&self) -> f32; @@ -184,9 +194,12 @@ impl TTS { /** * Speaks the specified text, optionally interrupting current speech. */ - pub fn speak>(&mut self, text: S, interrupt: bool) -> Result<&Self, Error> { - self.0.speak(text.into().as_str(), interrupt)?; - Ok(self) + pub fn speak>( + &mut self, + text: S, + interrupt: bool, + ) -> Result, Error> { + self.0.speak(text.into().as_str(), interrupt) } /**