Implement callbacks for web backend.

This commit is contained in:
Nolan Darilek 2020-09-24 14:26:30 -05:00
parent 532d5d9b58
commit a22242af50
3 changed files with 43 additions and 11 deletions

View File

@ -9,7 +9,7 @@ exclude = ["*.cfg", "*.yml"]
edition = "2018" edition = "2018"
[lib] [lib]
crate-type = ["lib", "staticlib"] crate-type = ["lib", "cdylib", "staticlib"]
[dependencies] [dependencies]
lazy_static = "1" lazy_static = "1"
@ -34,4 +34,4 @@ objc = "0.2"
[target.wasm32-unknown-unknown.dependencies] [target.wasm32-unknown-unknown.dependencies]
wasm-bindgen = "0.2" wasm-bindgen = "0.2"
web-sys = { version = "0.3", features = ["SpeechSynthesis", "SpeechSynthesisUtterance", "Window", ] } web-sys = { version = "0.3", features = ["EventTarget", "SpeechSynthesis", "SpeechSynthesisEvent", "SpeechSynthesisUtterance", "Window", ] }

View File

@ -3,32 +3,43 @@ use std::sync::Mutex;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use log::{info, trace}; use log::{info, trace};
use web_sys::SpeechSynthesisUtterance; use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::{SpeechSynthesisEvent, SpeechSynthesisUtterance};
use crate::{Backend, Error, Features, UtteranceId}; use crate::{Backend, BackendId, Error, Features, UtteranceId, CALLBACKS};
pub struct Web { pub struct Web {
id: BackendId,
rate: f32, rate: f32,
pitch: f32, pitch: f32,
volume: f32, volume: f32,
} }
lazy_static! { lazy_static! {
static ref NEXT_UTTERANCE_ID: Mutex<u64> = Mutex::new(0); static ref NEXT_BACKEND_ID: Mutex<u64> = Mutex::new(0);
} }
impl Web { impl Web {
pub fn new() -> Result<Self, Error> { pub fn new() -> Result<Self, Error> {
info!("Initializing Web backend"); info!("Initializing Web backend");
Ok(Web { let mut backend_id = NEXT_BACKEND_ID.lock().unwrap();
let rv = Web {
id: BackendId::Web(*backend_id),
rate: 1., rate: 1.,
pitch: 1., pitch: 1.,
volume: 1., volume: 1.,
}) };
*backend_id += 1;
Ok(rv)
} }
} }
impl Backend for Web { impl Backend for Web {
fn id(&self) -> Option<BackendId> {
Some(self.id)
}
fn supported_features(&self) -> Features { fn supported_features(&self) -> Features {
Features { Features {
stop: true, stop: true,
@ -36,6 +47,7 @@ impl Backend for Web {
pitch: true, pitch: true,
volume: true, volume: true,
is_speaking: true, is_speaking: true,
utterance_callbacks: true,
} }
} }
@ -45,15 +57,33 @@ impl Backend for Web {
utterance.set_rate(self.rate); utterance.set_rate(self.rate);
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 utterance_id = UtteranceId::Web(utterance.clone());
let callback = Closure::wrap(Box::new(move |evt: SpeechSynthesisEvent| {
let callbacks = CALLBACKS.lock().unwrap();
let callback = callbacks.get(&id).unwrap();
if let Some(f) = callback.utterance_begin {
let utterance_id = UtteranceId::Web(evt.utterance());
f(utterance_id);
}
}) as Box<dyn Fn(_)>);
utterance.set_onstart(Some(callback.as_ref().unchecked_ref()));
let callback = Closure::wrap(Box::new(move |evt: SpeechSynthesisEvent| {
let callbacks = CALLBACKS.lock().unwrap();
let callback = callbacks.get(&id).unwrap();
if let Some(f) = callback.utterance_end {
let utterance_id = UtteranceId::Web(evt.utterance());
f(utterance_id);
}
}) as Box<dyn Fn(_)>);
utterance.set_onend(Some(callback.as_ref().unchecked_ref()));
if interrupt { if interrupt {
self.stop()?; self.stop()?;
} }
if let Some(window) = web_sys::window() { if let Some(window) = web_sys::window() {
let speech_synthesis = window.speech_synthesis().unwrap(); let speech_synthesis = window.speech_synthesis().unwrap();
speech_synthesis.speak(&utterance); speech_synthesis.speak(&utterance);
let mut utterance_id = NEXT_UTTERANCE_ID.lock().unwrap(); Ok(Some(utterance_id))
*utterance_id += 1;
Ok(Some(UtteranceId::Web(*utterance_id)))
} else { } else {
Err(Error::NoneError) Err(Error::NoneError)
} }

View File

@ -25,6 +25,8 @@ 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)] #[cfg(windows)]
use tts_winrt_bindings::windows::media::playback::MediaPlaybackItem; use tts_winrt_bindings::windows::media::playback::MediaPlaybackItem;
@ -63,7 +65,7 @@ pub enum UtteranceId {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
SpeechDispatcher(u64), SpeechDispatcher(u64),
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
Web(u64), Web(SpeechSynthesisUtterance),
#[cfg(windows)] #[cfg(windows)]
WinRT(MediaPlaybackItem), WinRT(MediaPlaybackItem),
#[cfg(any(target_os = "macos", target_os = "ios"))] #[cfg(any(target_os = "macos", target_os = "ios"))]