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