mirror of https://github.com/ndarilek/tts-rs.git
Compare commits
10 Commits
6443be6512
...
4ae7b18677
Author | SHA1 | Date |
---|---|---|
Bear_03 | 4ae7b18677 | |
Nolan Darilek | 5c528f1d8e | |
Nolan Darilek | 9fb8107acf | |
Nolan Darilek | 8dabcc99c4 | |
Bear-03 | 4f4ab53252 | |
Bear-03 | 91a0f03f1a | |
Bear-03 | 87cf05f78e | |
Bear_03 | 9cd66c0358 | |
Bear-03 | b85ffc80d4 | |
Bear-03 | bf522b42a7 |
24
Cargo.toml
24
Cargo.toml
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "tts"
|
||||
version = "0.25.5"
|
||||
version = "0.25.6"
|
||||
authors = ["Nolan Darilek <nolan@thewordnerd.info>"]
|
||||
repository = "https://github.com/ndarilek/tts-rs"
|
||||
description = "High-level Text-To-Speech (TTS) interface"
|
||||
|
@ -30,7 +30,14 @@ env_logger = "0.10"
|
|||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
tolk = { version = "0.5", optional = true }
|
||||
windows = { version = "0.48", features = ["Foundation", "Foundation_Collections", "Media_Core", "Media_Playback", "Media_SpeechSynthesis", "Storage_Streams"] }
|
||||
windows = { version = "0.51", features = [
|
||||
"Foundation",
|
||||
"Foundation_Collections",
|
||||
"Media_Core",
|
||||
"Media_Playback",
|
||||
"Media_SpeechSynthesis",
|
||||
"Storage_Streams",
|
||||
] }
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
speech-dispatcher = { version = "0.16", default-features = false }
|
||||
|
@ -43,7 +50,16 @@ 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", "SpeechSynthesisVoice", "Window", ] }
|
||||
web-sys = { version = "0.3", features = [
|
||||
"EventTarget",
|
||||
"SpeechSynthesis",
|
||||
"SpeechSynthesisErrorCode",
|
||||
"SpeechSynthesisErrorEvent",
|
||||
"SpeechSynthesisEvent",
|
||||
"SpeechSynthesisUtterance",
|
||||
"SpeechSynthesisVoice",
|
||||
"Window",
|
||||
] }
|
||||
|
||||
[target.'cfg(target_os="android")'.dependencies]
|
||||
jni = "0.21"
|
||||
|
@ -52,4 +68,4 @@ ndk-glue = "0.7"
|
|||
|
||||
[package.metadata.docs.rs]
|
||||
no-default-features = true
|
||||
features = ["speech_dispatcher_0_11"]
|
||||
features = ["speech_dispatcher_0_11"]
|
||||
|
|
|
@ -250,6 +250,7 @@ impl Backend for Android {
|
|||
utterance_callbacks: true,
|
||||
voice: false,
|
||||
get_voice: false,
|
||||
synthesize: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -284,6 +285,10 @@ impl Backend for Android {
|
|||
}
|
||||
}
|
||||
|
||||
fn synthesize(&mut self, text: &str) -> Result<Vec<u8>, Error> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn stop(&mut self) -> Result<(), Error> {
|
||||
let vm = Self::vm()?;
|
||||
let mut env = vm.get_env()?;
|
||||
|
|
|
@ -125,6 +125,10 @@ impl Backend for AppKit {
|
|||
Ok(None)
|
||||
}
|
||||
|
||||
fn synthesize(&mut self, text: &str) -> Result<Vec<u8>, Error> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn stop(&mut self) -> Result<(), Error> {
|
||||
trace!("stop()");
|
||||
unsafe {
|
||||
|
|
|
@ -170,6 +170,7 @@ impl Backend for AvFoundation {
|
|||
voice: true,
|
||||
get_voice: false,
|
||||
utterance_callbacks: true,
|
||||
synthesize: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,6 +207,10 @@ impl Backend for AvFoundation {
|
|||
Ok(Some(UtteranceId::AvFoundation(utterance)))
|
||||
}
|
||||
|
||||
fn synthesize(&mut self, text: &str) -> Result<Vec<u8>, Error> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn stop(&mut self) -> Result<(), Error> {
|
||||
trace!("stop()");
|
||||
unsafe {
|
||||
|
|
|
@ -85,6 +85,7 @@ impl Backend for SpeechDispatcher {
|
|||
voice: true,
|
||||
get_voice: false,
|
||||
utterance_callbacks: true,
|
||||
synthesize: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,6 +109,10 @@ impl Backend for SpeechDispatcher {
|
|||
}
|
||||
}
|
||||
|
||||
fn synthesize(&mut self, text: &str) -> Result<Vec<u8>, Error> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn stop(&mut self) -> Result<(), Error> {
|
||||
trace!("stop()");
|
||||
self.0.cancel()?;
|
||||
|
|
|
@ -39,6 +39,10 @@ impl Backend for Tolk {
|
|||
Ok(None)
|
||||
}
|
||||
|
||||
fn synthesize(&mut self, text: &str) -> Result<Vec<u8>, Error> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn stop(&mut self) -> Result<(), Error> {
|
||||
trace!("stop()");
|
||||
self.0.silence();
|
||||
|
|
|
@ -59,6 +59,7 @@ impl Backend for Web {
|
|||
voice: true,
|
||||
get_voice: true,
|
||||
utterance_callbacks: true,
|
||||
synthesize: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,6 +122,10 @@ impl Backend for Web {
|
|||
}
|
||||
}
|
||||
|
||||
fn synthesize(&mut self, text: &str) -> Result<Vec<u8>, Error> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn stop(&mut self) -> Result<(), Error> {
|
||||
trace!("stop()");
|
||||
if let Some(window) = web_sys::window() {
|
||||
|
|
|
@ -12,8 +12,11 @@ use windows::{
|
|||
Media::{
|
||||
Core::MediaSource,
|
||||
Playback::{MediaPlayer, MediaPlayerAudioCategory},
|
||||
SpeechSynthesis::{SpeechSynthesizer, VoiceGender, VoiceInformation},
|
||||
SpeechSynthesis::{
|
||||
SpeechSynthesisStream, SpeechSynthesizer, VoiceGender, VoiceInformation,
|
||||
},
|
||||
},
|
||||
Storage::Streams::DataReader,
|
||||
};
|
||||
|
||||
use crate::{Backend, BackendId, Error, Features, Gender, UtteranceId, Voice, CALLBACKS};
|
||||
|
@ -138,6 +141,20 @@ impl WinRt {
|
|||
voice: SpeechSynthesizer::DefaultVoice()?,
|
||||
})
|
||||
}
|
||||
|
||||
fn create_synthesis_stream(&mut self, text: &str) -> Result<SpeechSynthesisStream, Error> {
|
||||
self.synth.Options()?.SetSpeakingRate(self.rate.into())?;
|
||||
self.synth.Options()?.SetAudioPitch(self.pitch.into())?;
|
||||
self.synth.Options()?.SetAudioVolume(self.volume.into())?;
|
||||
|
||||
self.synth.SetVoice(&self.voice)?;
|
||||
let stream = self
|
||||
.synth
|
||||
.SynthesizeTextToStreamAsync(&text.into())?
|
||||
.get()?;
|
||||
|
||||
Ok(stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl Backend for WinRt {
|
||||
|
@ -155,6 +172,7 @@ impl Backend for WinRt {
|
|||
voice: true,
|
||||
get_voice: true,
|
||||
utterance_callbacks: true,
|
||||
synthesize: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -189,18 +207,12 @@ impl Backend for WinRt {
|
|||
}
|
||||
}
|
||||
if no_utterances {
|
||||
self.synth.Options()?.SetSpeakingRate(self.rate.into())?;
|
||||
self.synth.Options()?.SetAudioPitch(self.pitch.into())?;
|
||||
self.synth.Options()?.SetAudioVolume(self.volume.into())?;
|
||||
self.synth.SetVoice(&self.voice)?;
|
||||
let stream = self
|
||||
.synth
|
||||
.SynthesizeTextToStreamAsync(&text.into())?
|
||||
.get()?;
|
||||
let content_type = stream.ContentType()?;
|
||||
let source = MediaSource::CreateFromStream(&stream, &content_type)?;
|
||||
self.player.SetSource(&source)?;
|
||||
let stream = self.create_synthesis_stream(text)?;
|
||||
|
||||
let media_source = MediaSource::CreateFromStream(&stream, &stream.ContentType()?)?;
|
||||
self.player.SetSource(&media_source)?;
|
||||
self.player.Play()?;
|
||||
|
||||
let mut callbacks = CALLBACKS.lock().unwrap();
|
||||
let callbacks = callbacks.get_mut(&self.id).unwrap();
|
||||
if let Some(callback) = callbacks.utterance_begin.as_mut() {
|
||||
|
@ -210,6 +222,18 @@ impl Backend for WinRt {
|
|||
Ok(Some(utterance_id))
|
||||
}
|
||||
|
||||
fn synthesize(&mut self, text: &str) -> Result<Vec<u8>, Error> {
|
||||
let stream = self.create_synthesis_stream(text)?;
|
||||
|
||||
let size = stream.Size()?;
|
||||
let data_reader = DataReader::CreateDataReader(&stream.GetInputStreamAt(0)?)?;
|
||||
let mut bytes = vec![0; size as usize];
|
||||
data_reader.LoadAsync(size as u32)?;
|
||||
data_reader.ReadBytes(&mut bytes)?;
|
||||
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
fn stop(&mut self) -> std::result::Result<(), Error> {
|
||||
trace!("stop()");
|
||||
if !self.is_speaking()? {
|
||||
|
|
17
src/lib.rs
17
src/lib.rs
|
@ -162,6 +162,7 @@ unsafe impl Sync for UtteranceId {}
|
|||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct Features {
|
||||
pub is_speaking: bool,
|
||||
pub synthesize: bool,
|
||||
pub pitch: bool,
|
||||
pub rate: bool,
|
||||
pub stop: bool,
|
||||
|
@ -217,6 +218,7 @@ pub trait Backend: Clone {
|
|||
fn id(&self) -> Option<BackendId>;
|
||||
fn supported_features(&self) -> Features;
|
||||
fn speak(&mut self, text: &str, interrupt: bool) -> Result<Option<UtteranceId>, Error>;
|
||||
fn synthesize(&mut self, text: &str) -> Result<Vec<u8>, Error>;
|
||||
fn stop(&mut self) -> Result<(), Error>;
|
||||
fn min_rate(&self) -> f32;
|
||||
fn max_rate(&self) -> f32;
|
||||
|
@ -374,6 +376,15 @@ impl Tts {
|
|||
.speak(text.into().as_str(), interrupt)
|
||||
}
|
||||
|
||||
pub fn synthesize<S: Into<String>>(&mut self, text: S) -> Result<Vec<u8>, Error> {
|
||||
let Features { synthesize, .. } = self.supported_features();
|
||||
if synthesize {
|
||||
self.0.write().unwrap().synthesize(text.into().as_str())
|
||||
} else {
|
||||
Err(Error::UnsupportedFeature)
|
||||
}
|
||||
}
|
||||
|
||||
/// Stops current speech.
|
||||
pub fn stop(&mut self) -> Result<&Self, Error> {
|
||||
let Features { stop, .. } = self.supported_features();
|
||||
|
@ -571,7 +582,7 @@ impl Tts {
|
|||
if utterance_callbacks {
|
||||
let mut callbacks = CALLBACKS.lock().unwrap();
|
||||
let id = self.0.read().unwrap().id().unwrap();
|
||||
let mut callbacks = callbacks.get_mut(&id).unwrap();
|
||||
let callbacks = callbacks.get_mut(&id).unwrap();
|
||||
callbacks.utterance_begin = callback;
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -591,7 +602,7 @@ impl Tts {
|
|||
if utterance_callbacks {
|
||||
let mut callbacks = CALLBACKS.lock().unwrap();
|
||||
let id = self.0.read().unwrap().id().unwrap();
|
||||
let mut callbacks = callbacks.get_mut(&id).unwrap();
|
||||
let callbacks = callbacks.get_mut(&id).unwrap();
|
||||
callbacks.utterance_end = callback;
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -611,7 +622,7 @@ impl Tts {
|
|||
if utterance_callbacks {
|
||||
let mut callbacks = CALLBACKS.lock().unwrap();
|
||||
let id = self.0.read().unwrap().id().unwrap();
|
||||
let mut callbacks = callbacks.get_mut(&id).unwrap();
|
||||
let callbacks = callbacks.get_mut(&id).unwrap();
|
||||
callbacks.utterance_stop = callback;
|
||||
Ok(())
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue