diff --git a/Cargo.toml b/Cargo.toml
index 68c4a1d..eccc258 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -41,7 +41,7 @@ objc = { version = "0.2", features = ["exception"] }
[target.wasm32-unknown-unknown.dependencies]
wasm-bindgen = "0.2"
-web-sys = { version = "0.3", features = ["EventTarget", "SpeechSynthesis", "SpeechSynthesisErrorCode", "SpeechSynthesisErrorEvent", "SpeechSynthesisEvent", "SpeechSynthesisUtterance", "Window", ] }
+web-sys = { version = "0.3", features = ["EventTarget", "SpeechSynthesis", "SpeechSynthesisErrorCode", "SpeechSynthesisErrorEvent", "SpeechSynthesisEvent", "SpeechSynthesisUtterance", "SpeechSynthesisVoice", "Window", ] }
[target.'cfg(target_os="android")'.dependencies]
jni = "0.19"
diff --git a/examples/web/Cargo.toml b/examples/web/Cargo.toml
index e93d316..745c74e 100644
--- a/examples/web/Cargo.toml
+++ b/examples/web/Cargo.toml
@@ -7,5 +7,7 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+console_log = "0.2"
+log = "0.4"
seed = "0.8"
tts = { path = "../.." }
\ No newline at end of file
diff --git a/examples/web/index.html b/examples/web/index.html
index 15e486f..1ec233a 100644
--- a/examples/web/index.html
+++ b/examples/web/index.html
@@ -1,5 +1,5 @@
-
+
Example
diff --git a/examples/web/src/main.rs b/examples/web/src/main.rs
index fb03c38..a2c258e 100644
--- a/examples/web/src/main.rs
+++ b/examples/web/src/main.rs
@@ -15,13 +15,20 @@ enum Msg {
RateChanged(String),
PitchChanged(String),
VolumeChanged(String),
+ VoiceChanged(String),
Speak,
}
fn init(_: Url, _: &mut impl Orders) -> Model {
- let tts = Tts::default().unwrap();
+ let mut tts = Tts::default().unwrap();
+ if tts.voices().unwrap().iter().len() > 0 {
+ if tts.voice().unwrap().is_none() {
+ tts.set_voice(tts.voices().unwrap().first().unwrap())
+ .expect("Failed to set voice");
+ }
+ }
Model {
- text: Default::default(),
+ text: "Hello, world. This is a test of the current text-to-speech values.".into(),
tts,
}
}
@@ -42,6 +49,13 @@ fn update(msg: Msg, model: &mut Model, _: &mut impl Orders) {
let volume = volume.parse::().unwrap();
model.tts.set_volume(volume).unwrap();
}
+ VoiceChanged(voice) => {
+ for v in model.tts.voices().unwrap() {
+ if v.id() == voice {
+ model.tts.set_voice(&v).unwrap();
+ }
+ }
+ }
Speak => {
model.tts.speak(&model.text, false).unwrap();
}
@@ -49,6 +63,7 @@ fn update(msg: Msg, model: &mut Model, _: &mut impl Orders) {
}
fn view(model: &Model) -> Node {
+ let should_show_voices = model.tts.voices().unwrap().iter().len() > 0;
form![
div![label![
"Text to speak",
@@ -96,6 +111,36 @@ fn view(model: &Model) -> Node {
input_ev(Ev::Input, Msg::VolumeChanged)
],
],],
+ if should_show_voices {
+ div![
+ label!["Voice"],
+ select![
+ model.tts.voices().unwrap().iter().map(|v| {
+ let selected = if let Some(voice) = model.tts.voice().unwrap() {
+ voice.id() == v.id()
+ } else {
+ false
+ };
+ option![
+ attrs! {
+ At::Value => v.id()
+ },
+ if selected {
+ attrs! {
+ At::Selected => selected
+ }
+ } else {
+ attrs! {}
+ },
+ v.name()
+ ]
+ }),
+ input_ev(Ev::Change, Msg::VoiceChanged)
+ ]
+ ]
+ } else {
+ div!["Your browser does not seem to support selecting voices."]
+ },
button![
"Speak",
ev(Ev::Click, |e| {
@@ -107,5 +152,6 @@ fn view(model: &Model) -> Node {
}
fn main() {
+ console_log::init().expect("Error initializing logger");
App::start("app", init, update, view);
}
diff --git a/src/backends/web.rs b/src/backends/web.rs
index 57a8af3..a39dff2 100644
--- a/src/backends/web.rs
+++ b/src/backends/web.rs
@@ -1,15 +1,18 @@
#[cfg(target_arch = "wasm32")]
-use std::sync::Mutex;
+use std::{str::FromStr, sync::Mutex};
use lazy_static::lazy_static;
use log::{info, trace};
+use unic_langid::LanguageIdentifier;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::{
SpeechSynthesisErrorCode, SpeechSynthesisErrorEvent, SpeechSynthesisEvent,
- SpeechSynthesisUtterance,
+ SpeechSynthesisUtterance, SpeechSynthesisVoice,
};
+use crate::Gender;
+use crate::Voice;
use crate::{Backend, BackendId, Error, Features, UtteranceId, CALLBACKS};
#[derive(Clone, Debug)]
@@ -18,6 +21,7 @@ pub struct Web {
rate: f32,
pitch: f32,
volume: f32,
+ voice: Option,
}
lazy_static! {
@@ -35,6 +39,7 @@ impl Web {
rate: 1.,
pitch: 1.,
volume: 1.,
+ voice: None,
};
*backend_id += 1;
Ok(rv)
@@ -53,7 +58,8 @@ impl Backend for Web {
pitch: true,
volume: true,
is_speaking: true,
- voices: true,
+ voice: true,
+ get_voice: true,
utterance_callbacks: true,
}
}
@@ -64,6 +70,9 @@ impl Backend for Web {
utterance.set_rate(self.rate);
utterance.set_pitch(self.pitch);
utterance.set_volume(self.volume);
+ if self.voice.is_some() {
+ utterance.set_voice(self.voice.as_ref());
+ }
let id = self.id().unwrap();
let mut uid = NEXT_UTTERANCE_ID.lock().unwrap();
let utterance_id = UtteranceId::Web(*uid);
@@ -198,16 +207,53 @@ impl Backend for Web {
}
}
- fn voice(&self) -> Result {
- unimplemented!()
+ fn voice(&self) -> Result