Merge branch 'master' into feature/voices

This commit is contained in:
francois-caddet 2022-03-20 13:02:37 +01:00
commit 88f4598ec6
54 changed files with 2181 additions and 445 deletions

View File

@ -1,81 +1,103 @@
name: Release
on:
push:
tags:
- "v*"
jobs:
build_linux:
name: Build Linux
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: |
sudo apt-get update
sudo apt-get install -y libspeechd-dev
rustup update
cargo build --release
rustup target add wasm32-unknown-unknown
cargo build --release --target wasm32-unknown-unknown
build_windows:
name: Build Windows
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- run: |
choco install -y llvm
rustup update
cargo build --release
build_macos:
name: Build MacOS
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- run: |
rustup update
cargo build --release
build_ios:
name: Build iOS
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- run: |
rustup update
rustup target add aarch64-apple-ios x86_64-apple-ios
cargo install cargo-lipo
cargo lipo --release
publish_winrt_bindings:
name: Publish winrt_bindings
runs-on: windows-latest
needs: [build_windows]
env:
CARGO_TOKEN: ${{ secrets.CARGO_TOKEN }}
steps:
- uses: actions/checkout@v2
- run: |
choco install -y llvm
rustup update
cargo login $CARGO_TOKEN
cd winrt_bindings
cargo package
cargo publish || true
publish:
name: Publish
runs-on: ubuntu-latest
needs: [build_linux, build_windows, build_macos, build_ios]
env:
CARGO_TOKEN: ${{ secrets.CARGO_TOKEN }}
steps:
- uses: actions/checkout@v2
- run: |
sudo apt-get update
sudo apt-get install -y libspeechd-dev
rustup update
cargo login $CARGO_TOKEN
cargo publish
name: Release
on:
push:
tags:
- "v*"
jobs:
check:
name: Check
strategy:
matrix:
os: [windows-latest, ubuntu-latest, 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-latest
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-latest
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
- run: |
sudo apt-get update
sudo apt-get install -y libspeechd-dev
cargo login $CARGO_TOKEN
cargo publish --no-default-features

View File

@ -1,49 +1,117 @@
name: Test
on:
push:
pull_request:
jobs:
build_linux:
name: Build Linux
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: |
sudo apt-get update
sudo apt-get install -y libspeechd-dev
rustup update
cargo build --release
rustup target add wasm32-unknown-unknown
cargo build --release --target wasm32-unknown-unknown
build_windows:
name: Build Windows
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- run: |
choco install -y llvm
rustup update
cargo build --release
build_macos:
name: Build MacOS
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- run: |
rustup update
cargo build --release
build_ios:
name: Build iOS
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- run: |
rustup update
rustup target add aarch64-apple-ios x86_64-apple-ios
cargo install cargo-lipo
cargo lipo --release
name: Test
on:
push:
pull_request:
jobs:
check:
name: Check
strategy:
matrix:
os: [windows-latest, ubuntu-latest, 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-latest
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
check_android:
name: Check Android
runs-on: ubuntu-latest
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
check_web_example:
name: Check Web Example
runs-on: ubuntu-latest
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

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
Cargo.lock
target
*.dll

View File

@ -1,48 +1,57 @@
[package]
name = "tts"
version = "0.8.0"
version = "0.21.1"
authors = ["Nolan Darilek <nolan@thewordnerd.info>"]
repository = "https://github.com/ndarilek/tts-rs"
description = "High-level Text-To-Speech (TTS) interface"
license = "MIT"
exclude = ["*.cfg", "*.yml"]
edition = "2018"
edition = "2021"
[package.metadata.patch.speech-dispatcher]
version = "0.7.0"
patches = [
"speech-dispatcher.patch"
]
#patches = [
# "speech-dispatcher.patch"
#]
[patch.crates-io]
speech-dispatcher = { path = './target/patch/speech-dispatcher-0.7.0'}
#[patch.crates-io]
#speech-dispatcher = { path = './target/patch/speech-dispatcher-0.7.0'}
[lib]
crate-type = ["lib", "cdylib", "staticlib"]
[features]
speech_dispatcher_0_10 = ["speech-dispatcher/0_10"]
default = ["speech_dispatcher_0_10"]
[dependencies]
dyn-clonable = "0.9"
lazy_static = "1"
log = "0.4"
thiserror = "1"
unic-langid = "0.9.0"
serde = { version = "1.0", optional = true, features = ["derive"] }
[dev-dependencies]
env_logger = "0.7"
env_logger = "0.9"
[target.'cfg(windows)'.dependencies]
tolk = ">= 0.2.1"
winrt = "0.7"
tts_winrt_bindings = { version = "0.1", path="winrt_bindings" }
tolk = { version = "0.5", optional = true }
windows = { version = "0.34", features = ["alloc", "Foundation", "Media_Core", "Media_Playback", "Media_SpeechSynthesis", "Storage_Streams"] }
[target.'cfg(target_os = "linux")'.dependencies]
speech-dispatcher = "0.7"
speech-dispatcher = { version = "0.13", default-features = false }
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
cocoa-foundation = "0.1"
core-foundation = "0.9"
libc = "0.2"
objc = "0.2"
objc = { version = "0.2", features = ["exception"] }
[target.wasm32-unknown-unknown.dependencies]
wasm-bindgen = "0.2"
web-sys = { version = "0.3", features = ["EventTarget", "SpeechSynthesis", "SpeechSynthesisEvent", "SpeechSynthesisUtterance", "Window", ] }
web-sys = { version = "0.3", features = ["EventTarget", "SpeechSynthesis", "SpeechSynthesisErrorCode", "SpeechSynthesisErrorEvent", "SpeechSynthesisEvent", "SpeechSynthesisUtterance", "Window", ] }
[target.'cfg(target_os="android")'.dependencies]
jni = "0.19"
ndk-glue = "0.6"

33
Makefile.toml Normal file
View File

@ -0,0 +1,33 @@
[tasks.build-android-example]
script = [
"cd examples/android",
"./gradlew assembleDebug",
]
[tasks.run-android-example]
script = [
"cd examples/android",
"./gradlew runDebug",
]
[tasks.log-android]
command = "adb"
args = ["logcat", "RustStdoutStderr:D", "*:S"]
[tasks.install-trunk]
install_crate = { crate_name = "trunk", binary = "trunk", test_arg = "--help" }
[tasks.install-wasm-bindgen-cli]
install_crate = { crate_name = "wasm-bindgen-cli", binary = "wasm-bindgen", test_arg = "--help" }
[tasks.build-web-example]
dependencies = ["install-trunk", "install-wasm-bindgen-cli"]
cwd = "examples/web"
command = "trunk"
args = ["build"]
[tasks.run-web-example]
dependencies = ["install-trunk", "install-wasm-bindgen-cli"]
cwd = "examples/web"
command = "trunk"
args = ["serve"]

View File

@ -3,10 +3,23 @@
This library provides a high-level Text-To-Speech (TTS) interface supporting various backends. Currently supported backends are:
* Windows
* Screen readers/SAPI via Tolk
* Screen readers/SAPI via Tolk (requires `tolk` Cargo feature)
* WinRT
* Linux via [Speech Dispatcher](https://freebsoft.org/speechd)
* MacOS
* MacOS/iOS
* AppKit on MacOS 10.13 and below
* AVFoundation on MacOS 10.14 and above, and iOS
* Android
* WebAssembly
## Android Setup
On most platforms, this library is plug-and-play. Because of JNI's complexity, Android setup is a bit more involved. In general, look to the Android example for guidance. Here are some rough steps to get going:
* Set up _Cargo.toml_ as the example does. Be sure to depend on `ndk-glue`.
* Place _Bridge.java_ appropriately in your app. This is needed to support various Android TTS callbacks.
* Create a main activity similar to _MainActivity.kt_. In particular, you need to derive `android.app.NativeActivity`, and you need a `System.loadLibrary(...)` call appropriate for your app. `System.loadLibrary(...)` is needed to trigger `JNI_OnLoad`.
* * Even though you've loaded the library in your main activity, add a metadata tag to your activity in _AndroidManifest.xml_ referencing it. Yes, this is redundant but necessary.
* Set if your various build.gradle scripts to reference the plugins, dependencies, etc. from the example. In particular, you'll want to set up [cargo-ndk-android-gradle](https://github.com/willir/cargo-ndk-android-gradle/) and either [depend on androidx.annotation](https://developer.android.com/reference/androidx/annotation/package-summary) or otherwise configure your app to keep the class _rs.tts.Bridge_.
And I think that should about do it. Good luck!

View File

@ -1,7 +1,10 @@
fn main() {
if std::env::var("TARGET").unwrap().contains("-apple") {
println!("cargo:rustc-link-lib=framework=AVFoundation");
if !std::env::var("CARGO_CFG_TARGET_OS").unwrap().contains("ios") {
if !std::env::var("CARGO_CFG_TARGET_OS")
.unwrap()
.contains("ios")
{
println!("cargo:rustc-link-lib=framework=AppKit");
}
}

39
examples/99bottles.rs Normal file
View File

@ -0,0 +1,39 @@
use std::io;
use std::{thread, time};
#[cfg(target_os = "macos")]
use cocoa_foundation::base::id;
#[cfg(target_os = "macos")]
use cocoa_foundation::foundation::NSRunLoop;
#[cfg(target_os = "macos")]
use objc::{msg_send, sel, sel_impl};
use tts::*;
fn main() -> Result<(), Error> {
env_logger::init();
let mut tts = Tts::default()?;
let mut bottles = 99;
while bottles > 0 {
tts.speak(format!("{} bottles of beer on the wall,", bottles), false)?;
tts.speak(format!("{} bottles of beer,", bottles), false)?;
tts.speak("Take one down, pass it around", false)?;
tts.speak("Give us a bit to drink this...", false)?;
let time = time::Duration::from_secs(15);
thread::sleep(time);
bottles -= 1;
tts.speak(format!("{} bottles of beer on the wall,", bottles), 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.
// It shouldn't be needed in an app or game that almost certainly has one already.
#[cfg(target_os = "macos")]
{
let run_loop: id = unsafe { NSRunLoop::currentRunLoop() };
unsafe {
let _: () = msg_send![run_loop, run];
}
}
io::stdin().read_line(&mut _input)?;
Ok(())
}

16
examples/android/.gitignore vendored Normal file
View File

@ -0,0 +1,16 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
Cargo.lock

3
examples/android/.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="1.6" />
</component>
</project>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="PLATFORM" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="11" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
<option name="useQualifiedModuleNames" value="true" />
</GradleProjectSettings>
</option>
</component>
</project>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="BintrayJCenter" />
<option name="name" value="BintrayJCenter" />
<option name="url" value="https://jcenter.bintray.com/" />
</remote-repository>
<remote-repository>
<option name="id" value="Google" />
<option name="name" value="Google" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
</component>
</project>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" default="false" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
</component>
</project>

1
examples/android/app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

View File

@ -0,0 +1,56 @@
plugins {
id "com.android.application"
id "kotlin-android"
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "rs.tts"
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
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"
cargoNdk {
module = "."
}
project.afterEvaluate {
android.applicationVariants.all { variant ->
task "run${variant.name.capitalize()}"(type: Exec, dependsOn: "install${variant.name.capitalize()}", group: "run") {
commandLine = ["adb", "shell", "monkey", "-p", variant.applicationId + " 1"]
doLast {
println "Launching ${variant.applicationId}"
}
}
}
}

21
examples/android/app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1 @@
jniLibs

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="rs.tts">
<application android:allowBackup="true" android:label="@string/app_name">
<activity android:name=".MainActivity">
<meta-data android:name="android.app.lib_name" android:value="hello_world" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1,24 @@
package rs.tts;
import android.speech.tts.TextToSpeech;
import android.speech.tts.UtteranceProgressListener;
@androidx.annotation.Keep
public class Bridge extends UtteranceProgressListener implements TextToSpeech.OnInitListener {
public int backendId;
public Bridge(int backendId) {
this.backendId = backendId;
}
public native void onInit(int status);
public native void onStart(String utteranceId);
public native void onStop(String utteranceId, Boolean interrupted);
public native void onDone(String utteranceId);
public native void onError(String utteranceId) ;
}

View File

@ -0,0 +1,11 @@
package rs.tts
import android.app.NativeActivity
class MainActivity : NativeActivity() {
companion object {
init {
System.loadLibrary("hello_world")
}
}
}

View File

@ -0,0 +1,3 @@
<resources>
<string name="app_name">TTS-RS</string>
</resources>

View File

@ -0,0 +1,29 @@
// 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()
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
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View File

@ -0,0 +1,14 @@
[package]
name = "hello_world"
version = "0.1.0"
authors = ["Nolan Darilek <nolan@thewordnerd.info>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["dylib"]
[dependencies]
ndk-glue = "0.6"
tts = { path = "../.." }

View File

@ -0,0 +1,21 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official

Binary file not shown.

View File

@ -0,0 +1,6 @@
#Mon Dec 28 17:32:22 CST 2020
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

172
examples/android/gradlew vendored Executable file
View File

@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
examples/android/gradlew.bat vendored Normal file
View File

@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -0,0 +1 @@
include ":app"

View File

@ -0,0 +1,70 @@
use tts::*;
// The `loop {}` below only simulates an app loop.
// Without it, the `TTS` instance gets dropped before callbacks can run.
#[allow(unreachable_code)]
fn run() -> Result<(), Error> {
let mut tts = Tts::default()?;
let Features {
utterance_callbacks,
..
} = tts.supported_features();
if utterance_callbacks {
tts.on_utterance_begin(Some(Box::new(|utterance| {
println!("Started speaking {:?}", utterance)
})))?;
tts.on_utterance_end(Some(Box::new(|utterance| {
println!("Finished speaking {:?}", utterance)
})))?;
tts.on_utterance_stop(Some(Box::new(|utterance| {
println!("Stopped speaking {:?}", utterance)
})))?;
}
let Features { is_speaking, .. } = tts.supported_features();
if is_speaking {
println!("Are we speaking? {}", tts.is_speaking()?);
}
tts.speak("Hello, world.", false)?;
let Features { rate, .. } = tts.supported_features();
if rate {
let original_rate = tts.get_rate()?;
tts.speak(format!("Current rate: {}", original_rate), false)?;
tts.set_rate(tts.max_rate())?;
tts.speak("This is very fast.", false)?;
tts.set_rate(tts.min_rate())?;
tts.speak("This is very slow.", false)?;
tts.set_rate(tts.normal_rate())?;
tts.speak("This is the normal rate.", false)?;
tts.set_rate(original_rate)?;
}
let Features { pitch, .. } = tts.supported_features();
if pitch {
let original_pitch = tts.get_pitch()?;
tts.set_pitch(tts.max_pitch())?;
tts.speak("This is high-pitch.", false)?;
tts.set_pitch(tts.min_pitch())?;
tts.speak("This is low pitch.", false)?;
tts.set_pitch(tts.normal_pitch())?;
tts.speak("This is normal pitch.", false)?;
tts.set_pitch(original_pitch)?;
}
let Features { volume, .. } = tts.supported_features();
if volume {
let original_volume = tts.get_volume()?;
tts.set_volume(tts.max_volume())?;
tts.speak("This is loud!", false)?;
tts.set_volume(tts.min_volume())?;
tts.speak("This is quiet.", false)?;
tts.set_volume(tts.normal_volume())?;
tts.speak("This is normal volume.", false)?;
tts.set_volume(original_volume)?;
}
tts.speak("Goodbye.", false)?;
loop {}
Ok(())
}
#[cfg_attr(target_os = "android", ndk_glue::main(backtrace = "on"))]
pub fn main() {
run().expect("Failed to run");
}

89
examples/clone_drop.rs Normal file
View File

@ -0,0 +1,89 @@
use std::io;
#[cfg(target_os = "macos")]
use cocoa_foundation::base::id;
#[cfg(target_os = "macos")]
use cocoa_foundation::foundation::NSRunLoop;
#[cfg(target_os = "macos")]
use objc::{msg_send, sel, sel_impl};
use tts::*;
fn main() -> Result<(), Error> {
env_logger::init();
let tts = Tts::default()?;
if Tts::screen_reader_available() {
println!("A screen reader is available on this platform.");
} else {
println!("No screen reader is available on this platform.");
}
let Features {
utterance_callbacks,
..
} = tts.supported_features();
if utterance_callbacks {
tts.on_utterance_begin(Some(Box::new(|utterance| {
println!("Started speaking {:?}", utterance)
})))?;
tts.on_utterance_end(Some(Box::new(|utterance| {
println!("Finished speaking {:?}", utterance)
})))?;
tts.on_utterance_stop(Some(Box::new(|utterance| {
println!("Stopped speaking {:?}", utterance)
})))?;
}
let mut tts_clone = tts.clone();
drop(tts);
let Features { is_speaking, .. } = tts_clone.supported_features();
if is_speaking {
println!("Are we speaking? {}", tts_clone.is_speaking()?);
}
tts_clone.speak("Hello, world.", false)?;
let Features { rate, .. } = tts_clone.supported_features();
if rate {
let original_rate = tts_clone.get_rate()?;
tts_clone.speak(format!("Current rate: {}", original_rate), false)?;
tts_clone.set_rate(tts_clone.max_rate())?;
tts_clone.speak("This is very fast.", false)?;
tts_clone.set_rate(tts_clone.min_rate())?;
tts_clone.speak("This is very slow.", false)?;
tts_clone.set_rate(tts_clone.normal_rate())?;
tts_clone.speak("This is the normal rate.", false)?;
tts_clone.set_rate(original_rate)?;
}
let Features { pitch, .. } = tts_clone.supported_features();
if pitch {
let original_pitch = tts_clone.get_pitch()?;
tts_clone.set_pitch(tts_clone.max_pitch())?;
tts_clone.speak("This is high-pitch.", false)?;
tts_clone.set_pitch(tts_clone.min_pitch())?;
tts_clone.speak("This is low pitch.", false)?;
tts_clone.set_pitch(tts_clone.normal_pitch())?;
tts_clone.speak("This is normal pitch.", false)?;
tts_clone.set_pitch(original_pitch)?;
}
let Features { volume, .. } = tts_clone.supported_features();
if volume {
let original_volume = tts_clone.get_volume()?;
tts_clone.set_volume(tts_clone.max_volume())?;
tts_clone.speak("This is loud!", false)?;
tts_clone.set_volume(tts_clone.min_volume())?;
tts_clone.speak("This is quiet.", false)?;
tts_clone.set_volume(tts_clone.normal_volume())?;
tts_clone.speak("This is normal volume.", false)?;
tts_clone.set_volume(original_volume)?;
}
tts_clone.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.
// It shouldn't be needed in an app or game that almost certainly has one already.
#[cfg(target_os = "macos")]
{
let run_loop: id = unsafe { NSRunLoop::currentRunLoop() };
unsafe {
let _: () = msg_send![run_loop, run];
}
}
io::stdin().read_line(&mut _input)?;
Ok(())
}

View File

@ -11,7 +11,12 @@ use tts::*;
fn main() -> Result<(), Error> {
env_logger::init();
let mut tts = TTS::default()?;
let mut tts = Tts::default()?;
if Tts::screen_reader_available() {
println!("A screen reader is available on this platform.");
} else {
println!("No screen reader is available on this platform.");
}
let Features {
utterance_callbacks,
..
@ -23,6 +28,13 @@ fn main() -> Result<(), Error> {
tts.on_utterance_end(Some(Box::new(|utterance| {
println!("Finished speaking {:?}", utterance)
})))?;
tts.on_utterance_stop(Some(Box::new(|utterance| {
println!("Stopped speaking {:?}", utterance)
})))?;
}
let Features { is_speaking, .. } = tts.supported_features();
if is_speaking {
println!("Are we speaking? {}", tts.is_speaking()?);
}
tts.speak("Hello, world.", false)?;
let Features { rate, .. } = tts.supported_features();

14
examples/latency.rs Normal file
View File

@ -0,0 +1,14 @@
use std::io;
use tts::*;
fn main() -> Result<(), Error> {
env_logger::init();
let mut tts = Tts::default()?;
println!("Press Enter and wait for speech.");
loop {
let mut _input = String::new();
io::stdin().read_line(&mut _input)?;
tts.speak("Hello, world.", true)?;
}
}

15
examples/ramble.rs Normal file
View File

@ -0,0 +1,15 @@
use std::{thread, time};
use tts::*;
fn main() -> Result<(), Error> {
env_logger::init();
let mut tts = Tts::default()?;
let mut phrase = 1;
loop {
tts.speak(format!("Phrase {}", phrase), false)?;
let time = time::Duration::from_secs(5);
thread::sleep(time);
phrase += 1;
}
}

View File

@ -0,0 +1,2 @@
[build]
target = "wasm32-unknown-unknown"

1
examples/web/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
dist

11
examples/web/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "web"
version = "0.1.0"
authors = ["Nolan Darilek <nolan@thewordnerd.info>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
seed = "0.8"
tts = { path = "../.." }

12
examples/web/index.html Normal file
View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
</head>
<body>
<div id="app"></div>
</body>
</html>

111
examples/web/src/main.rs Normal file
View File

@ -0,0 +1,111 @@
#![allow(clippy::wildcard_imports)]
use seed::{prelude::*, *};
use tts::Tts;
#[derive(Clone)]
struct Model {
text: String,
tts: Tts,
}
#[derive(Clone)]
enum Msg {
TextChanged(String),
RateChanged(String),
PitchChanged(String),
VolumeChanged(String),
Speak,
}
fn init(_: Url, _: &mut impl Orders<Msg>) -> Model {
let tts = Tts::default().unwrap();
Model {
text: Default::default(),
tts,
}
}
fn update(msg: Msg, model: &mut Model, _: &mut impl Orders<Msg>) {
use Msg::*;
match msg {
TextChanged(text) => model.text = text,
RateChanged