summaryrefslogtreecommitdiffstats
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
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
-rwxr-xr-xpico/Android.mk31
-rwxr-xr-xpico/AndroidManifest.xml7
-rw-r--r--pico/compat/include/TtsEngine.h232
-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
-rwxr-xr-xpico/compat/src/com/android/tts/compat/CompatTtsService.java145
-rwxr-xr-xpico/compat/src/com/android/tts/compat/SynthProxy.java183
-rw-r--r--pico/proguard.flags2
-rw-r--r--pico/src/com/svox/pico/PicoService.java29
-rw-r--r--pico/tts/com_svox_picottsengine.cpp4
11 files changed, 1679 insertions, 3 deletions
diff --git a/pico/Android.mk b/pico/Android.mk
index 59f7adf..c6b0e8a 100755
--- a/pico/Android.mk
+++ b/pico/Android.mk
@@ -12,12 +12,17 @@ include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+ $(call all-java-files-under, compat)
LOCAL_PACKAGE_NAME := PicoTts
+LOCAL_REQUIRED_MODULES := libttscompat
+
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
include $(BUILD_PACKAGE)
+
# Build Pico Shared Library
LOCAL_PATH:= $(TOP_LOCAL_PATH)/tts
@@ -27,7 +32,7 @@ LOCAL_SRC_FILES:= com_svox_picottsengine.cpp svox_ssml_parser.cpp
LOCAL_C_INCLUDES += \
external/svox/pico/lib \
- frameworks
+ external/svox/pico/compat/include
LOCAL_STATIC_LIBRARIES:= libsvoxpico
@@ -85,4 +90,26 @@ LOCAL_LDFLAGS+= $(TOOL_LDFLAGS)
include $(BUILD_STATIC_LIBRARY)
+
+# Build compatibility library
+LOCAL_PATH:= $(TOP_LOCAL_PATH)/compat/jni
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= libttscompat
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES:= \
+ com_android_tts_compat_SynthProxy.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libandroid_runtime \
+ libnativehelper \
+ libmedia \
+ libutils \
+ libcutils \
+ libdl
+
+include $(BUILD_SHARED_LIBRARY)
+
+
endif # TARGET_SIMULATOR
diff --git a/pico/AndroidManifest.xml b/pico/AndroidManifest.xml
index c8cb570..077c17f 100755
--- a/pico/AndroidManifest.xml
+++ b/pico/AndroidManifest.xml
@@ -18,6 +18,13 @@
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
<application android:label="@string/app_name">
+ <service android:name=".PicoService"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.TTS_SERVICE" />
+ </intent-filter>
+ </service>
+
<activity android:name=".DownloadVoiceData" android:label="@string/app_name"
android:theme="@android:style/Theme.Translucent.NoTitleBar">
<intent-filter>
diff --git a/pico/compat/include/TtsEngine.h b/pico/compat/include/TtsEngine.h
new file mode 100644
index 0000000..998e353
--- /dev/null
+++ b/pico/compat/include/TtsEngine.h
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+#include <media/AudioSystem.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.
+// An example of the implementation of this interface can be found in
+// FIXME: add path+name to implementation of default TTS engine
+// Libraries implementing this interface are used in:
+// frameworks/base/tts/jni/android_tts_SpeechSynthesis.cpp
+
+namespace android {
+
+#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"
+
+
+enum tts_synth_status {
+ TTS_SYNTH_DONE = 0,
+ TTS_SYNTH_PENDING = 1
+};
+
+enum tts_callback_status {
+ TTS_CALLBACK_HALT = 0,
+ TTS_CALLBACK_CONTINUE = 1
+};
+
+// The callback is used by the implementation of this interface to notify its
+// client, the Android TTS service, that the last requested synthesis has been
+// completed. // TODO reword
+// 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 tts_callback_status (synthDoneCB_t)(void *&, uint32_t,
+ uint32_t, int, int8_t *&, size_t&, tts_synth_status);
+
+class TtsEngine;
+extern "C" TtsEngine* getTtsEngine();
+
+enum tts_result {
+ TTS_SUCCESS = 0,
+ TTS_FAILURE = -1,
+ TTS_FEATURE_UNSUPPORTED = -2,
+ TTS_VALUE_INVALID = -3,
+ TTS_PROPERTY_UNSUPPORTED = -4,
+ TTS_PROPERTY_SIZE_TOO_SMALL = -5,
+ TTS_MISSING_RESOURCES = -6
+};
+
+enum tts_support_result {
+ TTS_LANG_COUNTRY_VAR_AVAILABLE = 2,
+ TTS_LANG_COUNTRY_AVAILABLE = 1,
+ TTS_LANG_AVAILABLE = 0,
+ TTS_LANG_MISSING_DATA = -1,
+ TTS_LANG_NOT_SUPPORTED = -2
+};
+
+class TtsEngine
+{
+public:
+ virtual ~TtsEngine() {}
+
+ // Initialize the TTS engine and returns whether initialization succeeded.
+ // @param synthDoneCBPtr synthesis callback function pointer
+ // @return TTS_SUCCESS, or TTS_FAILURE
+ virtual tts_result init(synthDoneCB_t synthDoneCBPtr, const char *engineConfig);
+
+ // Shut down the TTS engine and releases all associated resources.
+ // @return TTS_SUCCESS, or TTS_FAILURE
+ virtual tts_result shutdown();
+
+ // 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
+ virtual tts_result stop();
+
+ // 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.
+ virtual tts_support_result isLanguageAvailable(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
+ virtual tts_result loadLanguage(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
+ virtual tts_result setLanguage(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
+ virtual tts_result getLanguage(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
+ virtual tts_result setAudioFormat(AudioSystem::audio_format& encoding, uint32_t& rate,
+ int& channels);
+
+ // 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
+ virtual tts_result setProperty(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
+ virtual tts_result getProperty(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
+ virtual tts_result synthesizeText(const char *text, int8_t *buffer,
+ size_t bufferSize, void *userdata);
+
+};
+
+} // namespace android
+
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 */
diff --git a/pico/compat/src/com/android/tts/compat/CompatTtsService.java b/pico/compat/src/com/android/tts/compat/CompatTtsService.java
new file mode 100755
index 0000000..1ec7ebf
--- /dev/null
+++ b/pico/compat/src/com/android/tts/compat/CompatTtsService.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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.
+ */
+package com.android.tts.compat;
+
+import android.database.Cursor;
+import android.net.Uri;
+import android.speech.tts.SynthesisRequest;
+import android.speech.tts.TextToSpeech;
+import android.speech.tts.TextToSpeechService;
+import android.util.Log;
+
+import java.io.File;
+
+public abstract class CompatTtsService extends TextToSpeechService {
+
+ private static final boolean DBG = false;
+ private static final String TAG = "CompatTtsService";
+
+ private SynthProxy mNativeSynth = null;
+
+ protected abstract String getSoFilename();
+
+ @Override
+ public void onCreate() {
+ if (DBG) Log.d(TAG, "onCreate()");
+ super.onCreate();
+
+ String soFilename = getSoFilename();
+
+ File f = new File(soFilename);
+ if (!f.exists()) {
+ Log.e(TAG, "Invalid TTS Binary: " + soFilename);
+ return;
+ }
+
+ if (mNativeSynth != null) {
+ mNativeSynth.stopSync();
+ mNativeSynth.shutdown();
+ mNativeSynth = null;
+ }
+
+ // Load the engineConfig from the plugin if it has any special configuration
+ // to be loaded. By convention, if an engine wants the TTS framework to pass
+ // in any configuration, it must put it into its content provider which has the URI:
+ // content://<packageName>.providers.SettingsProvider
+ // That content provider must provide a Cursor which returns the String that
+ // is to be passed back to the native .so file for the plugin when getString(0) is
+ // called on it.
+ // Note that the TTS framework does not care what this String data is: it is something
+ // that comes from the engine plugin and is consumed only by the engine plugin itself.
+ String engineConfig = "";
+ Cursor c = getContentResolver().query(Uri.parse("content://" + getPackageName()
+ + ".providers.SettingsProvider"), null, null, null, null);
+ if (c != null){
+ c.moveToFirst();
+ engineConfig = c.getString(0);
+ c.close();
+ }
+ mNativeSynth = new SynthProxy(soFilename, engineConfig);
+ }
+
+ @Override
+ public void onDestroy() {
+ if (DBG) Log.d(TAG, "onDestroy()");
+ super.onDestroy();
+
+ if (mNativeSynth != null) {
+ mNativeSynth.shutdown();
+ }
+ mNativeSynth = null;
+ }
+
+ @Override
+ protected String[] onGetLanguage() {
+ if (mNativeSynth == null) return null;
+ return mNativeSynth.getLanguage();
+ }
+
+ @Override
+ protected int onIsLanguageAvailable(String lang, String country, String variant) {
+ if (DBG) Log.d(TAG, "onIsLanguageAvailable(" + lang + "," + country + "," + variant + ")");
+ if (mNativeSynth == null) return TextToSpeech.ERROR;
+ return mNativeSynth.isLanguageAvailable(lang, country, variant);
+ }
+
+ @Override
+ protected int onLoadLanguage(String lang, String country, String variant) {
+ if (DBG) Log.d(TAG, "onLoadLanguage(" + lang + "," + country + "," + variant + ")");
+ int result = onIsLanguageAvailable(lang, country, variant);
+ // TODO: actually load language
+ return result;
+ }
+
+ @Override
+ protected int onSynthesizeText(SynthesisRequest request) {
+ if (mNativeSynth == null) return TextToSpeech.ERROR;
+
+ // Set language
+ String lang = request.getLanguage();
+ String country = request.getCountry();
+ String variant = request.getVariant();
+ if (mNativeSynth.setLanguage(lang, country, variant) != TextToSpeech.SUCCESS) {
+ Log.e(TAG, "setLanguage(" + lang + "," + country + "," + variant + ") failed");
+ return TextToSpeech.ERROR;
+ }
+
+ // Set speech rate
+ int speechRate = request.getSpeechRate();
+ if (mNativeSynth.setSpeechRate(speechRate) != TextToSpeech.SUCCESS) {
+ Log.e(TAG, "setSpeechRate(" + speechRate + ") failed");
+ return TextToSpeech.ERROR;
+ }
+
+ // Set speech
+ int pitch = request.getPitch();
+ if (mNativeSynth.setPitch(pitch) != TextToSpeech.SUCCESS) {
+ Log.e(TAG, "setPitch(" + pitch + ") failed");
+ return TextToSpeech.ERROR;
+ }
+
+ // Synthesize
+ return mNativeSynth.speak(request);
+ }
+
+ @Override
+ protected void onStop() {
+ if (DBG) Log.d(TAG, "onStop()");
+ if (mNativeSynth == null) return;
+ mNativeSynth.stop();
+ }
+
+}
diff --git a/pico/compat/src/com/android/tts/compat/SynthProxy.java b/pico/compat/src/com/android/tts/compat/SynthProxy.java
new file mode 100755
index 0000000..68d0d92
--- /dev/null
+++ b/pico/compat/src/com/android/tts/compat/SynthProxy.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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.
+ */
+package com.android.tts.compat;
+
+import android.speech.tts.SynthesisRequest;
+import android.util.Log;
+
+/**
+ * The SpeechSynthesis class provides a high-level api to create and play
+ * synthesized speech. This class is used internally to talk to a native
+ * TTS library that implements the interface defined in
+ * frameworks/base/include/tts/TtsEngine.h
+ *
+ */
+public class SynthProxy {
+
+ static {
+ System.loadLibrary("ttscompat");
+ }
+
+ private final static String TAG = "SynthProxy";
+
+ // Default parameters of a filter to be applied when using the Pico engine.
+ // Such a huge filter gain is justified by how much energy in the low frequencies is "wasted" at
+ // the output of the synthesis. The low shelving filter removes it, leaving room for
+ // amplification.
+ private final static float PICO_FILTER_GAIN = 5.0f; // linear gain
+ private final static float PICO_FILTER_LOWSHELF_ATTENUATION = -18.0f; // in dB
+ private final static float PICO_FILTER_TRANSITION_FREQ = 1100.0f; // in Hz
+ private final static float PICO_FILTER_SHELF_SLOPE = 1.0f; // Q
+
+ private int mJniData = 0;
+
+ /**
+ * Constructor; pass the location of the native TTS .so to use.
+ */
+ public SynthProxy(String nativeSoLib, String engineConfig) {
+ boolean applyFilter = shouldApplyAudioFilter(nativeSoLib);
+ Log.v(TAG, "About to load "+ nativeSoLib + ", applyFilter=" + applyFilter);
+ mJniData = native_setup(nativeSoLib, engineConfig);
+ if (mJniData == 0) {
+ throw new RuntimeException("Failed to load " + nativeSoLib);
+ }
+ native_setLowShelf(applyFilter, PICO_FILTER_GAIN, PICO_FILTER_LOWSHELF_ATTENUATION,
+ PICO_FILTER_TRANSITION_FREQ, PICO_FILTER_SHELF_SLOPE);
+ }
+
+ // HACK: Apply audio filter if the engine is pico
+ private boolean shouldApplyAudioFilter(String nativeSoLib) {
+ return nativeSoLib.toLowerCase().contains("pico");
+ }
+
+ /**
+ * Stops and clears the AudioTrack.
+ */
+ public int stop() {
+ return native_stop(mJniData);
+ }
+
+ /**
+ * Synchronous stop of the synthesizer. This method returns when the synth
+ * has completed the stop procedure and doesn't use any of the resources it
+ * was using while synthesizing.
+ *
+ * @return {@link android.speech.tts.TextToSpeech#SUCCESS} or
+ * {@link android.speech.tts.TextToSpeech#ERROR}
+ */
+ public int stopSync() {
+ return native_stopSync(mJniData);
+ }
+
+ public int speak(SynthesisRequest request) {
+ return native_speak(mJniData, request.getText(), request);
+ }
+
+ /**
+ * Queries for language support.
+ * Return codes are defined in android.speech.tts.TextToSpeech
+ */
+ public int isLanguageAvailable(String language, String country, String variant) {
+ return native_isLanguageAvailable(mJniData, language, country, variant);
+ }
+
+ /**
+ * Updates the engine configuration.
+ */
+ public int setConfig(String engineConfig) {
+ return native_setProperty(mJniData, "engineConfig", engineConfig);
+ }
+
+ /**
+ * Sets the language.
+ */
+ public int setLanguage(String language, String country, String variant) {
+ return native_setLanguage(mJniData, language, country, variant);
+ }
+
+ /**
+ * Loads the language: it's not set, but prepared for use later.
+ */
+ public int loadLanguage(String language, String country, String variant) {
+ return native_loadLanguage(mJniData, language, country, variant);
+ }
+
+ /**
+ * Sets the speech rate.
+ */
+ public final int setSpeechRate(int speechRate) {
+ return native_setProperty(mJniData, "rate", String.valueOf(speechRate));
+ }
+
+ /**
+ * Sets the pitch of the synthesized voice.
+ */
+ public final int setPitch(int pitch) {
+ return native_setProperty(mJniData, "pitch", String.valueOf(pitch));
+ }
+
+ /**
+ * Returns the currently set language, country and variant information.
+ */
+ public String[] getLanguage() {
+ return native_getLanguage(mJniData);
+ }
+
+ /**
+ * Shuts down the native synthesizer.
+ */
+ public void shutdown() {
+ native_shutdown(mJniData);
+ }
+
+ @Override
+ protected void finalize() {
+ if (mJniData != 0) {
+ Log.w(TAG, "SynthProxy finalized without being shutdown");
+ native_finalize(mJniData);
+ mJniData = 0;
+ }
+ }
+
+ private native final int native_setup(String nativeSoLib, String engineConfig);
+
+ private native final int native_setLowShelf(boolean applyFilter, float filterGain,
+ float attenuationInDb, float freqInHz, float slope);
+
+ private native final void native_finalize(int jniData);
+
+ private native final int native_stop(int jniData);
+
+ private native final int native_stopSync(int jniData);
+
+ private native final int native_speak(int jniData, String text, SynthesisRequest request);
+
+ private native final int native_isLanguageAvailable(int jniData, String language,
+ String country, String variant);
+
+ private native final int native_setLanguage(int jniData, String language, String country,
+ String variant);
+
+ private native final int native_loadLanguage(int jniData, String language, String country,
+ String variant);
+
+ private native final int native_setProperty(int jniData, String name, String value);
+
+ private native final String[] native_getLanguage(int jniData);
+
+ private native final void native_shutdown(int jniData);
+
+}
diff --git a/pico/proguard.flags b/pico/proguard.flags
new file mode 100644
index 0000000..eff9d9b
--- /dev/null
+++ b/pico/proguard.flags
@@ -0,0 +1,2 @@
+# Some methods in this class are called form native code
+-keep class com.android.tts.compat.SynthProxy { *; }
diff --git a/pico/src/com/svox/pico/PicoService.java b/pico/src/com/svox/pico/PicoService.java
new file mode 100644
index 0000000..4c6e678
--- /dev/null
+++ b/pico/src/com/svox/pico/PicoService.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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.
+ */
+package com.svox.pico;
+
+import com.android.tts.compat.CompatTtsService;
+
+public class PicoService extends CompatTtsService {
+
+ private static final String TAG = "PicoService";
+
+ @Override
+ protected String getSoFilename() {
+ return "/system/lib/libttspico.so";
+ }
+
+}
diff --git a/pico/tts/com_svox_picottsengine.cpp b/pico/tts/com_svox_picottsengine.cpp
index 7ac4ec7..6c05d00 100644
--- a/pico/tts/com_svox_picottsengine.cpp
+++ b/pico/tts/com_svox_picottsengine.cpp
@@ -40,10 +40,12 @@
#include <utils/Log.h>
#include <utils/String16.h> /* for strlen16 */
#include <android_runtime/AndroidRuntime.h>
-#include <tts/TtsEngine.h>
+#include <TtsEngine.h>
+
#include <cutils/jstring.h>
#include <picoapi.h>
#include <picodefs.h>
+
#include "svox_ssml_parser.h"
using namespace android;