summaryrefslogtreecommitdiffstats
path: root/pico/compat/jni
diff options
context:
space:
mode:
authorBjorn Bringert <bringert@android.com>2011-03-14 18:42:10 +0000
committerBjorn Bringert <bringert@android.com>2011-04-15 09:57:18 +0100
commitf41e1f808fbcf9014c0a5668fba4eff6dd051366 (patch)
tree41540ee669544331efd78d35f97dabed6a9cff9e /pico/compat/jni
parent8a1dd2de697c7e5fc3290d1115b51ed0bcdcd726 (diff)
downloadexternal_svox-f41e1f808fbcf9014c0a5668fba4eff6dd051366.zip
external_svox-f41e1f808fbcf9014c0a5668fba4eff6dd051366.tar.gz
external_svox-f41e1f808fbcf9014c0a5668fba4eff6dd051366.tar.bz2
Port Pico to new TTS engine API
Requires TTS engine API added in change I7614ff788e11f897e87052f684f1b4938d539fb7 The compatibility layer in pico/compat/src/com/android/tts/compat/SynthProxy.java pico/compat/jni/com_android_tts_compat_SynthProxy.cpp is based on these files removed from the old TTS engine framework: frameworks/base/packages/TtsService/src/android/tts/SynthProxy.java frameworks/base/packages/TtsService/jni/android_tts_SynthProxy.cpp Bug: 4150618 Change-Id: I7a2cca6b5cfbac6158a87fad69cc796140adb2f3
Diffstat (limited to 'pico/compat/jni')
-rwxr-xr-xpico/compat/jni/Android.mk30
-rw-r--r--pico/compat/jni/com_android_tts_compat_SynthProxy.cpp706
-rw-r--r--pico/compat/jni/tts.h313
3 files changed, 1049 insertions, 0 deletions
diff --git a/pico/compat/jni/Android.mk b/pico/compat/jni/Android.mk
new file mode 100755
index 0000000..242047a
--- /dev/null
+++ b/pico/compat/jni/Android.mk
@@ -0,0 +1,30 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= libttscompat
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES:= \
+ android_tts_SynthProxy.cpp
+
+LOCAL_C_INCLUDES += \
+ frameworks/base/native/include \
+ $(JNI_H_INCLUDE)
+
+LOCAL_SHARED_LIBRARIES := \
+ libandroid_runtime \
+ libnativehelper \
+ libmedia \
+ libutils \
+ libcutils
+
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -ldl
+else
+ LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+LOCAL_ARM_MODE := arm
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/pico/compat/jni/com_android_tts_compat_SynthProxy.cpp b/pico/compat/jni/com_android_tts_compat_SynthProxy.cpp
new file mode 100644
index 0000000..c9c38c4
--- /dev/null
+++ b/pico/compat/jni/com_android_tts_compat_SynthProxy.cpp
@@ -0,0 +1,706 @@
+/*
+ * 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 <media/AudioTrack.h>
+#include <math.h>
+
+#include <dlfcn.h>
+
+#include "tts.h"
+
+#define DEFAULT_TTS_RATE 16000
+#define DEFAULT_TTS_BUFFERSIZE 2048
+
+// 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
+
+// android.media.AudioFormat.ENCODING_ values
+#define AUDIO_FORMAT_ENCODING_DEFAULT 1
+#define AUDIO_FORMAT_ENCODING_PCM_16_BIT 2
+#define AUDIO_FORMAT_ENCODING_PCM_8_BIT 3
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+// EQ data
+static double m_fa, m_fb, m_fc, m_fd, m_fe;
+static double x0; // x[n]
+static double x1; // x[n-1]
+static double x2; // x[n-2]
+static double out0;// y[n]
+static double out1;// y[n-1]
+static 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() {
+ double amp = float(pow(10.0, fFilterLowshelfAttenuation / 40.0));
+ double w = 2.0 * M_PI * (fFilterTransitionFreq / DEFAULT_TTS_RATE);
+ double sinw = float(sin(w));
+ double cosw = float(cos(w));
+ double beta = float(sqrt(amp)/fFilterShelfSlope);
+
+ // initialize low-shelf parameters
+ double b0 = amp * ((amp+1.0F) - ((amp-1.0F)*cosw) + (beta*sinw));
+ double b1 = 2.0F * amp * ((amp-1.0F) - ((amp+1.0F)*cosw));
+ double b2 = amp * ((amp+1.0F) - ((amp-1.0F)*cosw) - (beta*sinw));
+ double a0 = (amp+1.0F) + ((amp-1.0F)*cosw) + (beta*sinw);
+ double a1 = 2.0F * ((amp-1.0F) + ((amp+1.0F)*cosw));
+ double 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 jmethodID synthesisRequest_start;
+static jmethodID synthesisRequest_audioAvailable;
+static jmethodID synthesisRequest_done;
+
+static Mutex engineMutex;
+
+
+
+typedef android_tts_engine_t *(*android_tts_entrypoint)();
+
+// ----------------------------------------------------------------------------
+class SynthProxyJniStorage {
+ public:
+ android_tts_engine_t *mEngine;
+ void *mEngineLibHandle;
+ int8_t *mBuffer;
+ size_t mBufferSize;
+
+ SynthProxyJniStorage() {
+ mEngine = NULL;
+ mEngineLibHandle = NULL;
+ mBufferSize = DEFAULT_TTS_BUFFERSIZE;
+ mBuffer = new int8_t[mBufferSize];
+ memset(mBuffer, 0, mBufferSize);
+ }
+
+ ~SynthProxyJniStorage() {
+ if (mEngine) {
+ mEngine->funcs->shutdown(mEngine);
+ mEngine = NULL;
+ }
+ if (mEngineLibHandle) {
+ int res = dlclose(mEngineLibHandle);
+ LOGE_IF( res != 0, "~SynthProxyJniStorage(): dlclose returned %d", res);
+ }
+ delete[] mBuffer;
+ }
+
+};
+
+// ----------------------------------------------------------------------------
+
+struct SynthRequestData {
+ SynthProxyJniStorage *jniStorage;
+ JNIEnv *env;
+ jobject request;
+ bool startCalled;
+};
+
+// ----------------------------------------------------------------------------
+
+/*
+ * Calls into Java
+ */
+
+static bool checkException(JNIEnv *env)
+{
+ jthrowable ex = env->ExceptionOccurred();
+ if (ex == NULL) {
+ return false;
+ }
+ env->ExceptionClear();
+ LOGE_EX(env, ex);
+ env->DeleteLocalRef(ex);
+ return true;
+}
+
+static int callRequestStart(JNIEnv *env, jobject request,
+ uint32_t rate, android_tts_audio_format_t format, int channelCount)
+{
+ int encoding;
+
+ switch (format) {
+ case ANDROID_TTS_AUDIO_FORMAT_DEFAULT:
+ encoding = AUDIO_FORMAT_ENCODING_DEFAULT;
+ break;
+ case ANDROID_TTS_AUDIO_FORMAT_PCM_8_BIT:
+ encoding = AUDIO_FORMAT_ENCODING_PCM_8_BIT;
+ break;
+ case ANDROID_TTS_AUDIO_FORMAT_PCM_16_BIT:
+ encoding = AUDIO_FORMAT_ENCODING_PCM_16_BIT;
+ break;
+ default:
+ LOGE("Can't play, bad format");
+ return ANDROID_TTS_FAILURE;
+ }
+
+ int result = env->CallIntMethod(request, synthesisRequest_start, rate, encoding, channelCount);
+ if (checkException(env)) {
+ return ANDROID_TTS_FAILURE;
+ }
+ return result;
+}
+
+static int callRequestAudioAvailable(JNIEnv *env, jobject request, int8_t *buffer,
+ int offset, int length)
+{
+ // TODO: Not nice to have to copy the buffer. Use ByteBuffer?
+ jbyteArray javaBuffer = env->NewByteArray(length);
+ if (javaBuffer == NULL) {
+ LOGE("Failed to allocate byte array");
+ return ANDROID_TTS_FAILURE;
+ }
+
+ env->SetByteArrayRegion(javaBuffer, 0, length, static_cast<jbyte *>(buffer + offset));
+ if (checkException(env)) {
+ env->DeleteLocalRef(javaBuffer);
+ return ANDROID_TTS_FAILURE;
+ }
+ int result = env->CallIntMethod(request, synthesisRequest_audioAvailable,
+ javaBuffer, offset, length);
+ if (checkException(env)) {
+ env->DeleteLocalRef(javaBuffer);
+ return ANDROID_TTS_FAILURE;
+ }
+ env->DeleteLocalRef(javaBuffer);
+ return result;
+}
+
+static int callRequestDone(JNIEnv *env, jobject request)
+{
+ int result = env->CallIntMethod(request, synthesisRequest_done);
+ if (checkException(env)) {
+ return ANDROID_TTS_FAILURE;
+ }
+ return result;
+}
+
+/*
+ * Callback from TTS engine.
+ */
+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)
+{
+ if (*pUserdata == NULL){
+ LOGE("userdata == NULL");
+ return ANDROID_TTS_CALLBACK_HALT;
+ }
+
+ SynthRequestData *pRequestData = static_cast<SynthRequestData*>(*pUserdata);
+ SynthProxyJniStorage *pJniData = pRequestData->jniStorage;
+ JNIEnv *env = pRequestData->env;
+
+ if (*pWav != NULL && *pBufferSize > 0) {
+ if (bUseFilter) {
+ applyFilter(reinterpret_cast<int16_t*>(*pWav), *pBufferSize/2);
+ }
+
+ if (!pRequestData->startCalled) {
+ // TODO: is encoding one of the AudioFormat.ENCODING_* constants?
+ pRequestData->startCalled = true;
+ if (callRequestStart(env, pRequestData->request, rate, format, channelCount)
+ != ANDROID_TTS_SUCCESS) {
+ return ANDROID_TTS_CALLBACK_HALT;
+ }
+ }
+
+ if (callRequestAudioAvailable(env, pRequestData->request, *pWav, 0, *pBufferSize)
+ != ANDROID_TTS_SUCCESS) {
+ return ANDROID_TTS_CALLBACK_HALT;
+ }
+
+ memset(*pWav, 0, *pBufferSize);
+ }
+
+ if (pWav == NULL || status == ANDROID_TTS_SYNTH_DONE) {
+ callRequestDone(env, pRequestData->request);
+ env->DeleteGlobalRef(pRequestData->request);
+ delete pRequestData;
+ pRequestData = NULL;
+ return ANDROID_TTS_CALLBACK_HALT;
+ }
+
+ *pBufferSize = pJniData->mBufferSize;
+
+ return ANDROID_TTS_CALLBACK_CONTINUE;
+}
+
+
+// ----------------------------------------------------------------------------
+static int
+com_android_tts_compat_SynthProxy_setLowShelf(JNIEnv *env, jobject thiz, jboolean applyFilter,
+ jfloat filterGain, jfloat attenuationInDb, jfloat freqInHz, jfloat slope)
+{
+ 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");
+ return ANDROID_TTS_FAILURE;
+ }
+ }
+
+ return ANDROID_TTS_SUCCESS;
+}
+
+// ----------------------------------------------------------------------------
+static jint
+com_android_tts_compat_SynthProxy_native_setup(JNIEnv *env, jobject thiz,
+ jstring nativeSoLib, jstring engConfig)
+{
+ int result = 0;
+ bUseFilter = false;
+
+ 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("com_android_tts_compat_SynthProxy_native_setup(): engine_lib_handle == NULL");
+ } else {
+ android_tts_entrypoint get_TtsEngine =
+ reinterpret_cast<android_tts_entrypoint>(dlsym(engine_lib_handle, "android_getTtsEngine"));
+
+ // Support obsolete/legacy binary modules
+ if (get_TtsEngine == NULL) {
+ get_TtsEngine =
+ reinterpret_cast<android_tts_entrypoint>(dlsym(engine_lib_handle, "getTtsEngine"));
+ }
+
+ android_tts_engine_t *engine = (*get_TtsEngine)();
+ if (engine) {
+ Mutex::Autolock l(engineMutex);
+ engine->funcs->init(engine, __ttsSynthDoneCB, engConfigString);
+
+ SynthProxyJniStorage *pSynthData = new SynthProxyJniStorage();
+ pSynthData->mEngine = engine;
+ pSynthData->mEngineLibHandle = engine_lib_handle;
+ result = reinterpret_cast<jint>(pSynthData);
+ }
+ }
+
+ env->ReleaseStringUTFChars(nativeSoLib, nativeSoLibNativeString);
+ env->ReleaseStringUTFChars(engConfig, engConfigString);
+
+ return result;
+}
+
+static SynthProxyJniStorage *getSynthData(jint jniData)
+{
+ if (jniData == 0) {
+ LOGE("Engine not initialized");
+ return NULL;
+ }
+ return reinterpret_cast<SynthProxyJniStorage *>(jniData);
+}
+
+static void
+com_android_tts_compat_SynthProxy_native_finalize(JNIEnv *env, jobject thiz, jint jniData)
+{
+ SynthProxyJniStorage* pSynthData = getSynthData(jniData);
+ if (pSynthData == NULL) {
+ return;
+ }
+
+ Mutex::Autolock l(engineMutex);
+
+ delete pSynthData;
+}
+
+static void
+com_android_tts_compat_SynthProxy_shutdown(JNIEnv *env, jobject thiz, jint jniData)
+{
+ com_android_tts_compat_SynthProxy_native_finalize(env, thiz, jniData);
+}
+
+static int
+com_android_tts_compat_SynthProxy_isLanguageAvailable(JNIEnv *env, jobject thiz, jint jniData,
+ jstring language, jstring country, jstring variant)
+{
+ SynthProxyJniStorage* pSynthData = getSynthData(jniData);
+ if (pSynthData == NULL) {
+ return ANDROID_TTS_LANG_NOT_SUPPORTED;
+ }
+
+ android_tts_engine_t *engine = pSynthData->mEngine;
+ if (!engine) {
+ return ANDROID_TTS_LANG_NOT_SUPPORTED;
+ }
+
+ const char *langNativeString = env->GetStringUTFChars(language, 0);
+ const char *countryNativeString = env->GetStringUTFChars(country, 0);
+ const char *variantNativeString = env->GetStringUTFChars(variant, 0);
+
+ int result = engine->funcs->isLanguageAvailable(engine, langNativeString,
+ countryNativeString, variantNativeString);
+
+ env->ReleaseStringUTFChars(language, langNativeString);
+ env->ReleaseStringUTFChars(country, countryNativeString);
+ env->ReleaseStringUTFChars(variant, variantNativeString);
+
+ return result;
+}
+
+static int
+com_android_tts_compat_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData,
+ jstring language, jstring country, jstring variant)
+{
+ SynthProxyJniStorage* pSynthData = getSynthData(jniData);
+ if (pSynthData == NULL) {
+ return ANDROID_TTS_LANG_NOT_SUPPORTED;
+ }
+
+ Mutex::Autolock l(engineMutex);
+
+ android_tts_engine_t *engine = pSynthData->mEngine;
+ if (!engine) {
+ return ANDROID_TTS_LANG_NOT_SUPPORTED;
+ }
+
+ const char *langNativeString = env->GetStringUTFChars(language, 0);
+ const char *countryNativeString = env->GetStringUTFChars(country, 0);
+ const char *variantNativeString = env->GetStringUTFChars(variant, 0);
+
+ int result = engine->funcs->setLanguage(engine, langNativeString,
+ countryNativeString, variantNativeString);
+
+ env->ReleaseStringUTFChars(language, langNativeString);
+ env->ReleaseStringUTFChars(country, countryNativeString);
+ env->ReleaseStringUTFChars(variant, variantNativeString);
+
+ return result;
+}
+
+
+static int
+com_android_tts_compat_SynthProxy_loadLanguage(JNIEnv *env, jobject thiz, jint jniData,
+ jstring language, jstring country, jstring variant)
+{
+ SynthProxyJniStorage* pSynthData = getSynthData(jniData);
+ if (pSynthData == NULL) {
+ return ANDROID_TTS_LANG_NOT_SUPPORTED;
+ }
+
+ android_tts_engine_t *engine = pSynthData->mEngine;
+ if (!engine) {
+ return ANDROID_TTS_LANG_NOT_SUPPORTED;
+ }
+
+ const char *langNativeString = env->GetStringUTFChars(language, 0);
+ const char *countryNativeString = env->GetStringUTFChars(country, 0);
+ const char *variantNativeString = env->GetStringUTFChars(variant, 0);
+
+ int result = engine->funcs->loadLanguage(engine, langNativeString,
+ countryNativeString, variantNativeString);
+
+ env->ReleaseStringUTFChars(language, langNativeString);
+ env->ReleaseStringUTFChars(country, countryNativeString);
+ env->ReleaseStringUTFChars(variant, variantNativeString);
+
+ return result;
+}
+
+static int
+com_android_tts_compat_SynthProxy_setProperty(JNIEnv *env, jobject thiz, jint jniData,
+ jstring name, jstring value)
+{
+ SynthProxyJniStorage* pSynthData = getSynthData(jniData);
+ if (pSynthData == NULL) {
+ return ANDROID_TTS_FAILURE;
+ }
+
+ Mutex::Autolock l(engineMutex);
+
+ android_tts_engine_t *engine = pSynthData->mEngine;
+ if (!engine) {
+ return ANDROID_TTS_FAILURE;
+ }
+
+ const char *nameChars = env->GetStringUTFChars(name, 0);
+ const char *valueChars = env->GetStringUTFChars(value, 0);
+ size_t valueLength = env->GetStringUTFLength(value);
+
+ int result = engine->funcs->setProperty(engine, nameChars, valueChars, valueLength);
+
+ env->ReleaseStringUTFChars(name, nameChars);
+ env->ReleaseStringUTFChars(name, valueChars);
+
+ return result;
+}
+
+static int
+com_android_tts_compat_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData,
+ jstring textJavaString, jobject request)
+{
+ SynthProxyJniStorage* pSynthData = getSynthData(jniData);
+ if (pSynthData == NULL) {
+ return ANDROID_TTS_FAILURE;
+ }
+
+ initializeFilter();
+
+ Mutex::Autolock l(engineMutex);
+
+ android_tts_engine_t *engine = pSynthData->mEngine;
+ if (!engine) {
+ return ANDROID_TTS_FAILURE;
+ }
+
+ SynthRequestData *pRequestData = new SynthRequestData;
+ pRequestData->jniStorage = pSynthData;
+ pRequestData->env = env;
+ pRequestData->request = env->NewGlobalRef(request);
+ pRequestData->startCalled = false;
+
+ const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
+ memset(pSynthData->mBuffer, 0, pSynthData->mBufferSize);
+
+ int result = engine->funcs->synthesizeText(engine, textNativeString,
+ pSynthData->mBuffer, pSynthData->mBufferSize, static_cast<void *>(pRequestData));
+ env->ReleaseStringUTFChars(textJavaString, textNativeString);
+
+ return result;
+}
+
+static int
+com_android_tts_compat_SynthProxy_stop(JNIEnv *env, jobject thiz, jint jniData)
+{
+ SynthProxyJniStorage* pSynthData = getSynthData(jniData);
+ if (pSynthData == NULL) {
+ return ANDROID_TTS_FAILURE;
+ }
+
+ android_tts_engine_t *engine = pSynthData->mEngine;
+ if (!engine) {
+ return ANDROID_TTS_FAILURE;
+ }
+
+ return engine->funcs->stop(engine);
+}
+
+static int
+com_android_tts_compat_SynthProxy_stopSync(JNIEnv *env, jobject thiz, jint jniData)
+{
+ SynthProxyJniStorage* pSynthData = getSynthData(jniData);
+ if (pSynthData == NULL) {
+ return ANDROID_TTS_FAILURE;
+ }
+
+ // perform a regular stop
+ int result = com_android_tts_compat_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
+com_android_tts_compat_SynthProxy_getLanguage(JNIEnv *env, jobject thiz, jint jniData)
+{
+ SynthProxyJniStorage* pSynthData = getSynthData(jniData);
+ if (pSynthData == NULL) {
+ return NULL;
+ }
+
+ 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;
+ }
+}
+
+
+// Dalvik VM type signatures
+static JNINativeMethod gMethods[] = {
+ { "native_stop",
+ "(I)I",
+ (void*)com_android_tts_compat_SynthProxy_stop
+ },
+ { "native_stopSync",
+ "(I)I",
+ (void*)com_android_tts_compat_SynthProxy_stopSync
+ },
+ { "native_speak",
+ "(ILjava/lang/String;Landroid/speech/tts/SynthesisRequest;)I",
+ (void*)com_android_tts_compat_SynthProxy_speak
+ },
+ { "native_isLanguageAvailable",
+ "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
+ (void*)com_android_tts_compat_SynthProxy_isLanguageAvailable
+ },
+ { "native_setLanguage",
+ "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
+ (void*)com_android_tts_compat_SynthProxy_setLanguage
+ },
+ { "native_loadLanguage",
+ "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
+ (void*)com_android_tts_compat_SynthProxy_loadLanguage
+ },
+ { "native_setProperty",
+ "(ILjava/lang/String;Ljava/lang/String;)I",
+ (void*)com_android_tts_compat_SynthProxy_setProperty
+ },
+ { "native_getLanguage",
+ "(I)[Ljava/lang/String;",
+ (void*)com_android_tts_compat_SynthProxy_getLanguage
+ },
+ { "native_shutdown",
+ "(I)V",
+ (void*)com_android_tts_compat_SynthProxy_shutdown
+ },
+ { "native_setup",
+ "(Ljava/lang/String;Ljava/lang/String;)I",
+ (void*)com_android_tts_compat_SynthProxy_native_setup
+ },
+ { "native_setLowShelf",
+ "(ZFFFF)I",
+ (void*)com_android_tts_compat_SynthProxy_setLowShelf
+ },
+ { "native_finalize",
+ "(I)V",
+ (void*)com_android_tts_compat_SynthProxy_native_finalize
+ }
+};
+
+jint JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+ JNIEnv* env = NULL;
+
+ if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+ LOGE("ERROR: GetEnv failed\n");
+ return -1;
+ }
+ assert(env != NULL);
+
+ jclass classSynthesisRequest = env->FindClass(
+ "android/speech/tts/SynthesisRequest");
+ if (classSynthesisRequest == NULL) {
+ return -1;
+ }
+
+ synthesisRequest_start = env->GetMethodID(classSynthesisRequest,
+ "start", "(III)I");
+ if (synthesisRequest_start == NULL) {
+ return -1;
+ }
+
+ synthesisRequest_audioAvailable = env->GetMethodID(classSynthesisRequest,
+ "audioAvailable", "([BII)I");
+ if (synthesisRequest_audioAvailable == NULL) {
+ return -1;
+ }
+
+ synthesisRequest_done = env->GetMethodID(classSynthesisRequest,
+ "done", "()I");
+ if (synthesisRequest_done == NULL) {
+ return -1;
+ }
+
+ if (jniRegisterNativeMethods(
+ env, "com/android/tts/compat/SynthProxy", gMethods, NELEM(gMethods)) < 0) {
+ return -1;
+ }
+
+ /* success -- return valid version number */
+ return JNI_VERSION_1_4;
+}
diff --git a/pico/compat/jni/tts.h b/pico/compat/jni/tts.h
new file mode 100644
index 0000000..fb15108
--- /dev/null
+++ b/pico/compat/jni/tts.h
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+#ifndef ANDROID_TTS_H
+#define ANDROID_TTS_H
+
+// This header defines the interface used by the Android platform
+// to access Text-To-Speech functionality in shared libraries that implement
+// speech synthesis and the management of resources associated with the
+// synthesis.
+
+// The shared library must contain a function named "android_getTtsEngine"
+// that returns an 'android_tts_engine_t' instance.
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ANDROID_TTS_ENGINE_PROPERTY_CONFIG "engineConfig"
+#define ANDROID_TTS_ENGINE_PROPERTY_PITCH "pitch"
+#define ANDROID_TTS_ENGINE_PROPERTY_RATE "rate"
+#define ANDROID_TTS_ENGINE_PROPERTY_VOLUME "volume"
+
+typedef enum {
+ ANDROID_TTS_SUCCESS = 0,
+ ANDROID_TTS_FAILURE = -1,
+ ANDROID_TTS_FEATURE_UNSUPPORTED = -2,
+ ANDROID_TTS_VALUE_INVALID = -3,
+ ANDROID_TTS_PROPERTY_UNSUPPORTED = -4,
+ ANDROID_TTS_PROPERTY_SIZE_TOO_SMALL = -5,
+ ANDROID_TTS_MISSING_RESOURCES = -6
+} android_tts_result_t;
+
+typedef enum {
+ ANDROID_TTS_LANG_COUNTRY_VAR_AVAILABLE = 2,
+ ANDROID_TTS_LANG_COUNTRY_AVAILABLE = 1,
+ ANDROID_TTS_LANG_AVAILABLE = 0,
+ ANDROID_TTS_LANG_MISSING_DATA = -1,
+ ANDROID_TTS_LANG_NOT_SUPPORTED = -2
+} android_tts_support_result_t;
+
+typedef enum {
+ ANDROID_TTS_SYNTH_DONE = 0,
+ ANDROID_TTS_SYNTH_PENDING = 1
+} android_tts_synth_status_t;
+
+typedef enum {
+ ANDROID_TTS_CALLBACK_HALT = 0,
+ ANDROID_TTS_CALLBACK_CONTINUE = 1
+} android_tts_callback_status_t;
+
+// Supported audio formats
+typedef enum {
+ ANDROID_TTS_AUDIO_FORMAT_INVALID = -1,
+ ANDROID_TTS_AUDIO_FORMAT_DEFAULT = 0,
+ ANDROID_TTS_AUDIO_FORMAT_PCM_16_BIT = 1,
+ ANDROID_TTS_AUDIO_FORMAT_PCM_8_BIT = 2,
+} android_tts_audio_format_t;
+
+
+/* An android_tts_engine_t object can be anything, but must have,
+ * as its first field, a pointer to a table of functions.
+ *
+ * See the full definition of struct android_tts_engine_t_funcs_t
+ * below for details.
+ */
+typedef struct android_tts_engine_funcs_t android_tts_engine_funcs_t;
+
+typedef struct {
+ android_tts_engine_funcs_t *funcs;
+} android_tts_engine_t;
+
+/* This function must be located in the TTS Engine shared library
+ * and must return the address of an android_tts_engine_t library.
+ */
+extern android_tts_engine_t *android_getTtsEngine();
+
+/* Including the old version for legacy support (Froyo compatibility).
+ * This should return the same thing as android_getTtsEngine.
+ */
+extern "C" android_tts_engine_t *getTtsEngine();
+
+// A callback type used to notify the framework of new synthetized
+// audio samples, status will be SYNTH_DONE for the last sample of
+// the last request, of SYNTH_PENDING otherwise.
+//
+// This is passed by the framework to the engine through the
+// 'engine_init' function (see below).
+//
+// The callback for synthesis completed takes:
+// @param [inout] void *& - The userdata pointer set in the original
+// synth call
+// @param [in] uint32_t - Track sampling rate in Hz
+// @param [in] uint32_t - The audio format
+// @param [in] int - The number of channels
+// @param [inout] int8_t *& - A buffer of audio data only valid during the
+// execution of the callback
+// @param [inout] size_t & - The size of the buffer
+// @param [in] tts_synth_status - indicate whether the synthesis is done, or
+// if more data is to be synthesized.
+// @return TTS_CALLBACK_HALT to indicate the synthesis must stop,
+// TTS_CALLBACK_CONTINUE to indicate the synthesis must continue if
+// there is more data to produce.
+typedef android_tts_callback_status_t (*android_tts_synth_cb_t)
+ (void **pUserData,
+ uint32_t trackSamplingHz,
+ android_tts_audio_format_t audioFormat,
+ int channelCount,
+ int8_t **pAudioBuffer,
+ size_t *pBufferSize,
+ android_tts_synth_status_t status);
+
+
+// The table of function pointers that the android_tts_engine_t must point to.
+// Note that each of these functions will take a handle to the engine itself
+// as their first parameter.
+//
+
+struct android_tts_engine_funcs_t {
+ // reserved fields, ignored by the framework
+ // they must be placed here to ensure binary compatibility
+ // of legacy binary plugins.
+ void *reserved[2];
+
+ // Initialize the TTS engine and returns whether initialization succeeded.
+ // @param synthDoneCBPtr synthesis callback function pointer
+ // @return TTS_SUCCESS, or TTS_FAILURE
+ android_tts_result_t (*init)
+ (void *engine,
+ android_tts_synth_cb_t synthDonePtr,
+ const char *engineConfig);
+
+ // Shut down the TTS engine and releases all associated resources.
+ // @return TTS_SUCCESS, or TTS_FAILURE
+ android_tts_result_t (*shutdown)
+ (void *engine);
+
+ // Interrupt synthesis and flushes any synthesized data that hasn't been
+ // output yet. This will block until callbacks underway are completed.
+ // @return TTS_SUCCESS, or TTS_FAILURE
+ android_tts_result_t (*stop)
+ (void *engine);
+
+ // Returns the level of support for the language, country and variant.
+ // @return TTS_LANG_COUNTRY_VAR_AVAILABLE if the language, country and variant are supported,
+ // and the corresponding resources are correctly installed
+ // TTS_LANG_COUNTRY_AVAILABLE if the language and country are supported and the
+ // corresponding resources are correctly installed, but there is no match for
+ // the specified variant
+ // TTS_LANG_AVAILABLE if the language is supported and the
+ // corresponding resources are correctly installed, but there is no match for
+ // the specified country and variant
+ // TTS_LANG_MISSING_DATA if the required resources to provide any level of support
+ // for the language are not correctly installed
+ // TTS_LANG_NOT_SUPPORTED if the language is not supported by the TTS engine.
+ android_tts_support_result_t (*isLanguageAvailable)
+ (void *engine,
+ const char *lang,
+ const char *country,
+ const char *variant);
+
+ // Load the resources associated with the specified language. The loaded
+ // language will only be used once a call to setLanguage() with the same
+ // language value is issued. Language and country values are coded according to the ISO three
+ // letter codes for languages and countries, as can be retrieved from a java.util.Locale
+ // instance. The variant value is encoded as the variant string retrieved from a
+ // java.util.Locale instance built with that variant data.
+ // @param lang pointer to the ISO three letter code for the language
+ // @param country pointer to the ISO three letter code for the country
+ // @param variant pointer to the variant code
+ // @return TTS_SUCCESS, or TTS_FAILURE
+ android_tts_result_t (*loadLanguage)
+ (void *engine,
+ const char *lang,
+ const char *country,
+ const char *variant);
+
+ // Load the resources associated with the specified language, country and Locale variant.
+ // The loaded language will only be used once a call to setLanguageFromLocale() with the same
+ // language value is issued. Language and country values are coded according to the ISO three
+ // letter codes for languages and countries, as can be retrieved from a java.util.Locale
+ // instance. The variant value is encoded as the variant string retrieved from a
+ // java.util.Locale instance built with that variant data.
+ // @param lang pointer to the ISO three letter code for the language
+ // @param country pointer to the ISO three letter code for the country
+ // @param variant pointer to the variant code
+ // @return TTS_SUCCESS, or TTS_FAILURE
+ android_tts_result_t (*setLanguage)
+ (void *engine,
+ const char *lang,
+ const char *country,
+ const char *variant);
+
+ // Retrieve the currently set language, country and variant, or empty strings if none of
+ // parameters have been set. Language and country are represented by their 3-letter ISO code
+ // @param[out] pointer to the retrieved 3-letter code language value
+ // @param[out] pointer to the retrieved 3-letter code country value
+ // @param[out] pointer to the retrieved variant value
+ // @return TTS_SUCCESS, or TTS_FAILURE
+ android_tts_result_t (*getLanguage)
+ (void *engine,
+ char *language,
+ char *country,
+ char *variant);
+
+ // Notifies the engine what audio parameters should be used for the synthesis.
+ // This is meant to be used as a hint, the engine implementation will set the output values
+ // to those of the synthesis format, based on a given hint.
+ // @param[inout] encoding in: the desired audio sample format
+ // out: the format used by the TTS engine
+ // @param[inout] rate in: the desired audio sample rate
+ // out: the sample rate used by the TTS engine
+ // @param[inout] channels in: the desired number of audio channels
+ // out: the number of channels used by the TTS engine
+ // @return TTS_SUCCESS, or TTS_FAILURE
+ android_tts_result_t (*setAudioFormat)
+ (void *engine,
+ android_tts_audio_format_t* pEncoding,
+ uint32_t* pRate,
+ int* pChannels);
+
+ // Set a property for the the TTS engine
+ // "size" is the maximum size of "value" for properties "property"
+ // @param property pointer to the property name
+ // @param value pointer to the property value
+ // @param size maximum size required to store this type of property
+ // @return TTS_PROPERTY_UNSUPPORTED, or TTS_SUCCESS, or TTS_FAILURE,
+ // or TTS_VALUE_INVALID
+ android_tts_result_t (*setProperty)
+ (void *engine,
+ const char *property,
+ const char *value,
+ const size_t size);
+
+ // Retrieve a property from the TTS engine
+ // @param property pointer to the property name
+ // @param[out] value pointer to the retrieved language value
+ // @param[inout] iosize in: stores the size available to store the
+ // property value.
+ // out: stores the size required to hold the language
+ // value if getLanguage() returned
+ // TTS_PROPERTY_SIZE_TOO_SMALL, unchanged otherwise
+ // @return TTS_PROPERTY_UNSUPPORTED, or TTS_SUCCESS,
+ // or TTS_PROPERTY_SIZE_TOO_SMALL
+ android_tts_result_t (*getProperty)
+ (void *engine,
+ const char *property,
+ char *value,
+ size_t *iosize);
+
+ // Synthesize the text.
+ // As the synthesis is performed, the engine invokes the callback to notify
+ // the TTS framework that it has filled the given buffer, and indicates how
+ // many bytes it wrote. The callback is called repeatedly until the engine
+ // has generated all the audio data corresponding to the text.
+ // Note about the format of the input: the text parameter may use the
+ // following elements
+ // and their respective attributes as defined in the SSML 1.0 specification:
+ // * lang
+ // * say-as:
+ // o interpret-as
+ // * phoneme
+ // * voice:
+ // o gender,
+ // o age,
+ // o variant,
+ // o name
+ // * emphasis
+ // * break:
+ // o strength,
+ // o time
+ // * prosody:
+ // o pitch,
+ // o contour,
+ // o range,
+ // o rate,
+ // o duration,
+ // o volume
+ // * mark
+ // Differences between this text format and SSML are:
+ // * full SSML documents are not supported
+ // * namespaces are not supported
+ // Text is coded in UTF-8.
+ // @param text the UTF-8 text to synthesize
+ // @param userdata pointer to be returned when the call is invoked
+ // @param buffer the location where the synthesized data must be written
+ // @param bufferSize the number of bytes that can be written in buffer
+ // @return TTS_SUCCESS or TTS_FAILURE
+ android_tts_result_t (*synthesizeText)
+ (void *engine,
+ const char *text,
+ int8_t *buffer,
+ size_t bufferSize,
+ void *userdata);
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ANDROID_TTS_H */