mirror of https://github.com/ndarilek/tts-rs.git
Add voices feature
Implemented for AVFoundation backend but set_voice has no effect for now Warning: does not build on Linux or windows for now
This commit is contained in:
parent
6c091f3284
commit
5b0d1b6621
|
@ -29,6 +29,7 @@ speech-dispatcher = "0.6"
|
||||||
|
|
||||||
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
|
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
|
||||||
cocoa-foundation = "0.1"
|
cocoa-foundation = "0.1"
|
||||||
|
core-foundation = "0.9"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
objc = "0.2"
|
objc = "0.2"
|
||||||
|
|
||||||
|
|
|
@ -195,6 +195,18 @@ impl Backend for AppKit {
|
||||||
let is_speaking: i8 = unsafe { msg_send![self.0, isSpeaking] };
|
let is_speaking: i8 = unsafe { msg_send![self.0, isSpeaking] };
|
||||||
Ok(is_speaking == YES)
|
Ok(is_speaking == YES)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn voice(&self) -> Result<String,Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list_voices(&self) -> Vec<String> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_voice(&self, voice: String) -> Result<(),Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for AppKit {
|
impl Drop for AppKit {
|
||||||
|
|
|
@ -8,11 +8,15 @@ use objc::*;
|
||||||
|
|
||||||
use crate::{Backend, Error, Features};
|
use crate::{Backend, Error, Features};
|
||||||
|
|
||||||
|
mod voices;
|
||||||
|
use voices::AVSpeechSynthesisVoice;
|
||||||
|
|
||||||
pub struct AvFoundation {
|
pub struct AvFoundation {
|
||||||
synth: *mut Object,
|
synth: *mut Object,
|
||||||
rate: f32,
|
rate: f32,
|
||||||
volume: f32,
|
volume: f32,
|
||||||
pitch: f32,
|
pitch: f32,
|
||||||
|
voice: AVSpeechSynthesisVoice,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AvFoundation {
|
impl AvFoundation {
|
||||||
|
@ -25,6 +29,7 @@ impl AvFoundation {
|
||||||
rate: 0.5,
|
rate: 0.5,
|
||||||
volume: 1.,
|
volume: 1.,
|
||||||
pitch: 1.,
|
pitch: 1.,
|
||||||
|
voice: AVSpeechSynthesisVoice::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +43,7 @@ impl Backend for AvFoundation {
|
||||||
pitch: true,
|
pitch: true,
|
||||||
volume: true,
|
volume: true,
|
||||||
is_speaking: true,
|
is_speaking: true,
|
||||||
|
voices: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,6 +140,18 @@ impl Backend for AvFoundation {
|
||||||
let is_speaking: i8 = unsafe { msg_send![self.synth, isSpeaking] };
|
let is_speaking: i8 = unsafe { msg_send![self.synth, isSpeaking] };
|
||||||
Ok(is_speaking == 1)
|
Ok(is_speaking == 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn voice(&self) -> Result<String,Error> {
|
||||||
|
Ok(self.voice.identifier())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list_voices(&self) -> Vec<String> {
|
||||||
|
AVSpeechSynthesisVoice::list().iter().map(|v| {v.identifier()}).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_voice(&self, voice: String) -> Result<(),Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for AvFoundation {
|
impl Drop for AvFoundation {
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
|
||||||
|
use objc::runtime::*;
|
||||||
|
use objc::*;
|
||||||
|
use core_foundation::array::CFArray;
|
||||||
|
use core_foundation::string::CFString;
|
||||||
|
|
||||||
|
#[derive(Copy,Clone)]
|
||||||
|
pub struct AVSpeechSynthesisVoice(*const Object);
|
||||||
|
|
||||||
|
impl AVSpeechSynthesisVoice {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::list()[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list() -> Vec<Self> {
|
||||||
|
let voices: CFArray = unsafe{msg_send![class!(AVSpeechSynthesisVoice), speechVoices]};
|
||||||
|
voices.iter().map(|v| {
|
||||||
|
AVSpeechSynthesisVoice{0: *v as *mut Object}
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(self) -> String {
|
||||||
|
let name: CFString = unsafe{msg_send![self.0, name]};
|
||||||
|
name.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn identifier(self) -> String {
|
||||||
|
let identifier: CFString = unsafe{msg_send![self.0, identifier]};
|
||||||
|
identifier.to_string()
|
||||||
|
}
|
||||||
|
}
|
39
src/lib.rs
39
src/lib.rs
|
@ -46,6 +46,7 @@ pub struct Features {
|
||||||
pub pitch: bool,
|
pub pitch: bool,
|
||||||
pub volume: bool,
|
pub volume: bool,
|
||||||
pub is_speaking: bool,
|
pub is_speaking: bool,
|
||||||
|
pub voices: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Features {
|
impl Default for Features {
|
||||||
|
@ -56,6 +57,7 @@ impl Default for Features {
|
||||||
pitch: false,
|
pitch: false,
|
||||||
volume: false,
|
volume: false,
|
||||||
is_speaking: false,
|
is_speaking: false,
|
||||||
|
voices: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,6 +100,9 @@ pub trait Backend {
|
||||||
fn get_volume(&self) -> Result<f32, Error>;
|
fn get_volume(&self) -> Result<f32, Error>;
|
||||||
fn set_volume(&mut self, volume: f32) -> Result<(), Error>;
|
fn set_volume(&mut self, volume: f32) -> Result<(), Error>;
|
||||||
fn is_speaking(&self) -> Result<bool, Error>;
|
fn is_speaking(&self) -> Result<bool, Error>;
|
||||||
|
fn voice(&self) -> Result<String, Error>;
|
||||||
|
fn list_voices(&self) -> Vec<String>;
|
||||||
|
fn set_voice(&self, voice: String) -> Result<(),Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TTS(Box<dyn Backend>);
|
pub struct TTS(Box<dyn Backend>);
|
||||||
|
@ -371,4 +376,38 @@ impl TTS {
|
||||||
Err(Error::UnsupportedFeature)
|
Err(Error::UnsupportedFeature)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns list of available voices.
|
||||||
|
*/
|
||||||
|
pub fn list_voices(&self) -> Vec<String> {
|
||||||
|
self.0.list_voices()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current speaking voice.
|
||||||
|
*/
|
||||||
|
pub fn voice(&self) -> Result<String,Error> {
|
||||||
|
let Features { voices, .. } = self.supported_features();
|
||||||
|
if voices {
|
||||||
|
self.0.voice()
|
||||||
|
} else {
|
||||||
|
Err(Error::UnsupportedFeature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set speaking voice.
|
||||||
|
*/
|
||||||
|
pub fn set_voice(&self, voice: String) -> Result<(),Error> {
|
||||||
|
let Features {
|
||||||
|
voices: voices_feature,
|
||||||
|
..
|
||||||
|
} = self.0.supported_features();
|
||||||
|
if voices_feature {
|
||||||
|
self.0.set_voice(voice)
|
||||||
|
} else {
|
||||||
|
Err(Error::UnsupportedFeature)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue