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"
[lib]
crate-type = ["lib", "staticlib"]
crate-type = ["lib", "cdylib", "staticlib"]
[dependencies]
lazy_static = "1"
@ -34,4 +34,4 @@ objc = "0.2"
[target.wasm32-unknown-unknown.dependencies]
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 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 {
id: BackendId,
rate: f32,
pitch: f32,
volume: f32,
}
lazy_static! {
static ref NEXT_UTTERANCE_ID: Mutex<u64> = Mutex::new(0);
static ref NEXT_BACKEND_ID: Mutex<u64> = Mutex::new(0);
}
impl Web {
pub fn new() -> Result<Self, Error> {
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.,
pitch: 1.,
volume: 1.,
})
};
*backend_id += 1;
Ok(rv)
}
}
impl Backend for Web {
fn id(&self) -> Option<BackendId> {
Some(self.id)
}
fn supported_features(&self) -> Features {
Features {
stop: true,
@ -36,6 +47,7 @@ impl Backend for Web {
pitch: true,
volume: true,
is_speaking: true,
utterance_callbacks: true,
}
}
@ -45,15 +57,33 @@ impl Backend for Web {
utterance.set_rate(self.rate);
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 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 {
self.stop()?;
}
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)))
Ok(Some(utterance_id))
} else {
Err(Error::NoneError)
}

View File

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