mirror of https://github.com/ndarilek/tts-rs.git
Compare commits
28 Commits
Author | SHA1 | Date |
---|---|---|
Nolan Darilek | 3bc16f0c6f | |
Nolan Darilek | 3c8ae0ae42 | |
Nolan Darilek | 07edc20861 | |
Nolan Darilek | 96a5209a9f | |
Nolan Darilek | 20b18949e2 | |
Nolan Darilek | f29de0aede | |
Nolan Darilek | 9e1476fd36 | |
Nolan Darilek | 3032fe0fb3 | |
Nolan Darilek | edd09c24e7 | |
Nolan Darilek | b7b4e7dc85 | |
Nolan Darilek | f593340051 | |
Enyium | 12d8e1f532 | |
Marijn Suijten | 2a81dc9b70 | |
Esther Alter | a0c6cbaf6a | |
Nolan Darilek | 5c528f1d8e | |
Nolan Darilek | 9fb8107acf | |
Nolan Darilek | 8dabcc99c4 | |
Nolan Darilek | b369fb5614 | |
Nolan Darilek | e6e1cd49bf | |
Nolan Darilek | e2edc18e6e | |
Nolan Darilek | 3eba940a22 | |
Nolan Darilek | 7e761e1267 | |
Nolan Darilek | bf8eb07866 | |
Nolan Darilek | f5be2b7657 | |
Nolan Darilek | b7e7ed46dd | |
Nolan Darilek | 69eebf2ffa | |
Nolan Darilek | 6c6089daf9 | |
Alexey Stolybko | c874607afe |
|
@ -12,7 +12,7 @@ jobs:
|
|||
env:
|
||||
CARGO_TOKEN: ${{ secrets.CARGO_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libspeechd-dev
|
||||
|
|
|
@ -5,6 +5,17 @@ on:
|
|||
pull_request:
|
||||
|
||||
jobs:
|
||||
check_formatting:
|
||||
name: Check Formatting
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: |
|
||||
rustup toolchain install stable
|
||||
cargo fmt --all --check
|
||||
cd examples/web
|
||||
cargo fmt --all --check
|
||||
|
||||
check:
|
||||
name: Check
|
||||
strategy:
|
||||
|
@ -12,41 +23,38 @@ jobs:
|
|||
os: [windows-latest, ubuntu-22.04, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- run: sudo apt-get update; sudo apt-get install -y libspeechd-dev
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
- run: |
|
||||
rustup toolchain install stable
|
||||
cargo fmt --check
|
||||
cargo clippy
|
||||
cargo clippy --all-targets
|
||||
|
||||
check_web:
|
||||
name: Check Web
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- run: |
|
||||
rustup target add wasm32-unknown-unknown
|
||||
rustup toolchain install stable
|
||||
cargo fmt --all --check
|
||||
cargo clippy --target wasm32-unknown-unknown
|
||||
cargo clippy --all-targets --target wasm32-unknown-unknown
|
||||
|
||||
check_android:
|
||||
name: Check Android
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- run: |
|
||||
rustup target add aarch64-linux-android
|
||||
rustup toolchain install stable
|
||||
rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android
|
||||
cargo install -f cargo-apk
|
||||
cargo apk build
|
||||
cargo clippy --all-targets --target aarch64-linux-android
|
||||
|
||||
check_web_example:
|
||||
name: Check Web Example
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- run: |
|
||||
rustup target add wasm32-unknown-unknown
|
||||
rustup toolchain install stable
|
||||
|
|
30
Cargo.toml
30
Cargo.toml
|
@ -1,9 +1,10 @@
|
|||
[package]
|
||||
name = "tts"
|
||||
version = "0.25.0"
|
||||
version = "0.26.1"
|
||||
authors = ["Nolan Darilek <nolan@thewordnerd.info>"]
|
||||
repository = "https://github.com/ndarilek/tts-rs"
|
||||
description = "High-level Text-To-Speech (TTS) interface"
|
||||
documentation = "https://docs.rs/tts"
|
||||
license = "MIT"
|
||||
exclude = ["*.cfg", "*.yml"]
|
||||
edition = "2021"
|
||||
|
@ -26,11 +27,18 @@ serde = { version = "1", optional = true, features = ["derive"] }
|
|||
thiserror = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.10"
|
||||
env_logger = "0.11"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
tolk = { version = "0.5", optional = true }
|
||||
windows = { version = "0.43", features = ["Foundation", "Foundation_Collections", "Media_Core", "Media_Playback", "Media_SpeechSynthesis", "Storage_Streams"] }
|
||||
windows = { version = "0.56", features = [
|
||||
"Foundation",
|
||||
"Foundation_Collections",
|
||||
"Media_Core",
|
||||
"Media_Playback",
|
||||
"Media_SpeechSynthesis",
|
||||
"Storage_Streams",
|
||||
] }
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
speech-dispatcher = { version = "0.16", default-features = false }
|
||||
|
@ -43,13 +51,21 @@ objc = { version = "0.2", features = ["exception"] }
|
|||
|
||||
[target.wasm32-unknown-unknown.dependencies]
|
||||
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]
|
||||
jni = "0.20"
|
||||
jni = "0.21"
|
||||
ndk-context = "0.1"
|
||||
ndk-glue = "0.7"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
no-default-features = true
|
||||
features = ["speech_dispatcher_0_9"]
|
||||
features = ["speech_dispatcher_0_11"]
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
#[cfg(target_os = "macos")]
|
||||
use cocoa_foundation::base::id;
|
||||
#[cfg(target_os = "macos")]
|
||||
use cocoa_foundation::foundation::NSDefaultRunLoopMode;
|
||||
#[cfg(target_os = "macos")]
|
||||
use cocoa_foundation::foundation::NSRunLoop;
|
||||
#[cfg(target_os = "macos")]
|
||||
use objc::class;
|
||||
#[cfg(target_os = "macos")]
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
use std::{thread, time};
|
||||
|
||||
use tts::*;
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
|
@ -8,6 +17,14 @@ fn main() -> Result<(), Error> {
|
|||
let mut phrase = 1;
|
||||
loop {
|
||||
tts.speak(format!("Phrase {}", phrase), false)?;
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let run_loop: id = unsafe { NSRunLoop::currentRunLoop() };
|
||||
unsafe {
|
||||
let date: id = msg_send![class!(NSDate), distantFuture];
|
||||
let _: () = msg_send![run_loop, runMode:NSDefaultRunLoopMode beforeDate:date];
|
||||
}
|
||||
}
|
||||
let time = time::Duration::from_secs(5);
|
||||
thread::sleep(time);
|
||||
phrase += 1;
|
||||
|
|
|
@ -28,7 +28,7 @@ lazy_static! {
|
|||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
pub extern "system" fn JNI_OnLoad(vm: JavaVM, _: *mut c_void) -> jint {
|
||||
let env = vm.get_env().expect("Cannot get reference to the JNIEnv");
|
||||
let mut env = vm.get_env().expect("Cannot get reference to the JNIEnv");
|
||||
let b = env
|
||||
.find_class("rs/tts/Bridge")
|
||||
.expect("Failed to find `Bridge`");
|
||||
|
@ -42,7 +42,7 @@ pub extern "system" fn JNI_OnLoad(vm: JavaVM, _: *mut c_void) -> jint {
|
|||
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe extern "C" fn Java_rs_tts_Bridge_onInit(env: JNIEnv, obj: JObject, status: jint) {
|
||||
pub unsafe extern "C" fn Java_rs_tts_Bridge_onInit(mut env: JNIEnv, obj: JObject, status: jint) {
|
||||
let id = env
|
||||
.get_field(obj, "backendId", "I")
|
||||
.expect("Failed to get backend ID")
|
||||
|
@ -58,7 +58,7 @@ pub unsafe extern "C" fn Java_rs_tts_Bridge_onInit(env: JNIEnv, obj: JObject, st
|
|||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe extern "C" fn Java_rs_tts_Bridge_onStart(
|
||||
env: JNIEnv,
|
||||
mut env: JNIEnv,
|
||||
obj: JObject,
|
||||
utterance_id: JString,
|
||||
) {
|
||||
|
@ -69,7 +69,7 @@ pub unsafe extern "C" fn Java_rs_tts_Bridge_onStart(
|
|||
.expect("Failed to cast to int") as u64;
|
||||
let backend_id = BackendId::Android(backend_id);
|
||||
let utterance_id = CString::from(CStr::from_ptr(
|
||||
env.get_string(utterance_id).unwrap().as_ptr(),
|
||||
env.get_string(&utterance_id).unwrap().as_ptr(),
|
||||
))
|
||||
.into_string()
|
||||
.unwrap();
|
||||
|
@ -85,7 +85,7 @@ pub unsafe extern "C" fn Java_rs_tts_Bridge_onStart(
|
|||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe extern "C" fn Java_rs_tts_Bridge_onStop(
|
||||
env: JNIEnv,
|
||||
mut env: JNIEnv,
|
||||
obj: JObject,
|
||||
utterance_id: JString,
|
||||
) {
|
||||
|
@ -96,7 +96,7 @@ pub unsafe extern "C" fn Java_rs_tts_Bridge_onStop(
|
|||
.expect("Failed to cast to int") as u64;
|
||||
let backend_id = BackendId::Android(backend_id);
|
||||
let utterance_id = CString::from(CStr::from_ptr(
|
||||
env.get_string(utterance_id).unwrap().as_ptr(),
|
||||
env.get_string(&utterance_id).unwrap().as_ptr(),
|
||||
))
|
||||
.into_string()
|
||||
.unwrap();
|
||||
|
@ -112,7 +112,7 @@ pub unsafe extern "C" fn Java_rs_tts_Bridge_onStop(
|
|||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe extern "C" fn Java_rs_tts_Bridge_onDone(
|
||||
env: JNIEnv,
|
||||
mut env: JNIEnv,
|
||||
obj: JObject,
|
||||
utterance_id: JString,
|
||||
) {
|
||||
|
@ -123,7 +123,7 @@ pub unsafe extern "C" fn Java_rs_tts_Bridge_onDone(
|
|||
.expect("Failed to cast to int") as u64;
|
||||
let backend_id = BackendId::Android(backend_id);
|
||||
let utterance_id = CString::from(CStr::from_ptr(
|
||||
env.get_string(utterance_id).unwrap().as_ptr(),
|
||||
env.get_string(&utterance_id).unwrap().as_ptr(),
|
||||
))
|
||||
.into_string()
|
||||
.unwrap();
|
||||
|
@ -139,7 +139,7 @@ pub unsafe extern "C" fn Java_rs_tts_Bridge_onDone(
|
|||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe extern "C" fn Java_rs_tts_Bridge_onError(
|
||||
env: JNIEnv,
|
||||
mut env: JNIEnv,
|
||||
obj: JObject,
|
||||
utterance_id: JString,
|
||||
) {
|
||||
|
@ -150,7 +150,7 @@ pub unsafe extern "C" fn Java_rs_tts_Bridge_onError(
|
|||
.expect("Failed to cast to int") as u64;
|
||||
let backend_id = BackendId::Android(backend_id);
|
||||
let utterance_id = CString::from(CStr::from_ptr(
|
||||
env.get_string(utterance_id).unwrap().as_ptr(),
|
||||
env.get_string(&utterance_id).unwrap().as_ptr(),
|
||||
))
|
||||
.into_string()
|
||||
.unwrap();
|
||||
|
@ -182,20 +182,20 @@ impl Android {
|
|||
let ctx = ndk_context::android_context();
|
||||
let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }?;
|
||||
let context = unsafe { JObject::from_raw(ctx.context().cast()) };
|
||||
let env = vm.attach_current_thread_permanently()?;
|
||||
let mut env = vm.attach_current_thread_permanently()?;
|
||||
let bridge = BRIDGE.lock().unwrap();
|
||||
if let Some(bridge) = &*bridge {
|
||||
let bridge = env.new_object(bridge, "(I)V", &[(bid as jint).into()])?;
|
||||
let tts = env.new_object(
|
||||
"android/speech/tts/TextToSpeech",
|
||||
"(Landroid/content/Context;Landroid/speech/tts/TextToSpeech$OnInitListener;)V",
|
||||
&[context.into(), bridge.into()],
|
||||
&[(&context).into(), (&bridge).into()],
|
||||
)?;
|
||||
env.call_method(
|
||||
tts,
|
||||
&tts,
|
||||
"setOnUtteranceProgressListener",
|
||||
"(Landroid/speech/tts/UtteranceProgressListener;)I",
|
||||
&[bridge.into()],
|
||||
&[(&bridge).into()],
|
||||
)?;
|
||||
{
|
||||
let mut pending = PENDING_INITIALIZATIONS.write().unwrap();
|
||||
|
@ -255,7 +255,7 @@ impl Backend for Android {
|
|||
|
||||
fn speak(&mut self, text: &str, interrupt: bool) -> Result<Option<UtteranceId>, Error> {
|
||||
let vm = Self::vm()?;
|
||||
let env = vm.get_env()?;
|
||||
let mut env = vm.get_env()?;
|
||||
let tts = self.tts.as_obj();
|
||||
let text = env.new_string(text)?;
|
||||
let queue_mode = if interrupt { 0 } else { 1 };
|
||||
|
@ -270,10 +270,10 @@ impl Backend for Android {
|
|||
"speak",
|
||||
"(Ljava/lang/CharSequence;ILandroid/os/Bundle;Ljava/lang/String;)I",
|
||||
&[
|
||||
text.into(),
|
||||
(&text).into(),
|
||||
queue_mode.into(),
|
||||
JObject::null().into(),
|
||||
uid.into(),
|
||||
(&JObject::null()).into(),
|
||||
(&uid).into(),
|
||||
],
|
||||
)?;
|
||||
let rv = rv.i()?;
|
||||
|
@ -286,7 +286,7 @@ impl Backend for Android {
|
|||
|
||||
fn stop(&mut self) -> Result<(), Error> {
|
||||
let vm = Self::vm()?;
|
||||
let env = vm.get_env()?;
|
||||
let mut env = vm.get_env()?;
|
||||
let tts = self.tts.as_obj();
|
||||
let rv = env.call_method(tts, "stop", "()I", &[])?;
|
||||
let rv = rv.i()?;
|
||||
|
@ -315,7 +315,7 @@ impl Backend for Android {
|
|||
|
||||
fn set_rate(&mut self, rate: f32) -> Result<(), Error> {
|
||||
let vm = Self::vm()?;
|
||||
let env = vm.get_env()?;
|
||||
let mut env = vm.get_env()?;
|
||||
let tts = self.tts.as_obj();
|
||||
let rate = rate as jfloat;
|
||||
let rv = env.call_method(tts, "setSpeechRate", "(F)I", &[rate.into()])?;
|
||||
|
@ -346,7 +346,7 @@ impl Backend for Android {
|
|||
|
||||
fn set_pitch(&mut self, pitch: f32) -> Result<(), Error> {
|
||||
let vm = Self::vm()?;
|
||||
let env = vm.get_env()?;
|
||||
let mut env = vm.get_env()?;
|
||||
let tts = self.tts.as_obj();
|
||||
let pitch = pitch as jfloat;
|
||||
let rv = env.call_method(tts, "setPitch", "(F)I", &[pitch.into()])?;
|
||||
|
@ -381,7 +381,7 @@ impl Backend for Android {
|
|||
|
||||
fn is_speaking(&self) -> Result<bool, Error> {
|
||||
let vm = Self::vm()?;
|
||||
let env = vm.get_env()?;
|
||||
let mut env = vm.get_env()?;
|
||||
let tts = self.tts.as_obj();
|
||||
let rv = env.call_method(tts, "isSpeaking", "()Z", &[])?;
|
||||
let rv = rv.z()?;
|
||||
|
|
|
@ -188,6 +188,7 @@ impl Backend for SpeechDispatcher {
|
|||
.0
|
||||
.list_synthesis_voices()?
|
||||
.iter()
|
||||
.filter(|v| LanguageTag::parse(v.language.clone()).is_ok())
|
||||
.map(|v| Voice {
|
||||
id: v.name.clone(),
|
||||
name: v.name.clone(),
|
||||
|
|
50
src/lib.rs
50
src/lib.rs
|
@ -14,9 +14,10 @@ use std::collections::HashMap;
|
|||
#[cfg(target_os = "macos")]
|
||||
use std::ffi::CStr;
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
#[cfg(windows)]
|
||||
use std::string::FromUtf16Error;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::Mutex;
|
||||
use std::{boxed::Box, sync::RwLock};
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
|
@ -95,15 +96,15 @@ impl fmt::Display for BackendId {
|
|||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
match self {
|
||||
#[cfg(target_os = "android")]
|
||||
BackendId::Android(id) => writeln!(f, "{}", id),
|
||||
BackendId::Android(id) => writeln!(f, "Android({id})"),
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
BackendId::AvFoundation(id) => writeln!(f, "{}", id),
|
||||
BackendId::AvFoundation(id) => writeln!(f, "AvFoundation({id})"),
|
||||
#[cfg(target_os = "linux")]
|
||||
BackendId::SpeechDispatcher(id) => writeln!(f, "{}", id),
|
||||
BackendId::SpeechDispatcher(id) => writeln!(f, "SpeechDispatcher({id})"),
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
BackendId::Web(id) => writeln!(f, "Web({})", id),
|
||||
BackendId::Web(id) => writeln!(f, "Web({id})"),
|
||||
#[cfg(windows)]
|
||||
BackendId::WinRt(id) => writeln!(f, "{}", id),
|
||||
BackendId::WinRt(id) => writeln!(f, "WinRT({id})"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -143,13 +144,13 @@ impl fmt::Display for UtteranceId {
|
|||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
match self {
|
||||
#[cfg(target_os = "android")]
|
||||
UtteranceId::Android(id) => writeln!(f, "{}", id),
|
||||
UtteranceId::Android(id) => writeln!(f, "Android({id})"),
|
||||
#[cfg(target_os = "linux")]
|
||||
UtteranceId::SpeechDispatcher(id) => writeln!(f, "{}", id),
|
||||
UtteranceId::SpeechDispatcher(id) => writeln!(f, "SpeechDispatcher({id})"),
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
UtteranceId::Web(id) => writeln!(f, "Web({})", id),
|
||||
#[cfg(windows)]
|
||||
UtteranceId::WinRt(id) => writeln!(f, "{}", id),
|
||||
UtteranceId::WinRt(id) => writeln!(f, "WinRt({id})"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -173,7 +174,7 @@ pub struct Features {
|
|||
|
||||
impl fmt::Display for Features {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
writeln!(f, "{:#?}", self)
|
||||
writeln!(f, "{self:#?}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,7 +259,7 @@ lazy_static! {
|
|||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Tts(Arc<RwLock<Box<dyn Backend>>>);
|
||||
pub struct Tts(Rc<RwLock<Box<dyn Backend>>>);
|
||||
|
||||
unsafe impl Send for Tts {}
|
||||
|
||||
|
@ -271,18 +272,18 @@ impl Tts {
|
|||
#[cfg(target_os = "linux")]
|
||||
Backends::SpeechDispatcher => {
|
||||
let tts = backends::SpeechDispatcher::new()?;
|
||||
Ok(Tts(Arc::new(RwLock::new(Box::new(tts)))))
|
||||
Ok(Tts(Rc::new(RwLock::new(Box::new(tts)))))
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
Backends::Web => {
|
||||
let tts = backends::Web::new()?;
|
||||
Ok(Tts(Arc::new(RwLock::new(Box::new(tts)))))
|
||||
Ok(Tts(Rc::new(RwLock::new(Box::new(tts)))))
|
||||
}
|
||||
#[cfg(all(windows, feature = "tolk"))]
|
||||
Backends::Tolk => {
|
||||
let tts = backends::Tolk::new();
|
||||
if let Some(tts) = tts {
|
||||
Ok(Tts(Arc::new(RwLock::new(Box::new(tts)))))
|
||||
Ok(Tts(Rc::new(RwLock::new(Box::new(tts)))))
|
||||
} else {
|
||||
Err(Error::NoneError)
|
||||
}
|
||||
|
@ -290,20 +291,20 @@ impl Tts {
|
|||
#[cfg(windows)]
|
||||
Backends::WinRt => {
|
||||
let tts = backends::WinRt::new()?;
|
||||
Ok(Tts(Arc::new(RwLock::new(Box::new(tts)))))
|
||||
Ok(Tts(Rc::new(RwLock::new(Box::new(tts)))))
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
Backends::AppKit => Ok(Tts(Arc::new(RwLock::new(Box::new(
|
||||
backends::AppKit::new()?
|
||||
))))),
|
||||
Backends::AppKit => Ok(Tts(Rc::new(RwLock::new(
|
||||
Box::new(backends::AppKit::new()?),
|
||||
)))),
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
Backends::AvFoundation => Ok(Tts(Arc::new(RwLock::new(Box::new(
|
||||
Backends::AvFoundation => Ok(Tts(Rc::new(RwLock::new(Box::new(
|
||||
backends::AvFoundation::new()?,
|
||||
))))),
|
||||
#[cfg(target_os = "android")]
|
||||
Backends::Android => {
|
||||
let tts = backends::Android::new()?;
|
||||
Ok(Tts(Arc::new(RwLock::new(Box::new(tts)))))
|
||||
Ok(Tts(Rc::new(RwLock::new(Box::new(tts)))))
|
||||
}
|
||||
};
|
||||
if let Ok(backend) = backend {
|
||||
|
@ -317,6 +318,7 @@ impl Tts {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::should_implement_trait)]
|
||||
pub fn default() -> Result<Tts, Error> {
|
||||
#[cfg(target_os = "linux")]
|
||||
let tts = Tts::new(Backends::SpeechDispatcher);
|
||||
|
@ -570,7 +572,7 @@ impl Tts {
|
|||
if utterance_callbacks {
|
||||
let mut callbacks = CALLBACKS.lock().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;
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -590,7 +592,7 @@ impl Tts {
|
|||
if utterance_callbacks {
|
||||
let mut callbacks = CALLBACKS.lock().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;
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -610,7 +612,7 @@ impl Tts {
|
|||
if utterance_callbacks {
|
||||
let mut callbacks = CALLBACKS.lock().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;
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -639,7 +641,7 @@ impl Tts {
|
|||
|
||||
impl Drop for Tts {
|
||||
fn drop(&mut self) {
|
||||
if Arc::strong_count(&self.0) <= 1 {
|
||||
if Rc::strong_count(&self.0) <= 1 {
|
||||
if let Some(id) = self.0.read().unwrap().id() {
|
||||
let mut callbacks = CALLBACKS.lock().unwrap();
|
||||
callbacks.remove(&id);
|
||||
|
|
Loading…
Reference in New Issue