Skip to content

Commit a7800e1

Browse files
committed
Support Android TTS
1 parent 6796107 commit a7800e1

File tree

3 files changed

+109
-6
lines changed

3 files changed

+109
-6
lines changed

CMakeLists.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,9 @@ IF(RESET_INSTALL_PREFIX)
8484
ENDIF(NOT $ENV{FS2PATH} STREQUAL "")
8585
ENDIF(RESET_INSTALL_PREFIX)
8686

87-
IF(WIN32 OR APPLE)
87+
IF(WIN32 OR APPLE OR ANDROID)
8888
OPTION(FSO_USE_SPEECH "Use text-to-speach libraries" ON)
89-
ENDIF(WIN32 OR APPLE)
89+
ENDIF(WIN32 OR APPLE OR ANDROID)
9090

9191
IF (WIN32)
9292
OPTION(FSO_USE_VOICEREC "Enable voice recognition support" ON)
@@ -247,7 +247,7 @@ include(package)
247247
include(doxygen)
248248

249249
# Print used options to log
250-
IF(WIN32 OR APPLE)
250+
IF(WIN32 OR APPLE OR ANDROID)
251251
message(STATUS "Using text to speech: ${FSO_USE_SPEECH}")
252252
ENDIF()
253253
IF (WIN32)

cmake/finder/FindSpeech.cmake

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ if (WIN32)
99
if (NOT HAVE_SAPI_H)
1010
message(SEND_ERROR "sapi.h could not be found on your platform. Please disable speech support.")
1111
endif()
12-
elseif(APPLE)
13-
# it should just work
12+
elseif(APPLE OR ANDROID)
13+
# it should just work (TM)
1414
else()
1515
message(SEND_ERROR "Text to Speech is not supported on this platform!")
1616
endif()

code/sound/speech.cpp

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,89 @@
7171
#include "utils/unicode.h"
7272
#include "speech.h"
7373

74+
#ifdef __ANDROID__
75+
#include <jni.h>
76+
#include "SDL.h"
77+
#include "SDL_system.h"
78+
79+
static JNIEnv* env = nullptr;
80+
static jclass tts_manager = nullptr;
81+
static jmethodID tts_speak = nullptr, tts_stop = nullptr, tts_pause = nullptr, tts_resume = nullptr, tts_isSpeaking = nullptr, tts_shutdown = nullptr;
82+
83+
// Ask SDL for the JniEnviroment and get the name and path
84+
// of the external TTSManager on the android side of things
85+
// Then assign all methods ids for later use
86+
bool android_tts_init()
87+
{
88+
mprintf(("Speech : Init JNI TTSManager...\n"));
89+
env = (JNIEnv*)SDL_AndroidGetJNIEnv();
90+
if (env == nullptr) {
91+
mprintf(("Speech : Unable to get JNI environment!\n"));
92+
return false;
93+
}
94+
tts_manager = env->FindClass("com/shivansps/fsowrapper/tts/TTSManager");
95+
if (tts_manager == nullptr) {
96+
mprintf(("Speech : Unable to find the TTSManager class!\n"));
97+
return false;
98+
}
99+
100+
tts_speak = env->GetStaticMethodID(tts_manager, "speak", "(Ljava/lang/String;)Z");
101+
tts_stop = env->GetStaticMethodID(tts_manager, "stop", "()Z");
102+
tts_pause = env->GetStaticMethodID(tts_manager, "pause", "()Z");
103+
tts_resume = env->GetStaticMethodID(tts_manager, "resume", "()Z");
104+
tts_isSpeaking = env->GetStaticMethodID(tts_manager, "isSpeaking", "()Z");
105+
tts_shutdown = env->GetStaticMethodID(tts_manager, "shutdown", "()V");
106+
107+
if (!tts_speak || !tts_stop || !tts_pause || !tts_resume || !tts_isSpeaking || !tts_shutdown) {
108+
mprintf(("Speech : Unable to map at least one TTS method!\n"));
109+
return false;
110+
}
111+
mprintf(("Speech : Init Completed!\n"));
112+
return true;
113+
}
114+
115+
116+
bool android_tts_play(const char* text)
117+
{
118+
if(env == nullptr || tts_manager == nullptr || tts_speak == nullptr)
119+
return false;
120+
jstring j_txt = env->NewStringUTF(text != nullptr ? text : "");
121+
mprintf(("Speech : Playing TTS string: %s!\n",text ));
122+
jboolean ok = env->CallStaticBooleanMethod(tts_manager, tts_speak, j_txt);
123+
env->DeleteLocalRef(j_txt);
124+
if (ok != JNI_TRUE) {
125+
mprintf(("Speech : Error playing TTS string!\n"));
126+
return false;
127+
}
128+
return true;
129+
}
130+
131+
bool android_tts_stop()
132+
{
133+
return env->CallStaticBooleanMethod(tts_manager, tts_stop) == JNI_TRUE;
134+
}
135+
136+
bool android_tts_pause()
137+
{
138+
return env->CallStaticBooleanMethod(tts_manager, tts_pause) == JNI_TRUE;
139+
}
140+
141+
bool android_tts_resume()
142+
{
143+
return env->CallStaticBooleanMethod(tts_manager, tts_resume) == JNI_TRUE;
144+
}
145+
146+
bool android_tts_is_speaking()
147+
{
148+
return env->CallStaticBooleanMethod(tts_manager, tts_isSpeaking) == JNI_TRUE;
149+
}
150+
151+
void android_tts_deinit()
152+
{
153+
env->CallStaticVoidMethod(tts_manager, tts_shutdown);
154+
}
155+
#endif
156+
74157

75158
bool Speech_init = false;
76159

@@ -85,8 +168,9 @@ bool speech_init()
85168
(void **)&Voice_device);
86169

87170
Speech_init = SUCCEEDED(hr);
171+
#elif defined(__ANDROID__)
172+
Speech_init = android_tts_init();
88173
#else
89-
90174
speech_dev = open("/dev/speech", O_WRONLY | O_DIRECT);
91175
// speech_dev = fopen("/dev/speech", "w");
92176

@@ -109,6 +193,8 @@ void speech_deinit()
109193

110194
#ifdef _WIN32
111195
Voice_device->Release();
196+
#elif defined(__ANDROID__)
197+
android_tts_deinit();
112198
#else
113199
close(speech_dev);
114200
// fclose(speech_dev);
@@ -161,6 +247,9 @@ bool speech_play(const char *text)
161247

162248
speech_stop();
163249
return SUCCEEDED(Voice_device->Speak(wide_string.c_str(), SPF_ASYNC, NULL));
250+
#elif defined(__ANDROID__)
251+
speech_stop();
252+
return android_tts_play(text);
164253
#else
165254
int len = strlen(text);
166255
char Conversion_buffer[MAX_SPEECH_CHAR_LEN];
@@ -198,6 +287,8 @@ bool speech_pause()
198287
if(Speech_init == false) return true;
199288
#ifdef _WIN32
200289
return SUCCEEDED(Voice_device->Pause());
290+
#elif defined(__ANDROID__)
291+
return android_tts_pause();
201292
#else
202293
STUB_FUNCTION;
203294

@@ -210,6 +301,8 @@ bool speech_resume()
210301
if(Speech_init == false) return true;
211302
#ifdef _WIN32
212303
return SUCCEEDED(Voice_device->Resume());
304+
#elif defined(__ANDROID__)
305+
return android_tts_resume();
213306
#else
214307
STUB_FUNCTION;
215308

@@ -222,6 +315,8 @@ bool speech_stop()
222315
if(Speech_init == false) return true;
223316
#ifdef _WIN32
224317
return SUCCEEDED(Voice_device->Speak( NULL, SPF_PURGEBEFORESPEAK, NULL ));
318+
#elif defined(__ANDROID__)
319+
return android_tts_stop();
225320
#else
226321
STUB_FUNCTION;
227322

@@ -233,6 +328,9 @@ bool speech_set_volume(unsigned short volume)
233328
{
234329
#ifdef _WIN32
235330
return SUCCEEDED(Voice_device->SetVolume(volume));
331+
#elif defined(__ANDROID__)
332+
(void)volume;
333+
return true;
236334
#else
237335
STUB_FUNCTION;
238336

@@ -276,6 +374,9 @@ bool speech_set_voice(int voice)
276374
count++;
277375
}
278376
return false;
377+
#elif defined(__ANDROID__)
378+
(void)voice;
379+
return true;
279380
#else
280381
STUB_FUNCTION;
281382

@@ -294,6 +395,8 @@ bool speech_is_speaking()
294395
if (FAILED(hr)) return false;
295396

296397
return (pStatus.dwRunningState != SPRS_DONE);
398+
#elif defined(__ANDROID__)
399+
return android_tts_is_speaking();
297400
#else
298401
STUB_FUNCTION;
299402

0 commit comments

Comments
 (0)