mirror of https://github.com/ndarilek/tts-rs.git
Compare commits
5 Commits
87d37c474e
...
bde29226a0
Author | SHA1 | Date |
---|---|---|
Lee C. Baker | bde29226a0 | |
Nolan Darilek | 5c528f1d8e | |
Nolan Darilek | 9fb8107acf | |
Nolan Darilek | 8dabcc99c4 | |
Lee Baker | a55463dac8 |
24
Cargo.toml
24
Cargo.toml
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "tts"
|
name = "tts"
|
||||||
version = "0.25.5"
|
version = "0.25.6"
|
||||||
authors = ["Nolan Darilek <nolan@thewordnerd.info>"]
|
authors = ["Nolan Darilek <nolan@thewordnerd.info>"]
|
||||||
repository = "https://github.com/ndarilek/tts-rs"
|
repository = "https://github.com/ndarilek/tts-rs"
|
||||||
description = "High-level Text-To-Speech (TTS) interface"
|
description = "High-level Text-To-Speech (TTS) interface"
|
||||||
|
@ -30,7 +30,14 @@ env_logger = "0.10"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
tolk = { version = "0.5", optional = true }
|
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]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
speech-dispatcher = { version = "0.16", default-features = false }
|
speech-dispatcher = { version = "0.16", default-features = false }
|
||||||
|
@ -43,7 +50,16 @@ objc = { version = "0.2", features = ["exception"] }
|
||||||
|
|
||||||
[target.wasm32-unknown-unknown.dependencies]
|
[target.wasm32-unknown-unknown.dependencies]
|
||||||
wasm-bindgen = "0.2"
|
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]
|
[target.'cfg(target_os="android")'.dependencies]
|
||||||
jni = "0.21"
|
jni = "0.21"
|
||||||
|
@ -52,4 +68,4 @@ ndk-glue = "0.7"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
no-default-features = true
|
no-default-features = true
|
||||||
features = ["speech_dispatcher_0_11"]
|
features = ["speech_dispatcher_0_11"]
|
||||||
|
|
|
@ -8,7 +8,7 @@ use core_foundation::base::TCFType;
|
||||||
use core_foundation::string::CFString;
|
use core_foundation::string::CFString;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use log::{info, trace};
|
use log::{info, trace};
|
||||||
use objc::runtime::{Object, Sel};
|
use objc::runtime::{Class, Object, Sel};
|
||||||
use objc::{class, declare::ClassDecl, msg_send, sel, sel_impl};
|
use objc::{class, declare::ClassDecl, msg_send, sel, sel_impl};
|
||||||
use oxilangtag::LanguageTag;
|
use oxilangtag::LanguageTag;
|
||||||
|
|
||||||
|
@ -25,109 +25,114 @@ pub(crate) struct AvFoundation {
|
||||||
voice: Option<Voice>,
|
voice: Option<Voice>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_speech_sythesizer_delegate() -> Result<&'static Class, Error> {
|
||||||
|
let mut decl = ClassDecl::new("MyNSSpeechSynthesizerDelegate", class!(NSObject))
|
||||||
|
.ok_or(Error::OperationFailed)?;
|
||||||
|
decl.add_ivar::<u64>("backend_id");
|
||||||
|
|
||||||
|
extern "C" fn speech_synthesizer_did_start_speech_utterance(
|
||||||
|
this: &Object,
|
||||||
|
_: Sel,
|
||||||
|
_synth: *const Object,
|
||||||
|
utterance: id,
|
||||||
|
) {
|
||||||
|
trace!("speech_synthesizer_did_start_speech_utterance");
|
||||||
|
unsafe {
|
||||||
|
let backend_id: u64 = *this.get_ivar("backend_id");
|
||||||
|
let backend_id = BackendId::AvFoundation(backend_id);
|
||||||
|
trace!("Locking callbacks");
|
||||||
|
let mut callbacks = CALLBACKS.lock().unwrap();
|
||||||
|
trace!("Locked");
|
||||||
|
let callbacks = callbacks.get_mut(&backend_id).unwrap();
|
||||||
|
if let Some(callback) = callbacks.utterance_begin.as_mut() {
|
||||||
|
trace!("Calling utterance_begin");
|
||||||
|
let utterance_id = UtteranceId::AvFoundation(utterance);
|
||||||
|
callback(utterance_id);
|
||||||
|
trace!("Called");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trace!("Done speech_synthesizer_did_start_speech_utterance");
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn speech_synthesizer_did_finish_speech_utterance(
|
||||||
|
this: &Object,
|
||||||
|
_: Sel,
|
||||||
|
_synth: *const Object,
|
||||||
|
utterance: id,
|
||||||
|
) {
|
||||||
|
trace!("speech_synthesizer_did_finish_speech_utterance");
|
||||||
|
unsafe {
|
||||||
|
let backend_id: u64 = *this.get_ivar("backend_id");
|
||||||
|
let backend_id = BackendId::AvFoundation(backend_id);
|
||||||
|
trace!("Locking callbacks");
|
||||||
|
let mut callbacks = CALLBACKS.lock().unwrap();
|
||||||
|
trace!("Locked");
|
||||||
|
let callbacks = callbacks.get_mut(&backend_id).unwrap();
|
||||||
|
if let Some(callback) = callbacks.utterance_end.as_mut() {
|
||||||
|
trace!("Calling utterance_end");
|
||||||
|
let utterance_id = UtteranceId::AvFoundation(utterance);
|
||||||
|
callback(utterance_id);
|
||||||
|
trace!("Called");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trace!("Done speech_synthesizer_did_finish_speech_utterance");
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn speech_synthesizer_did_cancel_speech_utterance(
|
||||||
|
this: &Object,
|
||||||
|
_: Sel,
|
||||||
|
_synth: *const Object,
|
||||||
|
utterance: id,
|
||||||
|
) {
|
||||||
|
trace!("speech_synthesizer_did_cancel_speech_utterance");
|
||||||
|
unsafe {
|
||||||
|
let backend_id: u64 = *this.get_ivar("backend_id");
|
||||||
|
let backend_id = BackendId::AvFoundation(backend_id);
|
||||||
|
trace!("Locking callbacks");
|
||||||
|
let mut callbacks = CALLBACKS.lock().unwrap();
|
||||||
|
trace!("Locked");
|
||||||
|
let callbacks = callbacks.get_mut(&backend_id).unwrap();
|
||||||
|
if let Some(callback) = callbacks.utterance_stop.as_mut() {
|
||||||
|
trace!("Calling utterance_stop");
|
||||||
|
let utterance_id = UtteranceId::AvFoundation(utterance);
|
||||||
|
callback(utterance_id);
|
||||||
|
trace!("Called");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trace!("Done speech_synthesizer_did_cancel_speech_utterance");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
decl.add_method(
|
||||||
|
sel!(speechSynthesizer:didStartSpeechUtterance:),
|
||||||
|
speech_synthesizer_did_start_speech_utterance
|
||||||
|
as extern "C" fn(&Object, Sel, *const Object, id) -> (),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(speechSynthesizer:didFinishSpeechUtterance:),
|
||||||
|
speech_synthesizer_did_finish_speech_utterance
|
||||||
|
as extern "C" fn(&Object, Sel, *const Object, id) -> (),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(speechSynthesizer:didCancelSpeechUtterance:),
|
||||||
|
speech_synthesizer_did_cancel_speech_utterance
|
||||||
|
as extern "C" fn(&Object, Sel, *const Object, id) -> (),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(decl.register())
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref NEXT_BACKEND_ID: Mutex<u64> = Mutex::new(0);
|
static ref NEXT_BACKEND_ID: Mutex<u64> = Mutex::new(0);
|
||||||
|
static ref DELEGATE_CLASS: &'static Class = make_speech_sythesizer_delegate().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AvFoundation {
|
impl AvFoundation {
|
||||||
pub(crate) fn new() -> Result<Self, Error> {
|
pub(crate) fn new() -> Result<Self, Error> {
|
||||||
info!("Initializing AVFoundation backend");
|
info!("Initializing AVFoundation backend");
|
||||||
let mut decl = ClassDecl::new("MyNSSpeechSynthesizerDelegate", class!(NSObject))
|
|
||||||
.ok_or(Error::OperationFailed)?;
|
|
||||||
decl.add_ivar::<u64>("backend_id");
|
|
||||||
|
|
||||||
extern "C" fn speech_synthesizer_did_start_speech_utterance(
|
let delegate_obj: *mut Object = unsafe { msg_send![*DELEGATE_CLASS, new] };
|
||||||
this: &Object,
|
|
||||||
_: Sel,
|
|
||||||
_synth: *const Object,
|
|
||||||
utterance: id,
|
|
||||||
) {
|
|
||||||
trace!("speech_synthesizer_did_start_speech_utterance");
|
|
||||||
unsafe {
|
|
||||||
let backend_id: u64 = *this.get_ivar("backend_id");
|
|
||||||
let backend_id = BackendId::AvFoundation(backend_id);
|
|
||||||
trace!("Locking callbacks");
|
|
||||||
let mut callbacks = CALLBACKS.lock().unwrap();
|
|
||||||
trace!("Locked");
|
|
||||||
let callbacks = callbacks.get_mut(&backend_id).unwrap();
|
|
||||||
if let Some(callback) = callbacks.utterance_begin.as_mut() {
|
|
||||||
trace!("Calling utterance_begin");
|
|
||||||
let utterance_id = UtteranceId::AvFoundation(utterance);
|
|
||||||
callback(utterance_id);
|
|
||||||
trace!("Called");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
trace!("Done speech_synthesizer_did_start_speech_utterance");
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" fn speech_synthesizer_did_finish_speech_utterance(
|
|
||||||
this: &Object,
|
|
||||||
_: Sel,
|
|
||||||
_synth: *const Object,
|
|
||||||
utterance: id,
|
|
||||||
) {
|
|
||||||
trace!("speech_synthesizer_did_finish_speech_utterance");
|
|
||||||
unsafe {
|
|
||||||
let backend_id: u64 = *this.get_ivar("backend_id");
|
|
||||||
let backend_id = BackendId::AvFoundation(backend_id);
|
|
||||||
trace!("Locking callbacks");
|
|
||||||
let mut callbacks = CALLBACKS.lock().unwrap();
|
|
||||||
trace!("Locked");
|
|
||||||
let callbacks = callbacks.get_mut(&backend_id).unwrap();
|
|
||||||
if let Some(callback) = callbacks.utterance_end.as_mut() {
|
|
||||||
trace!("Calling utterance_end");
|
|
||||||
let utterance_id = UtteranceId::AvFoundation(utterance);
|
|
||||||
callback(utterance_id);
|
|
||||||
trace!("Called");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
trace!("Done speech_synthesizer_did_finish_speech_utterance");
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" fn speech_synthesizer_did_cancel_speech_utterance(
|
|
||||||
this: &Object,
|
|
||||||
_: Sel,
|
|
||||||
_synth: *const Object,
|
|
||||||
utterance: id,
|
|
||||||
) {
|
|
||||||
trace!("speech_synthesizer_did_cancel_speech_utterance");
|
|
||||||
unsafe {
|
|
||||||
let backend_id: u64 = *this.get_ivar("backend_id");
|
|
||||||
let backend_id = BackendId::AvFoundation(backend_id);
|
|
||||||
trace!("Locking callbacks");
|
|
||||||
let mut callbacks = CALLBACKS.lock().unwrap();
|
|
||||||
trace!("Locked");
|
|
||||||
let callbacks = callbacks.get_mut(&backend_id).unwrap();
|
|
||||||
if let Some(callback) = callbacks.utterance_stop.as_mut() {
|
|
||||||
trace!("Calling utterance_stop");
|
|
||||||
let utterance_id = UtteranceId::AvFoundation(utterance);
|
|
||||||
callback(utterance_id);
|
|
||||||
trace!("Called");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
trace!("Done speech_synthesizer_did_cancel_speech_utterance");
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
decl.add_method(
|
|
||||||
sel!(speechSynthesizer:didStartSpeechUtterance:),
|
|
||||||
speech_synthesizer_did_start_speech_utterance
|
|
||||||
as extern "C" fn(&Object, Sel, *const Object, id) -> (),
|
|
||||||
);
|
|
||||||
decl.add_method(
|
|
||||||
sel!(speechSynthesizer:didFinishSpeechUtterance:),
|
|
||||||
speech_synthesizer_did_finish_speech_utterance
|
|
||||||
as extern "C" fn(&Object, Sel, *const Object, id) -> (),
|
|
||||||
);
|
|
||||||
decl.add_method(
|
|
||||||
sel!(speechSynthesizer:didCancelSpeechUtterance:),
|
|
||||||
speech_synthesizer_did_cancel_speech_utterance
|
|
||||||
as extern "C" fn(&Object, Sel, *const Object, id) -> (),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let delegate_class = decl.register();
|
|
||||||
let delegate_obj: *mut Object = unsafe { msg_send![delegate_class, new] };
|
|
||||||
let mut backend_id = NEXT_BACKEND_ID.lock().unwrap();
|
let mut backend_id = NEXT_BACKEND_ID.lock().unwrap();
|
||||||
let rv = unsafe {
|
let rv = unsafe {
|
||||||
trace!("Creating synth");
|
trace!("Creating synth");
|
||||||
|
|
|
@ -571,7 +571,7 @@ impl Tts {
|
||||||
if utterance_callbacks {
|
if utterance_callbacks {
|
||||||
let mut callbacks = CALLBACKS.lock().unwrap();
|
let mut callbacks = CALLBACKS.lock().unwrap();
|
||||||
let id = self.0.read().unwrap().id().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;
|
callbacks.utterance_begin = callback;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -591,7 +591,7 @@ impl Tts {
|
||||||
if utterance_callbacks {
|
if utterance_callbacks {
|
||||||
let mut callbacks = CALLBACKS.lock().unwrap();
|
let mut callbacks = CALLBACKS.lock().unwrap();
|
||||||
let id = self.0.read().unwrap().id().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;
|
callbacks.utterance_end = callback;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -611,7 +611,7 @@ impl Tts {
|
||||||
if utterance_callbacks {
|
if utterance_callbacks {
|
||||||
let mut callbacks = CALLBACKS.lock().unwrap();
|
let mut callbacks = CALLBACKS.lock().unwrap();
|
||||||
let id = self.0.read().unwrap().id().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;
|
callbacks.utterance_stop = callback;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue