mirror of https://github.com/ndarilek/tts-rs.git
Merge 4f4ab53252
into b4f48fa439
This commit is contained in:
commit
a34c43eef9
|
@ -250,6 +250,7 @@ impl Backend for Android {
|
||||||
utterance_callbacks: true,
|
utterance_callbacks: true,
|
||||||
voice: false,
|
voice: false,
|
||||||
get_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> {
|
fn stop(&mut self) -> Result<(), Error> {
|
||||||
let vm = Self::vm()?;
|
let vm = Self::vm()?;
|
||||||
let env = vm.get_env()?;
|
let env = vm.get_env()?;
|
||||||
|
|
|
@ -123,6 +123,10 @@ impl Backend for AppKit {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn synthesize(&mut self, text: &str) -> Result<Vec<u8>, Error> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
fn stop(&mut self) -> Result<(), Error> {
|
fn stop(&mut self) -> Result<(), Error> {
|
||||||
trace!("stop()");
|
trace!("stop()");
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
|
@ -169,6 +169,7 @@ impl Backend for AvFoundation {
|
||||||
voice: true,
|
voice: true,
|
||||||
get_voice: false,
|
get_voice: false,
|
||||||
utterance_callbacks: true,
|
utterance_callbacks: true,
|
||||||
|
synthesize: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,6 +206,10 @@ impl Backend for AvFoundation {
|
||||||
Ok(Some(UtteranceId::AvFoundation(utterance)))
|
Ok(Some(UtteranceId::AvFoundation(utterance)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn synthesize(&mut self, text: &str) -> Result<Vec<u8>, Error> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
fn stop(&mut self) -> Result<(), Error> {
|
fn stop(&mut self) -> Result<(), Error> {
|
||||||
trace!("stop()");
|
trace!("stop()");
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
|
@ -85,6 +85,7 @@ impl Backend for SpeechDispatcher {
|
||||||
voice: true,
|
voice: true,
|
||||||
get_voice: false,
|
get_voice: false,
|
||||||
utterance_callbacks: true,
|
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> {
|
fn stop(&mut self) -> Result<(), Error> {
|
||||||
trace!("stop()");
|
trace!("stop()");
|
||||||
self.0.cancel()?;
|
self.0.cancel()?;
|
||||||
|
|
|
@ -39,6 +39,10 @@ impl Backend for Tolk {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn synthesize(&mut self, text: &str) -> Result<Vec<u8>, Error> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
fn stop(&mut self) -> Result<(), Error> {
|
fn stop(&mut self) -> Result<(), Error> {
|
||||||
trace!("stop()");
|
trace!("stop()");
|
||||||
self.0.silence();
|
self.0.silence();
|
||||||
|
|
|
@ -59,6 +59,7 @@ impl Backend for Web {
|
||||||
voice: true,
|
voice: true,
|
||||||
get_voice: true,
|
get_voice: true,
|
||||||
utterance_callbacks: 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> {
|
fn stop(&mut self) -> Result<(), Error> {
|
||||||
trace!("stop()");
|
trace!("stop()");
|
||||||
if let Some(window) = web_sys::window() {
|
if let Some(window) = web_sys::window() {
|
||||||
|
|
|
@ -13,8 +13,11 @@ use windows::{
|
||||||
Media::{
|
Media::{
|
||||||
Core::MediaSource,
|
Core::MediaSource,
|
||||||
Playback::{MediaPlayer, MediaPlayerAudioCategory},
|
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};
|
use crate::{Backend, BackendId, Error, Features, Gender, UtteranceId, Voice, CALLBACKS};
|
||||||
|
@ -139,6 +142,20 @@ impl WinRt {
|
||||||
voice: SpeechSynthesizer::DefaultVoice()?,
|
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 {
|
impl Backend for WinRt {
|
||||||
|
@ -156,6 +173,7 @@ impl Backend for WinRt {
|
||||||
voice: true,
|
voice: true,
|
||||||
get_voice: true,
|
get_voice: true,
|
||||||
utterance_callbacks: true,
|
utterance_callbacks: true,
|
||||||
|
synthesize: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,18 +208,12 @@ impl Backend for WinRt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if no_utterances {
|
if no_utterances {
|
||||||
self.synth.Options()?.SetSpeakingRate(self.rate.into())?;
|
let stream = self.create_synthesis_stream(text)?;
|
||||||
self.synth.Options()?.SetAudioPitch(self.pitch.into())?;
|
|
||||||
self.synth.Options()?.SetAudioVolume(self.volume.into())?;
|
let media_source = MediaSource::CreateFromStream(&stream, &stream.ContentType()?)?;
|
||||||
self.synth.SetVoice(&self.voice)?;
|
self.player.SetSource(&media_source)?;
|
||||||
let stream = self
|
|
||||||
.synth
|
|
||||||
.SynthesizeTextToStreamAsync(&text.into())?
|
|
||||||
.get()?;
|
|
||||||
let content_type = stream.ContentType()?;
|
|
||||||
let source = MediaSource::CreateFromStream(&stream, &content_type)?;
|
|
||||||
self.player.SetSource(&source)?;
|
|
||||||
self.player.Play()?;
|
self.player.Play()?;
|
||||||
|
|
||||||
let mut callbacks = CALLBACKS.lock().unwrap();
|
let mut callbacks = CALLBACKS.lock().unwrap();
|
||||||
let callbacks = callbacks.get_mut(&self.id).unwrap();
|
let callbacks = callbacks.get_mut(&self.id).unwrap();
|
||||||
if let Some(callback) = callbacks.utterance_begin.as_mut() {
|
if let Some(callback) = callbacks.utterance_begin.as_mut() {
|
||||||
|
@ -211,6 +223,18 @@ impl Backend for WinRt {
|
||||||
Ok(Some(utterance_id))
|
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> {
|
fn stop(&mut self) -> std::result::Result<(), Error> {
|
||||||
trace!("stop()");
|
trace!("stop()");
|
||||||
if !self.is_speaking()? {
|
if !self.is_speaking()? {
|
||||||
|
|
11
src/lib.rs
11
src/lib.rs
|
@ -162,6 +162,7 @@ unsafe impl Sync for UtteranceId {}
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub struct Features {
|
pub struct Features {
|
||||||
pub is_speaking: bool,
|
pub is_speaking: bool,
|
||||||
|
pub synthesize: bool,
|
||||||
pub pitch: bool,
|
pub pitch: bool,
|
||||||
pub rate: bool,
|
pub rate: bool,
|
||||||
pub stop: bool,
|
pub stop: bool,
|
||||||
|
@ -217,6 +218,7 @@ pub trait Backend: Clone {
|
||||||
fn id(&self) -> Option<BackendId>;
|
fn id(&self) -> Option<BackendId>;
|
||||||
fn supported_features(&self) -> Features;
|
fn supported_features(&self) -> Features;
|
||||||
fn speak(&mut self, text: &str, interrupt: bool) -> Result<Option<UtteranceId>, Error>;
|
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 stop(&mut self) -> Result<(), Error>;
|
||||||
fn min_rate(&self) -> f32;
|
fn min_rate(&self) -> f32;
|
||||||
fn max_rate(&self) -> f32;
|
fn max_rate(&self) -> f32;
|
||||||
|
@ -373,6 +375,15 @@ impl Tts {
|
||||||
.speak(text.into().as_str(), interrupt)
|
.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.
|
/// Stops current speech.
|
||||||
pub fn stop(&mut self) -> Result<&Self, Error> {
|
pub fn stop(&mut self) -> Result<&Self, Error> {
|
||||||
let Features { stop, .. } = self.supported_features();
|
let Features { stop, .. } = self.supported_features();
|
||||||
|
|
Loading…
Reference in New Issue