From 8479340f52554cfafabba57be045eeae397b0dd8 Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Mon, 22 May 2023 16:41:09 +0300 Subject: [PATCH] Add audio/general/text_to_speech project setting to enable/disable TTS. --- core/config/project_settings.cpp | 2 + doc/classes/OS.xml | 9 ++++ doc/classes/ProjectSettings.xml | 4 ++ .../pandemonium/Pandemonium.java | 7 ++-- .../pandemonium/PandemoniumLib.java | 4 +- .../pandemonium/tts/PandemoniumTTS.java | 20 ++++++--- platform/android/java_pandemonium_lib_jni.cpp | 8 ++-- platform/android/tts_android.cpp | 41 ++++++++++++++----- platform/android/tts_android.h | 3 ++ platform/iphone/os_iphone.mm | 12 +++++- platform/javascript/os_javascript.cpp | 10 +++++ platform/javascript/os_javascript.h | 1 + platform/osx/os_osx.mm | 12 +++++- platform/windows/os_windows.cpp | 12 +++++- platform/x11/os_x11.cpp | 16 +++++++- 15 files changed, 131 insertions(+), 30 deletions(-) diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 09ed90b1c..9db9935de 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1080,6 +1080,8 @@ ProjectSettings::ProjectSettings() { ProjectSettings::get_singleton()->set_custom_property_info("audio/general/ios/session_category", PropertyInfo(Variant::INT, "audio/general/ios/session_category", PROPERTY_HINT_ENUM, "Ambient,Multi Route,Play and Record,Playback,Record,Solo Ambient")); GLOBAL_DEF("audio/general/ios/mix_with_others", false); + GLOBAL_DEF_RST("audio/general/text_to_speech", false); + PoolStringArray extensions = PoolStringArray(); extensions.push_back("gd"); extensions.push_back("gdshader"); diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index 161d942c1..1a0c0aad0 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -1131,6 +1131,7 @@ - [code]id[/code] is voice identifier. - [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. [b]Note:[/b] This method is implemented on Android, iOS, HTML5, Linux, macOS, and Windows. + [b]Note:[/b] [member ProjectSettings.audio/general/text_to_speech] should be [code]true[/code] to use text-to-speech. @@ -1139,6 +1140,7 @@ Returns an [PoolStringArray] of voice identifiers for the [code]language[/code]. [b]Note:[/b] This method is implemented on Android, iOS, HTML5, Linux, macOS, and Windows. + [b]Note:[/b] [member ProjectSettings.audio/general/text_to_speech] should be [code]true[/code] to use text-to-speech. @@ -1146,6 +1148,7 @@ Returns [code]true[/code] if the synthesizer is in a paused state. [b]Note:[/b] This method is implemented on Android, iOS, HTML5, Linux, macOS, and Windows. + [b]Note:[/b] [member ProjectSettings.audio/general/text_to_speech] should be [code]true[/code] to use text-to-speech. @@ -1153,6 +1156,7 @@ 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, HTML5, Linux, macOS, and Windows. + [b]Note:[/b] [member ProjectSettings.audio/general/text_to_speech] should be [code]true[/code] to use text-to-speech. @@ -1160,6 +1164,7 @@ Puts the synthesizer into a paused state. [b]Note:[/b] This method is implemented on Android, iOS, HTML5, Linux, macOS, and Windows. + [b]Note:[/b] [member ProjectSettings.audio/general/text_to_speech] should be [code]true[/code] to use text-to-speech. @@ -1167,6 +1172,7 @@ Resumes the synthesizer if it was paused. [b]Note:[/b] This method is implemented on Android, iOS, HTML5, Linux, macOS, and Windows. + [b]Note:[/b] [member ProjectSettings.audio/general/text_to_speech] should be [code]true[/code] to use text-to-speech. @@ -1180,6 +1186,7 @@ - [code]TTS_UTTERANCE_BOUNDARY[/code] 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, HTML5, Linux, macOS, and Windows. + [b]Note:[/b] [member ProjectSettings.audio/general/text_to_speech] should be [code]true[/code] to use text-to-speech. @@ -1201,6 +1208,7 @@ [b]Note:[/b] On Windows and Linux, utterance [code]text[/code] 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, HTML5, Linux, macOS, and Windows. + [b]Note:[/b] [member ProjectSettings.audio/general/text_to_speech] should be [code]true[/code] to use text-to-speech. @@ -1208,6 +1216,7 @@ Stops synthesis in progress and removes all utterances from the queue. [b]Note:[/b] This method is implemented on Android, iOS, HTML5, Linux, macOS, and Windows. + [b]Note:[/b] [member ProjectSettings.audio/general/text_to_speech] should be [code]true[/code] to use text-to-speech. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index be3c26bb4..8ccfe1afa 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -303,6 +303,10 @@ Sets the [url=https://developer.apple.com/documentation/avfaudio/avaudiosessioncategory]AVAudioSessionCategory[/url] on iOS. Use the [code]Playback[/code] category to get sound output, even if the phone is in silent mode. + + If [code]true[/code], text-to-speech support is enabled, see [method OS.tts_get_voices] and [method OS.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. + The mixing rate used for audio (in Hz). In general, it's better to not touch this and leave it to the host operating system. diff --git a/platform/android/java/lib/src/org/pandemoniumengine/pandemonium/Pandemonium.java b/platform/android/java/lib/src/org/pandemoniumengine/pandemonium/Pandemonium.java index d823d2a73..9f43461f8 100644 --- a/platform/android/java/lib/src/org/pandemoniumengine/pandemonium/Pandemonium.java +++ b/platform/android/java/lib/src/org/pandemoniumengine/pandemonium/Pandemonium.java @@ -457,7 +457,9 @@ public class Pandemonium extends Fragment implements SensorEventListener, IDownl final String[] current_command_line = command_line; mView.queueEvent(() -> { - if (!PandemoniumLib.setup(current_command_line)) { + tts = new PandemoniumTTS(activity); + + if (!PandemoniumLib.setup(current_command_line, tts)) { pandemonium_initialized = false; Log.e(TAG, "Unable to setup the Pandemonium engine! Aborting..."); alert(R.string.error_engine_setup_message, R.string.text_error_title, this::forceQuit); @@ -711,7 +713,6 @@ public class Pandemonium extends Fragment implements SensorEventListener, IDownl final Activity activity = getActivity(); io = new PandemoniumIO(activity); netUtils = new PandemoniumNetUtils(activity); - tts = new PandemoniumTTS(activity); Context context = getContext(); directoryAccessHandler = new DirectoryAccessHandler(context); @@ -723,7 +724,7 @@ public class Pandemonium extends Fragment implements SensorEventListener, IDownl mMagnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE); - PandemoniumLib.initialize(activity, this, activity.getAssets(), io, netUtils, directoryAccessHandler, fileAccessHandler, use_apk_expansion, tts); + PandemoniumLib.initialize(activity, this, activity.getAssets(), io, netUtils, directoryAccessHandler, fileAccessHandler, use_apk_expansion); result_callback = null; diff --git a/platform/android/java/lib/src/org/pandemoniumengine/pandemonium/PandemoniumLib.java b/platform/android/java/lib/src/org/pandemoniumengine/pandemonium/PandemoniumLib.java index a3188937d..878d5e2d4 100644 --- a/platform/android/java/lib/src/org/pandemoniumengine/pandemonium/PandemoniumLib.java +++ b/platform/android/java/lib/src/org/pandemoniumengine/pandemonium/PandemoniumLib.java @@ -53,7 +53,7 @@ public class PandemoniumLib { /** * Invoked on the main thread to initialize Pandemonium native layer. */ - public static native void initialize(Activity activity, Pandemonium p_instance, AssetManager p_asset_manager, PandemoniumIO pandemoniumIO, PandemoniumNetUtils netUtils, DirectoryAccessHandler directoryAccessHandler, FileAccessHandler fileAccessHandler, boolean use_apk_expansion, GodotTTS tts); + public static native void initialize(Activity activity, Pandemonium p_instance, AssetManager p_asset_manager, PandemoniumIO pandemoniumIO, PandemoniumNetUtils netUtils, DirectoryAccessHandler directoryAccessHandler, FileAccessHandler fileAccessHandler, boolean use_apk_expansion); /** * Invoked on the main thread to clean up Pandemonium native layer. @@ -65,7 +65,7 @@ public class PandemoniumLib { * Invoked on the GL thread to complete setup for the Pandemonium native layer logic. * @param p_cmdline Command line arguments used to configure Pandemonium native layer components. */ - public static native boolean setup(String[] p_cmdline); + public static native boolean setup(String[] p_cmdline, PandemoniumTTS tts); /** * Invoked on the GL thread when the underlying Android surface has changed size. diff --git a/platform/android/java/lib/src/org/pandemoniumengine/pandemonium/tts/PandemoniumTTS.java b/platform/android/java/lib/src/org/pandemoniumengine/pandemonium/tts/PandemoniumTTS.java index d8816d517..7ca7292df 100644 --- a/platform/android/java/lib/src/org/pandemoniumengine/pandemonium/tts/PandemoniumTTS.java +++ b/platform/android/java/lib/src/org/pandemoniumengine/pandemonium/tts/PandemoniumTTS.java @@ -62,8 +62,9 @@ public class PandemoniumTTS extends UtteranceProgressListener { final private static int EVENT_CANCEL = 2; final private static int EVENT_BOUNDARY = 3; - final private TextToSpeech synth; - final private LinkedList queue; + final private Activity activity; + private TextToSpeech synth; + private LinkedList queue; final private Object lock = new Object(); private PandemoniumUtterance lastUtterance; @@ -71,10 +72,7 @@ public class PandemoniumTTS extends UtteranceProgressListener { private boolean paused; public PandemoniumTTS(Activity p_activity) { - synth = new TextToSpeech(p_activity, null); - queue = new LinkedList(); - - synth.setOnUtteranceProgressListener(this); + activity = p_activity; } private void updateTTS() { @@ -186,6 +184,16 @@ public class PandemoniumTTS extends UtteranceProgressListener { } } + /** + * Initialize synth and query. + */ + public void init() { + synth = new TextToSpeech(p_activity, null); + queue = new LinkedList(); + + synth.setOnUtteranceProgressListener(this); + } + /** * Adds an utterance to the queue. */ diff --git a/platform/android/java_pandemonium_lib_jni.cpp b/platform/android/java_pandemonium_lib_jni.cpp index 26256053f..73128791c 100644 --- a/platform/android/java_pandemonium_lib_jni.cpp +++ b/platform/android/java_pandemonium_lib_jni.cpp @@ -160,7 +160,7 @@ JNIEXPORT void JNICALL Java_org_pandemoniumengine_pandemonium_PandemoniumLib_set } } -JNIEXPORT void JNICALL Java_org_pandemoniumengine_pandemonium_PandemoniumLib_initialize(JNIEnv *env, jclass clazz, jobject p_activity, jobject p_pandemonium_instance, jobject p_asset_manager, jobject p_pandemonium_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 void JNICALL Java_org_pandemoniumengine_pandemonium_PandemoniumLib_initialize(JNIEnv *env, jclass clazz, jobject p_activity, jobject p_pandemonium_instance, jobject p_asset_manager, jobject p_pandemonium_io, jobject p_net_utils, jobject p_directory_access_handler, jobject p_file_access_handler, jboolean p_use_apk_expansion) { initialized = true; JavaVM *jvm; @@ -179,7 +179,6 @@ JNIEXPORT void JNICALL Java_org_pandemoniumengine_pandemonium_PandemoniumLib_ini 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(pandemonium_java, pandemonium_io_java, p_use_apk_expansion); @@ -193,7 +192,7 @@ JNIEXPORT void JNICALL Java_org_pandemoniumengine_pandemonium_PandemoniumLib_ond _terminate(env, false); } -JNIEXPORT jboolean JNICALL Java_org_pandemoniumengine_pandemonium_PandemoniumLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline) { +JNIEXPORT jboolean JNICALL Java_org_pandemoniumengine_pandemonium_PandemoniumLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline, jobject p_godot_tts) { setup_android_thread(); const char **cmdline = NULL; @@ -234,7 +233,10 @@ JNIEXPORT jboolean JNICALL Java_org_pandemoniumengine_pandemonium_PandemoniumLib return false; } + TTS_Android::setup(p_godot_tts); + java_class_wrapper = memnew(JavaClassWrapper(pandemonium_java->get_activity())); + ClassDB::register_class(); _initialize_java_modules(); return true; diff --git a/platform/android/tts_android.cpp b/platform/android/tts_android.cpp index a659c29c9..8efde427b 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 = 0; jclass TTS_Android::cls = 0; +jmethodID TTS_Android::_init = 0; jmethodID TTS_Android::_is_speaking = 0; jmethodID TTS_Android::_is_paused = 0; jmethodID TTS_Android::_get_voices = 0; @@ -99,23 +101,33 @@ Vector TTS_Android::str_to_utf16(const String &p_string) { } 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(); - 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); - _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"); + _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"); + + 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 ((OS::TTSUtteranceEvent)p_event == OS::TTS_UTTERANCE_BOUNDARY) { @@ -136,6 +148,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(); @@ -147,6 +160,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(); @@ -158,6 +172,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(); @@ -185,6 +200,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(); } @@ -207,6 +223,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(); @@ -216,6 +233,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(); @@ -225,6 +243,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."); const int *k = NULL; while ((k = ids.next(k))) { OS::get_singleton()->tts_post_utterance_event(OS::TTS_UTTERANCE_CANCELED, *k); diff --git a/platform/android/tts_android.h b/platform/android/tts_android.h index f7dc45111..5a1cde260 100644 --- a/platform/android/tts_android.h +++ b/platform/android/tts_android.h @@ -33,14 +33,17 @@ #include "core/variant/array.h" #include "core/os/os.h" +#include "core/config/project_settings.h" #include "core/string/ustring.h" #include 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/iphone/os_iphone.mm b/platform/iphone/os_iphone.mm index 92194f5fd..14ef92a66 100644 --- a/platform/iphone/os_iphone.mm +++ b/platform/iphone/os_iphone.mm @@ -95,36 +95,43 @@ OSIPhone *OSIPhone::get_singleton() { }; bool OSIPhone::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."); ERR_FAIL_COND_V(!tts, false); return [tts isSpeaking]; } bool OSIPhone::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."); ERR_FAIL_COND_V(!tts, false); return [tts isPaused]; } Array OSIPhone::tts_get_voices() const { + ERR_FAIL_COND_V_MSG(!tts, Array(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); ERR_FAIL_COND_V(!tts, Array()); return [tts getVoices]; } void OSIPhone::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."); ERR_FAIL_COND(!tts); [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 OSIPhone::tts_pause() { + ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); ERR_FAIL_COND(!tts); [tts pauseSpeaking]; } void OSIPhone::tts_resume() { + ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); ERR_FAIL_COND(!tts); [tts resumeSpeaking]; } void OSIPhone::tts_stop() { + ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); ERR_FAIL_COND(!tts); [tts stopSpeaking]; } @@ -225,7 +232,10 @@ Error OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p } // 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]; + } rendering_server->init(); //rendering_server->cursor_set_visible(false, 0); diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index afc57ef76..737c8d68a 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -32,6 +32,7 @@ #include "os_javascript.h" #include "core/io/json.h" +#include "core/project_settings.h" #include "drivers/gles2/rasterizer_gles2.h" #ifndef GLES3_DISABLED #include "drivers/gles3/rasterizer_gles3.h" @@ -69,10 +70,12 @@ void OS_JavaScript::request_quit_callback() { } bool OS_JavaScript::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 OS_JavaScript::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(); } @@ -91,11 +94,13 @@ void OS_JavaScript::update_voices_callback(int p_size, const char **p_voice) { } Array OS_JavaScript::tts_get_voices() const { + ERR_FAIL_COND_V_MSG(!tts, Array(), "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 OS_JavaScript::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(); } @@ -112,14 +117,17 @@ void OS_JavaScript::tts_speak(const String &p_text, const String &p_voice, int p } void OS_JavaScript::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 OS_JavaScript::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 OS_JavaScript::tts_stop() { + ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); for (Map::Element *E = utterance_ids.front(); E; E = E->next()) { tts_post_utterance_event(OS::TTS_UTTERANCE_CANCELED, E->key()); } @@ -1322,6 +1330,8 @@ OS_JavaScript *OS_JavaScript::get_singleton() { } OS_JavaScript::OS_JavaScript() { + tts = GLOBAL_GET("audio/general/text_to_speech"); + // Expose method for requesting quit. pandemonium_js_os_request_quit_cb(&request_quit_callback); // Set canvas ID diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h index b5eacff0e..6bc00e9a4 100644 --- a/platform/javascript/os_javascript.h +++ b/platform/javascript/os_javascript.h @@ -93,6 +93,7 @@ private: List audio_drivers; RenderingServer *rendering_server; + bool tts; bool swap_ok_cancel; bool idb_available; bool idb_needs_sync; diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 77a37cb84..9dfdc4bb8 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -1593,36 +1593,43 @@ int OS_OSX::get_current_video_driver() const { } bool OS_OSX::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."); ERR_FAIL_COND_V(!tts, false); return [tts isSpeaking]; } bool OS_OSX::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."); ERR_FAIL_COND_V(!tts, false); return [tts isPaused]; } Array OS_OSX::tts_get_voices() const { + ERR_FAIL_COND_V_MSG(!tts, Array(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); ERR_FAIL_COND_V(!tts, Array()); return [tts getVoices]; } void OS_OSX::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."); ERR_FAIL_COND(!tts); [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 OS_OSX::tts_pause() { + ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); ERR_FAIL_COND(!tts); [tts pauseSpeaking]; } void OS_OSX::tts_resume() { + ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); ERR_FAIL_COND(!tts); [tts resumeSpeaking]; } void OS_OSX::tts_stop() { + ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); ERR_FAIL_COND(!tts); [tts stopSpeaking]; } @@ -1646,7 +1653,10 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a CGDisplayRegisterReconfigurationCallback(displays_arrangement_changed, NULL); // Init TTS - tts = [[TTS_OSX alloc] init]; + bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech"); + if (tts_enabled) { + tts = [[TTS_OSX alloc] init]; + } window_delegate = [[PandemoniumWindowDelegate alloc] init]; diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index cab236298..b78904f90 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -263,36 +263,43 @@ void OS_Windows::_touch_event(bool p_pressed, float p_x, float p_y, int idx) { }; bool OS_Windows::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."); ERR_FAIL_COND_V(!tts, false); return tts->is_speaking(); } bool OS_Windows::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."); ERR_FAIL_COND_V(!tts, false); return tts->is_paused(); } Array OS_Windows::tts_get_voices() const { + ERR_FAIL_COND_V_MSG(!tts, Array(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); ERR_FAIL_COND_V(!tts, Array()); return tts->get_voices(); } void OS_Windows::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."); ERR_FAIL_COND(!tts); tts->speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt); } void OS_Windows::tts_pause() { + ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); ERR_FAIL_COND(!tts); tts->pause(); } void OS_Windows::tts_resume() { + ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); ERR_FAIL_COND(!tts); tts->resume(); } void OS_Windows::tts_stop() { + ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); ERR_FAIL_COND(!tts); tts->stop(); } @@ -1396,7 +1403,10 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int } // Init TTS - tts = memnew(TTS_Windows); + bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech"); + if (tts_enabled) { + tts = memnew(TTS_Windows); + } use_raw_input = true; diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 850ff985c..ab6d16a57 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -115,36 +115,43 @@ static String get_atom_name(Display *p_disp, Atom p_atom) { #ifdef SPEECHD_ENABLED bool OS_X11::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."); ERR_FAIL_COND_V(!tts, false); return tts->is_speaking(); } bool OS_X11::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."); ERR_FAIL_COND_V(!tts, false); return tts->is_paused(); } Array OS_X11::tts_get_voices() const { + ERR_FAIL_COND_V_MSG(!tts, Array(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); ERR_FAIL_COND_V(!tts, Array()); return tts->get_voices(); } void OS_X11::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."); ERR_FAIL_COND(!tts); tts->speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt); } void OS_X11::tts_pause() { + ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); ERR_FAIL_COND(!tts); tts->pause(); } void OS_X11::tts_resume() { + ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); ERR_FAIL_COND(!tts); tts->resume(); } void OS_X11::tts_stop() { + ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); ERR_FAIL_COND(!tts); tts->stop(); } @@ -422,7 +429,10 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a #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 rendering_server = memnew(RenderingServerRaster); @@ -897,7 +907,9 @@ void OS_X11::finalize() { #endif #ifdef SPEECHD_ENABLED - memdelete(tts); + if (tts) { + memdelete(tts); + } #endif #ifdef JOYDEV_ENABLED