From e56a0da2e52ff5d4dc1c252c48e694648c018763 Mon Sep 17 00:00:00 2001 From: Nolan Darilek Date: Wed, 30 Mar 2022 12:07:59 -0500 Subject: [PATCH] WIP: Reorganize, and try to get working with Speech Dispatcher. --- Cargo.toml | 10 -------- src/backends/speech_dispatcher.rs | 41 ++++++++++++++++++++++++++----- src/lib.rs | 36 +++++++++++++-------------- 3 files changed, 52 insertions(+), 35 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3245b48..326476a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,21 +8,11 @@ license = "MIT" exclude = ["*.cfg", "*.yml"] edition = "2021" -[package.metadata.patch.speech-dispatcher] -version = "0.7.0" -#patches = [ -# "speech-dispatcher.patch" -#] - -#[patch.crates-io] -#speech-dispatcher = { path = './target/patch/speech-dispatcher-0.7.0'} - [lib] crate-type = ["lib", "cdylib", "staticlib"] [features] speech_dispatcher_0_10 = ["speech-dispatcher/0_10"] -default = ["speech_dispatcher_0_10"] [dependencies] dyn-clonable = "0.9" diff --git a/src/backends/speech_dispatcher.rs b/src/backends/speech_dispatcher.rs index 4dc1565..2ccea1d 100644 --- a/src/backends/speech_dispatcher.rs +++ b/src/backends/speech_dispatcher.rs @@ -1,11 +1,15 @@ +use std::str::FromStr; #[cfg(target_os = "linux")] use std::{collections::HashMap, sync::Mutex}; use lazy_static::*; use log::{info, trace}; -use speech_dispatcher::*; +use speech_dispatcher::{Voice as SpdVoice, *}; +use unic_langid::{LanguageIdentifier, LanguageIdentifierError}; -use crate::{Backend, BackendId, Error, Features, UtteranceId, CALLBACKS}; +use crate::{ + Backend, BackendId, Error, Features, Gender, UtteranceId, Voice, VoiceImpl, CALLBACKS, +}; #[derive(Clone, Debug)] pub(crate) struct SpeechDispatcher(Connection); @@ -17,6 +21,24 @@ lazy_static! { }; } +impl VoiceImpl for SpdVoice { + fn id(self) -> String { + self.name + } + + fn name(self) -> String { + self.name + } + + fn gender(self) -> Gender { + Gender::Other + } + + fn language(self) -> Result { + LanguageIdentifier::from_str(&self.language) + } +} + impl SpeechDispatcher { pub(crate) fn new() -> std::result::Result { info!("Initializing SpeechDispatcher backend"); @@ -69,7 +91,7 @@ impl SpeechDispatcher { } } -impl Backend for SpeechDispatcher { +impl Backend for SpeechDispatcher { fn id(&self) -> Option { Some(BackendId::SpeechDispatcher(self.0.client_id())) } @@ -181,11 +203,18 @@ impl Backend for SpeechDispatcher { Ok(*is_speaking) } - fn voice(&self) -> Result { - unimplemented!() + fn voices(&self) -> Result>, Error> { + let rv = self + .0 + .list_synthesis_voices()? + .iter() + .cloned() + .map(|v| Voice(Box::new(v))) + .collect::>>(); + Ok(rv) } - fn list_voices(&self) -> Vec { + fn voice(&self) -> Result { unimplemented!() } diff --git a/src/lib.rs b/src/lib.rs index 8328edb..3945c0a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,13 +16,13 @@ use std::collections::HashMap; #[cfg(target_os = "macos")] use std::ffi::CStr; use std::fmt; +use std::marker::PhantomData; use std::sync::{Arc, Mutex}; use std::{boxed::Box, sync::RwLock}; #[cfg(any(target_os = "macos", target_os = "ios"))] use cocoa_foundation::base::id; use dyn_clonable::*; -pub use unic_langid::LanguageIdentifier; use lazy_static::lazy_static; #[cfg(target_os = "macos")] use libc::c_char; @@ -33,6 +33,8 @@ use speech_dispatcher::Error as SpeechDispatcherError; use thiserror::Error; #[cfg(all(windows, feature = "tolk"))] use tolk::Tolk; +pub use unic_langid::LanguageIdentifier; +use unic_langid::LanguageIdentifierError; mod backends; @@ -209,7 +211,7 @@ pub enum Error { } #[clonable] -pub trait Backend: Clone { +pub trait Backend: Clone { fn id(&self) -> Option; fn supported_features(&self) -> Features; fn speak(&mut self, text: &str, interrupt: bool) -> Result, Error>; @@ -230,8 +232,8 @@ pub trait Backend: Clone { fn get_volume(&self) -> Result; fn set_volume(&mut self, volume: f32) -> Result<(), Error>; fn is_speaking(&self) -> Result; + fn voices(&self) -> Result>, Error>; fn voice(&self) -> Result; - fn list_voices(&self) -> Vec; fn set_voice(&mut self, voice: &str) -> Result<(), Error>; } @@ -254,22 +256,22 @@ lazy_static! { } #[derive(Clone)] -pub struct Tts(Arc>>); +pub struct Tts(Arc>>>, PhantomData); -unsafe impl Send for Tts {} +unsafe impl Send for Tts {} -unsafe impl Sync for Tts {} +unsafe impl Sync for Tts {} -impl Tts { +impl Tts { /** * Create a new `TTS` instance with the specified backend. */ - pub fn new(backend: Backends) -> Result { + pub fn new(backend: Backends) -> Result, Error> { let backend = match backend { #[cfg(target_os = "linux")] Backends::SpeechDispatcher => { let tts = backends::SpeechDispatcher::new()?; - Ok(Tts(Arc::new(RwLock::new(Box::new(tts))))) + Ok(Tts(Arc::new(RwLock::new(Box::new(tts))), PhantomData)) } #[cfg(target_arch = "wasm32")] Backends::Web => { @@ -315,7 +317,7 @@ impl Tts { } } - pub fn default() -> Result { + pub fn default() -> Result, Error> { #[cfg(target_os = "linux")] let tts = Tts::new(Backends::SpeechDispatcher); #[cfg(all(windows, feature = "tolk"))] @@ -564,8 +566,8 @@ impl Tts { /** * Returns list of available voices. */ - pub fn list_voices(&self) -> Vec { - self.0.read().unwrap().list_voices() + pub fn voices(&self) -> Result>, Error> { + self.0.read().unwrap().voices() } /** @@ -680,7 +682,7 @@ impl Tts { } } -impl Drop for Tts { +impl Drop for Tts { fn drop(&mut self) { if Arc::strong_count(&self.0) <= 1 { if let Some(id) = self.0.read().unwrap().id() { @@ -698,14 +700,10 @@ pub enum Gender { } pub trait VoiceImpl: Sized { - type Backend: crate::Backend; - fn from_id(id: String) -> Self; - fn from_language(lang: LanguageIdentifier) -> Self; - fn list() -> Vec; + fn id(self) -> String; fn name(self) -> String; fn gender(self) -> Gender; - fn id(self) -> String; - fn language(self) -> LanguageIdentifier; + fn language(self) -> Result; } pub struct Voice(Box);