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