summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbruvzg <7645683+bruvzg@users.noreply.github.com>2023-05-16 14:18:12 +0300
committerbruvzg <7645683+bruvzg@users.noreply.github.com>2023-05-18 20:16:03 +0300
commit5b9984b5a21f23c8e0b56c64304c34995c8bd5b3 (patch)
treef37e0c338a5c701c49a9440a2d5d9ab7a39f5506
parent91f3cdfde0e8c5be58004228d62799912b8e4db7 (diff)
downloadredot-engine-5b9984b5a21f23c8e0b56c64304c34995c8bd5b3.tar.gz
Add `audio/general/text_to_speech` project setting to enable/disable TTS.
-rw-r--r--core/config/project_settings.cpp1
-rw-r--r--doc/classes/DisplayServer.xml9
-rw-r--r--doc/classes/ProjectSettings.xml4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java8
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotLib.java5
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/tts/GodotTTS.java20
-rw-r--r--platform/android/java_godot_lib_jni.cpp7
-rw-r--r--platform/android/java_godot_lib_jni.h4
-rw-r--r--platform/android/tts_android.cpp42
-rw-r--r--platform/android/tts_android.h3
-rw-r--r--platform/ios/display_server_ios.mm19
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp23
-rw-r--r--platform/macos/display_server_macos.mm19
-rw-r--r--platform/web/display_server_web.cpp12
-rw-r--r--platform/web/display_server_web.h1
-rw-r--r--platform/windows/display_server_windows.cpp19
16 files changed, 133 insertions, 63 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 264259eb2f..c3df4a2ebe 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -1290,6 +1290,7 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF("display/window/energy_saving/keep_screen_on.editor", false);
GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "audio/buses/default_bus_layout", PROPERTY_HINT_FILE, "*.tres"), "res://default_bus_layout.tres");
+ GLOBAL_DEF_RST("audio/general/text_to_speech", false);
GLOBAL_DEF_RST(PropertyInfo(Variant::FLOAT, "audio/general/2d_panning_strength", PROPERTY_HINT_RANGE, "0,2,0.01"), 0.5f);
GLOBAL_DEF_RST(PropertyInfo(Variant::FLOAT, "audio/general/3d_panning_strength", PROPERTY_HINT_RANGE, "0,2,0.01"), 0.5f);
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index beccaab048..47a7d2c67d 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -1017,6 +1017,7 @@
- [code]language[/code] is language code in [code]lang_Variant[/code] format. [code]lang[/code] part is a 2 or 3-letter code based on the ISO-639 standard, in lowercase. And [code]Variant[/code] part is an engine dependent string describing country, region or/and dialect.
Note that Godot depends on system libraries for text-to-speech functionality. These libraries are installed by default on Windows and macOS, but not on all Linux distributions. If they are not present, this method will return an empty list. This applies to both Godot users on Linux, as well as end-users on Linux running Godot games that use text-to-speech.
[b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows.
+ [b]Note:[/b] [member ProjectSettings.audio/general/text_to_speech] should be [code]true[/code] to use text-to-speech.
</description>
</method>
<method name="tts_get_voices_for_language" qualifiers="const">
@@ -1025,6 +1026,7 @@
<description>
Returns an [PackedStringArray] of voice identifiers for the [param language].
[b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows.
+ [b]Note:[/b] [member ProjectSettings.audio/general/text_to_speech] should be [code]true[/code] to use text-to-speech.
</description>
</method>
<method name="tts_is_paused" qualifiers="const">
@@ -1032,6 +1034,7 @@
<description>
Returns [code]true[/code] if the synthesizer is in a paused state.
[b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows.
+ [b]Note:[/b] [member ProjectSettings.audio/general/text_to_speech] should be [code]true[/code] to use text-to-speech.
</description>
</method>
<method name="tts_is_speaking" qualifiers="const">
@@ -1039,6 +1042,7 @@
<description>
Returns [code]true[/code] if the synthesizer is generating speech, or have utterance waiting in the queue.
[b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows.
+ [b]Note:[/b] [member ProjectSettings.audio/general/text_to_speech] should be [code]true[/code] to use text-to-speech.
</description>
</method>
<method name="tts_pause">
@@ -1046,6 +1050,7 @@
<description>
Puts the synthesizer into a paused state.
[b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows.
+ [b]Note:[/b] [member ProjectSettings.audio/general/text_to_speech] should be [code]true[/code] to use text-to-speech.
</description>
</method>
<method name="tts_resume">
@@ -1053,6 +1058,7 @@
<description>
Resumes the synthesizer if it was paused.
[b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows.
+ [b]Note:[/b] [member ProjectSettings.audio/general/text_to_speech] should be [code]true[/code] to use text-to-speech.
</description>
</method>
<method name="tts_set_utterance_callback">
@@ -1065,6 +1071,7 @@
- [constant TTS_UTTERANCE_BOUNDARY] callable's method should take two [int] parameters, the index of the character and the utterance ID.
[b]Note:[/b] The granularity of the boundary callbacks is engine dependent.
[b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows.
+ [b]Note:[/b] [member ProjectSettings.audio/general/text_to_speech] should be [code]true[/code] to use text-to-speech.
</description>
</method>
<method name="tts_speak">
@@ -1086,6 +1093,7 @@
[b]Note:[/b] On Windows and Linux (X11), utterance [param text] can use SSML markup. SSML support is engine and voice dependent. If the engine does not support SSML, you should strip out all XML markup before calling [method tts_speak].
[b]Note:[/b] The granularity of pitch, rate, and volume is engine and voice dependent. Values may be truncated.
[b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows.
+ [b]Note:[/b] [member ProjectSettings.audio/general/text_to_speech] should be [code]true[/code] to use text-to-speech.
</description>
</method>
<method name="tts_stop">
@@ -1093,6 +1101,7 @@
<description>
Stops synthesis in progress and removes all utterances from the queue.
[b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows.
+ [b]Note:[/b] [member ProjectSettings.audio/general/text_to_speech] should be [code]true[/code] to use text-to-speech.
</description>
</method>
<method name="virtual_keyboard_get_height" qualifiers="const">
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index ee3367c24c..0b60f0dbf6 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -371,6 +371,10 @@
The base strength of the panning effect for all [AudioStreamPlayer3D] nodes. The panning strength can be further scaled on each Node using [member AudioStreamPlayer3D.panning_strength]. A value of [code]0.0[/code] disables stereo panning entirely, leaving only volume attenuation in place. A value of [code]1.0[/code] completely mutes one of the channels if the sound is located exactly to the left (or right) of the listener.
The default value of [code]0.5[/code] is tuned for headphones. When using speakers, you may find lower values to sound better as speakers have a lower stereo separation compared to headphones.
</member>
+ <member name="audio/general/text_to_speech" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], text-to-speech support is enabled, see [method DisplayServer.tts_get_voices] and [method DisplayServer.tts_speak].
+ [b]Note:[/b] Enabling TTS can cause addition idle CPU usage and interfere with the sleep mode, so consider disabling it if TTS is not used.
+ </member>
<member name="audio/video/video_delay_compensation_ms" type="int" setter="" getter="" default="0">
Setting to hardcode audio delay when playing video. Best to leave this untouched unless you know what you are doing.
</member>
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
index 99527ccf3a..4c47ca9760 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -274,7 +274,9 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
// ...add to FrameLayout
containerLayout.addView(editText);
- if (!GodotLib.setup(command_line)) {
+ tts = new GodotTTS(activity);
+
+ if (!GodotLib.setup(command_line, tts)) {
Log.e(TAG, "Unable to setup the Godot engine! Aborting...");
alert(R.string.error_engine_setup_message, R.string.text_error_title, this::forceQuit);
return false;
@@ -574,7 +576,6 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
final Activity activity = getActivity();
io = new GodotIO(activity);
netUtils = new GodotNetUtils(activity);
- tts = new GodotTTS(activity);
Context context = getContext();
directoryAccessHandler = new DirectoryAccessHandler(context);
FileAccessHandler fileAccessHandler = new FileAccessHandler(context);
@@ -591,8 +592,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
netUtils,
directoryAccessHandler,
fileAccessHandler,
- use_apk_expansion,
- tts);
+ use_apk_expansion);
result_callback = null;
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
index d9aab950df..c725b1a7c9 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
@@ -61,8 +61,7 @@ public class GodotLib {
GodotNetUtils netUtils,
DirectoryAccessHandler directoryAccessHandler,
FileAccessHandler fileAccessHandler,
- boolean use_apk_expansion,
- GodotTTS tts);
+ boolean use_apk_expansion);
/**
* Invoked on the main thread to clean up Godot native layer.
@@ -74,7 +73,7 @@ public class GodotLib {
* Invoked on the GL thread to complete setup for the Godot native layer logic.
* @param p_cmdline Command line arguments used to configure Godot native layer components.
*/
- public static native boolean setup(String[] p_cmdline);
+ public static native boolean setup(String[] p_cmdline, GodotTTS tts);
/**
* Invoked on the GL thread when the underlying Android surface has changed size.
diff --git a/platform/android/java/lib/src/org/godotengine/godot/tts/GodotTTS.java b/platform/android/java/lib/src/org/godotengine/godot/tts/GodotTTS.java
index ebab8398de..edace53e7f 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/tts/GodotTTS.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/tts/GodotTTS.java
@@ -62,8 +62,9 @@ public class GodotTTS extends UtteranceProgressListener {
final private static int EVENT_CANCEL = 2;
final private static int EVENT_BOUNDARY = 3;
- final private TextToSpeech synth;
- final private LinkedList<GodotUtterance> queue;
+ final private Activity activity;
+ private TextToSpeech synth;
+ private LinkedList<GodotUtterance> queue;
final private Object lock = new Object();
private GodotUtterance lastUtterance;
@@ -71,10 +72,7 @@ public class GodotTTS extends UtteranceProgressListener {
private boolean paused;
public GodotTTS(Activity p_activity) {
- synth = new TextToSpeech(p_activity, null);
- queue = new LinkedList<GodotUtterance>();
-
- synth.setOnUtteranceProgressListener(this);
+ activity = p_activity;
}
private void updateTTS() {
@@ -187,6 +185,16 @@ public class GodotTTS extends UtteranceProgressListener {
}
/**
+ * Initialize synth and query.
+ */
+ public void init() {
+ synth = new TextToSpeech(activity, null);
+ queue = new LinkedList<GodotUtterance>();
+
+ synth.setOnUtteranceProgressListener(this);
+ }
+
+ /**
* Adds an utterance to the queue.
*/
public void speak(String text, String voice, int volume, float pitch, float rate, int utterance_id, boolean interrupt) {
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index 18091649e3..63435853e9 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -116,7 +116,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHei
}
}
-JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject p_activity, jobject p_godot_instance, jobject p_asset_manager, jobject p_godot_io, jobject p_net_utils, jobject p_directory_access_handler, jobject p_file_access_handler, jboolean p_use_apk_expansion, jobject p_godot_tts) {
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject p_activity, jobject p_godot_instance, jobject p_asset_manager, jobject p_godot_io, jobject p_net_utils, jobject p_directory_access_handler, jobject p_file_access_handler, jboolean p_use_apk_expansion) {
JavaVM *jvm;
env->GetJavaVM(&jvm);
@@ -133,7 +133,6 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv
DirAccessJAndroid::setup(p_directory_access_handler);
FileAccessFilesystemJAndroid::setup(p_file_access_handler);
NetSocketAndroid::setup(p_net_utils);
- TTS_Android::setup(p_godot_tts);
os_android = new OS_Android(godot_java, godot_io_java, p_use_apk_expansion);
@@ -144,7 +143,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env
_terminate(env, false);
}
-JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline) {
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline, jobject p_godot_tts) {
setup_android_thread();
const char **cmdline = nullptr;
@@ -185,6 +184,8 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env
return false;
}
+ TTS_Android::setup(p_godot_tts);
+
java_class_wrapper = memnew(JavaClassWrapper(godot_java->get_activity()));
GDREGISTER_CLASS(JNISingleton);
return true;
diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h
index 59ab2448d7..9158e89c13 100644
--- a/platform/android/java_godot_lib_jni.h
+++ b/platform/android/java_godot_lib_jni.h
@@ -37,9 +37,9 @@
// These functions can be called from within JAVA and are the means by which our JAVA implementation calls back into our C++ code.
// See java/src/org/godotengine/godot/GodotLib.java for the JAVA side of this (yes that's why we have the long names)
extern "C" {
-JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject p_activity, jobject p_godot_instance, jobject p_asset_manager, jobject p_godot_io, jobject p_net_utils, jobject p_directory_access_handler, jobject p_file_access_handler, jboolean p_use_apk_expansion, jobject p_godot_tts);
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject p_activity, jobject p_godot_instance, jobject p_asset_manager, jobject p_godot_io, jobject p_net_utils, jobject p_directory_access_handler, jobject p_file_access_handler, jboolean p_use_apk_expansion);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz);
-JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline);
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline, jobject p_godot_tts);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jobject p_surface, jint p_width, jint p_height);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface);
JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz);
diff --git a/platform/android/tts_android.cpp b/platform/android/tts_android.cpp
index c08c1d4941..aef59c2584 100644
--- a/platform/android/tts_android.cpp
+++ b/platform/android/tts_android.cpp
@@ -35,9 +35,11 @@
#include "string_android.h"
#include "thread_jandroid.h"
+bool TTS_Android::initialized = false;
jobject TTS_Android::tts = nullptr;
jclass TTS_Android::cls = nullptr;
+jmethodID TTS_Android::_init = nullptr;
jmethodID TTS_Android::_is_speaking = nullptr;
jmethodID TTS_Android::_is_paused = nullptr;
jmethodID TTS_Android::_get_voices = nullptr;
@@ -49,23 +51,34 @@ jmethodID TTS_Android::_stop_speaking = nullptr;
HashMap<int, Char16String> TTS_Android::ids;
void TTS_Android::setup(jobject p_tts) {
- JNIEnv *env = get_jni_env();
+ bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech");
+ if (tts_enabled) {
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND(env == nullptr);
+
+ tts = env->NewGlobalRef(p_tts);
- tts = env->NewGlobalRef(p_tts);
+ jclass c = env->GetObjectClass(tts);
+ cls = (jclass)env->NewGlobalRef(c);
- jclass c = env->GetObjectClass(tts);
- cls = (jclass)env->NewGlobalRef(c);
+ _init = env->GetMethodID(cls, "init", "()V");
+ _is_speaking = env->GetMethodID(cls, "isSpeaking", "()Z");
+ _is_paused = env->GetMethodID(cls, "isPaused", "()Z");
+ _get_voices = env->GetMethodID(cls, "getVoices", "()[Ljava/lang/String;");
+ _speak = env->GetMethodID(cls, "speak", "(Ljava/lang/String;Ljava/lang/String;IFFIZ)V");
+ _pause_speaking = env->GetMethodID(cls, "pauseSpeaking", "()V");
+ _resume_speaking = env->GetMethodID(cls, "resumeSpeaking", "()V");
+ _stop_speaking = env->GetMethodID(cls, "stopSpeaking", "()V");
- _is_speaking = env->GetMethodID(cls, "isSpeaking", "()Z");
- _is_paused = env->GetMethodID(cls, "isPaused", "()Z");
- _get_voices = env->GetMethodID(cls, "getVoices", "()[Ljava/lang/String;");
- _speak = env->GetMethodID(cls, "speak", "(Ljava/lang/String;Ljava/lang/String;IFFIZ)V");
- _pause_speaking = env->GetMethodID(cls, "pauseSpeaking", "()V");
- _resume_speaking = env->GetMethodID(cls, "resumeSpeaking", "()V");
- _stop_speaking = env->GetMethodID(cls, "stopSpeaking", "()V");
+ if (_init) {
+ env->CallVoidMethod(tts, _init);
+ initialized = true;
+ }
+ }
}
void TTS_Android::_java_utterance_callback(int p_event, int p_id, int p_pos) {
+ ERR_FAIL_COND_MSG(!initialized, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
if (ids.has(p_id)) {
int pos = 0;
if ((DisplayServer::TTSUtteranceEvent)p_event == DisplayServer::TTS_UTTERANCE_BOUNDARY) {
@@ -86,6 +99,7 @@ void TTS_Android::_java_utterance_callback(int p_event, int p_id, int p_pos) {
}
bool TTS_Android::is_speaking() {
+ ERR_FAIL_COND_V_MSG(!initialized, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
if (_is_speaking) {
JNIEnv *env = get_jni_env();
@@ -97,6 +111,7 @@ bool TTS_Android::is_speaking() {
}
bool TTS_Android::is_paused() {
+ ERR_FAIL_COND_V_MSG(!initialized, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
if (_is_paused) {
JNIEnv *env = get_jni_env();
@@ -108,6 +123,7 @@ bool TTS_Android::is_paused() {
}
Array TTS_Android::get_voices() {
+ ERR_FAIL_COND_V_MSG(!initialized, Array(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
Array list;
if (_get_voices) {
JNIEnv *env = get_jni_env();
@@ -135,6 +151,7 @@ Array TTS_Android::get_voices() {
}
void TTS_Android::speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) {
+ ERR_FAIL_COND_MSG(!initialized, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
if (p_interrupt) {
stop();
}
@@ -157,6 +174,7 @@ void TTS_Android::speak(const String &p_text, const String &p_voice, int p_volum
}
void TTS_Android::pause() {
+ ERR_FAIL_COND_MSG(!initialized, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
if (_pause_speaking) {
JNIEnv *env = get_jni_env();
@@ -166,6 +184,7 @@ void TTS_Android::pause() {
}
void TTS_Android::resume() {
+ ERR_FAIL_COND_MSG(!initialized, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
if (_resume_speaking) {
JNIEnv *env = get_jni_env();
@@ -175,6 +194,7 @@ void TTS_Android::resume() {
}
void TTS_Android::stop() {
+ ERR_FAIL_COND_MSG(!initialized, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
for (const KeyValue<int, Char16String> &E : ids) {
DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_CANCELED, E.key);
}
diff --git a/platform/android/tts_android.h b/platform/android/tts_android.h
index 8e00ac5000..39efef6ed1 100644
--- a/platform/android/tts_android.h
+++ b/platform/android/tts_android.h
@@ -31,6 +31,7 @@
#ifndef TTS_ANDROID_H
#define TTS_ANDROID_H
+#include "core/config/project_settings.h"
#include "core/string/ustring.h"
#include "core/templates/hash_map.h"
#include "core/variant/array.h"
@@ -39,9 +40,11 @@
#include <jni.h>
class TTS_Android {
+ static bool initialized;
static jobject tts;
static jclass cls;
+ static jmethodID _init;
static jmethodID _is_speaking;
static jmethodID _is_paused;
static jmethodID _get_voices;
diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm
index 469bc9be17..a52d2b185d 100644
--- a/platform/ios/display_server_ios.mm
+++ b/platform/ios/display_server_ios.mm
@@ -56,7 +56,10 @@ DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode
rendering_driver = p_rendering_driver;
// Init TTS
- tts = [[TTS_IOS alloc] init];
+ bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech");
+ if (tts_enabled) {
+ tts = [[TTS_IOS alloc] init];
+ }
#if defined(VULKAN_ENABLED)
context_vulkan = nullptr;
@@ -316,37 +319,37 @@ String DisplayServerIOS::get_name() const {
}
bool DisplayServerIOS::tts_is_speaking() const {
- ERR_FAIL_COND_V(!tts, false);
+ ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
return [tts isSpeaking];
}
bool DisplayServerIOS::tts_is_paused() const {
- ERR_FAIL_COND_V(!tts, false);
+ ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
return [tts isPaused];
}
TypedArray<Dictionary> DisplayServerIOS::tts_get_voices() const {
- ERR_FAIL_COND_V(!tts, TypedArray<Dictionary>());
+ ERR_FAIL_COND_V_MSG(!tts, TypedArray<Dictionary>(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
return [tts getVoices];
}
void DisplayServerIOS::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) {
- ERR_FAIL_COND(!tts);
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
[tts speak:p_text voice:p_voice volume:p_volume pitch:p_pitch rate:p_rate utterance_id:p_utterance_id interrupt:p_interrupt];
}
void DisplayServerIOS::tts_pause() {
- ERR_FAIL_COND(!tts);
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
[tts pauseSpeaking];
}
void DisplayServerIOS::tts_resume() {
- ERR_FAIL_COND(!tts);
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
[tts resumeSpeaking];
}
void DisplayServerIOS::tts_stop() {
- ERR_FAIL_COND(!tts);
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
[tts stopSpeaking];
}
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 9506189573..8ec7be97c6 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -303,37 +303,37 @@ void DisplayServerX11::_flush_mouse_motion() {
#ifdef SPEECHD_ENABLED
bool DisplayServerX11::tts_is_speaking() const {
- ERR_FAIL_COND_V(!tts, false);
+ ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
return tts->is_speaking();
}
bool DisplayServerX11::tts_is_paused() const {
- ERR_FAIL_COND_V(!tts, false);
+ ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
return tts->is_paused();
}
TypedArray<Dictionary> DisplayServerX11::tts_get_voices() const {
- ERR_FAIL_COND_V(!tts, TypedArray<Dictionary>());
+ ERR_FAIL_COND_V_MSG(!tts, TypedArray<Dictionary>(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
return tts->get_voices();
}
void DisplayServerX11::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) {
- ERR_FAIL_COND(!tts);
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
tts->speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt);
}
void DisplayServerX11::tts_pause() {
- ERR_FAIL_COND(!tts);
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
tts->pause();
}
void DisplayServerX11::tts_resume() {
- ERR_FAIL_COND(!tts);
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
tts->resume();
}
void DisplayServerX11::tts_stop() {
- ERR_FAIL_COND(!tts);
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
tts->stop();
}
@@ -5652,7 +5652,10 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
#ifdef SPEECHD_ENABLED
// Init TTS
- tts = memnew(TTS_Linux);
+ bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech");
+ if (tts_enabled) {
+ tts = memnew(TTS_Linux);
+ }
#endif
//!!!!!!!!!!!!!!!!!!!!!!!!!!
@@ -6026,7 +6029,9 @@ DisplayServerX11::~DisplayServerX11() {
}
#ifdef SPEECHD_ENABLED
- memdelete(tts);
+ if (tts) {
+ memdelete(tts);
+ }
#endif
#ifdef DBUS_ENABLED
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index 4657461dca..c24115d705 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -1741,37 +1741,37 @@ void DisplayServerMacOS::global_menu_clear(const String &p_menu_root) {
}
bool DisplayServerMacOS::tts_is_speaking() const {
- ERR_FAIL_COND_V(!tts, false);
+ ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
return [tts isSpeaking];
}
bool DisplayServerMacOS::tts_is_paused() const {
- ERR_FAIL_COND_V(!tts, false);
+ ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
return [tts isPaused];
}
TypedArray<Dictionary> DisplayServerMacOS::tts_get_voices() const {
- ERR_FAIL_COND_V(!tts, Array());
+ ERR_FAIL_COND_V_MSG(!tts, Array(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
return [tts getVoices];
}
void DisplayServerMacOS::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) {
- ERR_FAIL_COND(!tts);
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
[tts speak:p_text voice:p_voice volume:p_volume pitch:p_pitch rate:p_rate utterance_id:p_utterance_id interrupt:p_interrupt];
}
void DisplayServerMacOS::tts_pause() {
- ERR_FAIL_COND(!tts);
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
[tts pauseSpeaking];
}
void DisplayServerMacOS::tts_resume() {
- ERR_FAIL_COND(!tts);
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
[tts resumeSpeaking];
}
void DisplayServerMacOS::tts_stop() {
- ERR_FAIL_COND(!tts);
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
[tts stopSpeaking];
}
@@ -3845,7 +3845,10 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM
CGDisplayRegisterReconfigurationCallback(_displays_arrangement_changed, nullptr);
// Init TTS
- tts = [[TTS_MacOS alloc] init];
+ bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech");
+ if (tts_enabled) {
+ tts = [[TTS_MacOS alloc] init];
+ }
NSMenuItem *menu_item;
NSString *title;
diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp
index 28f1914c37..5ef581e287 100644
--- a/platform/web/display_server_web.cpp
+++ b/platform/web/display_server_web.cpp
@@ -30,6 +30,7 @@
#include "display_server_web.h"
+#include "core/config/project_settings.h"
#ifdef GLES3_ENABLED
#include "drivers/gles3/rasterizer_gles3.h"
#endif
@@ -298,10 +299,12 @@ const char *DisplayServerWeb::godot2dom_cursor(DisplayServer::CursorShape p_shap
}
bool DisplayServerWeb::tts_is_speaking() const {
+ ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
return godot_js_tts_is_speaking();
}
bool DisplayServerWeb::tts_is_paused() const {
+ ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
return godot_js_tts_is_paused();
}
@@ -320,11 +323,13 @@ void DisplayServerWeb::update_voices_callback(int p_size, const char **p_voice)
}
TypedArray<Dictionary> DisplayServerWeb::tts_get_voices() const {
+ ERR_FAIL_COND_V_MSG(!tts, TypedArray<Dictionary>(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
godot_js_tts_get_voices(update_voices_callback);
return voices;
}
void DisplayServerWeb::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) {
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
if (p_interrupt) {
tts_stop();
}
@@ -341,14 +346,17 @@ void DisplayServerWeb::tts_speak(const String &p_text, const String &p_voice, in
}
void DisplayServerWeb::tts_pause() {
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
godot_js_tts_pause();
}
void DisplayServerWeb::tts_resume() {
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
godot_js_tts_resume();
}
void DisplayServerWeb::tts_stop() {
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
for (const KeyValue<int, CharString> &E : utterance_ids) {
tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_CANCELED, E.key);
}
@@ -771,6 +779,8 @@ DisplayServer *DisplayServerWeb::create_func(const String &p_rendering_driver, W
DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Error &r_error) {
r_error = OK; // Always succeeds for now.
+ tts = GLOBAL_GET("audio/general/text_to_speech");
+
// Ensure the canvas ID.
godot_js_config_canvas_id_get(canvas_id, 256);
@@ -866,7 +876,7 @@ bool DisplayServerWeb::has_feature(Feature p_feature) const {
case FEATURE_VIRTUAL_KEYBOARD:
return godot_js_display_vk_available() != 0;
case FEATURE_TEXT_TO_SPEECH:
- return godot_js_display_tts_available() != 0;
+ return tts && (godot_js_display_tts_available() != 0);
default:
return false;
}
diff --git a/platform/web/display_server_web.h b/platform/web/display_server_web.h
index a72977e3c3..66ef243489 100644
--- a/platform/web/display_server_web.h
+++ b/platform/web/display_server_web.h
@@ -81,6 +81,7 @@ private:
MouseButton last_click_button_index = MouseButton::NONE;
bool swap_cancel_ok = false;
+ bool tts = false;
// utilities
static void dom2godot_mod(Ref<InputEventWithModifiers> ev, int p_mod, Key p_keycode);
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 6735f2b663..f971846959 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -179,37 +179,37 @@ void DisplayServerWindows::_register_raw_input_devices(WindowID p_target_window)
}
bool DisplayServerWindows::tts_is_speaking() const {
- ERR_FAIL_COND_V(!tts, false);
+ ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
return tts->is_speaking();
}
bool DisplayServerWindows::tts_is_paused() const {
- ERR_FAIL_COND_V(!tts, false);
+ ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
return tts->is_paused();
}
TypedArray<Dictionary> DisplayServerWindows::tts_get_voices() const {
- ERR_FAIL_COND_V(!tts, TypedArray<Dictionary>());
+ ERR_FAIL_COND_V_MSG(!tts, TypedArray<Dictionary>(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
return tts->get_voices();
}
void DisplayServerWindows::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) {
- ERR_FAIL_COND(!tts);
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
tts->speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt);
}
void DisplayServerWindows::tts_pause() {
- ERR_FAIL_COND(!tts);
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
tts->pause();
}
void DisplayServerWindows::tts_resume() {
- ERR_FAIL_COND(!tts);
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
tts->resume();
}
void DisplayServerWindows::tts_stop() {
- ERR_FAIL_COND(!tts);
+ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
tts->stop();
}
@@ -4152,7 +4152,10 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
rendering_driver = p_rendering_driver;
// Init TTS
- tts = memnew(TTS_Windows);
+ bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech");
+ if (tts_enabled) {
+ tts = memnew(TTS_Windows);
+ }
// Enforce default keep screen on value.
screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));