diff options
Diffstat (limited to 'packages/TtsService/jni/android_tts_SynthProxy.cpp')
-rw-r--r-- | packages/TtsService/jni/android_tts_SynthProxy.cpp | 1071 |
1 files changed, 0 insertions, 1071 deletions
diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp deleted file mode 100644 index e00fa85..0000000 --- a/packages/TtsService/jni/android_tts_SynthProxy.cpp +++ /dev/null @@ -1,1071 +0,0 @@ -/* - * Copyright (C) 2009-2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdio.h> -#include <unistd.h> - -#define LOG_TAG "SynthProxyJNI" - -#include <utils/Log.h> -#include <nativehelper/jni.h> -#include <nativehelper/JNIHelp.h> -#include <android_runtime/AndroidRuntime.h> -#include <android/tts.h> -#include <media/AudioTrack.h> -#include <math.h> - -#include <dlfcn.h> - -#define DEFAULT_TTS_RATE 16000 -#define DEFAULT_TTS_FORMAT AudioSystem::PCM_16_BIT -#define DEFAULT_TTS_NB_CHANNELS 1 -#define DEFAULT_TTS_BUFFERSIZE 2048 -#define DEFAULT_TTS_STREAM_TYPE AudioSystem::MUSIC -#define DEFAULT_VOLUME 1.0f - -// EQ + BOOST parameters -#define FILTER_LOWSHELF_ATTENUATION -18.0f // in dB -#define FILTER_TRANSITION_FREQ 1100.0f // in Hz -#define FILTER_SHELF_SLOPE 1.0f // Q -#define FILTER_GAIN 5.5f // linear gain - -#define USAGEMODE_PLAY_IMMEDIATELY 0 -#define USAGEMODE_WRITE_TO_FILE 1 - -#define SYNTHPLAYSTATE_IS_STOPPED 0 -#define SYNTHPLAYSTATE_IS_PLAYING 1 - -using namespace android; - -// ---------------------------------------------------------------------------- -struct fields_t { - jfieldID synthProxyFieldJniData; - jmethodID synthProxyMethodPost; -}; - -// structure to hold the data that is used each time the TTS engine has synthesized more data -struct afterSynthData_t { - jint jniStorage; - int usageMode; - FILE* outputFile; - AudioSystem::stream_type streamType; -}; - -// ---------------------------------------------------------------------------- -// EQ data -double amp; -double w; -double sinw; -double cosw; -double beta; -double a0, a1, a2, b0, b1, b2; -double m_fa, m_fb, m_fc, m_fd, m_fe; -double x0; // x[n] -double x1; // x[n-1] -double x2; // x[n-2] -double out0;// y[n] -double out1;// y[n-1] -double out2;// y[n-2] - -static float fFilterLowshelfAttenuation = FILTER_LOWSHELF_ATTENUATION; -static float fFilterTransitionFreq = FILTER_TRANSITION_FREQ; -static float fFilterShelfSlope = FILTER_SHELF_SLOPE; -static float fFilterGain = FILTER_GAIN; -static bool bUseFilter = false; - -void initializeEQ() { - - amp = float(pow(10.0, fFilterLowshelfAttenuation / 40.0)); - w = 2.0 * M_PI * (fFilterTransitionFreq / DEFAULT_TTS_RATE); - sinw = float(sin(w)); - cosw = float(cos(w)); - beta = float(sqrt(amp)/fFilterShelfSlope); - - // initialize low-shelf parameters - b0 = amp * ((amp+1.0F) - ((amp-1.0F)*cosw) + (beta*sinw)); - b1 = 2.0F * amp * ((amp-1.0F) - ((amp+1.0F)*cosw)); - b2 = amp * ((amp+1.0F) - ((amp-1.0F)*cosw) - (beta*sinw)); - a0 = (amp+1.0F) + ((amp-1.0F)*cosw) + (beta*sinw); - a1 = 2.0F * ((amp-1.0F) + ((amp+1.0F)*cosw)); - a2 = -((amp+1.0F) + ((amp-1.0F)*cosw) - (beta*sinw)); - - m_fa = fFilterGain * b0/a0; - m_fb = fFilterGain * b1/a0; - m_fc = fFilterGain * b2/a0; - m_fd = a1/a0; - m_fe = a2/a0; -} - -void initializeFilter() { - x0 = 0.0f; - x1 = 0.0f; - x2 = 0.0f; - out0 = 0.0f; - out1 = 0.0f; - out2 = 0.0f; -} - -void applyFilter(int16_t* buffer, size_t sampleCount) { - - for (size_t i=0 ; i<sampleCount ; i++) { - - x0 = (double) buffer[i]; - - out0 = (m_fa*x0) + (m_fb*x1) + (m_fc*x2) + (m_fd*out1) + (m_fe*out2); - - x2 = x1; - x1 = x0; - - out2 = out1; - out1 = out0; - - if (out0 > 32767.0f) { - buffer[i] = 32767; - } else if (out0 < -32768.0f) { - buffer[i] = -32768; - } else { - buffer[i] = (int16_t) out0; - } - } -} - - -// ---------------------------------------------------------------------------- -static fields_t javaTTSFields; - -// TODO move to synth member once we have multiple simultaneous engines running -static Mutex engineMutex; - -// ---------------------------------------------------------------------------- -class SynthProxyJniStorage { - public : - jobject tts_ref; - android_tts_engine_t* mEngine; - void* mEngineLibHandle; - AudioTrack* mAudioOut; - int8_t mPlayState; - Mutex mPlayLock; - AudioSystem::stream_type mStreamType; - uint32_t mSampleRate; - uint32_t mAudFormat; - int mNbChannels; - int8_t * mBuffer; - size_t mBufferSize; - float mVolume[2]; - - SynthProxyJniStorage() { - tts_ref = NULL; - mEngine = NULL; - mEngineLibHandle = NULL; - mAudioOut = NULL; - mPlayState = SYNTHPLAYSTATE_IS_STOPPED; - mStreamType = DEFAULT_TTS_STREAM_TYPE; - mSampleRate = DEFAULT_TTS_RATE; - mAudFormat = DEFAULT_TTS_FORMAT; - mNbChannels = DEFAULT_TTS_NB_CHANNELS; - mBufferSize = DEFAULT_TTS_BUFFERSIZE; - mBuffer = new int8_t[mBufferSize]; - memset(mBuffer, 0, mBufferSize); - mVolume[AudioTrack::LEFT] = DEFAULT_VOLUME; - mVolume[AudioTrack::RIGHT] = DEFAULT_VOLUME; - } - - ~SynthProxyJniStorage() { - //LOGV("entering ~SynthProxyJniStorage()"); - killAudio(); - if (mEngine) { - mEngine->funcs->shutdown(mEngine); - mEngine = NULL; - } - if (mEngineLibHandle) { - //LOGV("~SynthProxyJniStorage(): before close library"); - int res = dlclose(mEngineLibHandle); - LOGE_IF( res != 0, "~SynthProxyJniStorage(): dlclose returned %d", res); - } - delete mBuffer; - } - - void killAudio() { - if (mAudioOut) { - mAudioOut->stop(); - delete mAudioOut; - mAudioOut = NULL; - } - } - - void createAudioOut(AudioSystem::stream_type streamType, uint32_t rate, - AudioSystem::audio_format format, int channel) { - mSampleRate = rate; - mAudFormat = format; - mNbChannels = channel; - mStreamType = streamType; - - // retrieve system properties to ensure successful creation of the - // AudioTrack object for playback - int afSampleRate; - if (AudioSystem::getOutputSamplingRate(&afSampleRate, mStreamType) != NO_ERROR) { - afSampleRate = 44100; - } - int afFrameCount; - if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) { - afFrameCount = 2048; - } - uint32_t afLatency; - if (AudioSystem::getOutputLatency(&afLatency, mStreamType) != NO_ERROR) { - afLatency = 500; - } - uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate); - if (minBufCount < 2) minBufCount = 2; - int minFrameCount = (afFrameCount * rate * minBufCount)/afSampleRate; - - mPlayLock.lock(); - mAudioOut = new AudioTrack(mStreamType, rate, format, - (channel == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO, - minFrameCount > 4096 ? minFrameCount : 4096, - 0, 0, 0, 0); // not using an AudioTrack callback - - if (mAudioOut->initCheck() != NO_ERROR) { - LOGE("createAudioOut(): AudioTrack error"); - delete mAudioOut; - mAudioOut = NULL; - } else { - //LOGI("AudioTrack OK"); - mAudioOut->setVolume(mVolume[AudioTrack::LEFT], mVolume[AudioTrack::RIGHT]); - LOGV("AudioTrack ready"); - } - mPlayLock.unlock(); - } -}; - - -// ---------------------------------------------------------------------------- -void prepAudioTrack(SynthProxyJniStorage* pJniData, AudioSystem::stream_type streamType, - uint32_t rate, AudioSystem::audio_format format, int channel) { - // Don't bother creating a new audiotrack object if the current - // object is already initialized with the same audio parameters. - if ( pJniData->mAudioOut && - (rate == pJniData->mSampleRate) && - (format == pJniData->mAudFormat) && - (channel == pJniData->mNbChannels) && - (streamType == pJniData->mStreamType) ){ - return; - } - if (pJniData->mAudioOut){ - pJniData->killAudio(); - } - pJniData->createAudioOut(streamType, rate, format, channel); -} - - -// ---------------------------------------------------------------------------- -/* - * Callback from TTS engine. - * Directly speaks using AudioTrack or write to file - */ -extern "C" android_tts_callback_status_t -__ttsSynthDoneCB(void ** pUserdata, uint32_t rate, - android_tts_audio_format_t format, int channel, - int8_t **pWav, size_t *pBufferSize, - android_tts_synth_status_t status) -{ - //LOGV("ttsSynthDoneCallback: %d bytes", bufferSize); - AudioSystem::audio_format encoding; - - if (*pUserdata == NULL){ - LOGE("userdata == NULL"); - return ANDROID_TTS_CALLBACK_HALT; - } - switch (format) { - case ANDROID_TTS_AUDIO_FORMAT_PCM_8_BIT: - encoding = AudioSystem::PCM_8_BIT; - break; - case ANDROID_TTS_AUDIO_FORMAT_PCM_16_BIT: - encoding = AudioSystem::PCM_16_BIT; - break; - default: - LOGE("Can't play, bad format"); - return ANDROID_TTS_CALLBACK_HALT; - } - afterSynthData_t* pForAfter = (afterSynthData_t*) *pUserdata; - SynthProxyJniStorage* pJniData = (SynthProxyJniStorage*)(pForAfter->jniStorage); - - if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY){ - //LOGV("Direct speech"); - - if (*pWav == NULL) { - delete pForAfter; - pForAfter = NULL; - LOGV("Null: speech has completed"); - return ANDROID_TTS_CALLBACK_HALT; - } - - if (*pBufferSize > 0) { - prepAudioTrack(pJniData, pForAfter->streamType, rate, encoding, channel); - if (pJniData->mAudioOut) { - pJniData->mPlayLock.lock(); - if(pJniData->mAudioOut->stopped() - && (pJniData->mPlayState == SYNTHPLAYSTATE_IS_PLAYING)) { - pJniData->mAudioOut->start(); - } - pJniData->mPlayLock.unlock(); - if (bUseFilter) { - applyFilter((int16_t*)*pWav, *pBufferSize/2); - } - pJniData->mAudioOut->write(*pWav, *pBufferSize); - memset(*pWav, 0, *pBufferSize); - //LOGV("AudioTrack wrote: %d bytes", bufferSize); - } else { - LOGE("Can't play, null audiotrack"); - delete pForAfter; - pForAfter = NULL; - return ANDROID_TTS_CALLBACK_HALT; - } - } - } else if (pForAfter->usageMode == USAGEMODE_WRITE_TO_FILE) { - //LOGV("Save to file"); - if (*pWav == NULL) { - delete pForAfter; - LOGV("Null: speech has completed"); - return ANDROID_TTS_CALLBACK_HALT; - } - if (*pBufferSize > 0){ - if (bUseFilter) { - applyFilter((int16_t*)*pWav, *pBufferSize/2); - } - fwrite(*pWav, 1, *pBufferSize, pForAfter->outputFile); - memset(*pWav, 0, *pBufferSize); - } - } - // Future update: - // For sync points in the speech, call back into the SynthProxy class through the - // javaTTSFields.synthProxyMethodPost methode to notify - // playback has completed if the synthesis is done or if a marker has been reached. - - if (status == ANDROID_TTS_SYNTH_DONE) { - // this struct was allocated in the original android_tts_SynthProxy_speak call, - // all processing matching this call is now done. - LOGV("Speech synthesis done."); - if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY) { - // only delete for direct playback. When writing to a file, we still have work to do - // in android_tts_SynthProxy_synthesizeToFile. The struct will be deleted there. - delete pForAfter; - pForAfter = NULL; - } - return ANDROID_TTS_CALLBACK_HALT; - } - - // we don't update the wav (output) parameter as we'll let the next callback - // write at the same location, we've consumed the data already, but we need - // to update bufferSize to let the TTS engine know how much it can write the - // next time it calls this function. - *pBufferSize = pJniData->mBufferSize; - - return ANDROID_TTS_CALLBACK_CONTINUE; -} - - -// ---------------------------------------------------------------------------- -static int -android_tts_SynthProxy_setLowShelf(JNIEnv *env, jobject thiz, jboolean applyFilter, - jfloat filterGain, jfloat attenuationInDb, jfloat freqInHz, jfloat slope) -{ - int result = ANDROID_TTS_SUCCESS; - - bUseFilter = applyFilter; - if (applyFilter) { - fFilterLowshelfAttenuation = attenuationInDb; - fFilterTransitionFreq = freqInHz; - fFilterShelfSlope = slope; - fFilterGain = filterGain; - - if (fFilterShelfSlope != 0.0f) { - initializeEQ(); - } else { - LOGE("Invalid slope, can't be null"); - result = ANDROID_TTS_FAILURE; - } - } - - return result; -} - -// ---------------------------------------------------------------------------- -static int -android_tts_SynthProxy_native_setup(JNIEnv *env, jobject thiz, - jobject weak_this, jstring nativeSoLib, jstring engConfig) -{ - int result = ANDROID_TTS_FAILURE; - - bUseFilter = false; - - SynthProxyJniStorage* pJniStorage = new SynthProxyJniStorage(); - - prepAudioTrack(pJniStorage, - DEFAULT_TTS_STREAM_TYPE, DEFAULT_TTS_RATE, DEFAULT_TTS_FORMAT, DEFAULT_TTS_NB_CHANNELS); - - const char *nativeSoLibNativeString = env->GetStringUTFChars(nativeSoLib, 0); - const char *engConfigString = env->GetStringUTFChars(engConfig, 0); - - void *engine_lib_handle = dlopen(nativeSoLibNativeString, - RTLD_NOW | RTLD_LOCAL); - if (engine_lib_handle == NULL) { - LOGE("android_tts_SynthProxy_native_setup(): engine_lib_handle == NULL"); - } else { - android_tts_engine_t * (*get_TtsEngine)() = - reinterpret_cast<android_tts_engine_t* (*)()>(dlsym(engine_lib_handle, "android_getTtsEngine")); - - // Support obsolete/legacy binary modules - if (get_TtsEngine == NULL) { - get_TtsEngine = - reinterpret_cast<android_tts_engine_t* (*)()>(dlsym(engine_lib_handle, "getTtsEngine")); - } - - pJniStorage->mEngine = (*get_TtsEngine)(); - pJniStorage->mEngineLibHandle = engine_lib_handle; - - android_tts_engine_t *engine = pJniStorage->mEngine; - if (engine) { - Mutex::Autolock l(engineMutex); - engine->funcs->init( - engine, - __ttsSynthDoneCB, - engConfigString); - } - - result = ANDROID_TTS_SUCCESS; - } - - // we use a weak reference so the SynthProxy object can be garbage collected. - pJniStorage->tts_ref = env->NewGlobalRef(weak_this); - - // save the JNI resources so we can use them (and free them) later - env->SetIntField(thiz, javaTTSFields.synthProxyFieldJniData, (int)pJniStorage); - - env->ReleaseStringUTFChars(nativeSoLib, nativeSoLibNativeString); - env->ReleaseStringUTFChars(engConfig, engConfigString); - - return result; -} - - -static void -android_tts_SynthProxy_native_finalize(JNIEnv *env, jobject thiz, jint jniData) -{ - //LOGV("entering android_tts_SynthProxy_finalize()"); - if (jniData == 0) { - //LOGE("android_tts_SynthProxy_native_finalize(): invalid JNI data"); - return; - } - - Mutex::Autolock l(engineMutex); - - SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; - env->DeleteGlobalRef(pSynthData->tts_ref); - delete pSynthData; - - env->SetIntField(thiz, javaTTSFields.synthProxyFieldJniData, 0); -} - - -static void -android_tts_SynthProxy_shutdown(JNIEnv *env, jobject thiz, jint jniData) -{ - //LOGV("entering android_tts_SynthProxy_shutdown()"); - - // do everything a call to finalize would - android_tts_SynthProxy_native_finalize(env, thiz, jniData); -} - - -static int -android_tts_SynthProxy_isLanguageAvailable(JNIEnv *env, jobject thiz, jint jniData, - jstring language, jstring country, jstring variant) -{ - int result = ANDROID_TTS_LANG_NOT_SUPPORTED; - - if (jniData == 0) { - LOGE("android_tts_SynthProxy_isLanguageAvailable(): invalid JNI data"); - return result; - } - - SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; - const char *langNativeString = env->GetStringUTFChars(language, 0); - const char *countryNativeString = env->GetStringUTFChars(country, 0); - const char *variantNativeString = env->GetStringUTFChars(variant, 0); - - android_tts_engine_t *engine = pSynthData->mEngine; - - if (engine) { - result = engine->funcs->isLanguageAvailable(engine,langNativeString, - countryNativeString, variantNativeString); - } - env->ReleaseStringUTFChars(language, langNativeString); - env->ReleaseStringUTFChars(country, countryNativeString); - env->ReleaseStringUTFChars(variant, variantNativeString); - return result; -} - -static int -android_tts_SynthProxy_setConfig(JNIEnv *env, jobject thiz, jint jniData, jstring engineConfig) -{ - int result = ANDROID_TTS_FAILURE; - - if (jniData == 0) { - LOGE("android_tts_SynthProxy_setConfig(): invalid JNI data"); - return result; - } - - Mutex::Autolock l(engineMutex); - - SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; - const char *engineConfigNativeString = env->GetStringUTFChars(engineConfig, 0); - android_tts_engine_t *engine = pSynthData->mEngine; - - if (engine) { - result = engine->funcs->setProperty(engine,ANDROID_TTS_ENGINE_PROPERTY_CONFIG, - engineConfigNativeString, strlen(engineConfigNativeString)); - } - env->ReleaseStringUTFChars(engineConfig, engineConfigNativeString); - - return result; -} - -static int -android_tts_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData, - jstring language, jstring country, jstring variant) -{ - int result = ANDROID_TTS_LANG_NOT_SUPPORTED; - - if (jniData == 0) { - LOGE("android_tts_SynthProxy_setLanguage(): invalid JNI data"); - return result; - } - - Mutex::Autolock l(engineMutex); - - SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; - const char *langNativeString = env->GetStringUTFChars(language, 0); - const char *countryNativeString = env->GetStringUTFChars(country, 0); - const char *variantNativeString = env->GetStringUTFChars(variant, 0); - android_tts_engine_t *engine = pSynthData->mEngine; - - if (engine) { - result = engine->funcs->setLanguage(engine, langNativeString, - countryNativeString, variantNativeString); - } - env->ReleaseStringUTFChars(language, langNativeString); - env->ReleaseStringUTFChars(country, countryNativeString); - env->ReleaseStringUTFChars(variant, variantNativeString); - return result; -} - - -static int -android_tts_SynthProxy_loadLanguage(JNIEnv *env, jobject thiz, jint jniData, - jstring language, jstring country, jstring variant) -{ - int result = ANDROID_TTS_LANG_NOT_SUPPORTED; - - if (jniData == 0) { - LOGE("android_tts_SynthProxy_loadLanguage(): invalid JNI data"); - return result; - } - - SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; - const char *langNativeString = env->GetStringUTFChars(language, 0); - const char *countryNativeString = env->GetStringUTFChars(country, 0); - const char *variantNativeString = env->GetStringUTFChars(variant, 0); - android_tts_engine_t *engine = pSynthData->mEngine; - - if (engine) { - result = engine->funcs->loadLanguage(engine, langNativeString, - countryNativeString, variantNativeString); - } - env->ReleaseStringUTFChars(language, langNativeString); - env->ReleaseStringUTFChars(country, countryNativeString); - env->ReleaseStringUTFChars(variant, variantNativeString); - - return result; -} - - -static int -android_tts_SynthProxy_setSpeechRate(JNIEnv *env, jobject thiz, jint jniData, - jint speechRate) -{ - int result = ANDROID_TTS_FAILURE; - - if (jniData == 0) { - LOGE("android_tts_SynthProxy_setSpeechRate(): invalid JNI data"); - return result; - } - - int bufSize = 12; - char buffer [bufSize]; - sprintf(buffer, "%d", speechRate); - - Mutex::Autolock l(engineMutex); - - SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; - //LOGI("setting speech rate to %d", speechRate); - android_tts_engine_t *engine = pSynthData->mEngine; - - if (engine) { - result = engine->funcs->setProperty(engine, "rate", buffer, bufSize); - } - - return result; -} - - -static int -android_tts_SynthProxy_setPitch(JNIEnv *env, jobject thiz, jint jniData, - jint pitch) -{ - int result = ANDROID_TTS_FAILURE; - - if (jniData == 0) { - LOGE("android_tts_SynthProxy_setPitch(): invalid JNI data"); - return result; - } - - Mutex::Autolock l(engineMutex); - - int bufSize = 12; - char buffer [bufSize]; - sprintf(buffer, "%d", pitch); - - SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; - //LOGI("setting pitch to %d", pitch); - android_tts_engine_t *engine = pSynthData->mEngine; - - if (engine) { - result = engine->funcs->setProperty(engine, "pitch", buffer, bufSize); - } - - return result; -} - - -static int -android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData, - jstring textJavaString, jstring filenameJavaString) -{ - int result = ANDROID_TTS_FAILURE; - - if (jniData == 0) { - LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid JNI data"); - return result; - } - - SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; - if (!pSynthData->mEngine) { - LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid engine handle"); - return result; - } - - initializeFilter(); - - Mutex::Autolock l(engineMutex); - - // Retrieve audio parameters before writing the file header - AudioSystem::audio_format encoding; - uint32_t rate = DEFAULT_TTS_RATE; - int channels = DEFAULT_TTS_NB_CHANNELS; - android_tts_engine_t *engine = pSynthData->mEngine; - android_tts_audio_format_t format = ANDROID_TTS_AUDIO_FORMAT_DEFAULT; - - engine->funcs->setAudioFormat(engine, &format, &rate, &channels); - - switch (format) { - case ANDROID_TTS_AUDIO_FORMAT_PCM_16_BIT: - encoding = AudioSystem::PCM_16_BIT; - break; - case ANDROID_TTS_AUDIO_FORMAT_PCM_8_BIT: - encoding = AudioSystem::PCM_8_BIT; - break; - default: - LOGE("android_tts_SynthProxy_synthesizeToFile(): engine uses invalid format"); - return result; - } - - const char *filenameNativeString = - env->GetStringUTFChars(filenameJavaString, 0); - const char *textNativeString = env->GetStringUTFChars(textJavaString, 0); - - afterSynthData_t* pForAfter = new (afterSynthData_t); - pForAfter->jniStorage = jniData; - pForAfter->usageMode = USAGEMODE_WRITE_TO_FILE; - - pForAfter->outputFile = fopen(filenameNativeString, "wb"); - - if (pForAfter->outputFile == NULL) { - LOGE("android_tts_SynthProxy_synthesizeToFile(): error creating output file"); - delete pForAfter; - return result; - } - - // Write 44 blank bytes for WAV header, then come back and fill them in - // after we've written the audio data - char header[44]; - fwrite(header, 1, 44, pForAfter->outputFile); - - unsigned int unique_identifier; - - memset(pSynthData->mBuffer, 0, pSynthData->mBufferSize); - - result = engine->funcs->synthesizeText(engine, textNativeString, - pSynthData->mBuffer, pSynthData->mBufferSize, (void *)pForAfter); - - long filelen = ftell(pForAfter->outputFile); - - int samples = (((int)filelen) - 44) / 2; - header[0] = 'R'; - header[1] = 'I'; - header[2] = 'F'; - header[3] = 'F'; - ((uint32_t *)(&header[4]))[0] = filelen - 8; - header[8] = 'W'; - header[9] = 'A'; - header[10] = 'V'; - header[11] = 'E'; - - header[12] = 'f'; - header[13] = 'm'; - header[14] = 't'; - header[15] = ' '; - - ((uint32_t *)(&header[16]))[0] = 16; // size of fmt - - int sampleSizeInByte = (encoding == AudioSystem::PCM_16_BIT ? 2 : 1); - - ((unsigned short *)(&header[20]))[0] = 1; // format - ((unsigned short *)(&header[22]))[0] = channels; // channels - ((uint32_t *)(&header[24]))[0] = rate; // samplerate - ((uint32_t *)(&header[28]))[0] = rate * sampleSizeInByte * channels;// byterate - ((unsigned short *)(&header[32]))[0] = sampleSizeInByte * channels; // block align - ((unsigned short *)(&header[34]))[0] = sampleSizeInByte * 8; // bits per sample - - header[36] = 'd'; - header[37] = 'a'; - header[38] = 't'; - header[39] = 'a'; - - ((uint32_t *)(&header[40]))[0] = samples * 2; // size of data - - // Skip back to the beginning and rewrite the header - fseek(pForAfter->outputFile, 0, SEEK_SET); - fwrite(header, 1, 44, pForAfter->outputFile); - - fflush(pForAfter->outputFile); - fclose(pForAfter->outputFile); - - delete pForAfter; - pForAfter = NULL; - - env->ReleaseStringUTFChars(textJavaString, textNativeString); - env->ReleaseStringUTFChars(filenameJavaString, filenameNativeString); - - return result; -} - - -static int -android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData, - jstring textJavaString, jint javaStreamType, jfloat volume, jfloat pan) -{ - int result = ANDROID_TTS_FAILURE; - - if (jniData == 0) { - LOGE("android_tts_SynthProxy_speak(): invalid JNI data"); - return result; - } - - initializeFilter(); - - Mutex::Autolock l(engineMutex); - - SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; - - {//scope for lock on mPlayLock - Mutex::Autolock _l(pSynthData->mPlayLock); - - pSynthData->mPlayState = SYNTHPLAYSTATE_IS_PLAYING; - - // clip volume and pan - float vol = (volume > 1.0f) ? 1.0f : (volume < 0.0f) ? 0.0f : volume; - float panning = (pan > 1.0f) ? 1.0f : (pan < -1.0f) ? -1.0f : pan; - // compute playback volume based on volume and pan, using balance rule, in order to avoid - // lowering volume when panning in center - pSynthData->mVolume[AudioTrack::LEFT] = vol; - pSynthData->mVolume[AudioTrack::RIGHT] = vol; - if (panning > 0.0f) { - pSynthData->mVolume[AudioTrack::LEFT] *= (1.0f - panning); - } else if (panning < 0.0f) { - pSynthData->mVolume[AudioTrack::RIGHT] *= (1.0f + panning); - } - - // apply the volume if there is an output - if (NULL != pSynthData->mAudioOut) { - pSynthData->mAudioOut->setVolume(pSynthData->mVolume[AudioTrack::LEFT], - pSynthData->mVolume[AudioTrack::RIGHT]); - } - - //LOGV("android_tts_SynthProxy_speak() vol=%.3f pan=%.3f, mVolume=[%.1f %.1f]", - // volume, pan, - // pSynthData->mVolume[AudioTrack::LEFT], pSynthData->mVolume[AudioTrack::RIGHT]); - } - - afterSynthData_t* pForAfter = new (afterSynthData_t); - pForAfter->jniStorage = jniData; - pForAfter->usageMode = USAGEMODE_PLAY_IMMEDIATELY; - pForAfter->streamType = (AudioSystem::stream_type) javaStreamType; - - if (pSynthData->mEngine) { - const char *textNativeString = env->GetStringUTFChars(textJavaString, 0); - memset(pSynthData->mBuffer, 0, pSynthData->mBufferSize); - android_tts_engine_t *engine = pSynthData->mEngine; - - result = engine->funcs->synthesizeText(engine, textNativeString, - pSynthData->mBuffer, pSynthData->mBufferSize, (void *)pForAfter); - env->ReleaseStringUTFChars(textJavaString, textNativeString); - } - - return result; -} - - -static int -android_tts_SynthProxy_stop(JNIEnv *env, jobject thiz, jint jniData) -{ - int result = ANDROID_TTS_FAILURE; - - if (jniData == 0) { - LOGE("android_tts_SynthProxy_stop(): invalid JNI data"); - return result; - } - - SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; - - pSynthData->mPlayLock.lock(); - pSynthData->mPlayState = SYNTHPLAYSTATE_IS_STOPPED; - if (pSynthData->mAudioOut) { - pSynthData->mAudioOut->stop(); - } - pSynthData->mPlayLock.unlock(); - - android_tts_engine_t *engine = pSynthData->mEngine; - if (engine) { - result = engine->funcs->stop(engine); - } - - return result; -} - - -static int -android_tts_SynthProxy_stopSync(JNIEnv *env, jobject thiz, jint jniData) -{ - int result = ANDROID_TTS_FAILURE; - - if (jniData == 0) { - LOGE("android_tts_SynthProxy_stop(): invalid JNI data"); - return result; - } - - // perform a regular stop - result = android_tts_SynthProxy_stop(env, thiz, jniData); - // but wait on the engine having released the engine mutex which protects - // the synthesizer resources. - engineMutex.lock(); - engineMutex.unlock(); - - return result; -} - - -static jobjectArray -android_tts_SynthProxy_getLanguage(JNIEnv *env, jobject thiz, jint jniData) -{ - if (jniData == 0) { - LOGE("android_tts_SynthProxy_getLanguage(): invalid JNI data"); - return NULL; - } - - SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; - - if (pSynthData->mEngine) { - size_t bufSize = 100; - char lang[bufSize]; - char country[bufSize]; - char variant[bufSize]; - memset(lang, 0, bufSize); - memset(country, 0, bufSize); - memset(variant, 0, bufSize); - jobjectArray retLocale = (jobjectArray)env->NewObjectArray(3, - env->FindClass("java/lang/String"), env->NewStringUTF("")); - - android_tts_engine_t *engine = pSynthData->mEngine; - engine->funcs->getLanguage(engine, lang, country, variant); - env->SetObjectArrayElement(retLocale, 0, env->NewStringUTF(lang)); - env->SetObjectArrayElement(retLocale, 1, env->NewStringUTF(country)); - env->SetObjectArrayElement(retLocale, 2, env->NewStringUTF(variant)); - return retLocale; - } else { - return NULL; - } -} - - -JNIEXPORT int JNICALL -android_tts_SynthProxy_getRate(JNIEnv *env, jobject thiz, jint jniData) -{ - if (jniData == 0) { - LOGE("android_tts_SynthProxy_getRate(): invalid JNI data"); - return 0; - } - - SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; - size_t bufSize = 100; - - char buf[bufSize]; - memset(buf, 0, bufSize); - // TODO check return codes - android_tts_engine_t *engine = pSynthData->mEngine; - if (engine) { - engine->funcs->getProperty(engine,"rate", buf, &bufSize); - } - return atoi(buf); -} - -// Dalvik VM type signatures -static JNINativeMethod gMethods[] = { - { "native_stop", - "(I)I", - (void*)android_tts_SynthProxy_stop - }, - { "native_stopSync", - "(I)I", - (void*)android_tts_SynthProxy_stopSync - }, - { "native_speak", - "(ILjava/lang/String;IFF)I", - (void*)android_tts_SynthProxy_speak - }, - { "native_synthesizeToFile", - "(ILjava/lang/String;Ljava/lang/String;)I", - (void*)android_tts_SynthProxy_synthesizeToFile - }, - { "native_isLanguageAvailable", - "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", - (void*)android_tts_SynthProxy_isLanguageAvailable - }, - { "native_setConfig", - "(ILjava/lang/String;)I", - (void*)android_tts_SynthProxy_setConfig - }, - { "native_setLanguage", - "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", - (void*)android_tts_SynthProxy_setLanguage - }, - { "native_loadLanguage", - "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", - (void*)android_tts_SynthProxy_loadLanguage - }, - { "native_setSpeechRate", - "(II)I", - (void*)android_tts_SynthProxy_setSpeechRate - }, - { "native_setPitch", - "(II)I", - (void*)android_tts_SynthProxy_setPitch - }, - { "native_getLanguage", - "(I)[Ljava/lang/String;", - (void*)android_tts_SynthProxy_getLanguage - }, - { "native_getRate", - "(I)I", - (void*)android_tts_SynthProxy_getRate - }, - { "native_shutdown", - "(I)V", - (void*)android_tts_SynthProxy_shutdown - }, - { "native_setup", - "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)I", - (void*)android_tts_SynthProxy_native_setup - }, - { "native_setLowShelf", - "(ZFFFF)I", - (void*)android_tts_SynthProxy_setLowShelf - }, - { "native_finalize", - "(I)V", - (void*)android_tts_SynthProxy_native_finalize - } -}; - -#define SP_JNIDATA_FIELD_NAME "mJniData" -#define SP_POSTSPEECHSYNTHESIZED_METHOD_NAME "postNativeSpeechSynthesizedInJava" - -static const char* const kClassPathName = "android/tts/SynthProxy"; - -jint JNI_OnLoad(JavaVM* vm, void* reserved) -{ - JNIEnv* env = NULL; - jint result = -1; - jclass clazz; - - if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { - LOGE("ERROR: GetEnv failed\n"); - goto bail; - } - assert(env != NULL); - - clazz = env->FindClass(kClassPathName); - if (clazz == NULL) { - LOGE("Can't find %s", kClassPathName); - goto bail; - } - - javaTTSFields.synthProxyFieldJniData = NULL; - javaTTSFields.synthProxyMethodPost = NULL; - - javaTTSFields.synthProxyFieldJniData = env->GetFieldID(clazz, - SP_JNIDATA_FIELD_NAME, "I"); - if (javaTTSFields.synthProxyFieldJniData == NULL) { - LOGE("Can't find %s.%s field", kClassPathName, SP_JNIDATA_FIELD_NAME); - goto bail; - } - - javaTTSFields.synthProxyMethodPost = env->GetStaticMethodID(clazz, - SP_POSTSPEECHSYNTHESIZED_METHOD_NAME, "(Ljava/lang/Object;II)V"); - if (javaTTSFields.synthProxyMethodPost == NULL) { - LOGE("Can't find %s.%s method", kClassPathName, SP_POSTSPEECHSYNTHESIZED_METHOD_NAME); - goto bail; - } - - if (jniRegisterNativeMethods( - env, kClassPathName, gMethods, NELEM(gMethods)) < 0) - goto bail; - - /* success -- return valid version number */ - result = JNI_VERSION_1_4; - - bail: - return result; -} |