1 /*
2 * Copyright (C) 2009-2010 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <stdio.h>
18 #include <unistd.h>
19
20 #define LOG_TAG "SynthProxyJNI"
21
22 #include <utils/Log.h>
23 #include <nativehelper/jni.h>
24 #include <nativehelper/JNIHelp.h>
25 #include <android_runtime/AndroidRuntime.h>
26 #include <android_runtime/Log.h>
27 #include <math.h>
28
29 #include <dlfcn.h>
30
31 #include "tts.h"
32
33 #define DEFAULT_TTS_RATE 16000
34 #define DEFAULT_TTS_BUFFERSIZE 2048
35
36 // EQ + BOOST parameters
37 #define FILTER_LOWSHELF_ATTENUATION -18.0f // in dB
38 #define FILTER_TRANSITION_FREQ 1100.0f // in Hz
39 #define FILTER_SHELF_SLOPE 1.0f // Q
40 #define FILTER_GAIN 5.5f // linear gain
41
42 // android.media.AudioFormat.ENCODING_ values
43 //
44 // Note that these constants are different from those
45 // defined in the native code (system/audio.h and others).
46 // We use them because we use a Java AudioTrack to play
47 // back our data.
48 #define AUDIO_FORMAT_ENCODING_DEFAULT 1
49 #define AUDIO_FORMAT_ENCODING_PCM_16_BIT 2
50 #define AUDIO_FORMAT_ENCODING_PCM_8_BIT 3
51
52 using namespace android;
53
54 // ----------------------------------------------------------------------------
55 // EQ data
56 static double m_fa, m_fb, m_fc, m_fd, m_fe;
57 static double x0; // x[n]
58 static double x1; // x[n-1]
59 static double x2; // x[n-2]
60 static double out0;// y[n]
61 static double out1;// y[n-1]
62 static double out2;// y[n-2]
63
64 static float fFilterLowshelfAttenuation = FILTER_LOWSHELF_ATTENUATION;
65 static float fFilterTransitionFreq = FILTER_TRANSITION_FREQ;
66 static float fFilterShelfSlope = FILTER_SHELF_SLOPE;
67 static float fFilterGain = FILTER_GAIN;
68 static bool bUseFilter = false;
69
initializeEQ()70 void initializeEQ() {
71 double amp = float(pow(10.0, fFilterLowshelfAttenuation / 40.0));
72 double w = 2.0 * M_PI * (fFilterTransitionFreq / DEFAULT_TTS_RATE);
73 double sinw = float(sin(w));
74 double cosw = float(cos(w));
75 double beta = float(sqrt(amp)/fFilterShelfSlope);
76
77 // initialize low-shelf parameters
78 double b0 = amp * ((amp+1.0F) - ((amp-1.0F)*cosw) + (beta*sinw));
79 double b1 = 2.0F * amp * ((amp-1.0F) - ((amp+1.0F)*cosw));
80 double b2 = amp * ((amp+1.0F) - ((amp-1.0F)*cosw) - (beta*sinw));
81 double a0 = (amp+1.0F) + ((amp-1.0F)*cosw) + (beta*sinw);
82 double a1 = 2.0F * ((amp-1.0F) + ((amp+1.0F)*cosw));
83 double a2 = -((amp+1.0F) + ((amp-1.0F)*cosw) - (beta*sinw));
84
85 m_fa = fFilterGain * b0/a0;
86 m_fb = fFilterGain * b1/a0;
87 m_fc = fFilterGain * b2/a0;
88 m_fd = a1/a0;
89 m_fe = a2/a0;
90 }
91
initializeFilter()92 void initializeFilter() {
93 x0 = 0.0f;
94 x1 = 0.0f;
95 x2 = 0.0f;
96 out0 = 0.0f;
97 out1 = 0.0f;
98 out2 = 0.0f;
99 }
100
applyFilter(int16_t * buffer,size_t sampleCount)101 void applyFilter(int16_t* buffer, size_t sampleCount) {
102
103 for (size_t i=0 ; i<sampleCount ; i++) {
104
105 x0 = (double) buffer[i];
106
107 out0 = (m_fa*x0) + (m_fb*x1) + (m_fc*x2) + (m_fd*out1) + (m_fe*out2);
108
109 x2 = x1;
110 x1 = x0;
111
112 out2 = out1;
113 out1 = out0;
114
115 if (out0 > 32767.0f) {
116 buffer[i] = 32767;
117 } else if (out0 < -32768.0f) {
118 buffer[i] = -32768;
119 } else {
120 buffer[i] = (int16_t) out0;
121 }
122 }
123 }
124
125
126 // ----------------------------------------------------------------------------
127
128 static jmethodID synthesisRequest_start;
129 static jmethodID synthesisRequest_audioAvailable;
130 static jmethodID synthesisRequest_done;
131
132 static Mutex engineMutex;
133
134
135
136 typedef android_tts_engine_t *(*android_tts_entrypoint)();
137
138 // ----------------------------------------------------------------------------
139 class SynthProxyJniStorage {
140 public:
141 android_tts_engine_t *mEngine;
142 void *mEngineLibHandle;
143 int8_t *mBuffer;
144 size_t mBufferSize;
145
SynthProxyJniStorage()146 SynthProxyJniStorage() {
147 mEngine = NULL;
148 mEngineLibHandle = NULL;
149 mBufferSize = DEFAULT_TTS_BUFFERSIZE;
150 mBuffer = new int8_t[mBufferSize];
151 memset(mBuffer, 0, mBufferSize);
152 }
153
~SynthProxyJniStorage()154 ~SynthProxyJniStorage() {
155 if (mEngine) {
156 mEngine->funcs->shutdown(mEngine);
157 mEngine = NULL;
158 }
159 if (mEngineLibHandle) {
160 int res = dlclose(mEngineLibHandle);
161 ALOGE_IF( res != 0, "~SynthProxyJniStorage(): dlclose returned %d", res);
162 }
163 delete[] mBuffer;
164 }
165
166 };
167
168 // ----------------------------------------------------------------------------
169
170 struct SynthRequestData {
171 SynthProxyJniStorage *jniStorage;
172 JNIEnv *env;
173 jobject request;
174 bool startCalled;
175 };
176
177 // ----------------------------------------------------------------------------
178
179 /*
180 * Calls into Java
181 */
182
checkException(JNIEnv * env)183 static bool checkException(JNIEnv *env)
184 {
185 jthrowable ex = env->ExceptionOccurred();
186 if (ex == NULL) {
187 return false;
188 }
189 env->ExceptionClear();
190 LOGE_EX(env, ex);
191 env->DeleteLocalRef(ex);
192 return true;
193 }
194
callRequestStart(JNIEnv * env,jobject request,uint32_t rate,android_tts_audio_format_t format,int channelCount)195 static int callRequestStart(JNIEnv *env, jobject request,
196 uint32_t rate, android_tts_audio_format_t format, int channelCount)
197 {
198 int encoding;
199
200 switch (format) {
201 case ANDROID_TTS_AUDIO_FORMAT_DEFAULT:
202 encoding = AUDIO_FORMAT_ENCODING_DEFAULT;
203 break;
204 case ANDROID_TTS_AUDIO_FORMAT_PCM_8_BIT:
205 encoding = AUDIO_FORMAT_ENCODING_PCM_8_BIT;
206 break;
207 case ANDROID_TTS_AUDIO_FORMAT_PCM_16_BIT:
208 encoding = AUDIO_FORMAT_ENCODING_PCM_16_BIT;
209 break;
210 default:
211 ALOGE("Can't play, bad format");
212 return ANDROID_TTS_FAILURE;
213 }
214
215 int result = env->CallIntMethod(request, synthesisRequest_start, rate, encoding, channelCount);
216 if (checkException(env)) {
217 return ANDROID_TTS_FAILURE;
218 }
219 return result;
220 }
221
callRequestAudioAvailable(JNIEnv * env,jobject request,int8_t * buffer,int offset,int length)222 static int callRequestAudioAvailable(JNIEnv *env, jobject request, int8_t *buffer,
223 int offset, int length)
224 {
225 // TODO: Not nice to have to copy the buffer. Use ByteBuffer?
226 jbyteArray javaBuffer = env->NewByteArray(length);
227 if (javaBuffer == NULL) {
228 ALOGE("Failed to allocate byte array");
229 return ANDROID_TTS_FAILURE;
230 }
231
232 env->SetByteArrayRegion(javaBuffer, 0, length, static_cast<jbyte *>(buffer + offset));
233 if (checkException(env)) {
234 env->DeleteLocalRef(javaBuffer);
235 return ANDROID_TTS_FAILURE;
236 }
237 int result = env->CallIntMethod(request, synthesisRequest_audioAvailable,
238 javaBuffer, offset, length);
239 if (checkException(env)) {
240 env->DeleteLocalRef(javaBuffer);
241 return ANDROID_TTS_FAILURE;
242 }
243 env->DeleteLocalRef(javaBuffer);
244 return result;
245 }
246
callRequestDone(JNIEnv * env,jobject request)247 static int callRequestDone(JNIEnv *env, jobject request)
248 {
249 int result = env->CallIntMethod(request, synthesisRequest_done);
250 if (checkException(env)) {
251 return ANDROID_TTS_FAILURE;
252 }
253 return result;
254 }
255
256 /*
257 * Callback from TTS engine.
258 */
259 extern "C" android_tts_callback_status_t
__ttsSynthDoneCB(void ** pUserdata,uint32_t rate,android_tts_audio_format_t format,int channelCount,int8_t ** pWav,size_t * pBufferSize,android_tts_synth_status_t status)260 __ttsSynthDoneCB(void **pUserdata, uint32_t rate,
261 android_tts_audio_format_t format, int channelCount,
262 int8_t **pWav, size_t *pBufferSize,
263 android_tts_synth_status_t status)
264 {
265 if (*pUserdata == NULL){
266 ALOGE("userdata == NULL");
267 return ANDROID_TTS_CALLBACK_HALT;
268 }
269
270 SynthRequestData *pRequestData = static_cast<SynthRequestData*>(*pUserdata);
271 SynthProxyJniStorage *pJniData = pRequestData->jniStorage;
272 JNIEnv *env = pRequestData->env;
273
274 if (*pWav != NULL && *pBufferSize > 0) {
275 if (bUseFilter) {
276 applyFilter(reinterpret_cast<int16_t*>(*pWav), *pBufferSize/2);
277 }
278
279 if (!pRequestData->startCalled) {
280 // TODO: is encoding one of the AudioFormat.ENCODING_* constants?
281 pRequestData->startCalled = true;
282 if (callRequestStart(env, pRequestData->request, rate, format, channelCount)
283 != ANDROID_TTS_SUCCESS) {
284 return ANDROID_TTS_CALLBACK_HALT;
285 }
286 }
287
288 if (callRequestAudioAvailable(env, pRequestData->request, *pWav, 0, *pBufferSize)
289 != ANDROID_TTS_SUCCESS) {
290 return ANDROID_TTS_CALLBACK_HALT;
291 }
292
293 memset(*pWav, 0, *pBufferSize);
294 }
295
296 if (pWav == NULL || status == ANDROID_TTS_SYNTH_DONE) {
297 callRequestDone(env, pRequestData->request);
298 env->DeleteGlobalRef(pRequestData->request);
299 delete pRequestData;
300 pRequestData = NULL;
301 return ANDROID_TTS_CALLBACK_HALT;
302 }
303
304 *pBufferSize = pJniData->mBufferSize;
305
306 return ANDROID_TTS_CALLBACK_CONTINUE;
307 }
308
309
310 // ----------------------------------------------------------------------------
311 static jint
com_android_tts_compat_SynthProxy_setLowShelf(JNIEnv * env,jobject thiz,jboolean applyFilter,jfloat filterGain,jfloat attenuationInDb,jfloat freqInHz,jfloat slope)312 com_android_tts_compat_SynthProxy_setLowShelf(JNIEnv *env, jobject thiz, jboolean applyFilter,
313 jfloat filterGain, jfloat attenuationInDb, jfloat freqInHz, jfloat slope)
314 {
315 bUseFilter = applyFilter;
316 if (applyFilter) {
317 fFilterLowshelfAttenuation = attenuationInDb;
318 fFilterTransitionFreq = freqInHz;
319 fFilterShelfSlope = slope;
320 fFilterGain = filterGain;
321
322 if (fFilterShelfSlope != 0.0f) {
323 initializeEQ();
324 } else {
325 ALOGE("Invalid slope, can't be null");
326 return ANDROID_TTS_FAILURE;
327 }
328 }
329
330 return ANDROID_TTS_SUCCESS;
331 }
332
333 // ----------------------------------------------------------------------------
334 static jlong
com_android_tts_compat_SynthProxy_native_setup(JNIEnv * env,jobject thiz,jstring nativeSoLib,jstring engConfig)335 com_android_tts_compat_SynthProxy_native_setup(JNIEnv *env, jobject thiz,
336 jstring nativeSoLib, jstring engConfig)
337 {
338 jlong result = 0;
339 bUseFilter = false;
340
341 const char *nativeSoLibNativeString = env->GetStringUTFChars(nativeSoLib, 0);
342 const char *engConfigString = env->GetStringUTFChars(engConfig, 0);
343
344 void *engine_lib_handle = dlopen(nativeSoLibNativeString,
345 RTLD_NOW | RTLD_LOCAL);
346 if (engine_lib_handle == NULL) {
347 ALOGE("com_android_tts_compat_SynthProxy_native_setup(): engine_lib_handle == NULL");
348 } else {
349 android_tts_entrypoint get_TtsEngine =
350 reinterpret_cast<android_tts_entrypoint>(dlsym(engine_lib_handle, "android_getTtsEngine"));
351
352 // Support obsolete/legacy binary modules
353 if (get_TtsEngine == NULL) {
354 get_TtsEngine =
355 reinterpret_cast<android_tts_entrypoint>(dlsym(engine_lib_handle, "getTtsEngine"));
356 }
357
358 android_tts_engine_t *engine = (*get_TtsEngine)();
359 if (engine) {
360 Mutex::Autolock l(engineMutex);
361 engine->funcs->init(engine, __ttsSynthDoneCB, engConfigString);
362
363 SynthProxyJniStorage *pSynthData = new SynthProxyJniStorage();
364 pSynthData->mEngine = engine;
365 pSynthData->mEngineLibHandle = engine_lib_handle;
366 result = reinterpret_cast<jlong>(pSynthData);
367 }
368 }
369
370 env->ReleaseStringUTFChars(nativeSoLib, nativeSoLibNativeString);
371 env->ReleaseStringUTFChars(engConfig, engConfigString);
372
373 return result;
374 }
375
getSynthData(jlong jniData)376 static SynthProxyJniStorage *getSynthData(jlong jniData)
377 {
378 if (jniData == 0) {
379 ALOGE("Engine not initialized");
380 return NULL;
381 }
382 return reinterpret_cast<SynthProxyJniStorage *>(jniData);
383 }
384
385 static void
com_android_tts_compat_SynthProxy_native_finalize(JNIEnv * env,jobject thiz,jlong jniData)386 com_android_tts_compat_SynthProxy_native_finalize(JNIEnv *env, jobject thiz, jlong jniData)
387 {
388 SynthProxyJniStorage* pSynthData = getSynthData(jniData);
389 if (pSynthData == NULL) {
390 return;
391 }
392
393 Mutex::Autolock l(engineMutex);
394
395 delete pSynthData;
396 }
397
398 static void
com_android_tts_compat_SynthProxy_shutdown(JNIEnv * env,jobject thiz,jlong jniData)399 com_android_tts_compat_SynthProxy_shutdown(JNIEnv *env, jobject thiz, jlong jniData)
400 {
401 com_android_tts_compat_SynthProxy_native_finalize(env, thiz, jniData);
402 }
403
404 static jint
com_android_tts_compat_SynthProxy_isLanguageAvailable(JNIEnv * env,jobject thiz,jlong jniData,jstring language,jstring country,jstring variant)405 com_android_tts_compat_SynthProxy_isLanguageAvailable(JNIEnv *env, jobject thiz, jlong jniData,
406 jstring language, jstring country, jstring variant)
407 {
408 SynthProxyJniStorage* pSynthData = getSynthData(jniData);
409 if (pSynthData == NULL) {
410 return ANDROID_TTS_LANG_NOT_SUPPORTED;
411 }
412
413 android_tts_engine_t *engine = pSynthData->mEngine;
414 if (!engine) {
415 return ANDROID_TTS_LANG_NOT_SUPPORTED;
416 }
417
418 const char *langNativeString = env->GetStringUTFChars(language, 0);
419 const char *countryNativeString = env->GetStringUTFChars(country, 0);
420 const char *variantNativeString = env->GetStringUTFChars(variant, 0);
421
422 int result = engine->funcs->isLanguageAvailable(engine, langNativeString,
423 countryNativeString, variantNativeString);
424
425 env->ReleaseStringUTFChars(language, langNativeString);
426 env->ReleaseStringUTFChars(country, countryNativeString);
427 env->ReleaseStringUTFChars(variant, variantNativeString);
428
429 return (jint) result;
430 }
431
432 static jint
com_android_tts_compat_SynthProxy_setLanguage(JNIEnv * env,jobject thiz,jlong jniData,jstring language,jstring country,jstring variant)433 com_android_tts_compat_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jlong jniData,
434 jstring language, jstring country, jstring variant)
435 {
436 SynthProxyJniStorage* pSynthData = getSynthData(jniData);
437 if (pSynthData == NULL) {
438 return ANDROID_TTS_LANG_NOT_SUPPORTED;
439 }
440
441 Mutex::Autolock l(engineMutex);
442
443 android_tts_engine_t *engine = pSynthData->mEngine;
444 if (!engine) {
445 return ANDROID_TTS_LANG_NOT_SUPPORTED;
446 }
447
448 const char *langNativeString = env->GetStringUTFChars(language, 0);
449 const char *countryNativeString = env->GetStringUTFChars(country, 0);
450 const char *variantNativeString = env->GetStringUTFChars(variant, 0);
451
452 int result = engine->funcs->setLanguage(engine, langNativeString,
453 countryNativeString, variantNativeString);
454
455 env->ReleaseStringUTFChars(language, langNativeString);
456 env->ReleaseStringUTFChars(country, countryNativeString);
457 env->ReleaseStringUTFChars(variant, variantNativeString);
458
459 return (jint) result;
460 }
461
462
463 static jint
com_android_tts_compat_SynthProxy_loadLanguage(JNIEnv * env,jobject thiz,jlong jniData,jstring language,jstring country,jstring variant)464 com_android_tts_compat_SynthProxy_loadLanguage(JNIEnv *env, jobject thiz, jlong jniData,
465 jstring language, jstring country, jstring variant)
466 {
467 SynthProxyJniStorage* pSynthData = getSynthData(jniData);
468 if (pSynthData == NULL) {
469 return ANDROID_TTS_LANG_NOT_SUPPORTED;
470 }
471
472 android_tts_engine_t *engine = pSynthData->mEngine;
473 if (!engine) {
474 return ANDROID_TTS_LANG_NOT_SUPPORTED;
475 }
476
477 const char *langNativeString = env->GetStringUTFChars(language, 0);
478 const char *countryNativeString = env->GetStringUTFChars(country, 0);
479 const char *variantNativeString = env->GetStringUTFChars(variant, 0);
480
481 int result = engine->funcs->loadLanguage(engine, langNativeString,
482 countryNativeString, variantNativeString);
483
484 env->ReleaseStringUTFChars(language, langNativeString);
485 env->ReleaseStringUTFChars(country, countryNativeString);
486 env->ReleaseStringUTFChars(variant, variantNativeString);
487
488 return (jint) result;
489 }
490
491 static jint
com_android_tts_compat_SynthProxy_setProperty(JNIEnv * env,jobject thiz,jlong jniData,jstring name,jstring value)492 com_android_tts_compat_SynthProxy_setProperty(JNIEnv *env, jobject thiz, jlong jniData,
493 jstring name, jstring value)
494 {
495 SynthProxyJniStorage* pSynthData = getSynthData(jniData);
496 if (pSynthData == NULL) {
497 return ANDROID_TTS_FAILURE;
498 }
499
500 Mutex::Autolock l(engineMutex);
501
502 android_tts_engine_t *engine = pSynthData->mEngine;
503 if (!engine) {
504 return ANDROID_TTS_FAILURE;
505 }
506
507 const char *nameChars = env->GetStringUTFChars(name, 0);
508 const char *valueChars = env->GetStringUTFChars(value, 0);
509 size_t valueLength = env->GetStringUTFLength(value);
510
511 int result = engine->funcs->setProperty(engine, nameChars, valueChars, valueLength);
512
513 env->ReleaseStringUTFChars(name, nameChars);
514 env->ReleaseStringUTFChars(name, valueChars);
515
516 return (jint) result;
517 }
518
519 static jint
com_android_tts_compat_SynthProxy_speak(JNIEnv * env,jobject thiz,jlong jniData,jstring textJavaString,jobject request)520 com_android_tts_compat_SynthProxy_speak(JNIEnv *env, jobject thiz, jlong jniData,
521 jstring textJavaString, jobject request)
522 {
523 SynthProxyJniStorage* pSynthData = getSynthData(jniData);
524 if (pSynthData == NULL) {
525 return ANDROID_TTS_FAILURE;
526 }
527
528 initializeFilter();
529
530 Mutex::Autolock l(engineMutex);
531
532 android_tts_engine_t *engine = pSynthData->mEngine;
533 if (!engine) {
534 return ANDROID_TTS_FAILURE;
535 }
536
537 SynthRequestData *pRequestData = new SynthRequestData;
538 pRequestData->jniStorage = pSynthData;
539 pRequestData->env = env;
540 pRequestData->request = env->NewGlobalRef(request);
541 pRequestData->startCalled = false;
542
543 const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
544 memset(pSynthData->mBuffer, 0, pSynthData->mBufferSize);
545
546 int result = engine->funcs->synthesizeText(engine, textNativeString,
547 pSynthData->mBuffer, pSynthData->mBufferSize, static_cast<void *>(pRequestData));
548 env->ReleaseStringUTFChars(textJavaString, textNativeString);
549
550 return (jint) result;
551 }
552
553 static jint
com_android_tts_compat_SynthProxy_stop(JNIEnv * env,jobject thiz,jlong jniData)554 com_android_tts_compat_SynthProxy_stop(JNIEnv *env, jobject thiz, jlong jniData)
555 {
556 SynthProxyJniStorage* pSynthData = getSynthData(jniData);
557 if (pSynthData == NULL) {
558 return ANDROID_TTS_FAILURE;
559 }
560
561 android_tts_engine_t *engine = pSynthData->mEngine;
562 if (!engine) {
563 return ANDROID_TTS_FAILURE;
564 }
565
566 return (jint) engine->funcs->stop(engine);
567 }
568
569 static jint
com_android_tts_compat_SynthProxy_stopSync(JNIEnv * env,jobject thiz,jlong jniData)570 com_android_tts_compat_SynthProxy_stopSync(JNIEnv *env, jobject thiz, jlong jniData)
571 {
572 SynthProxyJniStorage* pSynthData = getSynthData(jniData);
573 if (pSynthData == NULL) {
574 return ANDROID_TTS_FAILURE;
575 }
576
577 // perform a regular stop
578 int result = com_android_tts_compat_SynthProxy_stop(env, thiz, jniData);
579 // but wait on the engine having released the engine mutex which protects
580 // the synthesizer resources.
581 engineMutex.lock();
582 engineMutex.unlock();
583
584 return (jint) result;
585 }
586
587 static jobjectArray
com_android_tts_compat_SynthProxy_getLanguage(JNIEnv * env,jobject thiz,jlong jniData)588 com_android_tts_compat_SynthProxy_getLanguage(JNIEnv *env, jobject thiz, jlong jniData)
589 {
590 SynthProxyJniStorage* pSynthData = getSynthData(jniData);
591 if (pSynthData == NULL) {
592 return NULL;
593 }
594
595 if (pSynthData->mEngine) {
596 size_t bufSize = 100;
597 char lang[bufSize];
598 char country[bufSize];
599 char variant[bufSize];
600 memset(lang, 0, bufSize);
601 memset(country, 0, bufSize);
602 memset(variant, 0, bufSize);
603 jobjectArray retLocale = (jobjectArray)env->NewObjectArray(3,
604 env->FindClass("java/lang/String"), env->NewStringUTF(""));
605
606 android_tts_engine_t *engine = pSynthData->mEngine;
607 engine->funcs->getLanguage(engine, lang, country, variant);
608 env->SetObjectArrayElement(retLocale, 0, env->NewStringUTF(lang));
609 env->SetObjectArrayElement(retLocale, 1, env->NewStringUTF(country));
610 env->SetObjectArrayElement(retLocale, 2, env->NewStringUTF(variant));
611 return retLocale;
612 } else {
613 return NULL;
614 }
615 }
616
617
618 // Dalvik VM type signatures
619 static JNINativeMethod gMethods[] = {
620 { "native_stop",
621 "(J)I",
622 (void*)com_android_tts_compat_SynthProxy_stop
623 },
624 { "native_stopSync",
625 "(J)I",
626 (void*)com_android_tts_compat_SynthProxy_stopSync
627 },
628 { "native_speak",
629 "(JLjava/lang/String;Landroid/speech/tts/SynthesisCallback;)I",
630 (void*)com_android_tts_compat_SynthProxy_speak
631 },
632 { "native_isLanguageAvailable",
633 "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
634 (void*)com_android_tts_compat_SynthProxy_isLanguageAvailable
635 },
636 { "native_setLanguage",
637 "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
638 (void*)com_android_tts_compat_SynthProxy_setLanguage
639 },
640 { "native_loadLanguage",
641 "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
642 (void*)com_android_tts_compat_SynthProxy_loadLanguage
643 },
644 { "native_setProperty",
645 "(JLjava/lang/String;Ljava/lang/String;)I",
646 (void*)com_android_tts_compat_SynthProxy_setProperty
647 },
648 { "native_getLanguage",
649 "(J)[Ljava/lang/String;",
650 (void*)com_android_tts_compat_SynthProxy_getLanguage
651 },
652 { "native_shutdown",
653 "(J)V",
654 (void*)com_android_tts_compat_SynthProxy_shutdown
655 },
656 { "native_setup",
657 "(Ljava/lang/String;Ljava/lang/String;)J",
658 (void*)com_android_tts_compat_SynthProxy_native_setup
659 },
660 { "native_setLowShelf",
661 "(ZFFFF)I",
662 (void*)com_android_tts_compat_SynthProxy_setLowShelf
663 },
664 { "native_finalize",
665 "(J)V",
666 (void*)com_android_tts_compat_SynthProxy_native_finalize
667 }
668 };
669
JNI_OnLoad(JavaVM * vm,void * reserved)670 jint JNI_OnLoad(JavaVM* vm, void* reserved)
671 {
672 JNIEnv* env = NULL;
673
674 if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
675 ALOGE("ERROR: GetEnv failed\n");
676 return -1;
677 }
678 assert(env != NULL);
679
680 jclass classSynthesisRequest = env->FindClass(
681 "android/speech/tts/SynthesisCallback");
682 if (classSynthesisRequest == NULL) {
683 return -1;
684 }
685
686 synthesisRequest_start = env->GetMethodID(classSynthesisRequest,
687 "start", "(III)I");
688 if (synthesisRequest_start == NULL) {
689 return -1;
690 }
691
692 synthesisRequest_audioAvailable = env->GetMethodID(classSynthesisRequest,
693 "audioAvailable", "([BII)I");
694 if (synthesisRequest_audioAvailable == NULL) {
695 return -1;
696 }
697
698 synthesisRequest_done = env->GetMethodID(classSynthesisRequest,
699 "done", "()I");
700 if (synthesisRequest_done == NULL) {
701 return -1;
702 }
703
704 if (jniRegisterNativeMethods(
705 env, "com/android/tts/compat/SynthProxy", gMethods, NELEM(gMethods)) < 0) {
706 return -1;
707 }
708
709 /* success -- return valid version number */
710 return JNI_VERSION_1_4;
711 }
712