Make `UtteranceId` use `u64` on most platforms, and add additional derives.

This commit is contained in:
Nolan Darilek 2020-10-08 07:16:10 -05:00
parent c2bbc5ac04
commit 174011bbb4
3 changed files with 63 additions and 19 deletions

View File

@ -18,6 +18,8 @@ pub struct Web {
lazy_static! { lazy_static! {
static ref NEXT_BACKEND_ID: Mutex<u64> = Mutex::new(0); static ref NEXT_BACKEND_ID: Mutex<u64> = Mutex::new(0);
static ref UTTERANCE_MAPPINGS: Mutex<Vec<(BackendId, UtteranceId)>> = Mutex::new(Vec::new());
static ref NEXT_UTTERANCE_ID: Mutex<u64> = Mutex::new(0);
} }
impl Web { impl Web {
@ -58,23 +60,29 @@ impl Backend for Web {
utterance.set_pitch(self.pitch); utterance.set_pitch(self.pitch);
utterance.set_volume(self.volume); utterance.set_volume(self.volume);
let id = self.id().unwrap(); let id = self.id().unwrap();
let utterance_id = UtteranceId::Web(utterance.clone()); let mut uid = NEXT_UTTERANCE_ID.lock().unwrap();
let callback = Closure::wrap(Box::new(move |evt: SpeechSynthesisEvent| { 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 mut callbacks = CALLBACKS.lock().unwrap();
let callback = callbacks.get_mut(&id).unwrap(); let callback = callbacks.get_mut(&id).unwrap();
if let Some(f) = callback.utterance_begin.as_mut() { if let Some(f) = callback.utterance_begin.as_mut() {
let utterance_id = UtteranceId::Web(evt.utterance());
f(utterance_id); f(utterance_id);
} }
}) as Box<dyn Fn(_)>); }) as Box<dyn Fn(_)>);
utterance.set_onstart(Some(callback.as_ref().unchecked_ref())); 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 mut callbacks = CALLBACKS.lock().unwrap();
let callback = callbacks.get_mut(&id).unwrap(); let callback = callbacks.get_mut(&id).unwrap();
if let Some(f) = callback.utterance_end.as_mut() { if let Some(f) = callback.utterance_end.as_mut() {
let utterance_id = UtteranceId::Web(evt.utterance());
f(utterance_id); f(utterance_id);
} }
let mut mappings = UTTERANCE_MAPPINGS.lock().unwrap();
mappings.retain(|v| v.1 != utterance_id);
}) as Box<dyn Fn(_)>); }) as Box<dyn Fn(_)>);
utterance.set_onend(Some(callback.as_ref().unchecked_ref())); utterance.set_onend(Some(callback.as_ref().unchecked_ref()));
if interrupt { 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);
}
}

View File

@ -30,6 +30,9 @@ pub struct WinRT {
lazy_static! { lazy_static! {
static ref NEXT_BACKEND_ID: Mutex<u64> = Mutex::new(0); static ref NEXT_BACKEND_ID: Mutex<u64> = Mutex::new(0);
static ref NEXT_UTTERANCE_ID: Mutex<u64> = Mutex::new(0);
static ref UTTERANCE_MAPPINGS: Mutex<Vec<(BackendId, MediaPlaybackItem, UtteranceId)>> =
Mutex::new(Vec::new());
static ref BACKEND_TO_MEDIA_PLAYER: Mutex<HashMap<BackendId, MediaPlayer>> = { static ref BACKEND_TO_MEDIA_PLAYER: Mutex<HashMap<BackendId, MediaPlayer>> = {
let v: HashMap<BackendId, MediaPlayer> = HashMap::new(); let v: HashMap<BackendId, MediaPlayer> = HashMap::new();
Mutex::new(v) Mutex::new(v)
@ -70,6 +73,16 @@ impl WinRT {
self.player.set_auto_play(true)?; self.player.set_auto_play(true)?;
self.player.set_source(&self.playback_list)?; self.player.set_source(&self.playback_list)?;
self.init_callbacks()?; 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(()) Ok(())
} }
@ -107,17 +120,31 @@ impl WinRT {
let callbacks = callbacks.get_mut(&id).unwrap(); let callbacks = callbacks.get_mut(&id).unwrap();
let old_item = args.old_item()?; let old_item = args.old_item()?;
if !old_item.is_null() { if !old_item.is_null() {
let mut mappings = UTTERANCE_MAPPINGS.lock().unwrap();
if let Some(callback) = callbacks.utterance_end.as_mut() { 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()?; let new_item = args.new_item()?;
if !new_item.is_null() { if !new_item.is_null() {
let mut last_spoken_utterance = LAST_SPOKEN_UTTERANCE.lock().unwrap(); let mut last_spoken_utterance = LAST_SPOKEN_UTTERANCE.lock().unwrap();
let utterance_id = UtteranceId::WinRT(new_item); let mappings = UTTERANCE_MAPPINGS.lock().unwrap();
last_spoken_utterance.insert(*id, utterance_id.clone()); 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() { 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()? { if !self.is_speaking()? {
self.player.play()?; 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)) Ok(Some(utterance_id))
} }
@ -254,12 +286,14 @@ impl Backend for WinRT {
impl Drop for WinRT { impl Drop for WinRT {
fn drop(&mut self) { 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(); let mut backend_to_playback_list = BACKEND_TO_PLAYBACK_LIST.lock().unwrap();
backend_to_playback_list.remove(&id); backend_to_playback_list.remove(&id);
let mut backend_to_media_player = BACKEND_TO_MEDIA_PLAYER.lock().unwrap(); let mut backend_to_media_player = BACKEND_TO_MEDIA_PLAYER.lock().unwrap();
backend_to_media_player.remove(&id); backend_to_media_player.remove(&id);
let mut last_spoken_utterance = LAST_SPOKEN_UTTERANCE.lock().unwrap(); let mut last_spoken_utterance = LAST_SPOKEN_UTTERANCE.lock().unwrap();
last_spoken_utterance.remove(&id); last_spoken_utterance.remove(&id);
let mut mappings = UTTERANCE_MAPPINGS.lock().unwrap();
mappings.retain(|v| v.0 != id);
} }
} }

View File

@ -25,11 +25,6 @@ use libc::c_char;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
use objc::{class, msg_send, sel, sel_impl}; use objc::{class, msg_send, sel, sel_impl};
use thiserror::Error; use thiserror::Error;
#[cfg(target_arch = "wasm32")]
use web_sys::SpeechSynthesisUtterance;
#[cfg(windows)]
use tts_winrt_bindings::windows::media::playback::MediaPlaybackItem;
mod backends; mod backends;
@ -60,14 +55,14 @@ pub enum BackendId {
AvFoundation(u64), AvFoundation(u64),
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum UtteranceId { pub enum UtteranceId {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
SpeechDispatcher(u64), SpeechDispatcher(u64),
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
Web(SpeechSynthesisUtterance), Web(u64),
#[cfg(windows)] #[cfg(windows)]
WinRT(MediaPlaybackItem), WinRT(u64),
#[cfg(any(target_os = "macos", target_os = "ios"))] #[cfg(any(target_os = "macos", target_os = "ios"))]
AvFoundation(id), AvFoundation(id),
} }