Compare commits

..

No commits in common. "master" and "v0.7.5" have entirely different histories.

13 changed files with 485 additions and 741 deletions

View File

@ -2,42 +2,51 @@ name: Build
on: on:
push: push:
branches: [master]
pull_request: pull_request:
branches: [master]
jobs: jobs:
check: build_linux:
name: Check name: Build Linux
strategy: runs-on: ubuntu-latest
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v2 - 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' }}
- run: | - run: |
choco install -qy llvm sudo apt-get update
echo LIBCLANG_PATH=c:\program` files\llvm\bin >> $Env:GITHUB_ENV sudo apt-get install -y libspeechd-dev
if: ${{ runner.os == 'Windows' }} rustup update
- uses: actions-rs/toolchain@v1 cargo check --release
with:
profile: minimal build_windows:
toolchain: stable name: Build Windows
components: rustfmt, clippy runs-on: windows-latest
override: true steps:
- uses: actions-rs/cargo@v1 - uses: actions/checkout@v2
with: - run: |
command: check choco install -y llvm
args: --all-features --examples rustup update
- uses: actions-rs/cargo@v1 cargo check --release --all-features
with:
command: fmt build_macos:
args: --all -- --check name: Build MacOS
- uses: actions-rs/clippy-check@v1 runs-on: macos-latest
with: steps:
token: ${{ secrets.GITHUB_TOKEN }} - uses: actions/checkout@v2
args: --all-features - run: |
rustup update
cargo check --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
build_android: build_android:
name: Build Android name: Build Android
@ -46,10 +55,5 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with: with:
lfs: true lfs: true
- uses: actions/cache@v2
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
restore-keys: ${{ runner.os }}-gradle
- run: | - run: |
./gradlew assemble ./gradlew assemble

View File

@ -11,87 +11,43 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: Swatinem/rust-cache@v1
- run: | - run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y libspeechd-dev sudo apt-get install -y libspeechd-dev
rustup update
cargo build --release cargo build --release
mkdir linux mv target linux
mv target/release/*.so linux
- uses: actions/upload-artifact@v1 - uses: actions/upload-artifact@v1
with: with:
name: linux name: linux
path: linux path: linux
build_linux_32:
name: Build Linux (32 bits)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: Swatinem/rust-cache@v1
- run: |
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install -y libspeechd-dev:i386 gcc-multilib
rustup target add i686-unknown-linux-gnu
cargo build --release --target i686-unknown-linux-gnu
mkdir linux-32
mv target/i686-unknown-linux-gnu/release/*.so linux-32
- uses: actions/upload-artifact@v1
with:
name: linux-32
path: linux-32
build_windows: build_windows:
name: Build Windows name: Build Windows
runs-on: windows-latest runs-on: windows-latest
env:
LIBCLANG_PATH: c:\program files\llvm\bin
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: Swatinem/rust-cache@v1
- run: | - run: |
choco install -qy llvm choco install -y llvm
cargo build --release rustup update
mkdir windows cargo build --all-features --release
move target\release\*.dll windows move target windows
- uses: actions/upload-artifact@v1 - uses: actions/upload-artifact@v1
with: with:
name: windows name: windows
path: windows path: windows
build_windows_32:
name: Build Windows (32 bits)
runs-on: windows-latest
env:
LIBCLANG_PATH: c:\program files\llvm\bin
steps:
- uses: actions/checkout@v2
- uses: Swatinem/rust-cache@v1
- run: |
choco install -qy llvm
rustup target add i686-pc-windows-msvc
cargo build --release --target i686-pc-windows-msvc
mkdir windows-32
move target\i686-pc-windows-msvc\release\*.dll windows-32
- uses: actions/upload-artifact@v1
with:
name: windows-32
path: windows-32
build_uwp: build_uwp:
name: Build UWP name: Build UWP
runs-on: windows-latest runs-on: windows-latest
env:
LIBCLANG_PATH: c:\program files\llvm\bin
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: Swatinem/rust-cache@v1
- run: | - run: |
choco install -y llvm choco install -y llvm
rustup update
cargo build --release cargo build --release
mkdir uwp move target uwp
move target\release\godot_tts.dll uwp\godot_tts.uwp.dll move uwp\release\godot_tts.dll uwp\release\godot_tts.uwp.dll
- uses: actions/upload-artifact@v1 - uses: actions/upload-artifact@v1
with: with:
name: uwp name: uwp
@ -102,11 +58,10 @@ jobs:
runs-on: macos-latest runs-on: macos-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: Swatinem/rust-cache@v1
- run: | - run: |
rustup update
cargo build --release cargo build --release
mkdir macos mv target macos
mv target/release/*.dylib macos
- uses: actions/upload-artifact@v1 - uses: actions/upload-artifact@v1
with: with:
name: macos name: macos
@ -119,11 +74,6 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with: with:
lfs: true lfs: true
- uses: actions/cache@v2
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
restore-keys: ${{ runner.os }}-gradle
- run: | - run: |
./gradlew assemble ./gradlew assemble
mv build android mv build android
@ -137,13 +87,12 @@ jobs:
runs-on: macos-latest runs-on: macos-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: Swatinem/rust-cache@v1
- run: | - run: |
rustup update
rustup target add aarch64-apple-ios x86_64-apple-ios rustup target add aarch64-apple-ios x86_64-apple-ios
cargo install cargo-lipo cargo install cargo-lipo
cargo lipo --release cargo lipo --release
mkdir ios mv target ios
mv target/universal/release/*.a ios
- uses: actions/upload-artifact@v1 - uses: actions/upload-artifact@v1
with: with:
name: ios name: ios
@ -155,9 +104,7 @@ jobs:
needs: needs:
[ [
build_linux, build_linux,
build_linux_32,
build_windows, build_windows,
build_windows_32,
build_uwp, build_uwp,
build_macos, build_macos,
build_android, build_android,
@ -168,15 +115,9 @@ jobs:
- uses: actions/download-artifact@v1 - uses: actions/download-artifact@v1
with: with:
name: linux name: linux
- uses: actions/download-artifact@v1
with:
name: linux-32
- uses: actions/download-artifact@v1 - uses: actions/download-artifact@v1
with: with:
name: windows name: windows
- uses: actions/download-artifact@v1
with:
name: windows-32
- uses: actions/download-artifact@v1 - uses: actions/download-artifact@v1
with: with:
name: uwp name: uwp
@ -190,15 +131,13 @@ jobs:
with: with:
name: ios name: ios
- run: | - run: |
mkdir -p godot-tts/target/release/32 mkdir -p godot-tts/target/release
cp linux/*.so godot-tts/target/release cp linux/release/*.so godot-tts/target/release
cp linux-32/*.so godot-tts/target/release/32 cp windows/release/*.dll godot-tts/target/release
cp windows/*.dll godot-tts/target/release cp uwp/release/godot_tts.uwp.dll godot-tts/target/release
cp windows-32/*.dll godot-tts/target/release/32 cp macos/release/*.dylib godot-tts/target/release
cp uwp/*.dll godot-tts/target/release
cp macos/*.dylib godot-tts/target/release
cp android/outputs/aar/godot-tts.aar godot-tts/ cp android/outputs/aar/godot-tts.aar godot-tts/
cp ios/*.a godot-tts/target/release cp ios/universal/release/*.a godot-tts/target/release
cp LICENSE godot-tts cp LICENSE godot-tts
cp README.md godot-tts cp README.md godot-tts
cp TTS.gd godot-tts.g* godot-tts cp TTS.gd godot-tts.g* godot-tts

View File

@ -7,7 +7,14 @@ edition = "2018"
[lib] [lib]
crate-type = ["staticlib", "cdylib"] crate-type = ["staticlib", "cdylib"]
[features]
use_tolk = ["tolk", "tts/use_tolk"]
[dependencies] [dependencies]
env_logger = "0.10" env_logger = "0.8"
gdnative = "0.11" gdnative = "0.9"
tts = { version = "0.25", features = ["tolk"] } tts = ">= 0.10.3"
[target.'cfg(windows)'.dependencies]
tolk = { version = ">= 0.2.1", optional = true }

View File

@ -33,10 +33,6 @@ The public-facing API is contained entirely within [TTS.GD](https://github.com/l
Download the [latest release](https://github.com/lightsoutgames/godot-tts/releases). Download the [latest release](https://github.com/lightsoutgames/godot-tts/releases).
## Usage
Inside your projects root folder create a new folder named _addons_. Then extract _godot-tts.zip_ into that folder. After attaching TTS.GD to a node you can make calls to the API.
## Notes on Android ## Notes on Android
Usage on Android is a bit more complicated: Usage on Android is a bit more complicated:

78
TTS.gd
View File

@ -36,71 +36,6 @@ func _ready():
pause_mode = Node.PAUSE_MODE_PROCESS pause_mode = Node.PAUSE_MODE_PROCESS
func _get_min_volume():
if OS.has_feature('JavaScript'):
return 0
else:
return 0
var min_volume setget , _get_min_volume
func _get_max_volume():
if OS.has_feature('JavaScript'):
return 1.0
else:
return 0
var max_volume setget , _get_max_volume
func _get_normal_volume():
if OS.has_feature('JavaScript'):
return 1.0
else:
return 0
var normal_volume setget , _get_normal_volume
var javascript_volume = self.normal_volume
func _set_volume(volume):
if volume < self.min_volume:
volume = self.min_volume
elif volume > self.max_volume:
volume = self.max_volume
if OS.has_feature('JavaScript'):
javascript_volume = volume
func _get_volume():
if OS.has_feature('JavaScript'):
return javascript_volume
else:
return 0
var volume setget _set_volume, _get_volume
func _get_volume_percentage():
return range_lerp(self.volume, self.min_volume, self.max_volume, 0, 100)
func _set_volume_percentage(v):
self.rate = range_lerp(v, 0, 100, self.min_volume, self.max_volume)
var volume_percentage setget _set_volume_percentage, _get_volume_percentage
func _get_normal_volume_percentage():
return range_lerp(self.normal_volume, self.min_volume, self.max_volume, 0, 100)
var normal_volume_percentage setget , _get_normal_volume_percentage
func _get_min_rate(): func _get_min_rate():
if OS.has_feature('JavaScript'): if OS.has_feature('JavaScript'):
return 0.1 return 0.1
@ -197,16 +132,15 @@ func speak(text, interrupt := true):
elif OS.has_feature('JavaScript'): elif OS.has_feature('JavaScript'):
var code = ( var code = (
""" """
let utterance = new SpeechSynthesisUtterance("%s") let utterance = new SpeechSynthesisUtterance("%s")
utterance.rate = %s utterance.rate = %s
utterance.volume = %s """
""" % [text.replace("\n", " "), javascript_rate]
% [text.replace("\n", " "), javascript_rate, javascript_volume]
) )
if interrupt: if interrupt:
code += """ code += """
window.speechSynthesis.cancel() window.speechSynthesis.cancel()
""" """
code += "window.speechSynthesis.speak(utterance)" code += "window.speechSynthesis.speak(utterance)"
JavaScript.eval(code) JavaScript.eval(code)
else: else:

View File

@ -2,17 +2,14 @@
Server.64="res://addons/godot-tts/target/debug/libgodot_tts.so" Server.64="res://addons/godot-tts/target/debug/libgodot_tts.so"
Windows.64="res://addons/godot-tts/target/debug/godot_tts.dll" Windows.64="res://addons/godot-tts/target/debug/godot_tts.dll"
Windows.32="res://addons/godot-tts/target/debug/32/godot_tts.dll"
UWP.64="res://addons/godot-tts/target/debug/godot_tts.dll" UWP.64="res://addons/godot-tts/target/debug/godot_tts.dll"
X11.64="res://addons/godot-tts/target/debug/libgodot_tts.so" X11.64="res://addons/godot-tts/target/debug/libgodot_tts.so"
X11.32="res://addons/godot-tts/target/debug/32/libgodot_tts.so"
OSX.64="res://addons/godot-tts/target/debug/libgodot_tts.dylib" OSX.64="res://addons/godot-tts/target/debug/libgodot_tts.dylib"
IOS="res://addons/godot-tts/target/debug/libgodot_tts.a" IOS="res://addons/godot-tts/target/universal/debug/libgodot_tts.a"
[dependencies] [dependencies]
Windows.64=[ "res://addons/godot-tts/target/debug/nvdaControllerClient64.dll", "res://addons/godot-tts/target/debug/SAAPI64.dll" ] Windows.64=["res://addons/godot-tts/target/debug/nvdaControllerClient64.dll", "res://addons/godot-tts/target/debug/SAAPI64.dll"]
Windows.32=[ "res://addons/godot-tts/target/debug/32/dolapi32.dll", "res://addons/godot-tts/target/debug/32/nvdaControllerClient32.dll", "res://addons/godot-tts/target/debug/32/SAAPI32.dll" ]
[general] [general]

View File

@ -2,17 +2,15 @@
Server.64="res://addons/godot-tts/target/release/libgodot_tts.so" Server.64="res://addons/godot-tts/target/release/libgodot_tts.so"
Windows.64="res://addons/godot-tts/target/release/godot_tts.dll" Windows.64="res://addons/godot-tts/target/release/godot_tts.dll"
Windows.32="res://addons/godot-tts/target/release/32/godot_tts.dll"
UWP.64="res://addons/godot-tts/target/release/godot_tts.uwp.dll" UWP.64="res://addons/godot-tts/target/release/godot_tts.uwp.dll"
X11.64="res://addons/godot-tts/target/release/libgodot_tts.so" X11.64="res://addons/godot-tts/target/release/libgodot_tts.so"
X11.32="res://addons/godot-tts/target/release/32/libgodot_tts.so"
OSX.64="res://addons/godot-tts/target/release/libgodot_tts.dylib" OSX.64="res://addons/godot-tts/target/release/libgodot_tts.dylib"
IOS="res://addons/godot-tts/target/release/libgodot_tts.a" IOS="res://addons/godot-tts/target/release/libgodot_tts.a"
[dependencies] [dependencies]
Windows.64=[ "res://addons/godot-tts/target/release/nvdaControllerClient64.dll", "res://addons/godot-tts/target/release/SAAPI64.dll" ] Windows.64=["res://addons/godot-tts/target/release/nvdaControllerClient64.dll", "res://addons/godot-tts/target/release/SAAPI64.dll"]
Windows.32=[ "res://addons/godot-tts/target/release/32/dolapi32.dll", "res://addons/godot-tts/target/release/32/nvdaControllerClient32.dll", "res://addons/godot-tts/target/release/32/SAAPI32.dll" ] X11.64=[ ]
[general] [general]

Binary file not shown.

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

286
gradlew vendored
View File

@ -1,129 +1,78 @@
#!/bin/sh #!/usr/bin/env sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
############################################################################## ##############################################################################
# ##
# Gradle start up script for POSIX generated by Gradle. ## Gradle start up script for UN*X
# ##
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
############################################################################## ##############################################################################
# Attempt to set APP_HOME # Attempt to set APP_HOME
# Resolve links: $0 may be a link # Resolve links: $0 may be a link
app_path=$0 PRG="$0"
# Need this for relative symlinks.
# Need this for daisy-chained symlinks. while [ -h "$PRG" ] ; do
while ls=`ls -ld "$PRG"`
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path link=`expr "$ls" : '.*-> \(.*\)$'`
[ -h "$app_path" ] if expr "$link" : '/.*' > /dev/null; then
do PRG="$link"
ls=$( ls -ld "$app_path" ) else
link=${ls#*' -> '} PRG=`dirname "$PRG"`"/$link"
case $link in #( fi
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done done
SAVED="`pwd`"
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle" APP_NAME="Gradle"
APP_BASE_NAME=${0##*/} 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. # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS='"-Xmx64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD="maximum"
warn () { warn () {
echo "$*" echo "$*"
} >&2 }
die () { die () {
echo echo
echo "$*" echo "$*"
echo echo
exit 1 exit 1
} >&2 }
# OS specific support (must be 'true' or 'false'). # OS specific support (must be 'true' or 'false').
cygwin=false cygwin=false
msys=false msys=false
darwin=false darwin=false
nonstop=false nonstop=false
case "$( uname )" in #( case "`uname`" in
CYGWIN* ) cygwin=true ;; #( CYGWIN* )
Darwin* ) darwin=true ;; #( cygwin=true
MSYS* | MINGW* ) msys=true ;; #( ;;
NONSTOP* ) nonstop=true ;; Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables # IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java JAVACMD="$JAVA_HOME/jre/sh/java"
else else
JAVACMD=$JAVA_HOME/bin/java JAVACMD="$JAVA_HOME/bin/java"
fi fi
if [ ! -x "$JAVACMD" ] ; then if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@ -132,7 +81,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi fi
else else
JAVACMD=java 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. 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 Please set the JAVA_HOME variable in your environment to match the
@ -140,95 +89,84 @@ location of your Java installation."
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
case $MAX_FD in #( MAX_FD_LIMIT=`ulimit -H -n`
max*) if [ $? -eq 0 ] ; then
MAX_FD=$( ulimit -H -n ) || if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
warn "Could not query maximum file descriptor limit" MAX_FD="$MAX_FD_LIMIT"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi fi
# Roll the args list around exactly as many times as the number of ulimit -n $MAX_FD
# args, so each arg winds up back in the position where it started, but if [ $? -ne 0 ] ; then
# possibly modified. warn "Could not set maximum file descriptor limit: $MAX_FD"
# fi
# NB: a `for` loop captures its iteration list before it begins, so else
# changing the positional parameters here affects neither the number of warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
# iterations, nor the values presented in `arg`. fi
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi fi
# Collect all arguments for the java command; # For Darwin, add options to specify how the application appears in the dock
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of if $darwin; then
# shell script including quotes and variable substitutions, so put them in GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
# double quotes to make sure that they get re-expanded; and fi
# * put everything else in single quotes, so that it's not re-expanded.
set -- \ # For Cygwin, switch paths to Windows format before running java
"-Dorg.gradle.appname=$APP_BASE_NAME" \ if $cygwin ; then
-classpath "$CLASSPATH" \ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
org.gradle.wrapper.GradleWrapperMain \ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
"$@" JAVACMD=`cygpath --unix "$JAVACMD"`
# Use "xargs" to parse quoted args. # We build the pattern for arguments to be converted via cygpath
# ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
# With -n1 it outputs one arg per line, with the quotes and backslashes removed. SEP=""
# for dir in $ROOTDIRSRAW ; do
# In Bash we could simply go: ROOTDIRS="$ROOTDIRS$SEP$dir"
# SEP="|"
# readarray ARGS < <( xargs -n1 <<<"$var" ) && done
# set -- "${ARGS[@]}" "$@" OURCYGPATTERN="(^($ROOTDIRS))"
# # Add a user-defined pattern to the cygpath arguments
# but POSIX shell has neither arrays nor command substitution, so instead we if [ "$GRADLE_CYGPATTERN" != "" ] ; then
# post-process each arg (as a line of input to sed) to backslash-escape any OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
# character that might be a shell metacharacter, then use eval to reverse fi
# that process (while maintaining the separation between arguments), and wrap # Now convert the arguments - kludge to limit ourselves to /bin/sh
# the whole thing up as a single "set" statement. i=0
# for arg in "$@" ; do
# This will of course break if any of these variables contains a newline or CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
# an unmatched quote. CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
#
eval "set -- $( if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
xargs -n1 | else
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | eval `echo args$i`="\"$arg\""
tr '\n' ' ' 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" "$@" exec "$JAVACMD" "$@"

43
gradlew.bat vendored
View File

@ -1,19 +1,3 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off @if "%DEBUG%" == "" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@ -29,18 +13,15 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. @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="-Xmx64m" "-Xms64m" set DEFAULT_JVM_OPTS="-Xmx64m"
@rem Find java.exe @rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute if "%ERRORLEVEL%" == "0" goto init
echo. echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -54,7 +35,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=% set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute if exist "%JAVA_EXE%" goto init
echo. echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@ -64,14 +45,28 @@ echo location of your Java installation.
goto fail 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 :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* "%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 :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell

View File

@ -1,329 +1,275 @@
use std::sync::mpsc::{channel, Receiver}; use std::sync::mpsc::{channel, Receiver};
use gdnative::prelude::*; use gdnative::prelude::*;
use tts::{Features, Tts, UtteranceId}; use tts::{Features, UtteranceId, TTS as Tts};
#[derive(NativeClass)] #[derive(NativeClass)]
struct Utterance(pub(crate) Option<UtteranceId>); #[inherit(Reference)]
struct Utterance(pub(crate) Option<UtteranceId>);
#[methods]
impl Utterance { #[methods]
fn new(_owner: &Reference) -> Self { impl Utterance {
Self(None) fn new(_owner: &Reference) -> Self {
} Self(None)
} }
}
#[allow(clippy::enum_variant_names)]
enum Msg { enum Msg {
UtteranceBegin(UtteranceId), UtteranceBegin(UtteranceId),
UtteranceEnd(UtteranceId), UtteranceEnd(UtteranceId),
UtteranceStop(UtteranceId), UtteranceStop(UtteranceId),
} }
#[allow(clippy::upper_case_acronyms)] #[derive(NativeClass)]
#[derive(NativeClass)] #[inherit(Node)]
#[inherit(Node)] #[register_with(Self::register)]
#[register_with(Self::register)] struct TTS(Tts, Receiver<Msg>);
struct TTS(Tts, Receiver<Msg>);
#[methods]
#[methods] impl TTS {
impl TTS { fn new(owner: &Node) -> Self {
fn new(owner: &Node) -> Self { owner.set_pause_mode(2);
owner.set_pause_mode(2); let tts = Tts::default().unwrap();
let tts = Tts::default().expect("Failed to initialize TTS"); let (tx, rx) = channel();
let (tx, rx) = channel(); let Features {
let Features { utterance_callbacks,
utterance_callbacks, ..
.. } = tts.supported_features();
} = tts.supported_features(); if utterance_callbacks {
if utterance_callbacks { let tx_end = tx.clone();
let tx_end = tx.clone(); let tx_stop = tx.clone();
let tx_stop = tx.clone(); tts.on_utterance_begin(Some(Box::new(move |utterance| {
tts.on_utterance_begin(Some(Box::new(move |utterance| { tx.send(Msg::UtteranceBegin(utterance)).unwrap();
tx.send(Msg::UtteranceBegin(utterance)) })))
.expect("Failed to send UtteranceBegin"); .expect("Failed to set utterance_begin callback");
}))) tts.on_utterance_end(Some(Box::new(move |utterance| {
.expect("Failed to set utterance_begin callback"); tx_end.send(Msg::UtteranceEnd(utterance)).unwrap();
tts.on_utterance_end(Some(Box::new(move |utterance| { })))
tx_end .expect("Failed to set utterance_end callback");
.send(Msg::UtteranceEnd(utterance)) tts.on_utterance_stop(Some(Box::new(move |utterance| {
.expect("Failed to send UtteranceEnd"); tx_stop.send(Msg::UtteranceStop(utterance)).unwrap();
}))) })))
.expect("Failed to set utterance_end callback"); .expect("Failed to set utterance_stop callback");
tts.on_utterance_stop(Some(Box::new(move |utterance| { }
tx_stop Self(tts, rx)
.send(Msg::UtteranceStop(utterance)) }
.expect("Failed to send UtteranceStop");
}))) fn register(builder: &ClassBuilder<Self>) {
.expect("Failed to set utterance_stop callback"); builder
} .add_property("rate")
Self(tts, rx) .with_getter(|this: &TTS, _| match this.0.get_rate() {
} Ok(rate) => rate,
_ => 0.,
fn register(builder: &ClassBuilder<Self>) { })
builder .with_setter(|this: &mut TTS, _, v: f32| {
.property("volume") let Features {
.with_getter(|this: &TTS, _| match this.0.get_volume() { rate: rate_supported,
Ok(volume) => volume, ..
_ => 0., } = this.0.supported_features();
}) if rate_supported {
.with_setter(|this: &mut TTS, _, v: f32| { let mut v = v;
let Features { if v < this.0.min_rate() {
volume: volume_supported, v = this.0.min_rate();
.. } else if v > this.0.max_rate() {
} = this.0.supported_features(); v = this.0.max_rate();
if volume_supported { }
let mut v = v; this.0.set_rate(v).unwrap();
if v < this.0.min_volume() { }
v = this.0.min_volume(); })
} else if v > this.0.max_volume() { .done();
v = this.0.max_volume(); builder
} .add_property("min_rate")
this.0.set_volume(v).expect("Failed to set volume"); .with_getter(|this: &TTS, _| {
} let Features {
}) rate: rate_supported,
.done(); ..
builder } = this.0.supported_features();
.property("min_volume") if rate_supported {
.with_getter(|this: &TTS, _| { this.0.min_rate()
let Features { } else {
volume: volume_supported, 0.
.. }
} = this.0.supported_features(); })
if volume_supported { .done();
this.0.min_volume() builder
} else { .add_property("max_rate")
0. .with_getter(|this: &TTS, _| {
} let Features {
}) rate: rate_supported,
.done(); ..
builder } = this.0.supported_features();
.property("max_volume") if rate_supported {
.with_getter(|this: &TTS, _| { this.0.max_rate()
let Features { } else {
volume: volume_supported, 0.
.. }
} = this.0.supported_features(); })
if volume_supported { .done();
this.0.max_volume() builder
} else { .add_property("normal_rate")
0. .with_getter(|this: &TTS, _| {
} let Features {
}) rate: rate_supported,
.done(); ..
builder } = this.0.supported_features();
.property("normal_volume") if rate_supported {
.with_getter(|this: &TTS, _| { this.0.normal_rate()
let Features { } else {
volume: volume_supported, 0.
.. }
} = this.0.supported_features(); })
if volume_supported { .done();
this.0.normal_volume() builder
} else { .add_property("can_detect_screen_reader")
0. .with_getter(|_: &TTS, _| {
} if cfg!(all(windows, features = "use_tolk")) {
}) true
.done(); } else {
builder false
.property("rate") }
.with_getter(|this: &TTS, _| match this.0.get_rate() { })
Ok(rate) => rate, .done();
_ => 0., #[allow(unreachable_code)]
}) builder
.with_setter(|this: &mut TTS, _, v: f32| { .add_property("has_screen_reader")
let Features { .with_getter(|_: &TTS, _| {
rate: rate_supported, #[cfg(all(windows, features = "use_tolk"))]
.. {
} = this.0.supported_features(); let tolk = tolk::Tolk::new();
if rate_supported { return tolk.detect_screen_reader().is_some();
let mut v = v; }
if v < this.0.min_rate() { false
v = this.0.min_rate(); })
} else if v > this.0.max_rate() { .done();
v = this.0.max_rate(); builder
} .add_property("can_detect_is_speaking")
this.0.set_rate(v).expect("Failed to set rate"); .with_getter(|this: &TTS, _| {
} let Features {
}) is_speaking: is_speaking_supported,
.done(); ..
builder } = this.0.supported_features();
.property("min_rate") return is_speaking_supported;
.with_getter(|this: &TTS, _| { })
let Features { .done();
rate: rate_supported, builder
.. .add_property("is_speaking")
} = this.0.supported_features(); .with_getter(|this: &TTS, _| {
if rate_supported { let Features {
this.0.min_rate() is_speaking: is_speaking_supported,
} else { ..
0. } = this.0.supported_features();
} if is_speaking_supported {
}) return this.0.is_speaking().unwrap();
.done(); } else {
builder return false;
.property("max_rate") }
.with_getter(|this: &TTS, _| { })
let Features { .done();
rate: rate_supported, builder.add_signal(Signal {
.. name: "utterance_begin",
} = this.0.supported_features(); args: &[SignalArgument {
if rate_supported { name: "utterance",
this.0.max_rate() default: Variant::default(),
} else { export_info: ExportInfo::new(VariantType::Object),
0. usage: PropertyUsage::DEFAULT,
} }],
}) });
.done(); builder.add_signal(Signal {
builder name: "utterance_end",
.property("normal_rate") args: &[SignalArgument {
.with_getter(|this: &TTS, _| { name: "utterance",
let Features { default: Variant::default(),
rate: rate_supported, export_info: ExportInfo::new(VariantType::Object),
.. usage: PropertyUsage::DEFAULT,
} = this.0.supported_features(); }],
if rate_supported { });
this.0.normal_rate() builder.add_signal(Signal {
} else { name: "utterance_stop",
0. args: &[SignalArgument {
} name: "utterance",
}) default: Variant::default(),
.done(); export_info: ExportInfo::new(VariantType::Object),
builder usage: PropertyUsage::DEFAULT,
.property("can_detect_screen_reader") }],
.with_getter(|_: &TTS, _| cfg!(windows)) });
.done(); }
builder
.property("has_screen_reader") #[export]
.with_getter(|_, _| Tts::screen_reader_available()) fn speak(&mut self, _owner: &Node, message: GodotString, interrupt: bool) -> Variant {
.done(); let message = message.to_string();
builder if let Ok(id) = self.0.speak(message, interrupt) {
.property("can_detect_is_speaking") let utterance: Instance<Utterance, Unique> = Instance::new();
.with_getter(|this: &TTS, _| { if id.is_some() {
let Features { utterance
is_speaking: is_speaking_supported, .map_mut(|u, _| u.0 = id)
.. .expect("Failed to set utterance ID");
} = this.0.supported_features(); }
is_speaking_supported let utterance = utterance.owned_to_variant();
}) utterance
.done(); } else {
builder Variant::default()
.property("is_speaking") }
.with_getter(|this: &TTS, _| { }
let Features {
is_speaking: is_speaking_supported, #[export]
.. fn stop(&mut self, _owner: &Node) {
} = this.0.supported_features(); self.0.stop().unwrap();
if is_speaking_supported { }
this.0
.is_speaking() #[export]
.expect("Failed to determine if speaking") fn is_rate_supported(&mut self, _owner: &Node) -> bool {
} else { let Features {
false rate: rate_supported,
} ..
}) } = self.0.supported_features();
.done(); rate_supported
builder }
.signal("utterance_begin")
.with_param_custom(SignalParam { #[export]
name: "utterance".into(), fn are_utterance_callbacks_supported(&mut self, _owner: &Node) -> bool {
default: Variant::default(), let Features {
export_info: ExportInfo::new(VariantType::Object), utterance_callbacks: supported,
usage: PropertyUsage::DEFAULT, ..
}) } = self.0.supported_features();
.done(); supported
builder }
.signal("utterance_end")
.with_param_custom(SignalParam { #[export]
name: "utterance".into(), fn _process(&mut self, owner: &Node, _delta: f32) {
default: Variant::default(), if let Some(msg) = self.1.try_recv().ok() {
export_info: ExportInfo::new(VariantType::Object), match msg {
usage: PropertyUsage::DEFAULT, Msg::UtteranceBegin(utterance_id) => {
}) let utterance: Instance<Utterance, Unique> = Instance::new();
.done(); utterance
builder .map_mut(|u, _| u.0 = Some(utterance_id))
.signal("utterance_stop") .expect("Failed to set utterance ID");
.with_param_custom(SignalParam { owner.emit_signal("utterance_begin", &[utterance.owned_to_variant()]);
name: "utterance".into(), }
default: Variant::default(), Msg::UtteranceEnd(utterance_id) => {
export_info: ExportInfo::new(VariantType::Object), let utterance: Instance<Utterance, Unique> = Instance::new();
usage: PropertyUsage::DEFAULT, utterance
}) .map_mut(|u, _| u.0 = Some(utterance_id))
.done(); .expect("Failed to set utterance ID");
} owner.emit_signal("utterance_end", &[utterance.owned_to_variant()]);
}
#[method] Msg::UtteranceStop(utterance_id) => {
fn speak(&mut self, message: String, interrupt: bool) -> Variant { let utterance: Instance<Utterance, Unique> = Instance::new();
if let Ok(id) = self.0.speak(message, interrupt) { utterance
let utterance: Instance<Utterance, Unique> = Instance::new(); .map_mut(|u, _| u.0 = Some(utterance_id))
if id.is_some() { .expect("Failed to set utterance ID");
utterance owner.emit_signal("utterance_stop", &[utterance.owned_to_variant()]);
.map_mut(|u, _| u.0 = id) }
.expect("Failed to set utterance ID"); }
} }
utterance.owned_to_variant() }
} else { }
Variant::default()
} fn init(handle: InitHandle) {
} env_logger::init();
handle.add_tool_class::<Utterance>();
#[method] handle.add_class::<TTS>();
fn stop(&mut self) { }
self.0.stop().expect("Failed to stop");
} godot_gdnative_init!();
godot_nativescript_init!(init);
#[method] godot_gdnative_terminate!();
fn is_rate_supported(&mut self) -> bool {
let Features {
rate: rate_supported,
..
} = self.0.supported_features();
rate_supported
}
#[method]
fn are_utterance_callbacks_supported(&mut self) -> bool {
let Features {
utterance_callbacks: supported,
..
} = self.0.supported_features();
supported
}
#[method]
fn _process(&mut self, #[base] base: &Node, _delta: f32) {
if let Ok(msg) = self.1.try_recv() {
match msg {
Msg::UtteranceBegin(utterance_id) => {
let utterance: Instance<Utterance, Unique> = Instance::new();
utterance
.map_mut(|u, _| u.0 = Some(utterance_id))
.expect("Failed to set utterance ID");
base.emit_signal("utterance_begin", &[utterance.owned_to_variant()]);
}
Msg::UtteranceEnd(utterance_id) => {
let utterance: Instance<Utterance, Unique> = Instance::new();
utterance
.map_mut(|u, _| u.0 = Some(utterance_id))
.expect("Failed to set utterance ID");
base.emit_signal("utterance_end", &[utterance.owned_to_variant()]);
}
Msg::UtteranceStop(utterance_id) => {
let utterance: Instance<Utterance, Unique> = Instance::new();
utterance
.map_mut(|u, _| u.0 = Some(utterance_id))
.expect("Failed to set utterance ID");
base.emit_signal("utterance_stop", &[utterance.owned_to_variant()]);
}
}
}
}
}
fn init(handle: InitHandle) {
env_logger::init();
handle.add_tool_class::<Utterance>();
handle.add_class::<TTS>();
}
godot_init!(init);

View File

@ -18,7 +18,6 @@ import android.view.accessibility.AccessibilityManager;
public class TTS extends GodotPlugin implements TextToSpeech.OnInitListener { public class TTS extends GodotPlugin implements TextToSpeech.OnInitListener {
private TextToSpeech tts = null; private TextToSpeech tts = null;
private float volume = 1f;
private float rate = 1f; private float rate = 1f;
private Integer utteranceId = 0; private Integer utteranceId = 0;
@ -37,15 +36,6 @@ public class TTS extends GodotPlugin implements TextToSpeech.OnInitListener {
tts.stop(); tts.stop();
} }
public float get_volume() {
return this.volume;
}
public void set_volume(float volume) {
this.volume = volume;
tts.Engine.KEY_PARAM_VOLUME = volume;
}
public float get_rate() { public float get_rate() {
return this.rate; return this.rate;
} }