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 <media/AudioTrack.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 52using namespace android; 53 54// ---------------------------------------------------------------------------- 55// EQ data 56static double m_fa, m_fb, m_fc, m_fd, m_fe; 57static double x0; // x[n] 58static double x1; // x[n-1] 59static double x2; // x[n-2] 60static double out0;// y[n] 61static double out1;// y[n-1] 62static double out2;// y[n-2] 63 64static float fFilterLowshelfAttenuation = FILTER_LOWSHELF_ATTENUATION; 65static float fFilterTransitionFreq = FILTER_TRANSITION_FREQ; 66static float fFilterShelfSlope = FILTER_SHELF_SLOPE; 67static float fFilterGain = FILTER_GAIN; 68static bool bUseFilter = false; 69 70void 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 92void 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 101void 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 128static jmethodID synthesisRequest_start; 129static jmethodID synthesisRequest_audioAvailable; 130static jmethodID synthesisRequest_done; 131 132static Mutex engineMutex; 133 134 135 136typedef android_tts_engine_t *(*android_tts_entrypoint)(); 137 138// ---------------------------------------------------------------------------- 139class SynthProxyJniStorage { 140 public: 141 android_tts_engine_t *mEngine; 142 void *mEngineLibHandle; 143 int8_t *mBuffer; 144 size_t mBufferSize; 145 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 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 170struct SynthRequestData { 171 SynthProxyJniStorage *jniStorage; 172 JNIEnv *env; 173 jobject request; 174 bool startCalled; 175}; 176 177// ---------------------------------------------------------------------------- 178 179/* 180 * Calls into Java 181 */ 182 183static 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 195static 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 222static 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 247static 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 */ 259extern "C" android_tts_callback_status_t 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// ---------------------------------------------------------------------------- 311static int 312com_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// ---------------------------------------------------------------------------- 334static jint 335com_android_tts_compat_SynthProxy_native_setup(JNIEnv *env, jobject thiz, 336 jstring nativeSoLib, jstring engConfig) 337{ 338 int 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<jint>(pSynthData); 367 } 368 } 369 370 env->ReleaseStringUTFChars(nativeSoLib, nativeSoLibNativeString); 371 env->ReleaseStringUTFChars(engConfig, engConfigString); 372 373 return result; 374} 375 376static SynthProxyJniStorage *getSynthData(jint jniData) 377{ 378 if (jniData == 0) { 379 ALOGE("Engine not initialized"); 380 return NULL; 381 } 382 return reinterpret_cast<SynthProxyJniStorage *>(jniData); 383} 384 385static void 386com_android_tts_compat_SynthProxy_native_finalize(JNIEnv *env, jobject thiz, jint 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 398static void 399com_android_tts_compat_SynthProxy_shutdown(JNIEnv *env, jobject thiz, jint jniData) 400{ 401 com_android_tts_compat_SynthProxy_native_finalize(env, thiz, jniData); 402} 403 404static int 405com_android_tts_compat_SynthProxy_isLanguageAvailable(JNIEnv *env, jobject thiz, jint 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 result; 430} 431 432static int 433com_android_tts_compat_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint 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 result; 460} 461 462 463static int 464com_android_tts_compat_SynthProxy_loadLanguage(JNIEnv *env, jobject thiz, jint 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 result; 489} 490 491static int 492com_android_tts_compat_SynthProxy_setProperty(JNIEnv *env, jobject thiz, jint 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 result; 517} 518 519static int 520com_android_tts_compat_SynthProxy_speak(JNIEnv *env, jobject thiz, jint 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 result; 551} 552 553static int 554com_android_tts_compat_SynthProxy_stop(JNIEnv *env, jobject thiz, jint 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 engine->funcs->stop(engine); 567} 568 569static int 570com_android_tts_compat_SynthProxy_stopSync(JNIEnv *env, jobject thiz, jint 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 result; 585} 586 587static jobjectArray 588com_android_tts_compat_SynthProxy_getLanguage(JNIEnv *env, jobject thiz, jint 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 619static JNINativeMethod gMethods[] = { 620 { "native_stop", 621 "(I)I", 622 (void*)com_android_tts_compat_SynthProxy_stop 623 }, 624 { "native_stopSync", 625 "(I)I", 626 (void*)com_android_tts_compat_SynthProxy_stopSync 627 }, 628 { "native_speak", 629 "(ILjava/lang/String;Landroid/speech/tts/SynthesisCallback;)I", 630 (void*)com_android_tts_compat_SynthProxy_speak 631 }, 632 { "native_isLanguageAvailable", 633 "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", 634 (void*)com_android_tts_compat_SynthProxy_isLanguageAvailable 635 }, 636 { "native_setLanguage", 637 "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", 638 (void*)com_android_tts_compat_SynthProxy_setLanguage 639 }, 640 { "native_loadLanguage", 641 "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", 642 (void*)com_android_tts_compat_SynthProxy_loadLanguage 643 }, 644 { "native_setProperty", 645 "(ILjava/lang/String;Ljava/lang/String;)I", 646 (void*)com_android_tts_compat_SynthProxy_setProperty 647 }, 648 { "native_getLanguage", 649 "(I)[Ljava/lang/String;", 650 (void*)com_android_tts_compat_SynthProxy_getLanguage 651 }, 652 { "native_shutdown", 653 "(I)V", 654 (void*)com_android_tts_compat_SynthProxy_shutdown 655 }, 656 { "native_setup", 657 "(Ljava/lang/String;Ljava/lang/String;)I", 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 "(I)V", 666 (void*)com_android_tts_compat_SynthProxy_native_finalize 667 } 668}; 669 670jint 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