mirror of
https://github.com/ndarilek/tts-rs.git
synced 2024-09-29 01:09:37 +00:00
Compare commits
63 Commits
Author | SHA1 | Date | |
---|---|---|---|
b31efe752d | |||
ae7bf7554c | |||
3bc16f0c6f | |||
3c8ae0ae42 | |||
07edc20861 | |||
96a5209a9f | |||
20b18949e2 | |||
f29de0aede | |||
9e1476fd36 | |||
3032fe0fb3 | |||
edd09c24e7 | |||
b7b4e7dc85 | |||
f593340051 | |||
|
12d8e1f532 | ||
|
2a81dc9b70 | ||
|
a0c6cbaf6a | ||
5c528f1d8e | |||
9fb8107acf | |||
8dabcc99c4 | |||
b369fb5614 | |||
e6e1cd49bf | |||
e2edc18e6e | |||
3eba940a22 | |||
7e761e1267 | |||
bf8eb07866 | |||
f5be2b7657 | |||
b7e7ed46dd | |||
69eebf2ffa | |||
6c6089daf9 | |||
|
c874607afe | ||
2667d4e943 | |||
8b506a89e0 | |||
dcaf5b914d | |||
359b1c8053 | |||
527b4cd61e | |||
97fa370dec | |||
915673eec6 | |||
cf72bad59a | |||
246e587f2d | |||
d65d79f8fb | |||
c339d2bee3 | |||
daaead1dc3 | |||
d547d84af0 | |||
f6766ec633 | |||
8102820f86 | |||
3c9a78a953 | |||
5470b9557d | |||
6770a2ed58 | |||
61d84a2120 | |||
7a91a1e827 | |||
e19e5ef0b7 | |||
3e4299d0e6 | |||
d42d20189a | |||
22ae0ef5a3 | |||
f5716c48f5 | |||
eb1d13976a | |||
259549e21d | |||
3679ad6153 | |||
|
94615a254a | ||
|
ddf96c10aa | ||
|
3fdd452646 | ||
b4f48fa439 | |||
919bc4249a |
88
.github/workflows/release.yml
vendored
88
.github/workflows/release.yml
vendored
|
@ -6,98 +6,16 @@ on:
|
|||
- "v*"
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Check
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-latest, ubuntu-22.04, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- run: sudo apt-get update; sudo apt-get install -y libspeechd-dev
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
components: rustfmt, clippy
|
||||
override: true
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
args: --all-features --examples
|
||||
if: ${{ runner.os != 'Linux' }}
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
args: --no-default-features --examples
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all --check
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-features
|
||||
if: ${{ runner.os != 'Linux' }}
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --no-default-features
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
|
||||
check_web:
|
||||
name: Check Web
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
target: wasm32-unknown-unknown
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
components: rustfmt, clippy
|
||||
override: true
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
args: --all-features --examples --target wasm32-unknown-unknown
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-features --target wasm32-unknown-unknown
|
||||
- uses: actions-rs/install@v0.1
|
||||
with:
|
||||
crate: cargo-make
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: make
|
||||
args: build-web-example
|
||||
|
||||
publish:
|
||||
name: Publish
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [check, check_web]
|
||||
env:
|
||||
CARGO_TOKEN: ${{ secrets.CARGO_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
target: wasm32-unknown-unknown
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
- uses: actions/checkout@v4
|
||||
- run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libspeechd-dev
|
||||
cargo login $CARGO_TOKEN
|
||||
cargo publish --no-default-features
|
||||
rustup toolchain install stable
|
||||
cargo publish
|
||||
|
|
117
.github/workflows/test.yml
vendored
117
.github/workflows/test.yml
vendored
|
@ -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,106 +23,40 @@ jobs:
|
|||
os: [windows-latest, ubuntu-22.04, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- uses: actions/checkout@v4
|
||||
- run: sudo apt-get update; sudo apt-get install -y libspeechd-dev
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
components: rustfmt, clippy
|
||||
override: true
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
args: --all-features --examples
|
||||
if: ${{ runner.os != 'Linux' }}
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
args: --no-default-features --examples
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all --check
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-features
|
||||
if: ${{ runner.os != 'Linux' }}
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --no-default-features
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
- run: |
|
||||
rustup toolchain install stable
|
||||
cargo clippy --all-targets
|
||||
|
||||
check_web:
|
||||
name: Check Web
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
target: wasm32-unknown-unknown
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
components: rustfmt, clippy
|
||||
override: true
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
args: --all-features --examples --target wasm32-unknown-unknown
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all --check
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-features --target wasm32-unknown-unknown
|
||||
- uses: actions/checkout@v4
|
||||
- run: |
|
||||
rustup target add wasm32-unknown-unknown
|
||||
rustup toolchain install stable
|
||||
cargo clippy --all-targets --target wasm32-unknown-unknown
|
||||
|
||||
check_android:
|
||||
name: Check Android
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
components: rustfmt, clippy
|
||||
override: true
|
||||
- uses: actions-rs/install@v0.1
|
||||
with:
|
||||
crate: cargo-apk
|
||||
# use-tool-cache: true
|
||||
- run: rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: apk
|
||||
args: build
|
||||
- uses: actions/checkout@v4
|
||||
- run: |
|
||||
rustup target add aarch64-linux-android
|
||||
rustup toolchain install stable
|
||||
cargo clippy --all-targets --target aarch64-linux-android
|
||||
|
||||
check_web_example:
|
||||
name: Check Web Example
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
target: wasm32-unknown-unknown
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
components: rustfmt, clippy
|
||||
override: true
|
||||
- uses: actions-rs/install@v0.1
|
||||
with:
|
||||
crate: cargo-make
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: make
|
||||
args: build-web-example
|
||||
- uses: actions/checkout@v4
|
||||
- run: |
|
||||
rustup target add wasm32-unknown-unknown
|
||||
rustup toolchain install stable
|
||||
cd examples/web
|
||||
cargo build --target wasm32-unknown-unknown
|
||||
|
|
35
Cargo.toml
35
Cargo.toml
|
@ -1,9 +1,10 @@
|
|||
[package]
|
||||
name = "tts"
|
||||
version = "0.24.0"
|
||||
version = "0.26.3"
|
||||
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"
|
||||
|
@ -12,27 +13,35 @@ edition = "2021"
|
|||
crate-type = ["lib", "cdylib", "staticlib"]
|
||||
|
||||
[features]
|
||||
speech_dispatcher_0_9 = ["speech-dispatcher/0_9"]
|
||||
speech_dispatcher_0_10 = ["speech-dispatcher/0_10"]
|
||||
speech_dispatcher_0_11 = ["speech-dispatcher/0_11"]
|
||||
default = ["speech_dispatcher_0_11"]
|
||||
|
||||
[dependencies]
|
||||
dyn-clonable = "0.9"
|
||||
oxilangtag = "0.1"
|
||||
lazy_static = "1"
|
||||
log = "0.4"
|
||||
serde = { version = "1", optional = true, features = ["derive"] }
|
||||
thiserror = "1"
|
||||
unic-langid = "0.9.0"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.9"
|
||||
env_logger = "0.11"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
tolk = { version = "0.5", optional = true }
|
||||
windows = { version = "0.39", features = ["Foundation", "Foundation_Collections", "Media_Core", "Media_Playback", "Media_SpeechSynthesis", "Storage_Streams"] }
|
||||
windows = { version = "0.58", features = [
|
||||
"Foundation",
|
||||
"Foundation_Collections",
|
||||
"Media_Core",
|
||||
"Media_Playback",
|
||||
"Media_SpeechSynthesis",
|
||||
"Storage_Streams",
|
||||
] }
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
speech-dispatcher = { version = "0.15", default-features = false }
|
||||
speech-dispatcher = { version = "0.16", default-features = false }
|
||||
|
||||
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
|
||||
cocoa-foundation = "0.1"
|
||||
|
@ -42,11 +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.19"
|
||||
ndk-glue = "0.6"
|
||||
jni = "0.21"
|
||||
ndk-context = "0.1"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
no-default-features = true
|
||||
features = ["speech_dispatcher_0_11"]
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
plugins {
|
||||
id "com.android.application"
|
||||
id "kotlin-android"
|
||||
id "org.mozilla.rust-android-gradle.rust-android"
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion "30.0.3"
|
||||
|
||||
namespace "rs.tts"
|
||||
compileSdkVersion 33
|
||||
ndkVersion "25.1.8937393"
|
||||
defaultConfig {
|
||||
applicationId "rs.tts"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 30
|
||||
targetSdkVersion 33
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
|
@ -21,27 +21,27 @@ android {
|
|||
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation "androidx.core:core-ktx:1.2.0"
|
||||
implementation "androidx.annotation:annotation:1.1.0"
|
||||
implementation "com.google.android.material:material:1.1.0"
|
||||
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
|
||||
}
|
||||
|
||||
apply plugin: "com.github.willir.rust.cargo-ndk-android"
|
||||
apply plugin: "org.mozilla.rust-android-gradle.rust-android"
|
||||
|
||||
cargoNdk {
|
||||
cargo {
|
||||
module = "."
|
||||
libname = "tts"
|
||||
targets = ["arm", "x86"]
|
||||
}
|
||||
|
||||
tasks.whenTaskAdded { task ->
|
||||
if ((task.name == 'javaPreCompileDebug' || task.name == 'javaPreCompileRelease')) {
|
||||
task.dependsOn "cargoBuild"
|
||||
}
|
||||
}
|
||||
|
||||
project.afterEvaluate {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="rs.tts">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application android:allowBackup="true" android:label="@string/app_name">
|
||||
<activity android:name=".MainActivity">
|
||||
<activity android:name=".MainActivity" android:exported="true">
|
||||
<meta-data android:name="android.app.lib_name" android:value="hello_world" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = "1.3.72"
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
maven {
|
||||
url "https://plugins.gradle.org/m2/"
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath "com.android.tools.build:gradle:4.1.1"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath "gradle.plugin.com.github.willir.rust:plugin:0.3.4"
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "com.android.application" version "7.3.0" apply false
|
||||
id "com.android.library" version "7.3.0" apply false
|
||||
id "org.jetbrains.kotlin.android" version "1.7.21" apply false
|
||||
id "org.mozilla.rust-android-gradle.rust-android" version "0.9.3" apply false
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
name = "hello_world"
|
||||
version = "0.1.0"
|
||||
authors = ["Nolan Darilek <nolan@thewordnerd.info>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -10,5 +10,5 @@ edition = "2018"
|
|||
crate-type = ["dylib"]
|
||||
|
||||
[dependencies]
|
||||
ndk-glue = "0.6"
|
||||
ndk-glue = "0.7"
|
||||
tts = { path = "../.." }
|
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
|
||||
|
|
|
@ -1 +1,8 @@
|
|||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
include ":app"
|
|
@ -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;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
name = "web"
|
||||
version = "0.1.0"
|
||||
authors = ["Nolan Darilek <nolan@thewordnerd.info>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -11,4 +11,3 @@ console_log = "0.2"
|
|||
log = "0.4"
|
||||
seed = "0.9"
|
||||
tts = { path = "../.." }
|
||||
wasm-bindgen = "0.2"
|
|
@ -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();
|
||||
|
@ -179,22 +179,23 @@ impl Android {
|
|||
let id = BackendId::Android(bid);
|
||||
*backend_id += 1;
|
||||
drop(backend_id);
|
||||
let native_activity = ndk_glue::native_activity();
|
||||
let vm = Self::vm()?;
|
||||
let env = vm.attach_current_thread_permanently()?;
|
||||
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 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",
|
||||
&[native_activity.activity().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();
|
||||
|
@ -229,9 +230,8 @@ impl Android {
|
|||
}
|
||||
|
||||
fn vm() -> Result<JavaVM, jni::errors::Error> {
|
||||
let native_activity = ndk_glue::native_activity();
|
||||
let vm_ptr = native_activity.vm();
|
||||
unsafe { jni::JavaVM::from_raw(vm_ptr) }
|
||||
let ctx = ndk_context::android_context();
|
||||
unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()?;
|
||||
|
|
|
@ -46,16 +46,18 @@ impl AppKit {
|
|||
) {
|
||||
unsafe {
|
||||
let strings: id = *this.get_ivar("strings");
|
||||
let str: id = msg_send!(strings, firstObject);
|
||||
let _: () = msg_send![str, release];
|
||||
let _: () = msg_send!(strings, removeObjectAtIndex:0);
|
||||
let count: u32 = msg_send![strings, count];
|
||||
if count > 0 {
|
||||
let str: id = msg_send!(strings, firstObject);
|
||||
let _: () = msg_send![str, release];
|
||||
let _: () = msg_send!(strings, removeObjectAtIndex:0);
|
||||
if count > 1 {
|
||||
let str: id = msg_send!(strings, firstObject);
|
||||
let _: BOOL = msg_send![synth, startSpeakingString: str];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
decl.add_method(
|
||||
sel!(speechSynthesizer:didFinishSpeaking:),
|
||||
speech_synthesizer_did_finish_speaking
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
use std::{str::FromStr, sync::Mutex};
|
||||
use std::sync::Mutex;
|
||||
|
||||
use cocoa_foundation::base::{id, nil, NO};
|
||||
use cocoa_foundation::foundation::NSString;
|
||||
use core_foundation::array::CFArray;
|
||||
use core_foundation::base::TCFType;
|
||||
use core_foundation::string::CFString;
|
||||
use lazy_static::lazy_static;
|
||||
use log::{info, trace};
|
||||
use objc::runtime::{Object, Sel};
|
||||
use objc::{class, declare::ClassDecl, msg_send, sel, sel_impl};
|
||||
use unic_langid::LanguageIdentifier;
|
||||
use oxilangtag::LanguageTag;
|
||||
|
||||
use crate::{Backend, BackendId, Error, Features, Gender, UtteranceId, Voice, CALLBACKS};
|
||||
|
||||
|
@ -290,21 +291,28 @@ impl Backend for AvFoundation {
|
|||
}
|
||||
|
||||
fn voices(&self) -> Result<Vec<Voice>, Error> {
|
||||
let voices: CFArray = unsafe { msg_send![class!(AVSpeechSynthesisVoice), speechVoices] };
|
||||
let voices: CFArray = unsafe {
|
||||
CFArray::wrap_under_get_rule(msg_send![class!(AVSpeechSynthesisVoice), speechVoices])
|
||||
};
|
||||
let rv = voices
|
||||
.iter()
|
||||
.map(|v| {
|
||||
let id: CFString = unsafe { msg_send![*v as *const Object, identifier] };
|
||||
let name: CFString = unsafe { msg_send![*v as *const Object, name] };
|
||||
let id: CFString = unsafe {
|
||||
CFString::wrap_under_get_rule(msg_send![*v as *const Object, identifier])
|
||||
};
|
||||
let name: CFString =
|
||||
unsafe { CFString::wrap_under_get_rule(msg_send![*v as *const Object, name]) };
|
||||
let gender: i64 = unsafe { msg_send![*v as *const Object, gender] };
|
||||
let gender = match gender {
|
||||
1 => Some(Gender::Male),
|
||||
2 => Some(Gender::Female),
|
||||
_ => None,
|
||||
};
|
||||
let language: CFString = unsafe { msg_send![*v as *const Object, language] };
|
||||
let language: CFString = unsafe {
|
||||
CFString::wrap_under_get_rule(msg_send![*v as *const Object, language])
|
||||
};
|
||||
let language = language.to_string();
|
||||
let language = LanguageIdentifier::from_str(&language).unwrap();
|
||||
let language = LanguageTag::parse(language).unwrap();
|
||||
Voice {
|
||||
id: id.to_string(),
|
||||
name: name.to_string(),
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#[cfg(target_os = "linux")]
|
||||
use std::{collections::HashMap, str::FromStr, sync::Mutex};
|
||||
use std::{collections::HashMap, sync::Mutex};
|
||||
|
||||
use lazy_static::*;
|
||||
use log::{info, trace};
|
||||
use oxilangtag::LanguageTag;
|
||||
use speech_dispatcher::*;
|
||||
use unic_langid::LanguageIdentifier;
|
||||
|
||||
use crate::{Backend, BackendId, Error, Features, UtteranceId, Voice, CALLBACKS};
|
||||
|
||||
|
@ -12,8 +12,8 @@ use crate::{Backend, BackendId, Error, Features, UtteranceId, Voice, CALLBACKS};
|
|||
pub(crate) struct SpeechDispatcher(Connection);
|
||||
|
||||
lazy_static! {
|
||||
static ref SPEAKING: Mutex<HashMap<u64, bool>> = {
|
||||
let m: HashMap<u64, bool> = HashMap::new();
|
||||
static ref SPEAKING: Mutex<HashMap<usize, bool>> = {
|
||||
let m: HashMap<usize, bool> = HashMap::new();
|
||||
Mutex::new(m)
|
||||
};
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ impl SpeechDispatcher {
|
|||
let mut callbacks = CALLBACKS.lock().unwrap();
|
||||
let backend_id = BackendId::SpeechDispatcher(client_id);
|
||||
let cb = callbacks.get_mut(&backend_id).unwrap();
|
||||
let utterance_id = UtteranceId::SpeechDispatcher(msg_id);
|
||||
let utterance_id = UtteranceId::SpeechDispatcher(msg_id as u64);
|
||||
if let Some(f) = cb.utterance_begin.as_mut() {
|
||||
f(utterance_id);
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ impl SpeechDispatcher {
|
|||
let mut callbacks = CALLBACKS.lock().unwrap();
|
||||
let backend_id = BackendId::SpeechDispatcher(client_id);
|
||||
let cb = callbacks.get_mut(&backend_id).unwrap();
|
||||
let utterance_id = UtteranceId::SpeechDispatcher(msg_id);
|
||||
let utterance_id = UtteranceId::SpeechDispatcher(msg_id as u64);
|
||||
if let Some(f) = cb.utterance_end.as_mut() {
|
||||
f(utterance_id);
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ impl SpeechDispatcher {
|
|||
let mut callbacks = CALLBACKS.lock().unwrap();
|
||||
let backend_id = BackendId::SpeechDispatcher(client_id);
|
||||
let cb = callbacks.get_mut(&backend_id).unwrap();
|
||||
let utterance_id = UtteranceId::SpeechDispatcher(msg_id);
|
||||
let utterance_id = UtteranceId::SpeechDispatcher(msg_id as u64);
|
||||
if let Some(f) = cb.utterance_stop.as_mut() {
|
||||
f(utterance_id);
|
||||
}
|
||||
|
@ -188,11 +188,12 @@ 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(),
|
||||
gender: None,
|
||||
language: LanguageIdentifier::from_str(&v.language).unwrap(),
|
||||
language: LanguageTag::parse(v.language.clone()).unwrap(),
|
||||
})
|
||||
.collect::<Vec<Voice>>();
|
||||
Ok(rv)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#[cfg(target_arch = "wasm32")]
|
||||
use std::{str::FromStr, sync::Mutex};
|
||||
use std::sync::Mutex;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use log::{info, trace};
|
||||
use unic_langid::LanguageIdentifier;
|
||||
use oxilangtag::LanguageTag;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{
|
||||
|
@ -248,7 +248,7 @@ impl Backend for Web {
|
|||
return Ok(());
|
||||
}
|
||||
}
|
||||
return Err(Error::OperationFailed);
|
||||
Err(Error::OperationFailed)
|
||||
} else {
|
||||
Err(Error::NoneError)
|
||||
}
|
||||
|
@ -264,7 +264,7 @@ impl Drop for Web {
|
|||
|
||||
impl From<SpeechSynthesisVoice> for Voice {
|
||||
fn from(other: SpeechSynthesisVoice) -> Self {
|
||||
let language = LanguageIdentifier::from_str(&other.lang()).unwrap();
|
||||
let language = LanguageTag::parse(other.lang()).unwrap();
|
||||
Voice {
|
||||
id: other.voice_uri(),
|
||||
name: other.name(),
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
#[cfg(windows)]
|
||||
use std::{
|
||||
collections::{HashMap, VecDeque},
|
||||
str::FromStr,
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use log::{info, trace};
|
||||
use unic_langid::LanguageIdentifier;
|
||||
use oxilangtag::LanguageTag;
|
||||
use windows::{
|
||||
Foundation::TypedEventHandler,
|
||||
Media::{
|
||||
|
@ -354,7 +353,7 @@ impl TryInto<Voice> for VoiceInformation {
|
|||
Gender::Female
|
||||
};
|
||||
let language: String = self.Language()?.try_into()?;
|
||||
let language = LanguageIdentifier::from_str(&language).unwrap();
|
||||
let language = LanguageTag::parse(language).unwrap();
|
||||
Ok(Voice {
|
||||
id: self.Id()?.try_into()?,
|
||||
name: self.DisplayName()?.try_into()?,
|
||||
|
|
58
src/lib.rs
58
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"))]
|
||||
|
@ -27,12 +28,12 @@ use lazy_static::lazy_static;
|
|||
use libc::c_char;
|
||||
#[cfg(target_os = "macos")]
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
pub use oxilangtag::LanguageTag;
|
||||
#[cfg(target_os = "linux")]
|
||||
use speech_dispatcher::Error as SpeechDispatcherError;
|
||||
use thiserror::Error;
|
||||
#[cfg(all(windows, feature = "tolk"))]
|
||||
use tolk::Tolk;
|
||||
pub use unic_langid::LanguageIdentifier;
|
||||
|
||||
mod backends;
|
||||
|
||||
|
@ -84,7 +85,7 @@ pub enum BackendId {
|
|||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
AvFoundation(u64),
|
||||
#[cfg(target_os = "linux")]
|
||||
SpeechDispatcher(u64),
|
||||
SpeechDispatcher(usize),
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
Web(u64),
|
||||
#[cfg(windows)]
|
||||
|
@ -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);
|
||||
|
@ -659,7 +661,7 @@ pub struct Voice {
|
|||
pub(crate) id: String,
|
||||
pub(crate) name: String,
|
||||
pub(crate) gender: Option<Gender>,
|
||||
pub(crate) language: LanguageIdentifier,
|
||||
pub(crate) language: LanguageTag<String>,
|
||||
}
|
||||
|
||||
impl Voice {
|
||||
|
@ -675,7 +677,7 @@ impl Voice {
|
|||
self.gender
|
||||
}
|
||||
|
||||
pub fn language(&self) -> LanguageIdentifier {
|
||||
pub fn language(&self) -> LanguageTag<String> {
|
||||
self.language.clone()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user