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