From e19eb56169e7491deb0bcf35d2eaabc15ae3dbc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Caddet?= Date: Sun, 27 Sep 2020 20:04:12 +0200 Subject: [PATCH] first implementation of a voice trait for macOS WARN: not tested --- Cargo.toml | 1 + examples/hello_world.rs | 4 ++-- src/backends/av_foundation/voices.rs | 30 ++++++++++++++++++++++------ src/lib.rs | 1 + 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8d3757f..cc236ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ crate-type = ["lib", "cdylib", "staticlib"] lazy_static = "1" log = "0.4" thiserror = "1" +unic-langid = "0.9.0" [dev-dependencies] env_logger = "0.7" diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 255cae2..264d9b8 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -59,7 +59,7 @@ fn main() -> Result<(), Error> { tts.speak("This is normal volume.", false)?; tts.set_volume(original_volume)?; } -/* let Features { voices, .. } = tts.supported_features(); + let Features { voices, .. } = tts.supported_features(); if voices { let original_voice = tts.voice()?; let voices_list = tts.list_voices(); @@ -72,7 +72,7 @@ fn main() -> Result<(), Error> { tts.speak(v,false)?; } tts.set_voice(original_voice)?; - }*/ + } tts.speak("Goodbye.", false)?; let mut _input = String::new(); // The below is only needed to make the example run on MacOS because there is no NSRunLoop in this context. diff --git a/src/backends/av_foundation/voices.rs b/src/backends/av_foundation/voices.rs index 14e7974..b8ee960 100644 --- a/src/backends/av_foundation/voices.rs +++ b/src/backends/av_foundation/voices.rs @@ -6,6 +6,10 @@ use cocoa_foundation::foundation::NSString; use cocoa_foundation::base::{nil,id}; use core_foundation::string::CFString; +use crate::backends::AvFoundation; +use crate::voices; +use crate::voices::Gender; + #[derive(Copy,Clone)] pub struct AVSpeechSynthesisVoice(*const Object); @@ -18,25 +22,39 @@ impl AVSpeechSynthesisVoice { }; AVSpeechSynthesisVoice{0:voice} } +} - pub fn default() -> Self { - AVSpeechSynthesisVoice::list()[0] - } +impl voices::Backend for AVSpeechSynthesisVoice { + type Backend = AvFoundation; - pub fn list() -> Vec { + fn list() -> Vec { let voices: CFArray = unsafe{msg_send![class!(AVSpeechSynthesisVoice), speechVoices]}; voices.iter().map(|v| { AVSpeechSynthesisVoice{0: *v as *const Object} }).collect() } - pub fn name(self) -> String { + fn name(self) -> String { let name: CFString = unsafe{msg_send![self.0, name]}; name.to_string() } - pub fn identifier(self) -> String { + fn gender(self) -> Gender { + let gender: i64 = unsafe{ msg_send![self.0, gender] }; + match gender { + 1 => Gender::Male, + 2 => Gender::Female, + _ => Gender::Other, + } + } + + fn id(self) -> String { let identifier: CFString = unsafe{msg_send![self.0, identifier]}; identifier.to_string() } + + fn language(self) -> voices::LanguageIdentifier { + let lang: CFString = unsafe{msg_send![self.0, language]}; + lang.to_string().parse().unwrap() + } } diff --git a/src/lib.rs b/src/lib.rs index bc29f4b..91f13c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,7 @@ use web_sys::SpeechSynthesisUtterance; use tts_winrt_bindings::windows::media::playback::MediaPlaybackItem; mod backends; +mod voices; pub enum Backends { #[cfg(target_os = "linux")]