From 174011bbb4d193a0ce097ae51578e709dfc1ebd3 Mon Sep 17 00:00:00 2001 From: Nolan Darilek Date: Thu, 8 Oct 2020 07:16:10 -0500 Subject: [PATCH] Make `UtteranceId` use `u64` on most platforms, and add additional derives. --- src/backends/web.rs | 25 ++++++++++++++++++----- src/backends/winrt.rs | 46 +++++++++++++++++++++++++++++++++++++------ src/lib.rs | 11 +++-------- 3 files changed, 63 insertions(+), 19 deletions(-) diff --git a/src/backends/web.rs b/src/backends/web.rs index 32be8f7..4696e7c 100644 --- a/src/backends/web.rs +++ b/src/backends/web.rs @@ -18,6 +18,8 @@ pub struct Web { lazy_static! { static ref NEXT_BACKEND_ID: Mutex = Mutex::new(0); + static ref UTTERANCE_MAPPINGS: Mutex> = Mutex::new(Vec::new()); + static ref NEXT_UTTERANCE_ID: Mutex = Mutex::new(0); } impl Web { @@ -58,23 +60,29 @@ impl Backend for Web { utterance.set_pitch(self.pitch); utterance.set_volume(self.volume); let id = self.id().unwrap(); - let utterance_id = UtteranceId::Web(utterance.clone()); - let callback = Closure::wrap(Box::new(move |evt: SpeechSynthesisEvent| { + let mut uid = NEXT_UTTERANCE_ID.lock().unwrap(); + let utterance_id = UtteranceId::Web(*uid); + *uid += 1; + drop(uid); + let mut mappings = UTTERANCE_MAPPINGS.lock().unwrap(); + mappings.push((self.id, utterance_id)); + drop(mappings); + let callback = Closure::wrap(Box::new(move |_evt: SpeechSynthesisEvent| { let mut callbacks = CALLBACKS.lock().unwrap(); let callback = callbacks.get_mut(&id).unwrap(); if let Some(f) = callback.utterance_begin.as_mut() { - let utterance_id = UtteranceId::Web(evt.utterance()); f(utterance_id); } }) as Box); utterance.set_onstart(Some(callback.as_ref().unchecked_ref())); - let callback = Closure::wrap(Box::new(move |evt: SpeechSynthesisEvent| { + let callback = Closure::wrap(Box::new(move |_evt: SpeechSynthesisEvent| { let mut callbacks = CALLBACKS.lock().unwrap(); let callback = callbacks.get_mut(&id).unwrap(); if let Some(f) = callback.utterance_end.as_mut() { - let utterance_id = UtteranceId::Web(evt.utterance()); f(utterance_id); } + let mut mappings = UTTERANCE_MAPPINGS.lock().unwrap(); + mappings.retain(|v| v.1 != utterance_id); }) as Box); utterance.set_onend(Some(callback.as_ref().unchecked_ref())); if interrupt { @@ -173,3 +181,10 @@ impl Backend for Web { } } } + +impl Drop for Web { + fn drop(&mut self) { + let mut mappings = UTTERANCE_MAPPINGS.lock().unwrap(); + mappings.retain(|v| v.0 != self.id); + } +} diff --git a/src/backends/winrt.rs b/src/backends/winrt.rs index 97bd9f0..e81894b 100644 --- a/src/backends/winrt.rs +++ b/src/backends/winrt.rs @@ -30,6 +30,9 @@ pub struct WinRT { lazy_static! { static ref NEXT_BACKEND_ID: Mutex = Mutex::new(0); + static ref NEXT_UTTERANCE_ID: Mutex = Mutex::new(0); + static ref UTTERANCE_MAPPINGS: Mutex> = + Mutex::new(Vec::new()); static ref BACKEND_TO_MEDIA_PLAYER: Mutex> = { let v: HashMap = HashMap::new(); Mutex::new(v) @@ -70,6 +73,16 @@ impl WinRT { self.player.set_auto_play(true)?; self.player.set_source(&self.playback_list)?; self.init_callbacks()?; + let mut mappings = UTTERANCE_MAPPINGS.lock().unwrap(); + let mut callbacks = CALLBACKS.lock().unwrap(); + let callbacks = callbacks.get_mut(&self.id).unwrap(); + if let Some(callback) = callbacks.utterance_end.as_mut() { + let mappings = UTTERANCE_MAPPINGS.lock().unwrap(); + for mapping in &*mappings { + callback(mapping.2); + } + } + mappings.retain(|v| v.0 != self.id); Ok(()) } @@ -107,17 +120,31 @@ impl WinRT { let callbacks = callbacks.get_mut(&id).unwrap(); let old_item = args.old_item()?; if !old_item.is_null() { + let mut mappings = UTTERANCE_MAPPINGS.lock().unwrap(); if let Some(callback) = callbacks.utterance_end.as_mut() { - callback(UtteranceId::WinRT(old_item)); + for mapping in &*mappings { + if mapping.1 == old_item { + callback(mapping.2); + } + } + mappings.retain(|v| v.1 != old_item); } } let new_item = args.new_item()?; if !new_item.is_null() { let mut last_spoken_utterance = LAST_SPOKEN_UTTERANCE.lock().unwrap(); - let utterance_id = UtteranceId::WinRT(new_item); - last_spoken_utterance.insert(*id, utterance_id.clone()); + let mappings = UTTERANCE_MAPPINGS.lock().unwrap(); + for mapping in &*mappings { + if mapping.1 == new_item { + last_spoken_utterance.insert(*id, mapping.2); + } + } if let Some(callback) = callbacks.utterance_begin.as_mut() { - callback(utterance_id); + for mapping in &*mappings { + if mapping.1 == new_item { + callback(mapping.2); + } + } } } } @@ -169,7 +196,12 @@ impl Backend for WinRT { if !self.is_speaking()? { self.player.play()?; } - let utterance_id = UtteranceId::WinRT(item); + let mut uid = NEXT_UTTERANCE_ID.lock().unwrap(); + let utterance_id = UtteranceId::WinRT(*uid); + *uid += 1; + drop(uid); + let mut mappings = UTTERANCE_MAPPINGS.lock().unwrap(); + mappings.push((self.id, item, utterance_id)); Ok(Some(utterance_id)) } @@ -254,12 +286,14 @@ impl Backend for WinRT { impl Drop for WinRT { fn drop(&mut self) { - let id = self.id().unwrap(); + let id = self.id; let mut backend_to_playback_list = BACKEND_TO_PLAYBACK_LIST.lock().unwrap(); backend_to_playback_list.remove(&id); let mut backend_to_media_player = BACKEND_TO_MEDIA_PLAYER.lock().unwrap(); backend_to_media_player.remove(&id); let mut last_spoken_utterance = LAST_SPOKEN_UTTERANCE.lock().unwrap(); last_spoken_utterance.remove(&id); + let mut mappings = UTTERANCE_MAPPINGS.lock().unwrap(); + mappings.retain(|v| v.0 != id); } } diff --git a/src/lib.rs b/src/lib.rs index 6949ca1..e3cbbde 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,11 +25,6 @@ use libc::c_char; #[cfg(target_os = "macos")] use objc::{class, msg_send, sel, sel_impl}; use thiserror::Error; -#[cfg(target_arch = "wasm32")] -use web_sys::SpeechSynthesisUtterance; - -#[cfg(windows)] -use tts_winrt_bindings::windows::media::playback::MediaPlaybackItem; mod backends; @@ -60,14 +55,14 @@ pub enum BackendId { AvFoundation(u64), } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum UtteranceId { #[cfg(target_os = "linux")] SpeechDispatcher(u64), #[cfg(target_arch = "wasm32")] - Web(SpeechSynthesisUtterance), + Web(u64), #[cfg(windows)] - WinRT(MediaPlaybackItem), + WinRT(u64), #[cfg(any(target_os = "macos", target_os = "ios"))] AvFoundation(id), }