summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjorn Bringert <bringert@android.com>2011-03-08 16:00:40 +0000
committerBjorn Bringert <bringert@android.com>2011-04-15 10:04:31 +0100
commit50e657bb2d005568f5dd8bc1d904d07b0d94018f (patch)
treebcf319e64984c80f4793602aeb366e7f6b33afdd
parent720dd9a89857512d14adabea4efbb542d25fbb02 (diff)
downloadframeworks_base-50e657bb2d005568f5dd8bc1d904d07b0d94018f.zip
frameworks_base-50e657bb2d005568f5dd8bc1d904d07b0d94018f.tar.gz
frameworks_base-50e657bb2d005568f5dd8bc1d904d07b0d94018f.tar.bz2
Add Java API for writing TTS engines
This removes the old non-public C++ API for TTS engines and replaces it with a Java API. The new API is still @hidden, until it has been approved. Bug: 4148636 Change-Id: I7614ff788e11f897e87052f684f1b4938d539fb7
-rw-r--r--Android.mk4
-rw-r--r--CleanSpec.mk1
-rw-r--r--core/java/android/speech/tts/BlockingMediaPlayer.java146
-rw-r--r--core/java/android/speech/tts/FileSynthesisRequest.java197
-rwxr-xr-xcore/java/android/speech/tts/ITextToSpeechCallback.aidl (renamed from core/java/android/speech/tts/ITtsCallback.aidl)8
-rw-r--r--core/java/android/speech/tts/ITextToSpeechService.aidl140
-rwxr-xr-xcore/java/android/speech/tts/ITts.aidl69
-rw-r--r--core/java/android/speech/tts/PlaybackSynthesisRequest.java197
-rw-r--r--core/java/android/speech/tts/SynthesisRequest.java151
-rwxr-xr-xcore/java/android/speech/tts/TextToSpeech.java1199
-rw-r--r--core/java/android/speech/tts/TextToSpeechService.java715
-rw-r--r--include/tts/TtsEngine.h232
-rw-r--r--native/include/android/tts.h313
-rw-r--r--packages/TtsService/Android.mk15
-rwxr-xr-xpackages/TtsService/AndroidManifest.xml17
-rw-r--r--packages/TtsService/MODULE_LICENSE_APACHE20
-rw-r--r--packages/TtsService/NOTICE190
-rwxr-xr-xpackages/TtsService/jni/Android.mk30
-rw-r--r--packages/TtsService/jni/android_tts_SynthProxy.cpp1071
-rw-r--r--packages/TtsService/proguard.flags5
-rw-r--r--packages/TtsService/res/drawable-hdpi/ic_launcher_text_to_speech.pngbin6931 -> 0 bytes
-rw-r--r--packages/TtsService/res/drawable-mdpi/ic_launcher_text_to_speech.pngbin6809 -> 0 bytes
-rwxr-xr-xpackages/TtsService/src/android/tts/SynthProxy.java238
-rwxr-xr-xpackages/TtsService/src/android/tts/TtsService.java1503
24 files changed, 2089 insertions, 4352 deletions
diff --git a/Android.mk b/Android.mk
index 28340cf..cd9ae7d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -137,8 +137,8 @@ LOCAL_SRC_FILES += \
core/java/android/view/IWindowSession.aidl \
core/java/android/speech/IRecognitionListener.aidl \
core/java/android/speech/IRecognitionService.aidl \
- core/java/android/speech/tts/ITts.aidl \
- core/java/android/speech/tts/ITtsCallback.aidl \
+ core/java/android/speech/tts/ITextToSpeechCallback.aidl \
+ core/java/android/speech/tts/ITextToSpeechService.aidl \
core/java/com/android/internal/app/IBatteryStats.aidl \
core/java/com/android/internal/app/IUsageStats.aidl \
core/java/com/android/internal/app/IMediaContainerService.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 023ce59..50292e4 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -96,6 +96,7 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/PerfTest_interme
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/RSTest_intermediates/)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/hardware/IUsbManager.java)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/nfc)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/core/java/android/speech/tts/BlockingMediaPlayer.java b/core/java/android/speech/tts/BlockingMediaPlayer.java
new file mode 100644
index 0000000..3cf60dd
--- /dev/null
+++ b/core/java/android/speech/tts/BlockingMediaPlayer.java
@@ -0,0 +1,146 @@
+/*
+ * 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 android.speech.tts;
+
+import android.content.Context;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.util.Log;
+
+/**
+ * A media player that allows blocking to wait for it to finish.
+ */
+class BlockingMediaPlayer {
+
+ private static final String TAG = "BlockMediaPlayer";
+
+ private static final String MEDIA_PLAYER_THREAD_NAME = "TTS-MediaPlayer";
+
+ private final Context mContext;
+ private final Uri mUri;
+ private final int mStreamType;
+ private final ConditionVariable mDone;
+ // Only accessed on the Handler thread
+ private MediaPlayer mPlayer;
+ private volatile boolean mFinished;
+
+ /**
+ * Creates a new blocking media player.
+ * Creating a blocking media player is a cheap operation.
+ *
+ * @param context
+ * @param uri
+ * @param streamType
+ */
+ public BlockingMediaPlayer(Context context, Uri uri, int streamType) {
+ mContext = context;
+ mUri = uri;
+ mStreamType = streamType;
+ mDone = new ConditionVariable();
+
+ }
+
+ /**
+ * Starts playback and waits for it to finish.
+ * Can be called from any thread.
+ *
+ * @return {@code true} if the playback finished normally, {@code false} if the playback
+ * failed or {@link #stop} was called before the playback finished.
+ */
+ public boolean startAndWait() {
+ HandlerThread thread = new HandlerThread(MEDIA_PLAYER_THREAD_NAME);
+ thread.start();
+ Handler handler = new Handler(thread.getLooper());
+ mFinished = false;
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ startPlaying();
+ }
+ });
+ mDone.block();
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ finish();
+ // No new messages should get posted to the handler thread after this
+ Looper.myLooper().quit();
+ }
+ });
+ return mFinished;
+ }
+
+ /**
+ * Stops playback. Can be called multiple times.
+ * Can be called from any thread.
+ */
+ public void stop() {
+ mDone.open();
+ }
+
+ /**
+ * Starts playback.
+ * Called on the handler thread.
+ */
+ private void startPlaying() {
+ mPlayer = MediaPlayer.create(mContext, mUri);
+ if (mPlayer == null) {
+ Log.w(TAG, "Failed to play " + mUri);
+ mDone.open();
+ return;
+ }
+ try {
+ mPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+ @Override
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ Log.w(TAG, "Audio playback error: " + what + ", " + extra);
+ mDone.open();
+ return true;
+ }
+ });
+ mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ mFinished = true;
+ mDone.open();
+ }
+ });
+ mPlayer.setAudioStreamType(mStreamType);
+ mPlayer.start();
+ } catch (IllegalArgumentException ex) {
+ Log.w(TAG, "MediaPlayer failed", ex);
+ mDone.open();
+ }
+ }
+
+ /**
+ * Stops playback and release the media player.
+ * Called on the handler thread.
+ */
+ private void finish() {
+ try {
+ mPlayer.stop();
+ } catch (IllegalStateException ex) {
+ // Do nothing, the player is already stopped
+ }
+ mPlayer.release();
+ }
+
+} \ No newline at end of file
diff --git a/core/java/android/speech/tts/FileSynthesisRequest.java b/core/java/android/speech/tts/FileSynthesisRequest.java
new file mode 100644
index 0000000..370ad53
--- /dev/null
+++ b/core/java/android/speech/tts/FileSynthesisRequest.java
@@ -0,0 +1,197 @@
+/*
+ * 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 android.speech.tts;
+
+import android.media.AudioFormat;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Speech synthesis request that writes the audio to a WAV file.
+ */
+class FileSynthesisRequest extends SynthesisRequest {
+
+ private static final String TAG = "FileSynthesisRequest";
+ private static final boolean DBG = false;
+
+ private static final int WAV_HEADER_LENGTH = 44;
+ private static final short WAV_FORMAT_PCM = 0x0001;
+
+ private final Object mStateLock = new Object();
+ private final File mFileName;
+ private int mSampleRateInHz;
+ private int mAudioFormat;
+ private int mChannelCount;
+ private RandomAccessFile mFile;
+ private boolean mStopped = false;
+
+ FileSynthesisRequest(String text, File fileName) {
+ super(text);
+ mFileName = fileName;
+ }
+
+ @Override
+ void stop() {
+ synchronized (mStateLock) {
+ mStopped = true;
+ cleanUp();
+ }
+ }
+
+ /**
+ * Must be called while holding the monitor on {@link #mStateLock}.
+ */
+ private void cleanUp() {
+ closeFile();
+ if (mFile != null) {
+ mFileName.delete();
+ }
+ }
+
+ /**
+ * Must be called while holding the monitor on {@link #mStateLock}.
+ */
+ private void closeFile() {
+ try {
+ if (mFile != null) {
+ mFile.close();
+ mFile = null;
+ }
+ } catch (IOException ex) {
+ Log.e(TAG, "Failed to close " + mFileName + ": " + ex);
+ }
+ }
+
+ @Override
+ public int start(int sampleRateInHz, int audioFormat, int channelCount) {
+ if (DBG) {
+ Log.d(TAG, "FileSynthesisRequest.start(" + sampleRateInHz + "," + audioFormat
+ + "," + channelCount + ")");
+ }
+ synchronized (mStateLock) {
+ if (mStopped) {
+ if (DBG) Log.d(TAG, "Request has been aborted.");
+ return TextToSpeech.ERROR;
+ }
+ if (mFile != null) {
+ cleanUp();
+ throw new IllegalArgumentException("FileSynthesisRequest.start() called twice");
+ }
+ mSampleRateInHz = sampleRateInHz;
+ mAudioFormat = audioFormat;
+ mChannelCount = channelCount;
+ try {
+ mFile = new RandomAccessFile(mFileName, "rw");
+ // Reserve space for WAV header
+ mFile.write(new byte[WAV_HEADER_LENGTH]);
+ return TextToSpeech.SUCCESS;
+ } catch (IOException ex) {
+ Log.e(TAG, "Failed to open " + mFileName + ": " + ex);
+ cleanUp();
+ return TextToSpeech.ERROR;
+ }
+ }
+ }
+
+ @Override
+ public int audioAvailable(byte[] buffer, int offset, int length) {
+ if (DBG) {
+ Log.d(TAG, "FileSynthesisRequest.audioAvailable(" + buffer + "," + offset
+ + "," + length + ")");
+ }
+ synchronized (mStateLock) {
+ if (mStopped) {
+ if (DBG) Log.d(TAG, "Request has been aborted.");
+ return TextToSpeech.ERROR;
+ }
+ if (mFile == null) {
+ Log.e(TAG, "File not open");
+ return TextToSpeech.ERROR;
+ }
+ try {
+ mFile.write(buffer, offset, length);
+ return TextToSpeech.SUCCESS;
+ } catch (IOException ex) {
+ Log.e(TAG, "Failed to write to " + mFileName + ": " + ex);
+ cleanUp();
+ return TextToSpeech.ERROR;
+ }
+ }
+ }
+
+ @Override
+ public int done() {
+ if (DBG) Log.d(TAG, "FileSynthesisRequest.done()");
+ synchronized (mStateLock) {
+ if (mStopped) {
+ if (DBG) Log.d(TAG, "Request has been aborted.");
+ return TextToSpeech.ERROR;
+ }
+ if (mFile == null) {
+ Log.e(TAG, "File not open");
+ return TextToSpeech.ERROR;
+ }
+ try {
+ // Write WAV header at start of file
+ mFile.seek(0);
+ int fileLen = (int) mFile.length();
+ mFile.write(makeWavHeader(mSampleRateInHz, mAudioFormat, mChannelCount, fileLen));
+ closeFile();
+ return TextToSpeech.SUCCESS;
+ } catch (IOException ex) {
+ Log.e(TAG, "Failed to write to " + mFileName + ": " + ex);
+ cleanUp();
+ return TextToSpeech.ERROR;
+ }
+ }
+ }
+
+ private byte[] makeWavHeader(int sampleRateInHz, int audioFormat, int channelCount,
+ int fileLength) {
+ // TODO: is AudioFormat.ENCODING_DEFAULT always the same as ENCODING_PCM_16BIT?
+ int sampleSizeInBytes = (audioFormat == AudioFormat.ENCODING_PCM_8BIT ? 1 : 2);
+ int byteRate = sampleRateInHz * sampleSizeInBytes * channelCount;
+ short blockAlign = (short) (sampleSizeInBytes * channelCount);
+ short bitsPerSample = (short) (sampleSizeInBytes * 8);
+
+ byte[] headerBuf = new byte[WAV_HEADER_LENGTH];
+ ByteBuffer header = ByteBuffer.wrap(headerBuf);
+ header.order(ByteOrder.LITTLE_ENDIAN);
+
+ header.put(new byte[]{ 'R', 'I', 'F', 'F' });
+ header.putInt(fileLength - 8); // RIFF chunk size
+ header.put(new byte[]{ 'W', 'A', 'V', 'E' });
+ header.put(new byte[]{ 'f', 'm', 't', ' ' });
+ header.putInt(16); // size of fmt chunk
+ header.putShort(WAV_FORMAT_PCM);
+ header.putShort((short) channelCount);
+ header.putInt(sampleRateInHz);
+ header.putInt(byteRate);
+ header.putShort(blockAlign);
+ header.putShort(bitsPerSample);
+ header.put(new byte[]{ 'd', 'a', 't', 'a' });
+ int dataLength = fileLength - WAV_HEADER_LENGTH;
+ header.putInt(dataLength);
+
+ return headerBuf;
+ }
+
+}
diff --git a/core/java/android/speech/tts/ITtsCallback.aidl b/core/java/android/speech/tts/ITextToSpeechCallback.aidl
index c9898eb..40902ae 100755
--- a/core/java/android/speech/tts/ITtsCallback.aidl
+++ b/core/java/android/speech/tts/ITextToSpeechCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Android Open Source Project
+ * 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.
@@ -13,15 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package android.speech.tts;
/**
- * AIDL for the callback from the TTS Service
- * ITtsCallback.java is autogenerated from this.
+ * Interface for callbacks from TextToSpeechService
*
* {@hide}
*/
-oneway interface ITtsCallback {
+oneway interface ITextToSpeechCallback {
void utteranceCompleted(String utteranceId);
}
diff --git a/core/java/android/speech/tts/ITextToSpeechService.aidl b/core/java/android/speech/tts/ITextToSpeechService.aidl
new file mode 100644
index 0000000..ff3fa11
--- /dev/null
+++ b/core/java/android/speech/tts/ITextToSpeechService.aidl
@@ -0,0 +1,140 @@
+/*
+ * 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 android.speech.tts;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.speech.tts.ITextToSpeechCallback;
+
+/**
+ * Interface for TextToSpeech to talk to TextToSpeechService.
+ *
+ * {@hide}
+ */
+interface ITextToSpeechService {
+
+ /**
+ * Tells the engine to synthesize some speech and play it back.
+ *
+ * @param callingApp The package name of the calling app. Used to connect requests
+ * callbacks and to clear requests when the calling app is stopping.
+ * @param text The text to synthesize.
+ * @param queueMode Determines what to do to requests already in the queue.
+ * @param param Request parameters.
+ */
+ int speak(in String callingApp, in String text, in int queueMode, in Bundle params);
+
+ /**
+ * Tells the engine to synthesize some speech and write it to a file.
+ *
+ * @param callingApp The package name of the calling app. Used to connect requests
+ * callbacks and to clear requests when the calling app is stopping.
+ * @param text The text to synthesize.
+ * @param filename The file to write the synthesized audio to.
+ * @param param Request parameters.
+ */
+ int synthesizeToFile(in String callingApp, in String text,
+ in String filename, in Bundle params);
+
+ /**
+ * Plays an existing audio resource.
+ *
+ * @param callingApp The package name of the calling app. Used to connect requests
+ * callbacks and to clear requests when the calling app is stopping.
+ * @param audioUri URI for the audio resource (a file or android.resource URI)
+ * @param queueMode Determines what to do to requests already in the queue.
+ * @param param Request parameters.
+ */
+ int playAudio(in String callingApp, in Uri audioUri, in int queueMode, in Bundle params);
+
+ /**
+ * Plays silence.
+ *
+ * @param callingApp The package name of the calling app. Used to connect requests
+ * callbacks and to clear requests when the calling app is stopping.
+ * @param duration Number of milliseconds of silence to play.
+ * @param queueMode Determines what to do to requests already in the queue.
+ * @param param Request parameters.
+ */
+ int playSilence(in String callingApp, in long duration, in int queueMode, in Bundle params);
+
+ /**
+ * Checks whether the service is currently playing some audio.
+ */
+ boolean isSpeaking();
+
+ /**
+ * Interrupts the current utterance (if from the given app) and removes any utterances
+ * in the queue that are from the given app.
+ *
+ * @param callingApp Package name of the app whose utterances
+ * should be interrupted and cleared.
+ */
+ int stop(in String callingApp);
+
+ /**
+ * Returns the language, country and variant currently being used by the TTS engine.
+ *
+ * Can be called from multiple threads.
+ *
+ * @return A 3-element array, containing language (ISO 3-letter code),
+ * country (ISO 3-letter code) and variant used by the engine.
+ * The country and variant may be {@code ""}. If country is empty, then variant must
+ * be empty too.
+ */
+ String[] getLanguage();
+
+ /**
+ * Checks whether the engine supports a given language.
+ *
+ * @param lang ISO-3 language code.
+ * @param country ISO-3 country code. May be empty or null.
+ * @param variant Language variant. May be empty or null.
+ * @return Code indicating the support status for the locale.
+ * One of {@link TextToSpeech#LANG_AVAILABLE},
+ * {@link TextToSpeech#LANG_COUNTRY_AVAILABLE},
+ * {@link TextToSpeech#LANG_COUNTRY_VAR_AVAILABLE},
+ * {@link TextToSpeech#LANG_MISSING_DATA}
+ * {@link TextToSpeech#LANG_NOT_SUPPORTED}.
+ */
+ int isLanguageAvailable(in String lang, in String country, in String variant);
+
+ /**
+ * Notifies the engine that it should load a speech synthesis language.
+ *
+ * @param lang ISO-3 language code.
+ * @param country ISO-3 country code. May be empty or null.
+ * @param variant Language variant. May be empty or null.
+ * @return Code indicating the support status for the locale.
+ * One of {@link TextToSpeech#LANG_AVAILABLE},
+ * {@link TextToSpeech#LANG_COUNTRY_AVAILABLE},
+ * {@link TextToSpeech#LANG_COUNTRY_VAR_AVAILABLE},
+ * {@link TextToSpeech#LANG_MISSING_DATA}
+ * {@link TextToSpeech#LANG_NOT_SUPPORTED}.
+ */
+ int loadLanguage(in String lang, in String country, in String variant);
+
+ /**
+ * Sets the callback that will be notified when playback of utterance from the
+ * given app are completed.
+ *
+ * @param callingApp Package name for the app whose utterance the callback will handle.
+ * @param cb The callback.
+ */
+ void setCallback(in String callingApp, ITextToSpeechCallback cb);
+
+}
diff --git a/core/java/android/speech/tts/ITts.aidl b/core/java/android/speech/tts/ITts.aidl
deleted file mode 100755
index c1051c4..0000000
--- a/core/java/android/speech/tts/ITts.aidl
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2009 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 android.speech.tts;
-
-import android.speech.tts.ITtsCallback;
-
-import android.content.Intent;
-
-/**
- * AIDL for the TTS Service
- * ITts.java is autogenerated from this.
- *
- * {@hide}
- */
-interface ITts {
- int setSpeechRate(in String callingApp, in int speechRate);
-
- int setPitch(in String callingApp, in int pitch);
-
- int speak(in String callingApp, in String text, in int queueMode, in String[] params);
-
- boolean isSpeaking();
-
- int stop(in String callingApp);
-
- void addSpeech(in String callingApp, in String text, in String packageName, in int resId);
-
- void addSpeechFile(in String callingApp, in String text, in String filename);
-
- String[] getLanguage();
-
- int isLanguageAvailable(in String language, in String country, in String variant, in String[] params);
-
- int setLanguage(in String callingApp, in String language, in String country, in String variant);
-
- boolean synthesizeToFile(in String callingApp, in String text, in String[] params, in String outputDirectory);
-
- int playEarcon(in String callingApp, in String earcon, in int queueMode, in String[] params);
-
- void addEarcon(in String callingApp, in String earcon, in String packageName, in int resId);
-
- void addEarconFile(in String callingApp, in String earcon, in String filename);
-
- int registerCallback(in String callingApp, ITtsCallback cb);
-
- int unregisterCallback(in String callingApp, ITtsCallback cb);
-
- int playSilence(in String callingApp, in long duration, in int queueMode, in String[] params);
-
- int setEngineByPackageName(in String enginePackageName);
-
- String getDefaultEngine();
-
- boolean areDefaultsEnforced();
-}
diff --git a/core/java/android/speech/tts/PlaybackSynthesisRequest.java b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
new file mode 100644
index 0000000..15a4ee9
--- /dev/null
+++ b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
@@ -0,0 +1,197 @@
+/*
+ * 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 android.speech.tts;
+
+import android.media.AudioFormat;
+import android.media.AudioTrack;
+import android.util.Log;
+
+/**
+ * Speech synthesis request that plays the audio as it is received.
+ */
+class PlaybackSynthesisRequest extends SynthesisRequest {
+
+ private static final String TAG = "PlaybackSynthesisRequest";
+ private static final boolean DBG = false;
+
+ private static final int MIN_AUDIO_BUFFER_SIZE = 8192;
+
+ /**
+ * Audio stream type. Must be one of the STREAM_ contants defined in
+ * {@link android.media.AudioManager}.
+ */
+ private final int mStreamType;
+
+ /**
+ * Volume, in the range [0.0f, 1.0f]. The default value is
+ * {@link TextToSpeech.Engine#DEFAULT_VOLUME} (1.0f).
+ */
+ private final float mVolume;
+
+ /**
+ * Left/right position of the audio, in the range [-1.0f, 1.0f].
+ * The default value is {@link TextToSpeech.Engine#DEFAULT_PAN} (0.0f).
+ */
+ private final float mPan;
+
+ private final Object mStateLock = new Object();
+ private AudioTrack mAudioTrack = null;
+ private boolean mStopped = false;
+
+ PlaybackSynthesisRequest(String text, int streamType, float volume, float pan) {
+ super(text);
+ mStreamType = streamType;
+ mVolume = volume;
+ mPan = pan;
+ }
+
+ @Override
+ void stop() {
+ if (DBG) Log.d(TAG, "stop()");
+ synchronized (mStateLock) {
+ mStopped = true;
+ cleanUp();
+ }
+ }
+
+ private void cleanUp() {
+ if (DBG) Log.d(TAG, "cleanUp()");
+ if (mAudioTrack != null) {
+ mAudioTrack.flush();
+ mAudioTrack.stop();
+ // TODO: do we need to wait for playback to finish before releasing?
+ mAudioTrack.release();
+ mAudioTrack = null;
+ }
+ }
+
+ // TODO: add a thread that writes to the AudioTrack?
+ @Override
+ public int start(int sampleRateInHz, int audioFormat, int channelCount) {
+ if (DBG) {
+ Log.d(TAG, "start(" + sampleRateInHz + "," + audioFormat
+ + "," + channelCount + ")");
+ }
+
+ int channelConfig;
+ if (channelCount == 1) {
+ channelConfig = AudioFormat.CHANNEL_OUT_MONO;
+ } else if (channelCount == 2){
+ channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
+ } else {
+ Log.e(TAG, "Unsupported number of channels: " + channelCount);
+ return TextToSpeech.ERROR;
+ }
+
+ int minBufferSizeInBytes
+ = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
+ int bufferSizeInBytes = Math.max(MIN_AUDIO_BUFFER_SIZE, minBufferSizeInBytes);
+
+ synchronized (mStateLock) {
+ if (mStopped) {
+ if (DBG) Log.d(TAG, "Request has been aborted.");
+ return TextToSpeech.ERROR;
+ }
+ if (mAudioTrack != null) {
+ Log.e(TAG, "start() called twice");
+ cleanUp();
+ return TextToSpeech.ERROR;
+ }
+
+ mAudioTrack = new AudioTrack(mStreamType, sampleRateInHz, channelConfig, audioFormat,
+ bufferSizeInBytes, AudioTrack.MODE_STREAM);
+ if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
+ cleanUp();
+ return TextToSpeech.ERROR;
+ }
+
+ setupVolume();
+ }
+
+ return TextToSpeech.SUCCESS;
+ }
+
+ private void setupVolume() {
+ float vol = clip(mVolume, 0.0f, 1.0f);
+ float panning = clip(mPan, -1.0f, 1.0f);
+ float volLeft = vol;
+ float volRight = vol;
+ if (panning > 0.0f) {
+ volLeft *= (1.0f - panning);
+ } else if (panning < 0.0f) {
+ volRight *= (1.0f + panning);
+ }
+ if (DBG) Log.d(TAG, "volLeft=" + volLeft + ",volRight=" + volRight);
+ if (mAudioTrack.setStereoVolume(volLeft, volRight) != AudioTrack.SUCCESS) {
+ Log.e(TAG, "Failed to set volume");
+ }
+ }
+
+ private float clip(float value, float min, float max) {
+ return value > max ? max : (value < min ? min : value);
+ }
+
+ @Override
+ public int audioAvailable(byte[] buffer, int offset, int length) {
+ if (DBG) {
+ Log.d(TAG, "audioAvailable(byte[" + buffer.length + "],"
+ + offset + "," + length + "), thread ID=" + android.os.Process.myTid());
+ }
+ synchronized (mStateLock) {
+ if (mStopped) {
+ if (DBG) Log.d(TAG, "Request has been aborted.");
+ return TextToSpeech.ERROR;
+ }
+ if (mAudioTrack == null) {
+ Log.e(TAG, "audioAvailable(): Not started");
+ return TextToSpeech.ERROR;
+ }
+ int playState = mAudioTrack.getPlayState();
+ if (playState == AudioTrack.PLAYSTATE_STOPPED) {
+ if (DBG) Log.d(TAG, "AudioTrack stopped, restarting");
+ mAudioTrack.play();
+ }
+ // TODO: loop until all data is written?
+ if (DBG) Log.d(TAG, "AudioTrack.write()");
+ int count = mAudioTrack.write(buffer, offset, length);
+ if (DBG) Log.d(TAG, "AudioTrack.write() returned " + count);
+ if (count < 0) {
+ Log.e(TAG, "Writing to AudioTrack failed: " + count);
+ cleanUp();
+ return TextToSpeech.ERROR;
+ } else {
+ return TextToSpeech.SUCCESS;
+ }
+ }
+ }
+
+ @Override
+ public int done() {
+ if (DBG) Log.d(TAG, "done()");
+ synchronized (mStateLock) {
+ if (mStopped) {
+ if (DBG) Log.d(TAG, "Request has been aborted.");
+ return TextToSpeech.ERROR;
+ }
+ if (mAudioTrack == null) {
+ Log.e(TAG, "done(): Not started");
+ return TextToSpeech.ERROR;
+ }
+ cleanUp();
+ }
+ return TextToSpeech.SUCCESS;
+ }
+} \ No newline at end of file
diff --git a/core/java/android/speech/tts/SynthesisRequest.java b/core/java/android/speech/tts/SynthesisRequest.java
new file mode 100644
index 0000000..3f2ec5d
--- /dev/null
+++ b/core/java/android/speech/tts/SynthesisRequest.java
@@ -0,0 +1,151 @@
+/*
+ * 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 android.speech.tts;
+
+/**
+ * A request for speech synthesis given to a TTS engine for processing.
+ *
+ * @hide Pending approval
+ */
+public abstract class SynthesisRequest {
+
+ private final String mText;
+ private String mLanguage;
+ private String mCountry;
+ private String mVariant;
+ private int mSpeechRate;
+ private int mPitch;
+
+ public SynthesisRequest(String text) {
+ mText = text;
+ }
+
+ /**
+ * Sets the locale for the request.
+ */
+ void setLanguage(String language, String country, String variant) {
+ mLanguage = language;
+ mCountry = country;
+ mVariant = variant;
+ }
+
+ /**
+ * Sets the speech rate.
+ */
+ void setSpeechRate(int speechRate) {
+ mSpeechRate = speechRate;
+ }
+
+ /**
+ * Sets the pitch.
+ */
+ void setPitch(int pitch) {
+ mPitch = pitch;
+ }
+
+ /**
+ * Gets the text which should be synthesized.
+ */
+ public String getText() {
+ return mText;
+ }
+
+ /**
+ * Gets the ISO 3-letter language code for the language to use.
+ */
+ public String getLanguage() {
+ return mLanguage;
+ }
+
+ /**
+ * Gets the ISO 3-letter country code for the language to use.
+ */
+ public String getCountry() {
+ return mCountry;
+ }
+
+ /**
+ * Gets the language variant to use.
+ */
+ public String getVariant() {
+ return mVariant;
+ }
+
+ /**
+ * Gets the speech rate to use. {@link TextToSpeech.Engine#DEFAULT_RATE} (100)
+ * is the normal rate.
+ */
+ public int getSpeechRate() {
+ return mSpeechRate;
+ }
+
+ /**
+ * Gets the pitch to use. {@link TextToSpeech.Engine#DEFAULT_PITCH} (100)
+ * is the normal pitch.
+ */
+ public int getPitch() {
+ return mPitch;
+ }
+
+ /**
+ * Aborts the speech request.
+ *
+ * Can be called from multiple threads.
+ */
+ abstract void stop();
+
+ /**
+ * The service should call this when it starts to synthesize audio for this
+ * request.
+ *
+ * This method should only be called on the synthesis thread,
+ * while in {@link TextToSpeechService#onSynthesizeText}.
+ *
+ * @param sampleRateInHz Sample rate in HZ of the generated audio.
+ * @param audioFormat Audio format of the generated audio. Must be one of
+ * the ENCODING_ constants defined in {@link android.media.AudioFormat}.
+ * @param channelCount The number of channels
+ * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+ */
+ public abstract int start(int sampleRateInHz, int audioFormat, int channelCount);
+
+ /**
+ * The service should call this method when synthesized audio is ready for consumption.
+ *
+ * This method should only be called on the synthesis thread,
+ * while in {@link TextToSpeechService#onSynthesizeText}.
+ *
+ * @param buffer The generated audio data. This method will not hold on to {@code buffer},
+ * so the caller is free to modify it after this method returns.
+ * @param offset The offset into {@code buffer} where the audio data starts.
+ * @param length The number of bytes of audio data in {@code buffer}.
+ * Must be less than or equal to {@code buffer.length - offset}.
+ * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+ */
+ public abstract int audioAvailable(byte[] buffer, int offset, int length);
+
+ /**
+ * The service should call this method when all the synthesized audio for a request has
+ * been passed to {@link #audioAvailable}.
+ *
+ * This method should only be called on the synthesis thread,
+ * while in {@link TextToSpeechService#onSynthesizeText}.
+ *
+ * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+ */
+ public abstract int done();
+
+} \ No newline at end of file
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 95830ec..2add7b5 100755
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 Google Inc.
+ * Copyright (C) 2009 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
@@ -15,22 +15,31 @@
*/
package android.speech.tts;
-import android.speech.tts.ITts;
-import android.speech.tts.ITtsCallback;
-
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.provider.Settings;
+import android.text.TextUtils;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Locale;
+import java.util.Map;
/**
*
@@ -49,11 +58,11 @@ public class TextToSpeech {
/**
* Denotes a successful operation.
*/
- public static final int SUCCESS = 0;
+ public static final int SUCCESS = 0;
/**
* Denotes a generic operation failure.
*/
- public static final int ERROR = -1;
+ public static final int ERROR = -1;
/**
* Queue mode where all entries in the playback queue (media to be played
@@ -65,20 +74,17 @@ public class TextToSpeech {
*/
public static final int QUEUE_ADD = 1;
-
/**
* Denotes the language is available exactly as specified by the locale.
*/
public static final int LANG_COUNTRY_VAR_AVAILABLE = 2;
-
/**
* Denotes the language is available for the language and country specified
* by the locale, but not the variant.
*/
public static final int LANG_COUNTRY_AVAILABLE = 1;
-
/**
* Denotes the language is available for the language by the locale,
* but not the country and variant.
@@ -95,7 +101,6 @@ public class TextToSpeech {
*/
public static final int LANG_NOT_SUPPORTED = -2;
-
/**
* Broadcast Action: The TextToSpeech synthesizer has completed processing
* of all the text in the speech queue.
@@ -104,7 +109,6 @@ public class TextToSpeech {
public static final String ACTION_TTS_QUEUE_PROCESSING_COMPLETED =
"android.speech.tts.TTS_QUEUE_PROCESSING_COMPLETED";
-
/**
* Interface definition of a callback to be invoked indicating the completion of the
* TextToSpeech engine initialization.
@@ -112,103 +116,117 @@ public class TextToSpeech {
public interface OnInitListener {
/**
* Called to signal the completion of the TextToSpeech engine initialization.
+ *
* @param status {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
*/
public void onInit(int status);
}
/**
- * Interface definition of a callback to be invoked indicating the TextToSpeech engine has
- * completed synthesizing an utterance with an utterance ID set.
- *
+ * Listener that will be called when the TTS service has
+ * completed synthesizing an utterance. This is only called if the utterance
+ * has an utterance ID (see {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID}).
*/
public interface OnUtteranceCompletedListener {
/**
- * Called to signal the completion of the synthesis of the utterance that was identified
- * with the string parameter. This identifier is the one originally passed in the
- * parameter hashmap of the synthesis request in
- * {@link TextToSpeech#speak(String, int, HashMap)} or
- * {@link TextToSpeech#synthesizeToFile(String, HashMap, String)} with the
- * {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID} key.
+ * Called when an utterance has been synthesized.
+ *
* @param utteranceId the identifier of the utterance.
*/
public void onUtteranceCompleted(String utteranceId);
}
-
/**
- * Internal constants for the TextToSpeech functionality
- *
+ * Constants and parameter names for controlling text-to-speech.
*/
public class Engine {
- // default values for a TTS engine when settings are not found in the provider
+
/**
- * {@hide}
+ * Default speech rate.
+ * @hide
*/
- public static final int DEFAULT_RATE = 100; // 1x
+ public static final int DEFAULT_RATE = 100;
+
/**
- * {@hide}
+ * Default pitch.
+ * @hide
*/
- public static final int DEFAULT_PITCH = 100;// 1x
+ public static final int DEFAULT_PITCH = 100;
+
/**
- * {@hide}
+ * Default volume.
+ * @hide
*/
public static final float DEFAULT_VOLUME = 1.0f;
+
/**
- * {@hide}
- */
- protected static final String DEFAULT_VOLUME_STRING = "1.0";
- /**
- * {@hide}
+ * Default pan (centered).
+ * @hide
*/
public static final float DEFAULT_PAN = 0.0f;
- /**
- * {@hide}
- */
- protected static final String DEFAULT_PAN_STRING = "0.0";
/**
- * {@hide}
+ * Default value for {@link Settings.Secure#TTS_USE_DEFAULTS}.
+ * @hide
*/
public static final int USE_DEFAULTS = 0; // false
+
/**
- * {@hide}
+ * Package name of the default TTS engine.
+ *
+ * TODO: This should come from a system property
+ *
+ * @hide
*/
- public static final String DEFAULT_SYNTH = "com.svox.pico";
+ public static final String DEFAULT_ENGINE = "com.svox.pico";
- // default values for rendering
/**
* Default audio stream used when playing synthesized speech.
*/
public static final int DEFAULT_STREAM = AudioManager.STREAM_MUSIC;
- // return codes for a TTS engine's check data activity
/**
* Indicates success when checking the installation status of the resources used by the
* TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
*/
public static final int CHECK_VOICE_DATA_PASS = 1;
+
/**
* Indicates failure when checking the installation status of the resources used by the
* TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
*/
public static final int CHECK_VOICE_DATA_FAIL = 0;
+
/**
* Indicates erroneous data when checking the installation status of the resources used by
* the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
*/
public static final int CHECK_VOICE_DATA_BAD_DATA = -1;
+
/**
* Indicates missing resources when checking the installation status of the resources used
* by the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
*/
public static final int CHECK_VOICE_DATA_MISSING_DATA = -2;
+
/**
* Indicates missing storage volume when checking the installation status of the resources
* used by the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
*/
public static final int CHECK_VOICE_DATA_MISSING_VOLUME = -3;
+ /**
+ * Intent for starting a TTS service. Services that handle this intent must
+ * extend {@link TextToSpeechService}. Normal applications should not use this intent
+ * directly, instead they should talk to the TTS service using the the methods in this
+ * class.
+ *
+ * @hide Pending API council approval
+ */
+ @SdkConstant(SdkConstantType.SERVICE_ACTION)
+ public static final String INTENT_ACTION_TTS_SERVICE =
+ "android.intent.action.TTS_SERVICE";
+
// intents to ask engine to install data or check its data
/**
* Activity Action: Triggers the platform TextToSpeech engine to
@@ -231,6 +249,7 @@ public class TextToSpeech {
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_TTS_DATA_INSTALLED =
"android.speech.tts.engine.TTS_DATA_INSTALLED";
+
/**
* Activity Action: Starts the activity from the platform TextToSpeech
* engine to verify the proper installation and availability of the
@@ -258,23 +277,36 @@ public class TextToSpeech {
public static final String ACTION_CHECK_TTS_DATA =
"android.speech.tts.engine.CHECK_TTS_DATA";
+ /**
+ * Activity intent for getting some sample text to use for demonstrating TTS.
+ *
+ * @hide This intent was used by engines written against the old API.
+ * Not sure if it should be exposed.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_GET_SAMPLE_TEXT =
+ "android.speech.tts.engine.GET_SAMPLE_TEXT";
+
// extras for a TTS engine's check data activity
/**
* Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
* the TextToSpeech engine specifies the path to its resources.
*/
public static final String EXTRA_VOICE_DATA_ROOT_DIRECTORY = "dataRoot";
+
/**
* Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
* the TextToSpeech engine specifies the file names of its resources under the
* resource path.
*/
public static final String EXTRA_VOICE_DATA_FILES = "dataFiles";
+
/**
* Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
* the TextToSpeech engine specifies the locale associated with each resource file.
*/
public static final String EXTRA_VOICE_DATA_FILES_INFO = "dataFilesInfo";
+
/**
* Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
* the TextToSpeech engine returns an ArrayList<String> of all the available voices.
@@ -282,6 +314,7 @@ public class TextToSpeech {
* optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").
*/
public static final String EXTRA_AVAILABLE_VOICES = "availableVoices";
+
/**
* Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
* the TextToSpeech engine returns an ArrayList<String> of all the unavailable voices.
@@ -289,6 +322,7 @@ public class TextToSpeech {
* optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").
*/
public static final String EXTRA_UNAVAILABLE_VOICES = "unavailableVoices";
+
/**
* Extra information sent with the {@link #ACTION_CHECK_TTS_DATA} intent where the
* caller indicates to the TextToSpeech engine which specific sets of voice data to
@@ -311,134 +345,87 @@ public class TextToSpeech {
// keys for the parameters passed with speak commands. Hidden keys are used internally
// to maintain engine state for each TextToSpeech instance.
/**
- * {@hide}
+ * @hide
*/
public static final String KEY_PARAM_RATE = "rate";
+
/**
- * {@hide}
+ * @hide
*/
public static final String KEY_PARAM_LANGUAGE = "language";
+
/**
- * {@hide}
+ * @hide
*/
public static final String KEY_PARAM_COUNTRY = "country";
+
/**
- * {@hide}
+ * @hide
*/
public static final String KEY_PARAM_VARIANT = "variant";
+
/**
- * {@hide}
+ * @hide
*/
public static final String KEY_PARAM_ENGINE = "engine";
+
/**
- * {@hide}
+ * @hide
*/
public static final String KEY_PARAM_PITCH = "pitch";
+
/**
* Parameter key to specify the audio stream type to be used when speaking text
- * or playing back a file.
+ * or playing back a file. The value should be one of the STREAM_ constants
+ * defined in {@link AudioManager}.
+ *
* @see TextToSpeech#speak(String, int, HashMap)
* @see TextToSpeech#playEarcon(String, int, HashMap)
*/
public static final String KEY_PARAM_STREAM = "streamType";
+
/**
* Parameter key to identify an utterance in the
* {@link TextToSpeech.OnUtteranceCompletedListener} after text has been
* spoken, a file has been played back or a silence duration has elapsed.
+ *
* @see TextToSpeech#speak(String, int, HashMap)
* @see TextToSpeech#playEarcon(String, int, HashMap)
* @see TextToSpeech#synthesizeToFile(String, HashMap, String)
*/
public static final String KEY_PARAM_UTTERANCE_ID = "utteranceId";
+
/**
* Parameter key to specify the speech volume relative to the current stream type
* volume used when speaking text. Volume is specified as a float ranging from 0 to 1
* where 0 is silence, and 1 is the maximum volume (the default behavior).
+ *
* @see TextToSpeech#speak(String, int, HashMap)
* @see TextToSpeech#playEarcon(String, int, HashMap)
*/
public static final String KEY_PARAM_VOLUME = "volume";
+
/**
* Parameter key to specify how the speech is panned from left to right when speaking text.
* Pan is specified as a float ranging from -1 to +1 where -1 maps to a hard-left pan,
* 0 to center (the default behavior), and +1 to hard-right.
+ *
* @see TextToSpeech#speak(String, int, HashMap)
* @see TextToSpeech#playEarcon(String, int, HashMap)
*/
public static final String KEY_PARAM_PAN = "pan";
- // key positions in the array of cached parameters
- /**
- * {@hide}
- */
- protected static final int PARAM_POSITION_RATE = 0;
- /**
- * {@hide}
- */
- protected static final int PARAM_POSITION_LANGUAGE = 2;
- /**
- * {@hide}
- */
- protected static final int PARAM_POSITION_COUNTRY = 4;
- /**
- * {@hide}
- */
- protected static final int PARAM_POSITION_VARIANT = 6;
- /**
- * {@hide}
- */
- protected static final int PARAM_POSITION_STREAM = 8;
- /**
- * {@hide}
- */
- protected static final int PARAM_POSITION_UTTERANCE_ID = 10;
-
- /**
- * {@hide}
- */
- protected static final int PARAM_POSITION_ENGINE = 12;
-
- /**
- * {@hide}
- */
- protected static final int PARAM_POSITION_PITCH = 14;
-
- /**
- * {@hide}
- */
- protected static final int PARAM_POSITION_VOLUME = 16;
-
- /**
- * {@hide}
- */
- protected static final int PARAM_POSITION_PAN = 18;
-
-
- /**
- * {@hide}
- * Total number of cached speech parameters.
- * This number should be equal to (max param position/2) + 1.
- */
- protected static final int NB_CACHED_PARAMS = 10;
}
- /**
- * Connection needed for the TTS.
- */
- private ServiceConnection mServiceConnection;
-
- private ITts mITts = null;
- private ITtsCallback mITtscallback = null;
- private Context mContext = null;
- private String mPackageName = "";
- private OnInitListener mInitListener = null;
- private boolean mStarted = false;
+ private final Context mContext;
+ private Connection mServiceConnection;
+ private OnInitListener mInitListener;
private final Object mStartLock = new Object();
- /**
- * Used to store the cached parameters sent along with each synthesis request to the
- * TTS service.
- */
- private String[] mCachedParams;
+
+ private String mRequestedEngine;
+ private final Map<String, Uri> mEarcons;
+ private final Map<String, Uri> mUtterances;
+ private final Bundle mParams = new Bundle();
/**
* The constructor for the TextToSpeech class.
@@ -451,84 +438,98 @@ public class TextToSpeech {
* TextToSpeech engine has initialized.
*/
public TextToSpeech(Context context, OnInitListener listener) {
+ this(context, listener, null);
+ }
+
+ /**
+ * @hide pending approval
+ */
+ public TextToSpeech(Context context, OnInitListener listener, String engine) {
mContext = context;
- mPackageName = mContext.getPackageName();
mInitListener = listener;
+ mRequestedEngine = engine;
- mCachedParams = new String[2*Engine.NB_CACHED_PARAMS]; // store key and value
- mCachedParams[Engine.PARAM_POSITION_RATE] = Engine.KEY_PARAM_RATE;
- mCachedParams[Engine.PARAM_POSITION_LANGUAGE] = Engine.KEY_PARAM_LANGUAGE;
- mCachedParams[Engine.PARAM_POSITION_COUNTRY] = Engine.KEY_PARAM_COUNTRY;
- mCachedParams[Engine.PARAM_POSITION_VARIANT] = Engine.KEY_PARAM_VARIANT;
- mCachedParams[Engine.PARAM_POSITION_STREAM] = Engine.KEY_PARAM_STREAM;
- mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID] = Engine.KEY_PARAM_UTTERANCE_ID;
- mCachedParams[Engine.PARAM_POSITION_ENGINE] = Engine.KEY_PARAM_ENGINE;
- mCachedParams[Engine.PARAM_POSITION_PITCH] = Engine.KEY_PARAM_PITCH;
- mCachedParams[Engine.PARAM_POSITION_VOLUME] = Engine.KEY_PARAM_VOLUME;
- mCachedParams[Engine.PARAM_POSITION_PAN] = Engine.KEY_PARAM_PAN;
-
- // Leave all defaults that are shown in Settings uninitialized/at the default
- // so that the values set in Settings will take effect if the application does
- // not try to change these settings itself.
- mCachedParams[Engine.PARAM_POSITION_RATE + 1] = "";
- mCachedParams[Engine.PARAM_POSITION_LANGUAGE + 1] = "";
- mCachedParams[Engine.PARAM_POSITION_COUNTRY + 1] = "";
- mCachedParams[Engine.PARAM_POSITION_VARIANT + 1] = "";
- mCachedParams[Engine.PARAM_POSITION_STREAM + 1] =
- String.valueOf(Engine.DEFAULT_STREAM);
- mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID + 1] = "";
- mCachedParams[Engine.PARAM_POSITION_ENGINE + 1] = "";
- mCachedParams[Engine.PARAM_POSITION_PITCH + 1] = "100";
- mCachedParams[Engine.PARAM_POSITION_VOLUME + 1] = Engine.DEFAULT_VOLUME_STRING;
- mCachedParams[Engine.PARAM_POSITION_PAN + 1] = Engine.DEFAULT_PAN_STRING;
+ mEarcons = new HashMap<String, Uri>();
+ mUtterances = new HashMap<String, Uri>();
initTts();
}
+ private String getPackageName() {
+ return mContext.getPackageName();
+ }
- private void initTts() {
- mStarted = false;
-
- // Initialize the TTS, run the callback after the binding is successful
- mServiceConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName name, IBinder service) {
- synchronized(mStartLock) {
- mITts = ITts.Stub.asInterface(service);
- mStarted = true;
- // Cache the default engine and current language
- setEngineByPackageName(getDefaultEngine());
- setLanguage(getLanguage());
- if (mInitListener != null) {
- // TODO manage failures and missing resources
- mInitListener.onInit(SUCCESS);
- }
- }
+ private <R> R runActionNoReconnect(Action<R> action, R errorResult, String method) {
+ return runAction(action, errorResult, method, false);
+ }
+
+ private <R> R runAction(Action<R> action, R errorResult, String method) {
+ return runAction(action, errorResult, method, true);
+ }
+
+ private <R> R runAction(Action<R> action, R errorResult, String method, boolean reconnect) {
+ synchronized (mStartLock) {
+ if (mServiceConnection == null) {
+ Log.w(TAG, method + " failed: not bound to TTS engine");
+ return errorResult;
}
+ return mServiceConnection.runAction(action, errorResult, method, reconnect);
+ }
+ }
- public void onServiceDisconnected(ComponentName name) {
- synchronized(mStartLock) {
- mITts = null;
- mInitListener = null;
- mStarted = false;
- }
+ private int initTts() {
+ String defaultEngine = getDefaultEngine();
+ String engine = defaultEngine;
+ if (!areDefaultsEnforced() && !TextUtils.isEmpty(mRequestedEngine)
+ && isEngineEnabled(engine)) {
+ engine = mRequestedEngine;
+ }
+
+ // Try requested engine
+ if (connectToEngine(engine)) {
+ return SUCCESS;
+ }
+
+ // Fall back to user's default engine if different from the already tested one
+ if (!engine.equals(defaultEngine)) {
+ if (connectToEngine(defaultEngine)) {
+ return SUCCESS;
}
- };
+ }
- Intent intent = new Intent("android.intent.action.START_TTS_SERVICE");
- intent.addCategory("android.intent.category.TTS");
- boolean bound = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
- if (!bound) {
- Log.e("TextToSpeech.java", "initTts() failed to bind to service");
- if (mInitListener != null) {
- mInitListener.onInit(ERROR);
+ // Fall back to the hardcoded default if different from the two above
+ if (!defaultEngine.equals(Engine.DEFAULT_ENGINE)
+ && !engine.equals(Engine.DEFAULT_ENGINE)) {
+ if (connectToEngine(Engine.DEFAULT_ENGINE)) {
+ return SUCCESS;
}
+ }
+
+ return ERROR;
+ }
+
+ private boolean connectToEngine(String engine) {
+ Connection connection = new Connection();
+ Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE);
+ intent.setPackage(engine);
+ boolean bound = mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE);
+ if (!bound) {
+ Log.e(TAG, "Failed to bind to " + engine);
+ dispatchOnInit(ERROR);
+ return false;
} else {
- // initialization listener will be called inside ServiceConnection
- Log.i("TextToSpeech.java", "initTts() successfully bound to service");
+ return true;
}
- // TODO handle plugin failures
}
+ private void dispatchOnInit(int result) {
+ synchronized (mStartLock) {
+ if (mInitListener != null) {
+ mInitListener.onInit(result);
+ mInitListener = null;
+ }
+ }
+ }
/**
* Releases the resources used by the TextToSpeech engine.
@@ -536,15 +537,17 @@ public class TextToSpeech {
* so the TextToSpeech engine can be cleanly stopped.
*/
public void shutdown() {
- try {
- mContext.unbindService(mServiceConnection);
- } catch (IllegalArgumentException e) {
- // Do nothing and fail silently since an error here indicates that
- // binding never succeeded in the first place.
- }
+ runActionNoReconnect(new Action<Void>() {
+ @Override
+ public Void run(ITextToSpeechService service) throws RemoteException {
+ service.setCallback(getPackageName(), null);
+ service.stop(getPackageName());
+ mServiceConnection.disconnect();
+ return null;
+ }
+ }, null, "shutdown");
}
-
/**
* Adds a mapping between a string of text and a sound resource in a
* package. After a call to this method, subsequent calls to
@@ -573,21 +576,9 @@ public class TextToSpeech {
* @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
*/
public int addSpeech(String text, String packagename, int resourceId) {
- synchronized(mStartLock) {
- if (!mStarted) {
- return ERROR;
- }
- try {
- mITts.addSpeech(mPackageName, text, packagename, resourceId);
- return SUCCESS;
- } catch (RemoteException e) {
- restart("addSpeech", e);
- } catch (NullPointerException e) {
- restart("addSpeech", e);
- } catch (IllegalStateException e) {
- restart("addSpeech", e);
- }
- return ERROR;
+ synchronized (mStartLock) {
+ mUtterances.put(text, makeResourceUri(packagename, resourceId));
+ return SUCCESS;
}
}
@@ -608,20 +599,8 @@ public class TextToSpeech {
*/
public int addSpeech(String text, String filename) {
synchronized (mStartLock) {
- if (!mStarted) {
- return ERROR;
- }
- try {
- mITts.addSpeechFile(mPackageName, text, filename);
- return SUCCESS;
- } catch (RemoteException e) {
- restart("addSpeech", e);
- } catch (NullPointerException e) {
- restart("addSpeech", e);
- } catch (IllegalStateException e) {
- restart("addSpeech", e);
- }
- return ERROR;
+ mUtterances.put(text, Uri.parse(filename));
+ return SUCCESS;
}
}
@@ -653,24 +632,11 @@ public class TextToSpeech {
*/
public int addEarcon(String earcon, String packagename, int resourceId) {
synchronized(mStartLock) {
- if (!mStarted) {
- return ERROR;
- }
- try {
- mITts.addEarcon(mPackageName, earcon, packagename, resourceId);
- return SUCCESS;
- } catch (RemoteException e) {
- restart("addEarcon", e);
- } catch (NullPointerException e) {
- restart("addEarcon", e);
- } catch (IllegalStateException e) {
- restart("addEarcon", e);
- }
- return ERROR;
+ mEarcons.put(earcon, makeResourceUri(packagename, resourceId));
+ return SUCCESS;
}
}
-
/**
* Adds a mapping between a string of text and a sound file.
* Use this to add custom earcons.
@@ -687,312 +653,199 @@ public class TextToSpeech {
* @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
*/
public int addEarcon(String earcon, String filename) {
- synchronized (mStartLock) {
- if (!mStarted) {
- return ERROR;
- }
- try {
- mITts.addEarconFile(mPackageName, earcon, filename);
- return SUCCESS;
- } catch (RemoteException e) {
- restart("addEarcon", e);
- } catch (NullPointerException e) {
- restart("addEarcon", e);
- } catch (IllegalStateException e) {
- restart("addEarcon", e);
- }
- return ERROR;
+ synchronized(mStartLock) {
+ mEarcons.put(earcon, Uri.parse(filename));
+ return SUCCESS;
}
}
+ private Uri makeResourceUri(String packageName, int resourceId) {
+ return new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
+ .encodedAuthority(packageName)
+ .appendEncodedPath(String.valueOf(resourceId))
+ .build();
+ }
/**
* Speaks the string using the specified queuing strategy and speech
* parameters.
*
- * @param text
- * The string of text to be spoken.
- * @param queueMode
- * The queuing strategy to use.
- * {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
- * @param params
- * The list of parameters to be used. Can be null if no parameters are given.
- * They are specified using a (key, value) pair, where the key can be
- * {@link Engine#KEY_PARAM_STREAM} or
- * {@link Engine#KEY_PARAM_UTTERANCE_ID}.
+ * @param text The string of text to be spoken.
+ * @param queueMode The queuing strategy to use, {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
+ * @param params Parameters for the request. Can be null.
+ * Supported parameter names:
+ * {@link Engine#KEY_PARAM_STREAM},
+ * {@link Engine#KEY_PARAM_UTTERANCE_ID},
+ * {@link Engine#KEY_PARAM_VOLUME},
+ * {@link Engine#KEY_PARAM_PAN}.
*
- * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+ * @return {@link #ERROR} or {@link #SUCCESS}.
*/
- public int speak(String text, int queueMode, HashMap<String,String> params)
- {
- synchronized (mStartLock) {
- int result = ERROR;
- Log.i("TextToSpeech.java - speak", "speak text of length " + text.length());
- if (!mStarted) {
- Log.e("TextToSpeech.java - speak", "service isn't started");
- return result;
- }
- try {
- if ((params != null) && (!params.isEmpty())) {
- setCachedParam(params, Engine.KEY_PARAM_STREAM, Engine.PARAM_POSITION_STREAM);
- setCachedParam(params, Engine.KEY_PARAM_UTTERANCE_ID,
- Engine.PARAM_POSITION_UTTERANCE_ID);
- setCachedParam(params, Engine.KEY_PARAM_ENGINE, Engine.PARAM_POSITION_ENGINE);
- setCachedParam(params, Engine.KEY_PARAM_VOLUME, Engine.PARAM_POSITION_VOLUME);
- setCachedParam(params, Engine.KEY_PARAM_PAN, Engine.PARAM_POSITION_PAN);
+ public int speak(final String text, final int queueMode, final HashMap<String, String> params) {
+ return runAction(new Action<Integer>() {
+ @Override
+ public Integer run(ITextToSpeechService service) throws RemoteException {
+ Uri utteranceUri = mUtterances.get(text);
+ if (utteranceUri != null) {
+ return service.playAudio(getPackageName(), utteranceUri, queueMode,
+ getParams(params));
+ } else {
+ return service.speak(getPackageName(), text, queueMode, getParams(params));
}
- result = mITts.speak(mPackageName, text, queueMode, mCachedParams);
- } catch (RemoteException e) {
- restart("speak", e);
- } catch (NullPointerException e) {
- restart("speak", e);
- } catch (IllegalStateException e) {
- restart("speak", e);
- } finally {
- resetCachedParams();
}
- return result;
- }
+ }, ERROR, "speak");
}
-
/**
* Plays the earcon using the specified queueing mode and parameters.
+ * The earcon must already have been added with {@link #addEarcon(String, String)} or
+ * {@link #addEarcon(String, String, int)}.
*
- * @param earcon
- * The earcon that should be played
- * @param queueMode
- * {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
- * @param params
- * The list of parameters to be used. Can be null if no parameters are given.
- * They are specified using a (key, value) pair, where the key can be
- * {@link Engine#KEY_PARAM_STREAM} or
+ * @param earcon The earcon that should be played
+ * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
+ * @param params Parameters for the request. Can be null.
+ * Supported parameter names:
+ * {@link Engine#KEY_PARAM_STREAM},
* {@link Engine#KEY_PARAM_UTTERANCE_ID}.
*
- * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+ * @return {@link #ERROR} or {@link #SUCCESS}.
*/
- public int playEarcon(String earcon, int queueMode,
- HashMap<String,String> params) {
- synchronized (mStartLock) {
- int result = ERROR;
- if (!mStarted) {
- return result;
- }
- try {
- if ((params != null) && (!params.isEmpty())) {
- String extra = params.get(Engine.KEY_PARAM_STREAM);
- if (extra != null) {
- mCachedParams[Engine.PARAM_POSITION_STREAM + 1] = extra;
- }
- setCachedParam(params, Engine.KEY_PARAM_STREAM, Engine.PARAM_POSITION_STREAM);
- setCachedParam(params, Engine.KEY_PARAM_UTTERANCE_ID,
- Engine.PARAM_POSITION_UTTERANCE_ID);
+ public int playEarcon(final String earcon, final int queueMode,
+ final HashMap<String, String> params) {
+ return runAction(new Action<Integer>() {
+ @Override
+ public Integer run(ITextToSpeechService service) throws RemoteException {
+ Uri earconUri = mEarcons.get(earcon);
+ if (earconUri == null) {
+ return ERROR;
}
- result = mITts.playEarcon(mPackageName, earcon, queueMode, null);
- } catch (RemoteException e) {
- restart("playEarcon", e);
- } catch (NullPointerException e) {
- restart("playEarcon", e);
- } catch (IllegalStateException e) {
- restart("playEarcon", e);
- } finally {
- resetCachedParams();
+ return service.playAudio(getPackageName(), earconUri, queueMode,
+ getParams(params));
}
- return result;
- }
+ }, ERROR, "playEarcon");
}
/**
* Plays silence for the specified amount of time using the specified
* queue mode.
*
- * @param durationInMs
- * A long that indicates how long the silence should last.
- * @param queueMode
- * {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
- * @param params
- * The list of parameters to be used. Can be null if no parameters are given.
- * They are specified using a (key, value) pair, where the key can be
+ * @param durationInMs The duration of the silence.
+ * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
+ * @param params Parameters for the request. Can be null.
+ * Supported parameter names:
* {@link Engine#KEY_PARAM_UTTERANCE_ID}.
*
- * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+ * @return {@link #ERROR} or {@link #SUCCESS}.
*/
- public int playSilence(long durationInMs, int queueMode, HashMap<String,String> params) {
- synchronized (mStartLock) {
- int result = ERROR;
- if (!mStarted) {
- return result;
- }
- try {
- if ((params != null) && (!params.isEmpty())) {
- setCachedParam(params, Engine.KEY_PARAM_UTTERANCE_ID,
- Engine.PARAM_POSITION_UTTERANCE_ID);
- }
- result = mITts.playSilence(mPackageName, durationInMs, queueMode, mCachedParams);
- } catch (RemoteException e) {
- restart("playSilence", e);
- } catch (NullPointerException e) {
- restart("playSilence", e);
- } catch (IllegalStateException e) {
- restart("playSilence", e);
- } finally {
- resetCachedParams();
+ public int playSilence(final long durationInMs, final int queueMode,
+ final HashMap<String, String> params) {
+ return runAction(new Action<Integer>() {
+ @Override
+ public Integer run(ITextToSpeechService service) throws RemoteException {
+ return service.playSilence(getPackageName(), durationInMs, queueMode,
+ getParams(params));
}
- return result;
- }
+ }, ERROR, "playSilence");
}
-
/**
- * Returns whether or not the TextToSpeech engine is busy speaking.
+ * Checks whether the TTS engine is busy speaking.
*
- * @return Whether or not the TextToSpeech engine is busy speaking.
+ * @return {@code true} if the TTS engine is speaking.
*/
public boolean isSpeaking() {
- synchronized (mStartLock) {
- if (!mStarted) {
- return false;
- }
- try {
- return mITts.isSpeaking();
- } catch (RemoteException e) {
- restart("isSpeaking", e);
- } catch (NullPointerException e) {
- restart("isSpeaking", e);
- } catch (IllegalStateException e) {
- restart("isSpeaking", e);
+ return runAction(new Action<Boolean>() {
+ @Override
+ public Boolean run(ITextToSpeechService service) throws RemoteException {
+ return service.isSpeaking();
}
- return false;
- }
+ }, false, "isSpeaking");
}
-
/**
* Interrupts the current utterance (whether played or rendered to file) and discards other
* utterances in the queue.
*
- * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+ * @return {@link #ERROR} or {@link #SUCCESS}.
*/
public int stop() {
- synchronized (mStartLock) {
- int result = ERROR;
- if (!mStarted) {
- return result;
+ return runAction(new Action<Integer>() {
+ @Override
+ public Integer run(ITextToSpeechService service) throws RemoteException {
+ return service.stop(getPackageName());
}
- try {
- result = mITts.stop(mPackageName);
- } catch (RemoteException e) {
- restart("stop", e);
- } catch (NullPointerException e) {
- restart("stop", e);
- } catch (IllegalStateException e) {
- restart("stop", e);
- }
- return result;
- }
+ }, ERROR, "stop");
}
-
/**
- * Sets the speech rate for the TextToSpeech engine.
+ * Sets the speech rate.
*
* This has no effect on any pre-recorded speech.
*
- * @param speechRate
- * The speech rate for the TextToSpeech engine. 1 is the normal speed,
- * lower values slow down the speech (0.5 is half the normal speech rate),
- * greater values accelerate it (2 is twice the normal speech rate).
+ * @param speechRate Speech rate. {@code 1.0} is the normal speech rate,
+ * lower values slow down the speech ({@code 0.5} is half the normal speech rate),
+ * greater values accelerate it ({@code 2.0} is twice the normal speech rate).
*
- * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+ * @return {@link #ERROR} or {@link #SUCCESS}.
*/
public int setSpeechRate(float speechRate) {
- synchronized (mStartLock) {
- int result = ERROR;
- if (!mStarted) {
- return result;
- }
- try {
- if (speechRate > 0) {
- int rate = (int)(speechRate*100);
- mCachedParams[Engine.PARAM_POSITION_RATE + 1] = String.valueOf(rate);
- // the rate is not set here, instead it is cached so it will be associated
- // with all upcoming utterances.
- if (speechRate > 0.0f) {
- result = SUCCESS;
- } else {
- result = ERROR;
- }
+ if (speechRate > 0.0f) {
+ int intRate = (int)(speechRate * 100);
+ if (intRate > 0) {
+ synchronized (mStartLock) {
+ mParams.putInt(Engine.KEY_PARAM_RATE, intRate);
}
- } catch (NullPointerException e) {
- restart("setSpeechRate", e);
- } catch (IllegalStateException e) {
- restart("setSpeechRate", e);
+ return SUCCESS;
}
- return result;
}
+ return ERROR;
}
-
/**
* Sets the speech pitch for the TextToSpeech engine.
*
* This has no effect on any pre-recorded speech.
*
- * @param pitch
- * The pitch for the TextToSpeech engine. 1 is the normal pitch,
+ * @param pitch Speech pitch. {@code 1.0} is the normal pitch,
* lower values lower the tone of the synthesized voice,
* greater values increase it.
*
- * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+ * @return {@link #ERROR} or {@link #SUCCESS}.
*/
public int setPitch(float pitch) {
- synchronized (mStartLock) {
- int result = ERROR;
- if (!mStarted) {
- return result;
- }
- try {
- // the pitch is not set here, instead it is cached so it will be associated
- // with all upcoming utterances.
- if (pitch > 0) {
- int p = (int)(pitch*100);
- mCachedParams[Engine.PARAM_POSITION_PITCH + 1] = String.valueOf(p);
- result = SUCCESS;
+ if (pitch > 0.0f) {
+ int intPitch = (int)(pitch * 100);
+ if (intPitch > 0) {
+ synchronized (mStartLock) {
+ mParams.putInt(Engine.KEY_PARAM_PITCH, intPitch);
}
- } catch (NullPointerException e) {
- restart("setPitch", e);
- } catch (IllegalStateException e) {
- restart("setPitch", e);
+ return SUCCESS;
}
- return result;
}
+ return ERROR;
}
-
/**
- * Sets the language for the TextToSpeech engine.
- * The TextToSpeech engine will try to use the closest match to the specified
+ * Sets the text-to-speech language.
+ * The TTS engine will try to use the closest match to the specified
* language as represented by the Locale, but there is no guarantee that the exact same Locale
* will be used. Use {@link #isLanguageAvailable(Locale)} to check the level of support
* before choosing the language to use for the next utterances.
*
- * @param loc
- * The locale describing the language to be used.
+ * @param loc The locale describing the language to be used.
*
- * @return code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
+ * @return Code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
* {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE},
* {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
*/
- public int setLanguage(Locale loc) {
- synchronized (mStartLock) {
- int result = LANG_NOT_SUPPORTED;
- if (!mStarted) {
- return result;
- }
- if (loc == null) {
- return result;
- }
- try {
+ public int setLanguage(final Locale loc) {
+ return runAction(new Action<Integer>() {
+ @Override
+ public Integer run(ITextToSpeechService service) throws RemoteException {
+ if (loc == null) {
+ return LANG_NOT_SUPPORTED;
+ }
String language = loc.getISO3Language();
String country = loc.getISO3Country();
String variant = loc.getVariant();
@@ -1000,294 +853,316 @@ public class TextToSpeech {
// the available parts.
// Note that the language is not actually set here, instead it is cached so it
// will be associated with all upcoming utterances.
- result = mITts.isLanguageAvailable(language, country, variant, mCachedParams);
+ int result = service.loadLanguage(language, country, variant);
if (result >= LANG_AVAILABLE){
- mCachedParams[Engine.PARAM_POSITION_LANGUAGE + 1] = language;
- if (result >= LANG_COUNTRY_AVAILABLE){
- mCachedParams[Engine.PARAM_POSITION_COUNTRY + 1] = country;
- } else {
- mCachedParams[Engine.PARAM_POSITION_COUNTRY + 1] = "";
- }
- if (result >= LANG_COUNTRY_VAR_AVAILABLE){
- mCachedParams[Engine.PARAM_POSITION_VARIANT + 1] = variant;
- } else {
- mCachedParams[Engine.PARAM_POSITION_VARIANT + 1] = "";
+ if (result < LANG_COUNTRY_VAR_AVAILABLE) {
+ variant = "";
+ if (result < LANG_COUNTRY_AVAILABLE) {
+ country = "";
+ }
}
+ mParams.putString(Engine.KEY_PARAM_LANGUAGE, language);
+ mParams.putString(Engine.KEY_PARAM_COUNTRY, country);
+ mParams.putString(Engine.KEY_PARAM_VARIANT, variant);
}
- } catch (RemoteException e) {
- restart("setLanguage", e);
- } catch (NullPointerException e) {
- restart("setLanguage", e);
- } catch (IllegalStateException e) {
- restart("setLanguage", e);
+ return result;
}
- return result;
- }
+ }, LANG_NOT_SUPPORTED, "setLanguage");
}
-
/**
* Returns a Locale instance describing the language currently being used by the TextToSpeech
* engine.
+ *
* @return language, country (if any) and variant (if any) used by the engine stored in a Locale
- * instance, or null is the TextToSpeech engine has failed.
+ * instance, or {@code null} on error.
*/
public Locale getLanguage() {
- synchronized (mStartLock) {
- if (!mStarted) {
- return null;
- }
- try {
- // Only do a call to the native synth if there is nothing in the cached params
- if (mCachedParams[Engine.PARAM_POSITION_LANGUAGE + 1].length() < 1){
- String[] locStrings = mITts.getLanguage();
- if ((locStrings != null) && (locStrings.length == 3)) {
- return new Locale(locStrings[0], locStrings[1], locStrings[2]);
- } else {
- return null;
- }
- } else {
- return new Locale(mCachedParams[Engine.PARAM_POSITION_LANGUAGE + 1],
- mCachedParams[Engine.PARAM_POSITION_COUNTRY + 1],
- mCachedParams[Engine.PARAM_POSITION_VARIANT + 1]);
+ return runAction(new Action<Locale>() {
+ @Override
+ public Locale run(ITextToSpeechService service) throws RemoteException {
+ String[] locStrings = service.getLanguage();
+ if (locStrings != null && locStrings.length == 3) {
+ return new Locale(locStrings[0], locStrings[1], locStrings[2]);
}
- } catch (RemoteException e) {
- restart("getLanguage", e);
- } catch (NullPointerException e) {
- restart("getLanguage", e);
- } catch (IllegalStateException e) {
- restart("getLanguage", e);
+ return null;
}
- return null;
- }
+ }, null, "getLanguage");
}
/**
* Checks if the specified language as represented by the Locale is available and supported.
*
- * @param loc
- * The Locale describing the language to be used.
+ * @param loc The Locale describing the language to be used.
*
- * @return code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
+ * @return Code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
* {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE},
* {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
*/
- public int isLanguageAvailable(Locale loc) {
- synchronized (mStartLock) {
- int result = LANG_NOT_SUPPORTED;
- if (!mStarted) {
- return result;
+ public int isLanguageAvailable(final Locale loc) {
+ return runAction(new Action<Integer>() {
+ @Override
+ public Integer run(ITextToSpeechService service) throws RemoteException {
+ return service.isLanguageAvailable(loc.getISO3Language(),
+ loc.getISO3Country(), loc.getVariant());
}
- try {
- result = mITts.isLanguageAvailable(loc.getISO3Language(),
- loc.getISO3Country(), loc.getVariant(), mCachedParams);
- } catch (RemoteException e) {
- restart("isLanguageAvailable", e);
- } catch (NullPointerException e) {
- restart("isLanguageAvailable", e);
- } catch (IllegalStateException e) {
- restart("isLanguageAvailable", e);
- }
- return result;
- }
+ }, LANG_NOT_SUPPORTED, "isLanguageAvailable");
}
-
/**
* Synthesizes the given text to a file using the specified parameters.
*
- * @param text
- * The String of text that should be synthesized
- * @param params
- * The list of parameters to be used. Can be null if no parameters are given.
- * They are specified using a (key, value) pair, where the key can be
+ * @param text Thetext that should be synthesized
+ * @param params Parameters for the request. Can be null.
+ * Supported parameter names:
* {@link Engine#KEY_PARAM_UTTERANCE_ID}.
- * @param filename
- * The string that gives the full output filename; it should be
+ * @param filename Absolute file filename to write the generated audio data to.It should be
* something like "/sdcard/myappsounds/mysound.wav".
*
- * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+ * @return {@link #ERROR} or {@link #SUCCESS}.
*/
- public int synthesizeToFile(String text, HashMap<String,String> params,
- String filename) {
- Log.i("TextToSpeech.java", "synthesizeToFile()");
- synchronized (mStartLock) {
- int result = ERROR;
- Log.i("TextToSpeech.java - synthesizeToFile", "synthesizeToFile text of length "
- + text.length());
- if (!mStarted) {
- Log.e("TextToSpeech.java - synthesizeToFile", "service isn't started");
- return result;
+ public int synthesizeToFile(final String text, final HashMap<String, String> params,
+ final String filename) {
+ return runAction(new Action<Integer>() {
+ @Override
+ public Integer run(ITextToSpeechService service) throws RemoteException {
+ return service.synthesizeToFile(getPackageName(), text, filename,
+ getParams(params));
}
- try {
- if ((params != null) && (!params.isEmpty())) {
- // no need to read the stream type here
- setCachedParam(params, Engine.KEY_PARAM_UTTERANCE_ID,
- Engine.PARAM_POSITION_UTTERANCE_ID);
- setCachedParam(params, Engine.KEY_PARAM_ENGINE, Engine.PARAM_POSITION_ENGINE);
- }
- result = mITts.synthesizeToFile(mPackageName, text, mCachedParams, filename) ?
- SUCCESS : ERROR;
- } catch (RemoteException e) {
- restart("synthesizeToFile", e);
- } catch (NullPointerException e) {
- restart("synthesizeToFile", e);
- } catch (IllegalStateException e) {
- restart("synthesizeToFile", e);
- } finally {
- resetCachedParams();
- }
- return result;
+ }, ERROR, "synthesizeToFile");
+ }
+
+ private Bundle getParams(HashMap<String, String> params) {
+ if (params != null && !params.isEmpty()) {
+ Bundle bundle = new Bundle(mParams);
+ copyIntParam(bundle, params, Engine.KEY_PARAM_STREAM);
+ copyStringParam(bundle, params, Engine.KEY_PARAM_UTTERANCE_ID);
+ copyFloatParam(bundle, params, Engine.KEY_PARAM_VOLUME);
+ copyFloatParam(bundle, params, Engine.KEY_PARAM_PAN);
+ return bundle;
+ } else {
+ return mParams;
}
}
+ private void copyStringParam(Bundle bundle, HashMap<String, String> params, String key) {
+ String value = params.get(key);
+ if (value != null) {
+ bundle.putString(key, value);
+ }
+ }
- /**
- * Convenience method to reset the cached parameters to the current default values
- * if they are not persistent between calls to the service.
- */
- private void resetCachedParams() {
- mCachedParams[Engine.PARAM_POSITION_STREAM + 1] =
- String.valueOf(Engine.DEFAULT_STREAM);
- mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID+ 1] = "";
- mCachedParams[Engine.PARAM_POSITION_VOLUME + 1] = Engine.DEFAULT_VOLUME_STRING;
- mCachedParams[Engine.PARAM_POSITION_PAN + 1] = Engine.DEFAULT_PAN_STRING;
+ private void copyIntParam(Bundle bundle, HashMap<String, String> params, String key) {
+ String valueString = params.get(key);
+ if (!TextUtils.isEmpty(valueString)) {
+ try {
+ int value = Integer.parseInt(valueString);
+ bundle.putInt(key, value);
+ } catch (NumberFormatException ex) {
+ // don't set the value in the bundle
+ }
+ }
}
- /**
- * Convenience method to save a parameter in the cached parameter array, at the given index,
- * for a property saved in the given hashmap.
- */
- private void setCachedParam(HashMap<String,String> params, String key, int keyIndex) {
- String extra = params.get(key);
- if (extra != null) {
- mCachedParams[keyIndex+1] = extra;
+ private void copyFloatParam(Bundle bundle, HashMap<String, String> params, String key) {
+ String valueString = params.get(key);
+ if (!TextUtils.isEmpty(valueString)) {
+ try {
+ float value = Float.parseFloat(valueString);
+ bundle.putFloat(key, value);
+ } catch (NumberFormatException ex) {
+ // don't set the value in the bundle
+ }
}
}
/**
- * Sets the OnUtteranceCompletedListener that will fire when an utterance completes.
+ * Sets the listener that will be notified when synthesis of an utterance completes.
*
- * @param listener
- * The OnUtteranceCompletedListener
+ * @param listener The listener to use.
*
- * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+ * @return {@link #ERROR} or {@link #SUCCESS}.
*/
- public int setOnUtteranceCompletedListener(
- final OnUtteranceCompletedListener listener) {
- synchronized (mStartLock) {
- int result = ERROR;
- if (!mStarted) {
- return result;
- }
- mITtscallback = new ITtsCallback.Stub() {
- public void utteranceCompleted(String utteranceId) throws RemoteException {
- if (listener != null) {
- listener.onUtteranceCompleted(utteranceId);
+ public int setOnUtteranceCompletedListener(final OnUtteranceCompletedListener listener) {
+ return runAction(new Action<Integer>() {
+ @Override
+ public Integer run(ITextToSpeechService service) throws RemoteException {
+ ITextToSpeechCallback.Stub callback = new ITextToSpeechCallback.Stub() {
+ public void utteranceCompleted(String utteranceId) {
+ if (listener != null) {
+ listener.onUtteranceCompleted(utteranceId);
+ }
}
- }
- };
- try {
- result = mITts.registerCallback(mPackageName, mITtscallback);
- } catch (RemoteException e) {
- restart("registerCallback", e);
- } catch (NullPointerException e) {
- restart("registerCallback", e);
- } catch (IllegalStateException e) {
- restart("registerCallback", e);
+ };
+ service.setCallback(getPackageName(), callback);
+ return SUCCESS;
}
- return result;
- }
+ }, ERROR, "setOnUtteranceCompletedListener");
}
/**
- * Sets the speech synthesis engine to be used by its packagename.
+ * Sets the TTS engine to use.
*
- * @param enginePackageName
- * The packagename for the synthesis engine (ie, "com.svox.pico")
+ * @param enginePackageName The package name for the synthesis engine (e.g. "com.svox.pico")
*
- * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+ * @return {@link #ERROR} or {@link #SUCCESS}.
*/
+ // TODO: add @Deprecated{This method does not tell the caller when the new engine
+ // has been initialized. You should create a new TextToSpeech object with the new
+ // engine instead.}
public int setEngineByPackageName(String enginePackageName) {
- synchronized (mStartLock) {
- int result = TextToSpeech.ERROR;
- if (!mStarted) {
- return result;
- }
- try {
- result = mITts.setEngineByPackageName(enginePackageName);
- if (result == TextToSpeech.SUCCESS){
- mCachedParams[Engine.PARAM_POSITION_ENGINE + 1] = enginePackageName;
- }
- } catch (RemoteException e) {
- restart("setEngineByPackageName", e);
- } catch (NullPointerException e) {
- restart("setEngineByPackageName", e);
- } catch (IllegalStateException e) {
- restart("setEngineByPackageName", e);
- }
- return result;
- }
+ mRequestedEngine = enginePackageName;
+ return initTts();
}
-
/**
- * Gets the packagename of the default speech synthesis engine.
+ * Gets the package name of the default speech synthesis engine.
*
- * @return Packagename of the TTS engine that the user has chosen as their default.
+ * @return Package name of the TTS engine that the user has chosen as their default.
*/
public String getDefaultEngine() {
- synchronized (mStartLock) {
- String engineName = "";
- if (!mStarted) {
- return engineName;
- }
- try {
- engineName = mITts.getDefaultEngine();
- } catch (RemoteException e) {
- restart("getDefaultEngine", e);
- } catch (NullPointerException e) {
- restart("getDefaultEngine", e);
- } catch (IllegalStateException e) {
- restart("getDefaultEngine", e);
+ String engine = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.TTS_DEFAULT_SYNTH);
+ return engine != null ? engine : Engine.DEFAULT_ENGINE;
+ }
+
+ /**
+ * Checks whether the user's settings should override settings requested by the calling
+ * application.
+ */
+ public boolean areDefaultsEnforced() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.TTS_USE_DEFAULTS, Engine.USE_DEFAULTS) == 1;
+ }
+
+ private boolean isEngineEnabled(String engine) {
+ if (Engine.DEFAULT_ENGINE.equals(engine)) {
+ return true;
+ }
+ for (String enabled : getEnabledEngines()) {
+ if (engine.equals(enabled)) {
+ return true;
}
- return engineName;
}
+ return false;
}
+ private String[] getEnabledEngines() {
+ String str = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.TTS_ENABLED_PLUGINS);
+ if (TextUtils.isEmpty(str)) {
+ return new String[0];
+ }
+ return str.split(" ");
+ }
/**
- * Returns whether or not the user is forcing their defaults to override the
- * Text-To-Speech settings set by applications.
+ * Gets a list of all installed TTS engines.
*
- * @return Whether or not defaults are enforced.
+ * @return A list of engine info objects. The list can be empty, but will never by {@code null}.
+ *
+ * @hide Pending approval
*/
- public boolean areDefaultsEnforced() {
- synchronized (mStartLock) {
- boolean defaultsEnforced = false;
- if (!mStarted) {
- return defaultsEnforced;
+ public List<EngineInfo> getEngines() {
+ PackageManager pm = mContext.getPackageManager();
+ Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE);
+ List<ResolveInfo> resolveInfos = pm.queryIntentServices(intent, 0);
+ if (resolveInfos == null) return Collections.emptyList();
+ List<EngineInfo> engines = new ArrayList<EngineInfo>(resolveInfos.size());
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ ServiceInfo service = resolveInfo.serviceInfo;
+ if (service != null) {
+ EngineInfo engine = new EngineInfo();
+ // Using just the package name isn't great, since it disallows having
+ // multiple engines in the same package, but that's what the existing API does.
+ engine.name = service.packageName;
+ CharSequence label = service.loadLabel(pm);
+ engine.label = TextUtils.isEmpty(label) ? engine.name : label.toString();
+ engine.icon = service.getIconResource();
+ engines.add(engine);
}
+ }
+ return engines;
+ }
+
+ private class Connection implements ServiceConnection {
+ private ITextToSpeechService mService;
+
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.i(TAG, "Connected to " + name);
+ synchronized(mStartLock) {
+ if (mServiceConnection != null) {
+ // Disconnect any previous service connection
+ mServiceConnection.disconnect();
+ }
+ mServiceConnection = this;
+ mService = ITextToSpeechService.Stub.asInterface(service);
+ dispatchOnInit(SUCCESS);
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ synchronized(mStartLock) {
+ mService = null;
+ // If this is the active connection, clear it
+ if (mServiceConnection == this) {
+ mServiceConnection = null;
+ }
+ }
+ }
+
+ public void disconnect() {
+ mContext.unbindService(this);
+ }
+
+ public <R> R runAction(Action<R> action, R errorResult, String method, boolean reconnect) {
try {
- defaultsEnforced = mITts.areDefaultsEnforced();
- } catch (RemoteException e) {
- restart("areDefaultsEnforced", e);
- } catch (NullPointerException e) {
- restart("areDefaultsEnforced", e);
- } catch (IllegalStateException e) {
- restart("areDefaultsEnforced", e);
+ synchronized (mStartLock) {
+ if (mService == null) {
+ Log.w(TAG, method + " failed: not connected to TTS engine");
+ return errorResult;
+ }
+ return action.run(mService);
+ }
+ } catch (RemoteException ex) {
+ Log.e(TAG, method + " failed", ex);
+ if (reconnect) {
+ disconnect();
+ initTts();
+ }
+ return errorResult;
}
- return defaultsEnforced;
}
}
+ private interface Action<R> {
+ R run(ITextToSpeechService service) throws RemoteException;
+ }
+
/**
- * Restarts the TTS after a failure.
+ * Information about an installed text-to-speech engine.
+ *
+ * @see TextToSpeech#getEngines
+ * @hide Pending approval
*/
- private void restart(String method, Exception e) {
- // TTS died; restart it.
- Log.e(TAG, method, e);
- mStarted = false;
- initTts();
+ public static class EngineInfo {
+ /**
+ * Engine package name..
+ */
+ public String name;
+ /**
+ * Localized label for the engine.
+ */
+ public String label;
+ /**
+ * Icon for the engine.
+ */
+ public int icon;
+
+ @Override
+ public String toString() {
+ return "EngineInfo{name=" + name + "}";
+ }
+
}
}
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
new file mode 100644
index 0000000..a408ea2
--- /dev/null
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -0,0 +1,715 @@
+/*
+ * 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 android.speech.tts;
+
+import android.app.Service;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.speech.tts.TextToSpeech.Engine;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Locale;
+
+
+/**
+ * Abstract base class for TTS engine implementations.
+ *
+ * @hide Pending approval
+ */
+public abstract class TextToSpeechService extends Service {
+
+ private static final boolean DBG = false;
+ private static final String TAG = "TextToSpeechService";
+
+ private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000;
+ private static final String SYNTH_THREAD_NAME = "SynthThread";
+
+ private SynthHandler mSynthHandler;
+
+ private CallbackMap mCallbacks;
+
+ @Override
+ public void onCreate() {
+ if (DBG) Log.d(TAG, "onCreate()");
+ super.onCreate();
+
+ SynthThread synthThread = new SynthThread();
+ synthThread.start();
+ mSynthHandler = new SynthHandler(synthThread.getLooper());
+
+ mCallbacks = new CallbackMap();
+
+ // Load default language
+ onLoadLanguage(getDefaultLanguage(), getDefaultCountry(), getDefaultVariant());
+ }
+
+ @Override
+ public void onDestroy() {
+ if (DBG) Log.d(TAG, "onDestroy()");
+
+ // Tell the synthesizer to stop
+ mSynthHandler.quit();
+
+ // Unregister all callbacks.
+ mCallbacks.kill();
+
+ super.onDestroy();
+ }
+
+ /**
+ * Checks whether the engine supports a given language.
+ *
+ * Can be called on multiple threads.
+ *
+ * @param lang ISO-3 language code.
+ * @param country ISO-3 country code. May be empty or null.
+ * @param variant Language variant. May be empty or null.
+ * @return Code indicating the support status for the locale.
+ * One of {@link TextToSpeech#LANG_AVAILABLE},
+ * {@link TextToSpeech#LANG_COUNTRY_AVAILABLE},
+ * {@link TextToSpeech#LANG_COUNTRY_VAR_AVAILABLE},
+ * {@link TextToSpeech#LANG_MISSING_DATA}
+ * {@link TextToSpeech#LANG_NOT_SUPPORTED}.
+ */
+ protected abstract int onIsLanguageAvailable(String lang, String country, String variant);
+
+ /**
+ * Returns the language, country and variant currently being used by the TTS engine.
+ *
+ * Can be called on multiple threads.
+ *
+ * @return A 3-element array, containing language (ISO 3-letter code),
+ * country (ISO 3-letter code) and variant used by the engine.
+ * The country and variant may be {@code ""}. If country is empty, then variant must
+ * be empty too.
+ * @see Locale#getISO3Language()
+ * @see Locale#getISO3Country()
+ * @see Locale#getVariant()
+ */
+ protected abstract String[] onGetLanguage();
+
+ /**
+ * Notifies the engine that it should load a speech synthesis language. There is no guarantee
+ * that this method is always called before the language is used for synthesis. It is merely
+ * a hint to the engine that it will probably get some synthesis requests for this language
+ * at some point in the future.
+ *
+ * Can be called on multiple threads.
+ *
+ * @param lang ISO-3 language code.
+ * @param country ISO-3 country code. May be empty or null.
+ * @param variant Language variant. May be empty or null.
+ * @return Code indicating the support status for the locale.
+ * One of {@link TextToSpeech#LANG_AVAILABLE},
+ * {@link TextToSpeech#LANG_COUNTRY_AVAILABLE},
+ * {@link TextToSpeech#LANG_COUNTRY_VAR_AVAILABLE},
+ * {@link TextToSpeech#LANG_MISSING_DATA}
+ * {@link TextToSpeech#LANG_NOT_SUPPORTED}.
+ */
+ protected abstract int onLoadLanguage(String lang, String country, String variant);
+
+ /**
+ * Notifies the service that it should stop any in-progress speech synthesis.
+ * This method can be called even if no speech synthesis is currently in progress.
+ *
+ * Can be called on multiple threads, but not on the synthesis thread.
+ */
+ protected abstract void onStop();
+
+ /**
+ * Tells the service to synthesize speech from the given text. This method should
+ * block until the synthesis is finished.
+ *
+ * Called on the synthesis thread.
+ *
+ * @param request The synthesis request. The method should
+ * call {@link SynthesisRequest#start}, {@link SynthesisRequest#audioAvailable},
+ * and {@link SynthesisRequest#done} on this request.
+ * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+ */
+ protected abstract int onSynthesizeText(SynthesisRequest request);
+
+ private boolean areDefaultsEnforced() {
+ return getSecureSettingInt(Settings.Secure.TTS_USE_DEFAULTS,
+ TextToSpeech.Engine.USE_DEFAULTS) == 1;
+ }
+
+ private int getDefaultSpeechRate() {
+ return getSecureSettingInt(Settings.Secure.TTS_DEFAULT_RATE, Engine.DEFAULT_RATE);
+ }
+
+ private String getDefaultLanguage() {
+ return getSecureSettingString(Settings.Secure.TTS_DEFAULT_LANG,
+ Locale.getDefault().getISO3Language());
+ }
+
+ private String getDefaultCountry() {
+ return getSecureSettingString(Settings.Secure.TTS_DEFAULT_COUNTRY,
+ Locale.getDefault().getISO3Country());
+ }
+
+ private String getDefaultVariant() {
+ return getSecureSettingString(Settings.Secure.TTS_DEFAULT_VARIANT,
+ Locale.getDefault().getVariant());
+ }
+
+ private int getSecureSettingInt(String name, int defaultValue) {
+ return Settings.Secure.getInt(getContentResolver(), name, defaultValue);
+ }
+
+ private String getSecureSettingString(String name, String defaultValue) {
+ String value = Settings.Secure.getString(getContentResolver(), name);
+ return value != null ? value : defaultValue;
+ }
+
+ /**
+ * Synthesizer thread. This thread is used to run {@link SynthHandler}.
+ */
+ private class SynthThread extends HandlerThread implements MessageQueue.IdleHandler {
+
+ private boolean mFirstIdle = true;
+
+ public SynthThread() {
+ super(SYNTH_THREAD_NAME, android.os.Process.THREAD_PRIORITY_AUDIO);
+ }
+
+ @Override
+ protected void onLooperPrepared() {
+ getLooper().getQueue().addIdleHandler(this);
+ }
+
+ @Override
+ public boolean queueIdle() {
+ if (mFirstIdle) {
+ mFirstIdle = false;
+ } else {
+ broadcastTtsQueueProcessingCompleted();
+ }
+ return true;
+ }
+
+ private void broadcastTtsQueueProcessingCompleted() {
+ Intent i = new Intent(TextToSpeech.ACTION_TTS_QUEUE_PROCESSING_COMPLETED);
+ if (DBG) Log.d(TAG, "Broadcasting: " + i);
+ sendBroadcast(i);
+ }
+ }
+
+ private class SynthHandler extends Handler {
+
+ private SpeechItem mCurrentSpeechItem = null;
+
+ public SynthHandler(Looper looper) {
+ super(looper);
+ }
+
+ private void dispatchUtteranceCompleted(SpeechItem item) {
+ String utteranceId = item.getUtteranceId();
+ if (!TextUtils.isEmpty(utteranceId)) {
+ mCallbacks.dispatchUtteranceCompleted(item.getCallingApp(), utteranceId);
+ }
+ }
+
+ private synchronized SpeechItem getCurrentSpeechItem() {
+ return mCurrentSpeechItem;
+ }
+
+ private synchronized SpeechItem setCurrentSpeechItem(SpeechItem speechItem) {
+ SpeechItem old = mCurrentSpeechItem;
+ mCurrentSpeechItem = speechItem;
+ return old;
+ }
+
+ public boolean isSpeaking() {
+ return getCurrentSpeechItem() != null;
+ }
+
+ public void quit() {
+ // Don't process any more speech items
+ getLooper().quit();
+ // Stop the current speech item
+ SpeechItem current = setCurrentSpeechItem(null);
+ if (current != null) {
+ current.stop();
+ }
+ }
+
+ /**
+ * Adds a speech item to the queue.
+ *
+ * Called on a service binder thread.
+ */
+ public int enqueueSpeechItem(int queueMode, final SpeechItem speechItem) {
+ if (!speechItem.isValid()) {
+ return TextToSpeech.ERROR;
+ }
+ // TODO: The old code also supported the undocumented queueMode == 2,
+ // which clears out all pending items from the calling app, as well as all
+ // non-file items from other apps.
+ if (queueMode == TextToSpeech.QUEUE_FLUSH) {
+ stop(speechItem.getCallingApp());
+ }
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ setCurrentSpeechItem(speechItem);
+ if (speechItem.play() == TextToSpeech.SUCCESS) {
+ dispatchUtteranceCompleted(speechItem);
+ }
+ setCurrentSpeechItem(null);
+ }
+ };
+ Message msg = Message.obtain(this, runnable);
+ // The obj is used to remove all callbacks from the given app in stop(String).
+ msg.obj = speechItem.getCallingApp();
+ if (sendMessage(msg)) {
+ return TextToSpeech.SUCCESS;
+ } else {
+ Log.w(TAG, "SynthThread has quit");
+ return TextToSpeech.ERROR;
+ }
+ }
+
+ /**
+ * Stops all speech output and removes any utterances still in the queue for
+ * the calling app.
+ *
+ * Called on a service binder thread.
+ */
+ public int stop(String callingApp) {
+ if (TextUtils.isEmpty(callingApp)) {
+ return TextToSpeech.ERROR;
+ }
+ removeCallbacksAndMessages(callingApp);
+ SpeechItem current = setCurrentSpeechItem(null);
+ if (current != null && TextUtils.equals(callingApp, current.getCallingApp())) {
+ current.stop();
+ }
+ return TextToSpeech.SUCCESS;
+ }
+ }
+
+ /**
+ * An item in the synth thread queue.
+ */
+ private static abstract class SpeechItem {
+ private final String mCallingApp;
+ private final Bundle mParams;
+ private boolean mStarted = false;
+ private boolean mStopped = false;
+
+ public SpeechItem(String callingApp, Bundle params) {
+ mCallingApp = callingApp;
+ mParams = params;
+ }
+
+ public String getCallingApp() {
+ return mCallingApp;
+ }
+
+ /**
+ * Checker whether the item is valid. If this method returns false, the item should not
+ * be played.
+ */
+ public abstract boolean isValid();
+
+ /**
+ * Plays the speech item. Blocks until playback is finished.
+ * Must not be called more than once.
+ *
+ * Only called on the synthesis thread.
+ *
+ * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+ */
+ public int play() {
+ synchronized (this) {
+ if (mStarted) {
+ throw new IllegalStateException("play() called twice");
+ }
+ mStarted = true;
+ }
+ return playImpl();
+ }
+
+ /**
+ * Stops the speech item.
+ * Must not be called more than once.
+ *
+ * Can be called on multiple threads, but not on the synthesis thread.
+ */
+ public void stop() {
+ synchronized (this) {
+ if (mStopped) {
+ throw new IllegalStateException("stop() called twice");
+ }
+ mStopped = true;
+ }
+ stopImpl();
+ }
+
+ protected abstract int playImpl();
+
+ protected abstract void stopImpl();
+
+ public int getStreamType() {
+ return getIntParam(Engine.KEY_PARAM_STREAM, Engine.DEFAULT_STREAM);
+ }
+
+ public float getVolume() {
+ return getFloatParam(Engine.KEY_PARAM_VOLUME, Engine.DEFAULT_VOLUME);
+ }
+
+ public float getPan() {
+ return getFloatParam(Engine.KEY_PARAM_PAN, Engine.DEFAULT_PAN);
+ }
+
+ public String getUtteranceId() {
+ return getStringParam(Engine.KEY_PARAM_UTTERANCE_ID, null);
+ }
+
+ protected String getStringParam(String key, String defaultValue) {
+ return mParams == null ? defaultValue : mParams.getString(key, defaultValue);
+ }
+
+ protected int getIntParam(String key, int defaultValue) {
+ return mParams == null ? defaultValue : mParams.getInt(key, defaultValue);
+ }
+
+ protected float getFloatParam(String key, float defaultValue) {
+ return mParams == null ? defaultValue : mParams.getFloat(key, defaultValue);
+ }
+ }
+
+ private class SynthesisSpeechItem extends SpeechItem {
+ private final String mText;
+ private SynthesisRequest mSynthesisRequest;
+
+ public SynthesisSpeechItem(String callingApp, Bundle params, String text) {
+ super(callingApp, params);
+ mText = text;
+ }
+
+ public String getText() {
+ return mText;
+ }
+
+ @Override
+ public boolean isValid() {
+ if (TextUtils.isEmpty(mText)) {
+ Log.w(TAG, "Got empty text");
+ return false;
+ }
+ if (mText.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH){
+ Log.w(TAG, "Text too long: " + mText.length() + " chars");
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected int playImpl() {
+ SynthesisRequest synthesisRequest;
+ synchronized (this) {
+ mSynthesisRequest = createSynthesisRequest();
+ synthesisRequest = mSynthesisRequest;
+ }
+ setRequestParams(synthesisRequest);
+ return TextToSpeechService.this.onSynthesizeText(synthesisRequest);
+ }
+
+ protected SynthesisRequest createSynthesisRequest() {
+ return new PlaybackSynthesisRequest(mText, getStreamType(), getVolume(), getPan());
+ }
+
+ private void setRequestParams(SynthesisRequest request) {
+ if (areDefaultsEnforced()) {
+ request.setLanguage(getDefaultLanguage(), getDefaultCountry(), getDefaultVariant());
+ request.setSpeechRate(getDefaultSpeechRate());
+ } else {
+ request.setLanguage(getLanguage(), getCountry(), getVariant());
+ request.setSpeechRate(getSpeechRate());
+ }
+ request.setPitch(getPitch());
+ }
+
+ @Override
+ protected void stopImpl() {
+ SynthesisRequest synthesisRequest;
+ synchronized (this) {
+ synthesisRequest = mSynthesisRequest;
+ }
+ synthesisRequest.stop();
+ TextToSpeechService.this.onStop();
+ }
+
+ public String getLanguage() {
+ return getStringParam(Engine.KEY_PARAM_LANGUAGE, getDefaultLanguage());
+ }
+
+ private boolean hasLanguage() {
+ return !TextUtils.isEmpty(getStringParam(Engine.KEY_PARAM_LANGUAGE, null));
+ }
+
+ private String getCountry() {
+ if (!hasLanguage()) return getDefaultCountry();
+ return getStringParam(Engine.KEY_PARAM_COUNTRY, "");
+ }
+
+ private String getVariant() {
+ if (!hasLanguage()) return getDefaultVariant();
+ return getStringParam(Engine.KEY_PARAM_VARIANT, "");
+ }
+
+ private int getSpeechRate() {
+ return getIntParam(Engine.KEY_PARAM_RATE, getDefaultSpeechRate());
+ }
+
+ private int getPitch() {
+ return getIntParam(Engine.KEY_PARAM_PITCH, Engine.DEFAULT_PITCH);
+ }
+ }
+
+ private class SynthesisToFileSpeechItem extends SynthesisSpeechItem {
+ private final File mFile;
+
+ public SynthesisToFileSpeechItem(String callingApp, Bundle params, String text,
+ File file) {
+ super(callingApp, params, text);
+ mFile = file;
+ }
+
+ @Override
+ public boolean isValid() {
+ if (!super.isValid()) {
+ return false;
+ }
+ return checkFile(mFile);
+ }
+
+ @Override
+ protected SynthesisRequest createSynthesisRequest() {
+ return new FileSynthesisRequest(getText(), mFile);
+ }
+
+ /**
+ * Checks that the given file can be used for synthesis output.
+ */
+ private boolean checkFile(File file) {
+ try {
+ if (file.exists()) {
+ Log.v(TAG, "File " + file + " exists, deleting.");
+ if (!file.delete()) {
+ Log.e(TAG, "Failed to delete " + file);
+ return false;
+ }
+ }
+ if (!file.createNewFile()) {
+ Log.e(TAG, "Can't create file " + file);
+ return false;
+ }
+ if (!file.delete()) {
+ Log.e(TAG, "Failed to delete " + file);
+ return false;
+ }
+ return true;
+ } catch (IOException e) {
+ Log.e(TAG, "Can't use " + file + " due to exception " + e);
+ return false;
+ }
+ }
+ }
+
+ private class AudioSpeechItem extends SpeechItem {
+
+ private final BlockingMediaPlayer mPlayer;
+
+ public AudioSpeechItem(String callingApp, Bundle params, Uri uri) {
+ super(callingApp, params);
+ mPlayer = new BlockingMediaPlayer(TextToSpeechService.this, uri, getStreamType());
+ }
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ @Override
+ protected int playImpl() {
+ return mPlayer.startAndWait() ? TextToSpeech.SUCCESS : TextToSpeech.ERROR;
+ }
+
+ @Override
+ protected void stopImpl() {
+ mPlayer.stop();
+ }
+ }
+
+ private class SilenceSpeechItem extends SpeechItem {
+ private final long mDuration;
+ private final ConditionVariable mDone;
+
+ public SilenceSpeechItem(String callingApp, Bundle params, long duration) {
+ super(callingApp, params);
+ mDuration = duration;
+ mDone = new ConditionVariable();
+ }
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ @Override
+ protected int playImpl() {
+ boolean aborted = mDone.block(mDuration);
+ return aborted ? TextToSpeech.ERROR : TextToSpeech.SUCCESS;
+ }
+
+ @Override
+ protected void stopImpl() {
+ mDone.open();
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (TextToSpeech.Engine.INTENT_ACTION_TTS_SERVICE.equals(intent.getAction())) {
+ return mBinder;
+ }
+ return null;
+ }
+
+ /**
+ * Binder returned from {@code #onBind(Intent)}. The methods in this class can be
+ * called called from several different threads.
+ */
+ private final ITextToSpeechService.Stub mBinder = new ITextToSpeechService.Stub() {
+
+ public int speak(String callingApp, String text, int queueMode, Bundle params) {
+ SpeechItem item = new SynthesisSpeechItem(callingApp, params, text);
+ return mSynthHandler.enqueueSpeechItem(queueMode, item);
+ }
+
+ public int synthesizeToFile(String callingApp, String text, String filename,
+ Bundle params) {
+ File file = new File(filename);
+ SpeechItem item = new SynthesisToFileSpeechItem(callingApp, params, text, file);
+ return mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item);
+ }
+
+ public int playAudio(String callingApp, Uri audioUri, int queueMode, Bundle params) {
+ SpeechItem item = new AudioSpeechItem(callingApp, params, audioUri);
+ return mSynthHandler.enqueueSpeechItem(queueMode, item);
+ }
+
+ public int playSilence(String callingApp, long duration, int queueMode, Bundle params) {
+ SpeechItem item = new SilenceSpeechItem(callingApp, params, duration);
+ return mSynthHandler.enqueueSpeechItem(queueMode, item);
+ }
+
+ public boolean isSpeaking() {
+ return mSynthHandler.isSpeaking();
+ }
+
+ public int stop(String callingApp) {
+ return mSynthHandler.stop(callingApp);
+ }
+
+ public String[] getLanguage() {
+ return onGetLanguage();
+ }
+
+ public int isLanguageAvailable(String lang, String country, String variant) {
+ return onIsLanguageAvailable(lang, country, variant);
+ }
+
+ public int loadLanguage(String lang, String country, String variant) {
+ return onLoadLanguage(lang, country, variant);
+ }
+
+ public void setCallback(String packageName, ITextToSpeechCallback cb) {
+ mCallbacks.setCallback(packageName, cb);
+ }
+ };
+
+ private class CallbackMap extends RemoteCallbackList<ITextToSpeechCallback> {
+
+ private final HashMap<String, ITextToSpeechCallback> mAppToCallback
+ = new HashMap<String, ITextToSpeechCallback>();
+
+ public void setCallback(String packageName, ITextToSpeechCallback cb) {
+ synchronized (mAppToCallback) {
+ ITextToSpeechCallback old;
+ if (cb != null) {
+ register(cb, packageName);
+ old = mAppToCallback.put(packageName, cb);
+ } else {
+ old = mAppToCallback.remove(packageName);
+ }
+ if (old != null && old != cb) {
+ unregister(old);
+ }
+ }
+ }
+
+ public void dispatchUtteranceCompleted(String packageName, String utteranceId) {
+ ITextToSpeechCallback cb;
+ synchronized (mAppToCallback) {
+ cb = mAppToCallback.get(packageName);
+ }
+ if (cb == null) return;
+ try {
+ cb.utteranceCompleted(utteranceId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback failed: " + e);
+ }
+ }
+
+ @Override
+ public void onCallbackDied(ITextToSpeechCallback callback, Object cookie) {
+ String packageName = (String) cookie;
+ synchronized (mAppToCallback) {
+ mAppToCallback.remove(packageName);
+ }
+ mSynthHandler.stop(packageName);
+ }
+
+ @Override
+ public void kill() {
+ synchronized (mAppToCallback) {
+ mAppToCallback.clear();
+ super.kill();
+ }
+ }
+
+ }
+
+}
diff --git a/include/tts/TtsEngine.h b/include/tts/TtsEngine.h
deleted file mode 100644
index 998e353..0000000
--- a/include/tts/TtsEngine.h
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * 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/native/include/android/tts.h b/native/include/android/tts.h
deleted file mode 100644
index fb15108..0000000
--- a/native/include/android/tts.h
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * 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/packages/TtsService/Android.mk b/packages/TtsService/Android.mk
deleted file mode 100644
index a1a3b9f..0000000
--- a/packages/TtsService/Android.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files) \
-
-LOCAL_PACKAGE_NAME := TtsService
-LOCAL_CERTIFICATE := platform
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/TtsService/AndroidManifest.xml b/packages/TtsService/AndroidManifest.xml
deleted file mode 100755
index 46e0ad1..0000000
--- a/packages/TtsService/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.tts">
- <application android:label="TTS Service"
- android:icon="@drawable/ic_launcher_text_to_speech">
- <service android:enabled="true"
- android:name=".TtsService"
- android:label="TTS Service">
- <intent-filter>
- <action android:name="android.intent.action.START_TTS_SERVICE"/>
- <category android:name="android.intent.category.TTS"/>
- </intent-filter>
- </service>
- </application>
- <uses-permission android:name="android.permission.INTERNET"/>
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-</manifest>
diff --git a/packages/TtsService/MODULE_LICENSE_APACHE2 b/packages/TtsService/MODULE_LICENSE_APACHE2
deleted file mode 100644
index e69de29..0000000
--- a/packages/TtsService/MODULE_LICENSE_APACHE2
+++ /dev/null
diff --git a/packages/TtsService/NOTICE b/packages/TtsService/NOTICE
deleted file mode 100644
index 64aaa8d..0000000
--- a/packages/TtsService/NOTICE
+++ /dev/null
@@ -1,190 +0,0 @@
-
- Copyright (c) 2009, 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.
-
- 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.
-
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
diff --git a/packages/TtsService/jni/Android.mk b/packages/TtsService/jni/Android.mk
deleted file mode 100755
index 5dc0c30..0000000
--- a/packages/TtsService/jni/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-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_MODULE:= libttssynthproxy
-
-LOCAL_ARM_MODE := arm
-
-include $(BUILD_SHARED_LIBRARY)
-
diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp
deleted file mode 100644
index e00fa85..0000000
--- a/packages/TtsService/jni/android_tts_SynthProxy.cpp
+++ /dev/null
@@ -1,1071 +0,0 @@
-/*
- * Copyright (C) 2009-2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <unistd.h>
-
-#define LOG_TAG "SynthProxyJNI"
-
-#include <utils/Log.h>
-#include <nativehelper/jni.h>
-#include <nativehelper/JNIHelp.h>
-#include <android_runtime/AndroidRuntime.h>
-#include <android/tts.h>
-#include <media/AudioTrack.h>
-#include <math.h>
-
-#include <dlfcn.h>
-
-#define DEFAULT_TTS_RATE 16000
-#define DEFAULT_TTS_FORMAT AudioSystem::PCM_16_BIT
-#define DEFAULT_TTS_NB_CHANNELS 1
-#define DEFAULT_TTS_BUFFERSIZE 2048
-#define DEFAULT_TTS_STREAM_TYPE AudioSystem::MUSIC
-#define DEFAULT_VOLUME 1.0f
-
-// EQ + BOOST parameters
-#define FILTER_LOWSHELF_ATTENUATION -18.0f // in dB
-#define FILTER_TRANSITION_FREQ 1100.0f // in Hz
-#define FILTER_SHELF_SLOPE 1.0f // Q
-#define FILTER_GAIN 5.5f // linear gain
-
-#define USAGEMODE_PLAY_IMMEDIATELY 0
-#define USAGEMODE_WRITE_TO_FILE 1
-
-#define SYNTHPLAYSTATE_IS_STOPPED 0
-#define SYNTHPLAYSTATE_IS_PLAYING 1
-
-using namespace android;
-
-// ----------------------------------------------------------------------------
-struct fields_t {
- jfieldID synthProxyFieldJniData;
- jmethodID synthProxyMethodPost;
-};
-
-// structure to hold the data that is used each time the TTS engine has synthesized more data
-struct afterSynthData_t {
- jint jniStorage;
- int usageMode;
- FILE* outputFile;
- AudioSystem::stream_type streamType;
-};
-
-// ----------------------------------------------------------------------------
-// EQ data
-double amp;
-double w;
-double sinw;
-double cosw;
-double beta;
-double a0, a1, a2, b0, b1, b2;
-double m_fa, m_fb, m_fc, m_fd, m_fe;
-double x0; // x[n]
-double x1; // x[n-1]
-double x2; // x[n-2]
-double out0;// y[n]
-double out1;// y[n-1]
-double out2;// y[n-2]
-
-static float fFilterLowshelfAttenuation = FILTER_LOWSHELF_ATTENUATION;
-static float fFilterTransitionFreq = FILTER_TRANSITION_FREQ;
-static float fFilterShelfSlope = FILTER_SHELF_SLOPE;
-static float fFilterGain = FILTER_GAIN;
-static bool bUseFilter = false;
-
-void initializeEQ() {
-
- amp = float(pow(10.0, fFilterLowshelfAttenuation / 40.0));
- w = 2.0 * M_PI * (fFilterTransitionFreq / DEFAULT_TTS_RATE);
- sinw = float(sin(w));
- cosw = float(cos(w));
- beta = float(sqrt(amp)/fFilterShelfSlope);
-
- // initialize low-shelf parameters
- b0 = amp * ((amp+1.0F) - ((amp-1.0F)*cosw) + (beta*sinw));
- b1 = 2.0F * amp * ((amp-1.0F) - ((amp+1.0F)*cosw));
- b2 = amp * ((amp+1.0F) - ((amp-1.0F)*cosw) - (beta*sinw));
- a0 = (amp+1.0F) + ((amp-1.0F)*cosw) + (beta*sinw);
- a1 = 2.0F * ((amp-1.0F) + ((amp+1.0F)*cosw));
- a2 = -((amp+1.0F) + ((amp-1.0F)*cosw) - (beta*sinw));
-
- m_fa = fFilterGain * b0/a0;
- m_fb = fFilterGain * b1/a0;
- m_fc = fFilterGain * b2/a0;
- m_fd = a1/a0;
- m_fe = a2/a0;
-}
-
-void initializeFilter() {
- x0 = 0.0f;
- x1 = 0.0f;
- x2 = 0.0f;
- out0 = 0.0f;
- out1 = 0.0f;
- out2 = 0.0f;
-}
-
-void applyFilter(int16_t* buffer, size_t sampleCount) {
-
- for (size_t i=0 ; i<sampleCount ; i++) {
-
- x0 = (double) buffer[i];
-
- out0 = (m_fa*x0) + (m_fb*x1) + (m_fc*x2) + (m_fd*out1) + (m_fe*out2);
-
- x2 = x1;
- x1 = x0;
-
- out2 = out1;
- out1 = out0;
-
- if (out0 > 32767.0f) {
- buffer[i] = 32767;
- } else if (out0 < -32768.0f) {
- buffer[i] = -32768;
- } else {
- buffer[i] = (int16_t) out0;
- }
- }
-}
-
-
-// ----------------------------------------------------------------------------
-static fields_t javaTTSFields;
-
-// TODO move to synth member once we have multiple simultaneous engines running
-static Mutex engineMutex;
-
-// ----------------------------------------------------------------------------
-class SynthProxyJniStorage {
- public :
- jobject tts_ref;
- android_tts_engine_t* mEngine;
- void* mEngineLibHandle;
- AudioTrack* mAudioOut;
- int8_t mPlayState;
- Mutex mPlayLock;
- AudioSystem::stream_type mStreamType;
- uint32_t mSampleRate;
- uint32_t mAudFormat;
- int mNbChannels;
- int8_t * mBuffer;
- size_t mBufferSize;
- float mVolume[2];
-
- SynthProxyJniStorage() {
- tts_ref = NULL;
- mEngine = NULL;
- mEngineLibHandle = NULL;
- mAudioOut = NULL;
- mPlayState = SYNTHPLAYSTATE_IS_STOPPED;
- mStreamType = DEFAULT_TTS_STREAM_TYPE;
- mSampleRate = DEFAULT_TTS_RATE;
- mAudFormat = DEFAULT_TTS_FORMAT;
- mNbChannels = DEFAULT_TTS_NB_CHANNELS;
- mBufferSize = DEFAULT_TTS_BUFFERSIZE;
- mBuffer = new int8_t[mBufferSize];
- memset(mBuffer, 0, mBufferSize);
- mVolume[AudioTrack::LEFT] = DEFAULT_VOLUME;
- mVolume[AudioTrack::RIGHT] = DEFAULT_VOLUME;
- }
-
- ~SynthProxyJniStorage() {
- //LOGV("entering ~SynthProxyJniStorage()");
- killAudio();
- if (mEngine) {
- mEngine->funcs->shutdown(mEngine);
- mEngine = NULL;
- }
- if (mEngineLibHandle) {
- //LOGV("~SynthProxyJniStorage(): before close library");
- int res = dlclose(mEngineLibHandle);
- LOGE_IF( res != 0, "~SynthProxyJniStorage(): dlclose returned %d", res);
- }
- delete mBuffer;
- }
-
- void killAudio() {
- if (mAudioOut) {
- mAudioOut->stop();
- delete mAudioOut;
- mAudioOut = NULL;
- }
- }
-
- void createAudioOut(AudioSystem::stream_type streamType, uint32_t rate,
- AudioSystem::audio_format format, int channel) {
- mSampleRate = rate;
- mAudFormat = format;
- mNbChannels = channel;
- mStreamType = streamType;
-
- // retrieve system properties to ensure successful creation of the
- // AudioTrack object for playback
- int afSampleRate;
- if (AudioSystem::getOutputSamplingRate(&afSampleRate, mStreamType) != NO_ERROR) {
- afSampleRate = 44100;
- }
- int afFrameCount;
- if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) {
- afFrameCount = 2048;
- }
- uint32_t afLatency;
- if (AudioSystem::getOutputLatency(&afLatency, mStreamType) != NO_ERROR) {
- afLatency = 500;
- }
- uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate);
- if (minBufCount < 2) minBufCount = 2;
- int minFrameCount = (afFrameCount * rate * minBufCount)/afSampleRate;
-
- mPlayLock.lock();
- mAudioOut = new AudioTrack(mStreamType, rate, format,
- (channel == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO,
- minFrameCount > 4096 ? minFrameCount : 4096,
- 0, 0, 0, 0); // not using an AudioTrack callback
-
- if (mAudioOut->initCheck() != NO_ERROR) {
- LOGE("createAudioOut(): AudioTrack error");
- delete mAudioOut;
- mAudioOut = NULL;
- } else {
- //LOGI("AudioTrack OK");
- mAudioOut->setVolume(mVolume[AudioTrack::LEFT], mVolume[AudioTrack::RIGHT]);
- LOGV("AudioTrack ready");
- }
- mPlayLock.unlock();
- }
-};
-
-
-// ----------------------------------------------------------------------------
-void prepAudioTrack(SynthProxyJniStorage* pJniData, AudioSystem::stream_type streamType,
- uint32_t rate, AudioSystem::audio_format format, int channel) {
- // Don't bother creating a new audiotrack object if the current
- // object is already initialized with the same audio parameters.
- if ( pJniData->mAudioOut &&
- (rate == pJniData->mSampleRate) &&
- (format == pJniData->mAudFormat) &&
- (channel == pJniData->mNbChannels) &&
- (streamType == pJniData->mStreamType) ){
- return;
- }
- if (pJniData->mAudioOut){
- pJniData->killAudio();
- }
- pJniData->createAudioOut(streamType, rate, format, channel);
-}
-
-
-// ----------------------------------------------------------------------------
-/*
- * Callback from TTS engine.
- * Directly speaks using AudioTrack or write to file
- */
-extern "C" android_tts_callback_status_t
-__ttsSynthDoneCB(void ** pUserdata, uint32_t rate,
- android_tts_audio_format_t format, int channel,
- int8_t **pWav, size_t *pBufferSize,
- android_tts_synth_status_t status)
-{
- //LOGV("ttsSynthDoneCallback: %d bytes", bufferSize);
- AudioSystem::audio_format encoding;
-
- if (*pUserdata == NULL){
- LOGE("userdata == NULL");
- return ANDROID_TTS_CALLBACK_HALT;
- }
- switch (format) {
- case ANDROID_TTS_AUDIO_FORMAT_PCM_8_BIT:
- encoding = AudioSystem::PCM_8_BIT;
- break;
- case ANDROID_TTS_AUDIO_FORMAT_PCM_16_BIT:
- encoding = AudioSystem::PCM_16_BIT;
- break;
- default:
- LOGE("Can't play, bad format");
- return ANDROID_TTS_CALLBACK_HALT;
- }
- afterSynthData_t* pForAfter = (afterSynthData_t*) *pUserdata;
- SynthProxyJniStorage* pJniData = (SynthProxyJniStorage*)(pForAfter->jniStorage);
-
- if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY){
- //LOGV("Direct speech");
-
- if (*pWav == NULL) {
- delete pForAfter;
- pForAfter = NULL;
- LOGV("Null: speech has completed");
- return ANDROID_TTS_CALLBACK_HALT;
- }
-
- if (*pBufferSize > 0) {
- prepAudioTrack(pJniData, pForAfter->streamType, rate, encoding, channel);
- if (pJniData->mAudioOut) {
- pJniData->mPlayLock.lock();
- if(pJniData->mAudioOut->stopped()
- && (pJniData->mPlayState == SYNTHPLAYSTATE_IS_PLAYING)) {
- pJniData->mAudioOut->start();
- }
- pJniData->mPlayLock.unlock();
- if (bUseFilter) {
- applyFilter((int16_t*)*pWav, *pBufferSize/2);
- }
- pJniData->mAudioOut->write(*pWav, *pBufferSize);
- memset(*pWav, 0, *pBufferSize);
- //LOGV("AudioTrack wrote: %d bytes", bufferSize);
- } else {
- LOGE("Can't play, null audiotrack");
- delete pForAfter;
- pForAfter = NULL;
- return ANDROID_TTS_CALLBACK_HALT;
- }
- }
- } else if (pForAfter->usageMode == USAGEMODE_WRITE_TO_FILE) {
- //LOGV("Save to file");
- if (*pWav == NULL) {
- delete pForAfter;
- LOGV("Null: speech has completed");
- return ANDROID_TTS_CALLBACK_HALT;
- }
- if (*pBufferSize > 0){
- if (bUseFilter) {
- applyFilter((int16_t*)*pWav, *pBufferSize/2);
- }
- fwrite(*pWav, 1, *pBufferSize, pForAfter->outputFile);
- memset(*pWav, 0, *pBufferSize);
- }
- }
- // Future update:
- // For sync points in the speech, call back into the SynthProxy class through the
- // javaTTSFields.synthProxyMethodPost methode to notify
- // playback has completed if the synthesis is done or if a marker has been reached.
-
- if (status == ANDROID_TTS_SYNTH_DONE) {
- // this struct was allocated in the original android_tts_SynthProxy_speak call,
- // all processing matching this call is now done.
- LOGV("Speech synthesis done.");
- if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY) {
- // only delete for direct playback. When writing to a file, we still have work to do
- // in android_tts_SynthProxy_synthesizeToFile. The struct will be deleted there.
- delete pForAfter;
- pForAfter = NULL;
- }
- return ANDROID_TTS_CALLBACK_HALT;
- }
-
- // we don't update the wav (output) parameter as we'll let the next callback
- // write at the same location, we've consumed the data already, but we need
- // to update bufferSize to let the TTS engine know how much it can write the
- // next time it calls this function.
- *pBufferSize = pJniData->mBufferSize;
-
- return ANDROID_TTS_CALLBACK_CONTINUE;
-}
-
-
-// ----------------------------------------------------------------------------
-static int
-android_tts_SynthProxy_setLowShelf(JNIEnv *env, jobject thiz, jboolean applyFilter,
- jfloat filterGain, jfloat attenuationInDb, jfloat freqInHz, jfloat slope)
-{
- int result = ANDROID_TTS_SUCCESS;
-
- bUseFilter = applyFilter;
- if (applyFilter) {
- fFilterLowshelfAttenuation = attenuationInDb;
- fFilterTransitionFreq = freqInHz;
- fFilterShelfSlope = slope;
- fFilterGain = filterGain;
-
- if (fFilterShelfSlope != 0.0f) {
- initializeEQ();
- } else {
- LOGE("Invalid slope, can't be null");
- result = ANDROID_TTS_FAILURE;
- }
- }
-
- return result;
-}
-
-// ----------------------------------------------------------------------------
-static int
-android_tts_SynthProxy_native_setup(JNIEnv *env, jobject thiz,
- jobject weak_this, jstring nativeSoLib, jstring engConfig)
-{
- int result = ANDROID_TTS_FAILURE;
-
- bUseFilter = false;
-
- SynthProxyJniStorage* pJniStorage = new SynthProxyJniStorage();
-
- prepAudioTrack(pJniStorage,
- DEFAULT_TTS_STREAM_TYPE, DEFAULT_TTS_RATE, DEFAULT_TTS_FORMAT, DEFAULT_TTS_NB_CHANNELS);
-
- const char *nativeSoLibNativeString = env->GetStringUTFChars(nativeSoLib, 0);
- const char *engConfigString = env->GetStringUTFChars(engConfig, 0);
-
- void *engine_lib_handle = dlopen(nativeSoLibNativeString,
- RTLD_NOW | RTLD_LOCAL);
- if (engine_lib_handle == NULL) {
- LOGE("android_tts_SynthProxy_native_setup(): engine_lib_handle == NULL");
- } else {
- android_tts_engine_t * (*get_TtsEngine)() =
- reinterpret_cast<android_tts_engine_t* (*)()>(dlsym(engine_lib_handle, "android_getTtsEngine"));
-
- // Support obsolete/legacy binary modules
- if (get_TtsEngine == NULL) {
- get_TtsEngine =
- reinterpret_cast<android_tts_engine_t* (*)()>(dlsym(engine_lib_handle, "getTtsEngine"));
- }
-
- pJniStorage->mEngine = (*get_TtsEngine)();
- pJniStorage->mEngineLibHandle = engine_lib_handle;
-
- android_tts_engine_t *engine = pJniStorage->mEngine;
- if (engine) {
- Mutex::Autolock l(engineMutex);
- engine->funcs->init(
- engine,
- __ttsSynthDoneCB,
- engConfigString);
- }
-
- result = ANDROID_TTS_SUCCESS;
- }
-
- // we use a weak reference so the SynthProxy object can be garbage collected.
- pJniStorage->tts_ref = env->NewGlobalRef(weak_this);
-
- // save the JNI resources so we can use them (and free them) later
- env->SetIntField(thiz, javaTTSFields.synthProxyFieldJniData, (int)pJniStorage);
-
- env->ReleaseStringUTFChars(nativeSoLib, nativeSoLibNativeString);
- env->ReleaseStringUTFChars(engConfig, engConfigString);
-
- return result;
-}
-
-
-static void
-android_tts_SynthProxy_native_finalize(JNIEnv *env, jobject thiz, jint jniData)
-{
- //LOGV("entering android_tts_SynthProxy_finalize()");
- if (jniData == 0) {
- //LOGE("android_tts_SynthProxy_native_finalize(): invalid JNI data");
- return;
- }
-
- Mutex::Autolock l(engineMutex);
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- env->DeleteGlobalRef(pSynthData->tts_ref);
- delete pSynthData;
-
- env->SetIntField(thiz, javaTTSFields.synthProxyFieldJniData, 0);
-}
-
-
-static void
-android_tts_SynthProxy_shutdown(JNIEnv *env, jobject thiz, jint jniData)
-{
- //LOGV("entering android_tts_SynthProxy_shutdown()");
-
- // do everything a call to finalize would
- android_tts_SynthProxy_native_finalize(env, thiz, jniData);
-}
-
-
-static int
-android_tts_SynthProxy_isLanguageAvailable(JNIEnv *env, jobject thiz, jint jniData,
- jstring language, jstring country, jstring variant)
-{
- int result = ANDROID_TTS_LANG_NOT_SUPPORTED;
-
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_isLanguageAvailable(): invalid JNI data");
- return result;
- }
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- const char *langNativeString = env->GetStringUTFChars(language, 0);
- const char *countryNativeString = env->GetStringUTFChars(country, 0);
- const char *variantNativeString = env->GetStringUTFChars(variant, 0);
-
- android_tts_engine_t *engine = pSynthData->mEngine;
-
- if (engine) {
- result = engine->funcs->isLanguageAvailable(engine,langNativeString,
- countryNativeString, variantNativeString);
- }
- env->ReleaseStringUTFChars(language, langNativeString);
- env->ReleaseStringUTFChars(country, countryNativeString);
- env->ReleaseStringUTFChars(variant, variantNativeString);
- return result;
-}
-
-static int
-android_tts_SynthProxy_setConfig(JNIEnv *env, jobject thiz, jint jniData, jstring engineConfig)
-{
- int result = ANDROID_TTS_FAILURE;
-
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_setConfig(): invalid JNI data");
- return result;
- }
-
- Mutex::Autolock l(engineMutex);
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- const char *engineConfigNativeString = env->GetStringUTFChars(engineConfig, 0);
- android_tts_engine_t *engine = pSynthData->mEngine;
-
- if (engine) {
- result = engine->funcs->setProperty(engine,ANDROID_TTS_ENGINE_PROPERTY_CONFIG,
- engineConfigNativeString, strlen(engineConfigNativeString));
- }
- env->ReleaseStringUTFChars(engineConfig, engineConfigNativeString);
-
- return result;
-}
-
-static int
-android_tts_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData,
- jstring language, jstring country, jstring variant)
-{
- int result = ANDROID_TTS_LANG_NOT_SUPPORTED;
-
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_setLanguage(): invalid JNI data");
- return result;
- }
-
- Mutex::Autolock l(engineMutex);
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- const char *langNativeString = env->GetStringUTFChars(language, 0);
- const char *countryNativeString = env->GetStringUTFChars(country, 0);
- const char *variantNativeString = env->GetStringUTFChars(variant, 0);
- android_tts_engine_t *engine = pSynthData->mEngine;
-
- if (engine) {
- result = engine->funcs->setLanguage(engine, langNativeString,
- countryNativeString, variantNativeString);
- }
- env->ReleaseStringUTFChars(language, langNativeString);
- env->ReleaseStringUTFChars(country, countryNativeString);
- env->ReleaseStringUTFChars(variant, variantNativeString);
- return result;
-}
-
-
-static int
-android_tts_SynthProxy_loadLanguage(JNIEnv *env, jobject thiz, jint jniData,
- jstring language, jstring country, jstring variant)
-{
- int result = ANDROID_TTS_LANG_NOT_SUPPORTED;
-
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_loadLanguage(): invalid JNI data");
- return result;
- }
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- const char *langNativeString = env->GetStringUTFChars(language, 0);
- const char *countryNativeString = env->GetStringUTFChars(country, 0);
- const char *variantNativeString = env->GetStringUTFChars(variant, 0);
- android_tts_engine_t *engine = pSynthData->mEngine;
-
- if (engine) {
- result = engine->funcs->loadLanguage(engine, langNativeString,
- countryNativeString, variantNativeString);
- }
- env->ReleaseStringUTFChars(language, langNativeString);
- env->ReleaseStringUTFChars(country, countryNativeString);
- env->ReleaseStringUTFChars(variant, variantNativeString);
-
- return result;
-}
-
-
-static int
-android_tts_SynthProxy_setSpeechRate(JNIEnv *env, jobject thiz, jint jniData,
- jint speechRate)
-{
- int result = ANDROID_TTS_FAILURE;
-
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_setSpeechRate(): invalid JNI data");
- return result;
- }
-
- int bufSize = 12;
- char buffer [bufSize];
- sprintf(buffer, "%d", speechRate);
-
- Mutex::Autolock l(engineMutex);
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- //LOGI("setting speech rate to %d", speechRate);
- android_tts_engine_t *engine = pSynthData->mEngine;
-
- if (engine) {
- result = engine->funcs->setProperty(engine, "rate", buffer, bufSize);
- }
-
- return result;
-}
-
-
-static int
-android_tts_SynthProxy_setPitch(JNIEnv *env, jobject thiz, jint jniData,
- jint pitch)
-{
- int result = ANDROID_TTS_FAILURE;
-
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_setPitch(): invalid JNI data");
- return result;
- }
-
- Mutex::Autolock l(engineMutex);
-
- int bufSize = 12;
- char buffer [bufSize];
- sprintf(buffer, "%d", pitch);
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- //LOGI("setting pitch to %d", pitch);
- android_tts_engine_t *engine = pSynthData->mEngine;
-
- if (engine) {
- result = engine->funcs->setProperty(engine, "pitch", buffer, bufSize);
- }
-
- return result;
-}
-
-
-static int
-android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData,
- jstring textJavaString, jstring filenameJavaString)
-{
- int result = ANDROID_TTS_FAILURE;
-
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid JNI data");
- return result;
- }
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- if (!pSynthData->mEngine) {
- LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid engine handle");
- return result;
- }
-
- initializeFilter();
-
- Mutex::Autolock l(engineMutex);
-
- // Retrieve audio parameters before writing the file header
- AudioSystem::audio_format encoding;
- uint32_t rate = DEFAULT_TTS_RATE;
- int channels = DEFAULT_TTS_NB_CHANNELS;
- android_tts_engine_t *engine = pSynthData->mEngine;
- android_tts_audio_format_t format = ANDROID_TTS_AUDIO_FORMAT_DEFAULT;
-
- engine->funcs->setAudioFormat(engine, &format, &rate, &channels);
-
- switch (format) {
- case ANDROID_TTS_AUDIO_FORMAT_PCM_16_BIT:
- encoding = AudioSystem::PCM_16_BIT;
- break;
- case ANDROID_TTS_AUDIO_FORMAT_PCM_8_BIT:
- encoding = AudioSystem::PCM_8_BIT;
- break;
- default:
- LOGE("android_tts_SynthProxy_synthesizeToFile(): engine uses invalid format");
- return result;
- }
-
- const char *filenameNativeString =
- env->GetStringUTFChars(filenameJavaString, 0);
- const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
-
- afterSynthData_t* pForAfter = new (afterSynthData_t);
- pForAfter->jniStorage = jniData;
- pForAfter->usageMode = USAGEMODE_WRITE_TO_FILE;
-
- pForAfter->outputFile = fopen(filenameNativeString, "wb");
-
- if (pForAfter->outputFile == NULL) {
- LOGE("android_tts_SynthProxy_synthesizeToFile(): error creating output file");
- delete pForAfter;
- return result;
- }
-
- // Write 44 blank bytes for WAV header, then come back and fill them in
- // after we've written the audio data
- char header[44];
- fwrite(header, 1, 44, pForAfter->outputFile);
-
- unsigned int unique_identifier;
-
- memset(pSynthData->mBuffer, 0, pSynthData->mBufferSize);
-
- result = engine->funcs->synthesizeText(engine, textNativeString,
- pSynthData->mBuffer, pSynthData->mBufferSize, (void *)pForAfter);
-
- long filelen = ftell(pForAfter->outputFile);
-
- int samples = (((int)filelen) - 44) / 2;
- header[0] = 'R';
- header[1] = 'I';
- header[2] = 'F';
- header[3] = 'F';
- ((uint32_t *)(&header[4]))[0] = filelen - 8;
- header[8] = 'W';
- header[9] = 'A';
- header[10] = 'V';
- header[11] = 'E';
-
- header[12] = 'f';
- header[13] = 'm';
- header[14] = 't';
- header[15] = ' ';
-
- ((uint32_t *)(&header[16]))[0] = 16; // size of fmt
-
- int sampleSizeInByte = (encoding == AudioSystem::PCM_16_BIT ? 2 : 1);
-
- ((unsigned short *)(&header[20]))[0] = 1; // format
- ((unsigned short *)(&header[22]))[0] = channels; // channels
- ((uint32_t *)(&header[24]))[0] = rate; // samplerate
- ((uint32_t *)(&header[28]))[0] = rate * sampleSizeInByte * channels;// byterate
- ((unsigned short *)(&header[32]))[0] = sampleSizeInByte * channels; // block align
- ((unsigned short *)(&header[34]))[0] = sampleSizeInByte * 8; // bits per sample
-
- header[36] = 'd';
- header[37] = 'a';
- header[38] = 't';
- header[39] = 'a';
-
- ((uint32_t *)(&header[40]))[0] = samples * 2; // size of data
-
- // Skip back to the beginning and rewrite the header
- fseek(pForAfter->outputFile, 0, SEEK_SET);
- fwrite(header, 1, 44, pForAfter->outputFile);
-
- fflush(pForAfter->outputFile);
- fclose(pForAfter->outputFile);
-
- delete pForAfter;
- pForAfter = NULL;
-
- env->ReleaseStringUTFChars(textJavaString, textNativeString);
- env->ReleaseStringUTFChars(filenameJavaString, filenameNativeString);
-
- return result;
-}
-
-
-static int
-android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData,
- jstring textJavaString, jint javaStreamType, jfloat volume, jfloat pan)
-{
- int result = ANDROID_TTS_FAILURE;
-
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_speak(): invalid JNI data");
- return result;
- }
-
- initializeFilter();
-
- Mutex::Autolock l(engineMutex);
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-
- {//scope for lock on mPlayLock
- Mutex::Autolock _l(pSynthData->mPlayLock);
-
- pSynthData->mPlayState = SYNTHPLAYSTATE_IS_PLAYING;
-
- // clip volume and pan
- float vol = (volume > 1.0f) ? 1.0f : (volume < 0.0f) ? 0.0f : volume;
- float panning = (pan > 1.0f) ? 1.0f : (pan < -1.0f) ? -1.0f : pan;
- // compute playback volume based on volume and pan, using balance rule, in order to avoid
- // lowering volume when panning in center
- pSynthData->mVolume[AudioTrack::LEFT] = vol;
- pSynthData->mVolume[AudioTrack::RIGHT] = vol;
- if (panning > 0.0f) {
- pSynthData->mVolume[AudioTrack::LEFT] *= (1.0f - panning);
- } else if (panning < 0.0f) {
- pSynthData->mVolume[AudioTrack::RIGHT] *= (1.0f + panning);
- }
-
- // apply the volume if there is an output
- if (NULL != pSynthData->mAudioOut) {
- pSynthData->mAudioOut->setVolume(pSynthData->mVolume[AudioTrack::LEFT],
- pSynthData->mVolume[AudioTrack::RIGHT]);
- }
-
- //LOGV("android_tts_SynthProxy_speak() vol=%.3f pan=%.3f, mVolume=[%.1f %.1f]",
- // volume, pan,
- // pSynthData->mVolume[AudioTrack::LEFT], pSynthData->mVolume[AudioTrack::RIGHT]);
- }
-
- afterSynthData_t* pForAfter = new (afterSynthData_t);
- pForAfter->jniStorage = jniData;
- pForAfter->usageMode = USAGEMODE_PLAY_IMMEDIATELY;
- pForAfter->streamType = (AudioSystem::stream_type) javaStreamType;
-
- if (pSynthData->mEngine) {
- const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
- memset(pSynthData->mBuffer, 0, pSynthData->mBufferSize);
- android_tts_engine_t *engine = pSynthData->mEngine;
-
- result = engine->funcs->synthesizeText(engine, textNativeString,
- pSynthData->mBuffer, pSynthData->mBufferSize, (void *)pForAfter);
- env->ReleaseStringUTFChars(textJavaString, textNativeString);
- }
-
- return result;
-}
-
-
-static int
-android_tts_SynthProxy_stop(JNIEnv *env, jobject thiz, jint jniData)
-{
- int result = ANDROID_TTS_FAILURE;
-
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_stop(): invalid JNI data");
- return result;
- }
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-
- pSynthData->mPlayLock.lock();
- pSynthData->mPlayState = SYNTHPLAYSTATE_IS_STOPPED;
- if (pSynthData->mAudioOut) {
- pSynthData->mAudioOut->stop();
- }
- pSynthData->mPlayLock.unlock();
-
- android_tts_engine_t *engine = pSynthData->mEngine;
- if (engine) {
- result = engine->funcs->stop(engine);
- }
-
- return result;
-}
-
-
-static int
-android_tts_SynthProxy_stopSync(JNIEnv *env, jobject thiz, jint jniData)
-{
- int result = ANDROID_TTS_FAILURE;
-
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_stop(): invalid JNI data");
- return result;
- }
-
- // perform a regular stop
- result = android_tts_SynthProxy_stop(env, thiz, jniData);
- // but wait on the engine having released the engine mutex which protects
- // the synthesizer resources.
- engineMutex.lock();
- engineMutex.unlock();
-
- return result;
-}
-
-
-static jobjectArray
-android_tts_SynthProxy_getLanguage(JNIEnv *env, jobject thiz, jint jniData)
-{
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_getLanguage(): invalid JNI data");
- return NULL;
- }
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-
- if (pSynthData->mEngine) {
- size_t bufSize = 100;
- char lang[bufSize];
- char country[bufSize];
- char variant[bufSize];
- memset(lang, 0, bufSize);
- memset(country, 0, bufSize);
- memset(variant, 0, bufSize);
- jobjectArray retLocale = (jobjectArray)env->NewObjectArray(3,
- env->FindClass("java/lang/String"), env->NewStringUTF(""));
-
- android_tts_engine_t *engine = pSynthData->mEngine;
- engine->funcs->getLanguage(engine, lang, country, variant);
- env->SetObjectArrayElement(retLocale, 0, env->NewStringUTF(lang));
- env->SetObjectArrayElement(retLocale, 1, env->NewStringUTF(country));
- env->SetObjectArrayElement(retLocale, 2, env->NewStringUTF(variant));
- return retLocale;
- } else {
- return NULL;
- }
-}
-
-
-JNIEXPORT int JNICALL
-android_tts_SynthProxy_getRate(JNIEnv *env, jobject thiz, jint jniData)
-{
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_getRate(): invalid JNI data");
- return 0;
- }
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- size_t bufSize = 100;
-
- char buf[bufSize];
- memset(buf, 0, bufSize);
- // TODO check return codes
- android_tts_engine_t *engine = pSynthData->mEngine;
- if (engine) {
- engine->funcs->getProperty(engine,"rate", buf, &bufSize);
- }
- return atoi(buf);
-}
-
-// Dalvik VM type signatures
-static JNINativeMethod gMethods[] = {
- { "native_stop",
- "(I)I",
- (void*)android_tts_SynthProxy_stop
- },
- { "native_stopSync",
- "(I)I",
- (void*)android_tts_SynthProxy_stopSync
- },
- { "native_speak",
- "(ILjava/lang/String;IFF)I",
- (void*)android_tts_SynthProxy_speak
- },
- { "native_synthesizeToFile",
- "(ILjava/lang/String;Ljava/lang/String;)I",
- (void*)android_tts_SynthProxy_synthesizeToFile
- },
- { "native_isLanguageAvailable",
- "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
- (void*)android_tts_SynthProxy_isLanguageAvailable
- },
- { "native_setConfig",
- "(ILjava/lang/String;)I",
- (void*)android_tts_SynthProxy_setConfig
- },
- { "native_setLanguage",
- "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
- (void*)android_tts_SynthProxy_setLanguage
- },
- { "native_loadLanguage",
- "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
- (void*)android_tts_SynthProxy_loadLanguage
- },
- { "native_setSpeechRate",
- "(II)I",
- (void*)android_tts_SynthProxy_setSpeechRate
- },
- { "native_setPitch",
- "(II)I",
- (void*)android_tts_SynthProxy_setPitch
- },
- { "native_getLanguage",
- "(I)[Ljava/lang/String;",
- (void*)android_tts_SynthProxy_getLanguage
- },
- { "native_getRate",
- "(I)I",
- (void*)android_tts_SynthProxy_getRate
- },
- { "native_shutdown",
- "(I)V",
- (void*)android_tts_SynthProxy_shutdown
- },
- { "native_setup",
- "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)I",
- (void*)android_tts_SynthProxy_native_setup
- },
- { "native_setLowShelf",
- "(ZFFFF)I",
- (void*)android_tts_SynthProxy_setLowShelf
- },
- { "native_finalize",
- "(I)V",
- (void*)android_tts_SynthProxy_native_finalize
- }
-};
-
-#define SP_JNIDATA_FIELD_NAME "mJniData"
-#define SP_POSTSPEECHSYNTHESIZED_METHOD_NAME "postNativeSpeechSynthesizedInJava"
-
-static const char* const kClassPathName = "android/tts/SynthProxy";
-
-jint JNI_OnLoad(JavaVM* vm, void* reserved)
-{
- JNIEnv* env = NULL;
- jint result = -1;
- jclass clazz;
-
- if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
- LOGE("ERROR: GetEnv failed\n");
- goto bail;
- }
- assert(env != NULL);
-
- clazz = env->FindClass(kClassPathName);
- if (clazz == NULL) {
- LOGE("Can't find %s", kClassPathName);
- goto bail;
- }
-
- javaTTSFields.synthProxyFieldJniData = NULL;
- javaTTSFields.synthProxyMethodPost = NULL;
-
- javaTTSFields.synthProxyFieldJniData = env->GetFieldID(clazz,
- SP_JNIDATA_FIELD_NAME, "I");
- if (javaTTSFields.synthProxyFieldJniData == NULL) {
- LOGE("Can't find %s.%s field", kClassPathName, SP_JNIDATA_FIELD_NAME);
- goto bail;
- }
-
- javaTTSFields.synthProxyMethodPost = env->GetStaticMethodID(clazz,
- SP_POSTSPEECHSYNTHESIZED_METHOD_NAME, "(Ljava/lang/Object;II)V");
- if (javaTTSFields.synthProxyMethodPost == NULL) {
- LOGE("Can't find %s.%s method", kClassPathName, SP_POSTSPEECHSYNTHESIZED_METHOD_NAME);
- goto bail;
- }
-
- if (jniRegisterNativeMethods(
- env, kClassPathName, gMethods, NELEM(gMethods)) < 0)
- goto bail;
-
- /* success -- return valid version number */
- result = JNI_VERSION_1_4;
-
- bail:
- return result;
-}
diff --git a/packages/TtsService/proguard.flags b/packages/TtsService/proguard.flags
deleted file mode 100644
index e8bee6b..0000000
--- a/packages/TtsService/proguard.flags
+++ /dev/null
@@ -1,5 +0,0 @@
--keep class android.tts.SynthProxy {
- int mJniData;
- # keep all declarations for native methods
- <methods>;
-}
diff --git a/packages/TtsService/res/drawable-hdpi/ic_launcher_text_to_speech.png b/packages/TtsService/res/drawable-hdpi/ic_launcher_text_to_speech.png
deleted file mode 100644
index f075e0f..0000000
--- a/packages/TtsService/res/drawable-hdpi/ic_launcher_text_to_speech.png
+++ /dev/null
Binary files differ
diff --git a/packages/TtsService/res/drawable-mdpi/ic_launcher_text_to_speech.png b/packages/TtsService/res/drawable-mdpi/ic_launcher_text_to_speech.png
deleted file mode 100644
index cbae7de..0000000
--- a/packages/TtsService/res/drawable-mdpi/ic_launcher_text_to_speech.png
+++ /dev/null
Binary files differ
diff --git a/packages/TtsService/src/android/tts/SynthProxy.java b/packages/TtsService/src/android/tts/SynthProxy.java
deleted file mode 100755
index f5f5fcf..0000000
--- a/packages/TtsService/src/android/tts/SynthProxy.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * 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.
- */
-package android.tts;
-
-import android.media.AudioManager;
-import android.media.AudioSystem;
-import android.util.Log;
-import java.lang.ref.WeakReference;
-
-/**
- * @hide
- *
- * 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
- *
- */
-@SuppressWarnings("unused")
-public class 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
-
- //
- // External API
- //
-
- /**
- * Constructor; pass the location of the native TTS .so to use.
- */
- public SynthProxy(String nativeSoLib, String engineConfig) {
- boolean applyFilter = nativeSoLib.toLowerCase().contains("pico");
- Log.v(TtsService.SERVICE_TAG, "About to load "+ nativeSoLib + ", applyFilter="+applyFilter);
- native_setup(new WeakReference<SynthProxy>(this), nativeSoLib, engineConfig);
- native_setLowShelf(applyFilter, PICO_FILTER_GAIN, PICO_FILTER_LOWSHELF_ATTENUATION,
- PICO_FILTER_TRANSITION_FREQ, PICO_FILTER_SHELF_SLOPE);
- }
-
- /**
- * 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);
- }
-
- /**
- * Synthesize speech and speak it directly using AudioTrack.
- */
- public int speak(String text, int streamType, float volume, float pan) {
- Log.i(TAG, "speak() on stream "+ streamType);
- if ((streamType > -1) && (streamType < AudioSystem.getNumStreamTypes())) {
- return native_speak(mJniData, text, streamType, volume, pan);
- } else {
- Log.e("SynthProxy", "Trying to speak with invalid stream type " + streamType);
- return native_speak(mJniData, text, AudioManager.STREAM_MUSIC, volume, pan);
- }
- }
-
- /**
- * Synthesize speech to a file. The current implementation writes a valid
- * WAV file to the given path, assuming it is writable. Something like
- * "/sdcard/???.wav" is recommended.
- */
- public int synthesizeToFile(String text, String filename) {
- Log.i(TAG, "synthesizeToFile() to file "+ filename);
- return native_synthesizeToFile(mJniData, text, filename);
- }
-
- /**
- * 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_setConfig(mJniData, 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_setSpeechRate(mJniData, speechRate);
- }
-
- /**
- * Sets the pitch of the synthesized voice.
- */
- public final int setPitch(int pitch) {
- return native_setPitch(mJniData, pitch);
- }
-
- /**
- * Returns the currently set language, country and variant information.
- */
- public String[] getLanguage() {
- return native_getLanguage(mJniData);
- }
-
- /**
- * Gets the currently set rate.
- */
- public int getRate() {
- return native_getRate(mJniData);
- }
-
- /**
- * Shuts down the native synthesizer.
- */
- public void shutdown() {
- native_shutdown(mJniData);
- }
-
- //
- // Internal
- //
-
- protected void finalize() {
- native_finalize(mJniData);
- mJniData = 0;
- }
-
- static {
- System.loadLibrary("ttssynthproxy");
- }
-
- private final static String TAG = "SynthProxy";
-
- /**
- * Accessed by native methods
- */
- private int mJniData = 0;
-
- private native final int native_setup(Object weak_this, 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, int streamType, float volume,
- float pan);
-
- private native final int native_synthesizeToFile(int jniData, String text, String filename);
-
- 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_setConfig(int jniData, String engineConfig);
-
- private native final int native_setSpeechRate(int jniData, int speechRate);
-
- private native final int native_setPitch(int jniData, int speechRate);
-
- private native final String[] native_getLanguage(int jniData);
-
- private native final int native_getRate(int jniData);
-
- private native final void native_shutdown(int jniData);
-
-
- /**
- * Callback from the C layer
- */
- @SuppressWarnings("unused")
- private static void postNativeSpeechSynthesizedInJava(Object tts_ref,
- int bufferPointer, int bufferSize) {
-
- Log.i("TTS plugin debug", "bufferPointer: " + bufferPointer
- + " bufferSize: " + bufferSize);
-
- SynthProxy nativeTTS = (SynthProxy)((WeakReference)tts_ref).get();
- // TODO notify TTS service of synthesis/playback completion,
- // method definition to be changed.
- }
-}
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
deleted file mode 100755
index c562327..0000000
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ /dev/null
@@ -1,1503 +0,0 @@
-/*
- * 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.
- */
-package android.tts;
-
-import android.app.Service;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.database.Cursor;
-import android.media.AudioManager;
-import android.media.MediaPlayer;
-import android.media.MediaPlayer.OnCompletionListener;
-import android.net.Uri;
-import android.os.IBinder;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.preference.PreferenceManager;
-import android.speech.tts.ITts.Stub;
-import android.speech.tts.ITtsCallback;
-import android.speech.tts.TextToSpeech;
-import android.util.Log;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.concurrent.TimeUnit;
-
-
-/**
- * @hide Synthesizes speech from text. This is implemented as a service so that
- * other applications can call the TTS without needing to bundle the TTS
- * in the build.
- *
- */
-public class TtsService extends Service implements OnCompletionListener {
-
- private static class SpeechItem {
- public static final int TEXT = 0;
- public static final int EARCON = 1;
- public static final int SILENCE = 2;
- public static final int TEXT_TO_FILE = 3;
- public String mText = "";
- public ArrayList<String> mParams = null;
- public int mType = TEXT;
- public long mDuration = 0;
- public String mFilename = null;
- public String mCallingApp = "";
-
- public SpeechItem(String source, String text, ArrayList<String> params, int itemType) {
- mText = text;
- mParams = params;
- mType = itemType;
- mCallingApp = source;
- }
-
- public SpeechItem(String source, long silenceTime, ArrayList<String> params) {
- mDuration = silenceTime;
- mParams = params;
- mType = SILENCE;
- mCallingApp = source;
- }
-
- public SpeechItem(String source, String text, ArrayList<String> params,
- int itemType, String filename) {
- mText = text;
- mParams = params;
- mType = itemType;
- mFilename = filename;
- mCallingApp = source;
- }
-
- }
-
- /**
- * Contains the information needed to access a sound resource; the name of
- * the package that contains the resource and the resID of the resource
- * within that package.
- */
- private static class SoundResource {
- public String mSourcePackageName = null;
- public int mResId = -1;
- public String mFilename = null;
-
- public SoundResource(String packageName, int id) {
- mSourcePackageName = packageName;
- mResId = id;
- mFilename = null;
- }
-
- public SoundResource(String file) {
- mSourcePackageName = null;
- mResId = -1;
- mFilename = file;
- }
- }
- // If the speech queue is locked for more than 5 seconds, something has gone
- // very wrong with processSpeechQueue.
- private static final int SPEECHQUEUELOCK_TIMEOUT = 5000;
- private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000;
- private static final int MAX_FILENAME_LENGTH = 250;
- private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_MUSIC;
- // TODO use TextToSpeech.DEFAULT_SYNTH once it is unhidden
- private static final String DEFAULT_SYNTH = "com.svox.pico";
- private static final String ACTION = "android.intent.action.START_TTS_SERVICE";
- private static final String CATEGORY = "android.intent.category.TTS";
- private static final String PKGNAME = "android.tts";
- protected static final String SERVICE_TAG = "TtsService";
-
- private final RemoteCallbackList<ITtsCallback> mCallbacks
- = new RemoteCallbackList<ITtsCallback>();
-
- private HashMap<String, ITtsCallback> mCallbacksMap;
-
- private Boolean mIsSpeaking;
- private Boolean mSynthBusy;
- private ArrayList<SpeechItem> mSpeechQueue;
- private HashMap<String, SoundResource> mEarcons;
- private HashMap<String, SoundResource> mUtterances;
- private MediaPlayer mPlayer;
- private SpeechItem mCurrentSpeechItem;
- private HashMap<SpeechItem, Boolean> mKillList; // Used to ensure that in-flight synth calls
- // are killed when stop is used.
- private TtsService mSelf;
-
- private ContentResolver mResolver;
-
- // lock for the speech queue (mSpeechQueue) and the current speech item (mCurrentSpeechItem)
- private final ReentrantLock speechQueueLock = new ReentrantLock();
- private final ReentrantLock synthesizerLock = new ReentrantLock();
-
- private static SynthProxy sNativeSynth = null;
- private String currentSpeechEngineSOFile = "";
-
- @Override
- public void onCreate() {
- super.onCreate();
- Log.v("TtsService", "TtsService.onCreate()");
-
- mResolver = getContentResolver();
-
- currentSpeechEngineSOFile = "";
- setEngine(getDefaultEngine());
-
- mSelf = this;
- mIsSpeaking = false;
- mSynthBusy = false;
-
- mEarcons = new HashMap<String, SoundResource>();
- mUtterances = new HashMap<String, SoundResource>();
- mCallbacksMap = new HashMap<String, android.speech.tts.ITtsCallback>();
-
- mSpeechQueue = new ArrayList<SpeechItem>();
- mPlayer = null;
- mCurrentSpeechItem = null;
- mKillList = new HashMap<SpeechItem, Boolean>();
-
- setDefaultSettings();
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
-
- killAllUtterances();
-
- // Don't hog the media player
- cleanUpPlayer();
-
- if (sNativeSynth != null) {
- sNativeSynth.shutdown();
- }
- sNativeSynth = null;
-
- // Unregister all callbacks.
- mCallbacks.kill();
-
- Log.v(SERVICE_TAG, "onDestroy() completed");
- }
-
-
- private int setEngine(String enginePackageName) {
- String soFilename = "";
- if (isDefaultEnforced()) {
- enginePackageName = getDefaultEngine();
- }
-
- // Make sure that the engine has been allowed by the user
- if (!enginePackageName.equals(DEFAULT_SYNTH)) {
- String[] enabledEngines = android.provider.Settings.Secure.getString(mResolver,
- android.provider.Settings.Secure.TTS_ENABLED_PLUGINS).split(" ");
- boolean isEnabled = false;
- for (int i=0; i<enabledEngines.length; i++) {
- if (enabledEngines[i].equals(enginePackageName)) {
- isEnabled = true;
- break;
- }
- }
- if (!isEnabled) {
- // Do not use an engine that the user has not enabled; fall back
- // to using the default synthesizer.
- enginePackageName = DEFAULT_SYNTH;
- }
- }
-
- // The SVOX TTS is an exception to how the TTS packaging scheme works
- // because it is part of the system and not a 3rd party add-on; thus
- // its binary is actually located under /system/lib/
- if (enginePackageName.equals(DEFAULT_SYNTH)) {
- soFilename = "/system/lib/libttspico.so";
- } else {
- // Find the package
- Intent intent = new Intent("android.intent.action.START_TTS_ENGINE");
- intent.setPackage(enginePackageName);
- ResolveInfo[] enginesArray = new ResolveInfo[0];
- PackageManager pm = getPackageManager();
- List <ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
- if ((resolveInfos == null) || resolveInfos.isEmpty()) {
- Log.e(SERVICE_TAG, "Invalid TTS Engine Package: " + enginePackageName);
- return TextToSpeech.ERROR;
- }
- enginesArray = resolveInfos.toArray(enginesArray);
- // Generate the TTS .so filename from the package
- ActivityInfo aInfo = enginesArray[0].activityInfo;
- soFilename = aInfo.name.replace(aInfo.packageName + ".", "") + ".so";
- soFilename = soFilename.toLowerCase();
- soFilename = "/data/data/" + aInfo.packageName + "/lib/libtts" + soFilename;
- }
-
- if (currentSpeechEngineSOFile.equals(soFilename)) {
- return TextToSpeech.SUCCESS;
- }
-
- File f = new File(soFilename);
- if (!f.exists()) {
- Log.e(SERVICE_TAG, "Invalid TTS Binary: " + soFilename);
- return TextToSpeech.ERROR;
- }
-
- if (sNativeSynth != null) {
- sNativeSynth.stopSync();
- sNativeSynth.shutdown();
- sNativeSynth = 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://" + enginePackageName
- + ".providers.SettingsProvider"), null, null, null, null);
- if (c != null){
- c.moveToFirst();
- engineConfig = c.getString(0);
- c.close();
- }
- sNativeSynth = new SynthProxy(soFilename, engineConfig);
- currentSpeechEngineSOFile = soFilename;
- return TextToSpeech.SUCCESS;
- }
-
-
-
- private void setDefaultSettings() {
- setLanguage("", this.getDefaultLanguage(), getDefaultCountry(), getDefaultLocVariant());
-
- // speech rate
- setSpeechRate("", getDefaultRate());
- }
-
-
- private boolean isDefaultEnforced() {
- return (android.provider.Settings.Secure.getInt(mResolver,
- android.provider.Settings.Secure.TTS_USE_DEFAULTS,
- TextToSpeech.Engine.USE_DEFAULTS)
- == 1 );
- }
-
- private String getDefaultEngine() {
- String defaultEngine = android.provider.Settings.Secure.getString(mResolver,
- android.provider.Settings.Secure.TTS_DEFAULT_SYNTH);
- if (defaultEngine == null) {
- return TextToSpeech.Engine.DEFAULT_SYNTH;
- } else {
- return defaultEngine;
- }
- }
-
- private int getDefaultRate() {
- return android.provider.Settings.Secure.getInt(mResolver,
- android.provider.Settings.Secure.TTS_DEFAULT_RATE,
- TextToSpeech.Engine.DEFAULT_RATE);
- }
-
- private int getDefaultPitch() {
- // Pitch is not user settable; the default pitch is always 100.
- return 100;
- }
-
- private String getDefaultLanguage() {
- String defaultLang = android.provider.Settings.Secure.getString(mResolver,
- android.provider.Settings.Secure.TTS_DEFAULT_LANG);
- if (defaultLang == null) {
- // no setting found, use the current Locale to determine the default language
- return Locale.getDefault().getISO3Language();
- } else {
- return defaultLang;
- }
- }
-
-
- private String getDefaultCountry() {
- String defaultCountry = android.provider.Settings.Secure.getString(mResolver,
- android.provider.Settings.Secure.TTS_DEFAULT_COUNTRY);
- if (defaultCountry == null) {
- // no setting found, use the current Locale to determine the default country
- return Locale.getDefault().getISO3Country();
- } else {
- return defaultCountry;
- }
- }
-
-
- private String getDefaultLocVariant() {
- String defaultVar = android.provider.Settings.Secure.getString(mResolver,
- android.provider.Settings.Secure.TTS_DEFAULT_VARIANT);
- if (defaultVar == null) {
- // no setting found, use the current Locale to determine the default variant
- return Locale.getDefault().getVariant();
- } else {
- return defaultVar;
- }
- }
-
-
- private int setSpeechRate(String callingApp, int rate) {
- int res = TextToSpeech.ERROR;
- try {
- if (isDefaultEnforced()) {
- res = sNativeSynth.setSpeechRate(getDefaultRate());
- } else {
- res = sNativeSynth.setSpeechRate(rate);
- }
- } catch (NullPointerException e) {
- // synth will become null during onDestroy()
- res = TextToSpeech.ERROR;
- }
- return res;
- }
-
-
- private int setPitch(String callingApp, int pitch) {
- int res = TextToSpeech.ERROR;
- try {
- res = sNativeSynth.setPitch(pitch);
- } catch (NullPointerException e) {
- // synth will become null during onDestroy()
- res = TextToSpeech.ERROR;
- }
- return res;
- }
-
-
- private int isLanguageAvailable(String lang, String country, String variant) {
- int res = TextToSpeech.LANG_NOT_SUPPORTED;
- try {
- res = sNativeSynth.isLanguageAvailable(lang, country, variant);
- } catch (NullPointerException e) {
- // synth will become null during onDestroy()
- res = TextToSpeech.LANG_NOT_SUPPORTED;
- }
- return res;
- }
-
-
- private String[] getLanguage() {
- try {
- return sNativeSynth.getLanguage();
- } catch (Exception e) {
- return null;
- }
- }
-
-
- private int setLanguage(String callingApp, String lang, String country, String variant) {
- Log.v(SERVICE_TAG, "TtsService.setLanguage(" + lang + ", " + country + ", " + variant + ")");
- int res = TextToSpeech.ERROR;
- try {
- if (isDefaultEnforced()) {
- res = sNativeSynth.setLanguage(getDefaultLanguage(), getDefaultCountry(),
- getDefaultLocVariant());
- } else {
- res = sNativeSynth.setLanguage(lang, country, variant);
- }
- } catch (NullPointerException e) {
- // synth will become null during onDestroy()
- res = TextToSpeech.ERROR;
- }
- return res;
- }
-
-
- /**
- * Adds a sound resource to the TTS.
- *
- * @param text
- * The text that should be associated with the sound resource
- * @param packageName
- * The name of the package which has the sound resource
- * @param resId
- * The resource ID of the sound within its package
- */
- private void addSpeech(String callingApp, String text, String packageName, int resId) {
- mUtterances.put(text, new SoundResource(packageName, resId));
- }
-
- /**
- * Adds a sound resource to the TTS.
- *
- * @param text
- * The text that should be associated with the sound resource
- * @param filename
- * The filename of the sound resource. This must be a complete
- * path like: (/sdcard/mysounds/mysoundbite.mp3).
- */
- private void addSpeech(String callingApp, String text, String filename) {
- mUtterances.put(text, new SoundResource(filename));
- }
-
- /**
- * Adds a sound resource to the TTS as an earcon.
- *
- * @param earcon
- * The text that should be associated with the sound resource
- * @param packageName
- * The name of the package which has the sound resource
- * @param resId
- * The resource ID of the sound within its package
- */
- private void addEarcon(String callingApp, String earcon, String packageName, int resId) {
- mEarcons.put(earcon, new SoundResource(packageName, resId));
- }
-
- /**
- * Adds a sound resource to the TTS as an earcon.
- *
- * @param earcon
- * The text that should be associated with the sound resource
- * @param filename
- * The filename of the sound resource. This must be a complete
- * path like: (/sdcard/mysounds/mysoundbite.mp3).
- */
- private void addEarcon(String callingApp, String earcon, String filename) {
- mEarcons.put(earcon, new SoundResource(filename));
- }
-
- /**
- * Speaks the given text using the specified queueing mode and parameters.
- *
- * @param text
- * The text that should be spoken
- * @param queueMode
- * TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances),
- * TextToSpeech.TTS_QUEUE_ADD for queued
- * @param params
- * An ArrayList of parameters. This is not implemented for all
- * engines.
- */
- private int speak(String callingApp, String text, int queueMode, ArrayList<String> params) {
- // Log.v(SERVICE_TAG, "TTS service received " + text);
- if (queueMode == TextToSpeech.QUEUE_FLUSH) {
- stop(callingApp);
- } else if (queueMode == 2) {
- stopAll(callingApp);
- }
- mSpeechQueue.add(new SpeechItem(callingApp, text, params, SpeechItem.TEXT));
- if (!mIsSpeaking) {
- processSpeechQueue();
- }
- return TextToSpeech.SUCCESS;
- }
-
- /**
- * Plays the earcon using the specified queueing mode and parameters.
- *
- * @param earcon
- * The earcon that should be played
- * @param queueMode
- * TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances),
- * TextToSpeech.TTS_QUEUE_ADD for queued
- * @param params
- * An ArrayList of parameters. This is not implemented for all
- * engines.
- */
- private int playEarcon(String callingApp, String earcon, int queueMode,
- ArrayList<String> params) {
- if (queueMode == TextToSpeech.QUEUE_FLUSH) {
- stop(callingApp);
- } else if (queueMode == 2) {
- stopAll(callingApp);
- }
- mSpeechQueue.add(new SpeechItem(callingApp, earcon, params, SpeechItem.EARCON));
- if (!mIsSpeaking) {
- processSpeechQueue();
- }
- return TextToSpeech.SUCCESS;
- }
-
- /**
- * Stops all speech output and removes any utterances still in the queue for the calling app.
- */
- private int stop(String callingApp) {
- int result = TextToSpeech.ERROR;
- boolean speechQueueAvailable = false;
- try{
- speechQueueAvailable =
- speechQueueLock.tryLock(SPEECHQUEUELOCK_TIMEOUT, TimeUnit.MILLISECONDS);
- if (speechQueueAvailable) {
- Log.i(SERVICE_TAG, "Stopping");
- for (int i = mSpeechQueue.size() - 1; i > -1; i--){
- if (mSpeechQueue.get(i).mCallingApp.equals(callingApp)){
- mSpeechQueue.remove(i);
- }
- }
- if ((mCurrentSpeechItem != null) &&
- mCurrentSpeechItem.mCallingApp.equals(callingApp)) {
- try {
- result = sNativeSynth.stop();
- } catch (NullPointerException e1) {
- // synth will become null during onDestroy()
- result = TextToSpeech.ERROR;
- }
- mKillList.put(mCurrentSpeechItem, true);
- if (mPlayer != null) {
- try {
- mPlayer.stop();
- } catch (IllegalStateException e) {
- // Do nothing, the player is already stopped.
- }
- }
- mIsSpeaking = false;
- mCurrentSpeechItem = null;
- } else {
- result = TextToSpeech.SUCCESS;
- }
- Log.i(SERVICE_TAG, "Stopped");
- } else {
- Log.e(SERVICE_TAG, "TTS stop(): queue locked longer than expected");
- result = TextToSpeech.ERROR;
- }
- } catch (InterruptedException e) {
- Log.e(SERVICE_TAG, "TTS stop: tryLock interrupted");
- e.printStackTrace();
- } finally {
- // This check is needed because finally will always run; even if the
- // method returns somewhere in the try block.
- if (speechQueueAvailable) {
- speechQueueLock.unlock();
- }
- return result;
- }
- }
-
-
- /**
- * Stops all speech output, both rendered to a file and directly spoken, and removes any
- * utterances still in the queue globally. Files that were being written are deleted.
- */
- @SuppressWarnings("finally")
- private int killAllUtterances() {
- int result = TextToSpeech.ERROR;
- boolean speechQueueAvailable = false;
-
- try {
- speechQueueAvailable = speechQueueLock.tryLock(SPEECHQUEUELOCK_TIMEOUT,
- TimeUnit.MILLISECONDS);
- if (speechQueueAvailable) {
- // remove every single entry in the speech queue
- mSpeechQueue.clear();
-
- // clear the current speech item
- if (mCurrentSpeechItem != null) {
- result = sNativeSynth.stopSync();
- mKillList.put(mCurrentSpeechItem, true);
- mIsSpeaking = false;
-
- // was the engine writing to a file?
- if (mCurrentSpeechItem.mType == SpeechItem.TEXT_TO_FILE) {
- // delete the file that was being written
- if (mCurrentSpeechItem.mFilename != null) {
- File tempFile = new File(mCurrentSpeechItem.mFilename);
- Log.v(SERVICE_TAG, "Leaving behind " + mCurrentSpeechItem.mFilename);
- if (tempFile.exists()) {
- Log.v(SERVICE_TAG, "About to delete "
- + mCurrentSpeechItem.mFilename);
- if (tempFile.delete()) {
- Log.v(SERVICE_TAG, "file successfully deleted");
- }
- }
- }
- }
-
- mCurrentSpeechItem = null;
- }
- } else {
- Log.e(SERVICE_TAG, "TTS killAllUtterances(): queue locked longer than expected");
- result = TextToSpeech.ERROR;
- }
- } catch (InterruptedException e) {
- Log.e(SERVICE_TAG, "TTS killAllUtterances(): tryLock interrupted");
- result = TextToSpeech.ERROR;
- } finally {
- // This check is needed because finally will always run, even if the
- // method returns somewhere in the try block.
- if (speechQueueAvailable) {
- speechQueueLock.unlock();
- }
- return result;
- }
- }
-
-
- /**
- * Stops all speech output and removes any utterances still in the queue globally, except
- * those intended to be synthesized to file.
- */
- private int stopAll(String callingApp) {
- int result = TextToSpeech.ERROR;
- boolean speechQueueAvailable = false;
- try{
- speechQueueAvailable =
- speechQueueLock.tryLock(SPEECHQUEUELOCK_TIMEOUT, TimeUnit.MILLISECONDS);
- if (speechQueueAvailable) {
- for (int i = mSpeechQueue.size() - 1; i > -1; i--){
- if (mSpeechQueue.get(i).mType != SpeechItem.TEXT_TO_FILE){
- mSpeechQueue.remove(i);
- }
- }
- if ((mCurrentSpeechItem != null) &&
- ((mCurrentSpeechItem.mType != SpeechItem.TEXT_TO_FILE) ||
- mCurrentSpeechItem.mCallingApp.equals(callingApp))) {
- try {
- result = sNativeSynth.stop();
- } catch (NullPointerException e1) {
- // synth will become null during onDestroy()
- result = TextToSpeech.ERROR;
- }
- mKillList.put(mCurrentSpeechItem, true);
- if (mPlayer != null) {
- try {
- mPlayer.stop();
- } catch (IllegalStateException e) {
- // Do nothing, the player is already stopped.
- }
- }
- mIsSpeaking = false;
- mCurrentSpeechItem = null;
- } else {
- result = TextToSpeech.SUCCESS;
- }
- Log.i(SERVICE_TAG, "Stopped all");
- } else {
- Log.e(SERVICE_TAG, "TTS stopAll(): queue locked longer than expected");
- result = TextToSpeech.ERROR;
- }
- } catch (InterruptedException e) {
- Log.e(SERVICE_TAG, "TTS stopAll: tryLock interrupted");
- e.printStackTrace();
- } finally {
- // This check is needed because finally will always run; even if the
- // method returns somewhere in the try block.
- if (speechQueueAvailable) {
- speechQueueLock.unlock();
- }
- return result;
- }
- }
-
- public void onCompletion(MediaPlayer arg0) {
- // mCurrentSpeechItem may become null if it is stopped at the same
- // time it completes.
- SpeechItem currentSpeechItemCopy = mCurrentSpeechItem;
- if (currentSpeechItemCopy != null) {
- String callingApp = currentSpeechItemCopy.mCallingApp;
- ArrayList<String> params = currentSpeechItemCopy.mParams;
- String utteranceId = "";
- if (params != null) {
- for (int i = 0; i < params.size() - 1; i = i + 2) {
- String param = params.get(i);
- if (param.equals(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID)) {
- utteranceId = params.get(i + 1);
- }
- }
- }
- if (utteranceId.length() > 0) {
- dispatchUtteranceCompletedCallback(utteranceId, callingApp);
- }
- }
- processSpeechQueue();
- }
-
- private int playSilence(String callingApp, long duration, int queueMode,
- ArrayList<String> params) {
- if (queueMode == TextToSpeech.QUEUE_FLUSH) {
- stop(callingApp);
- }
- mSpeechQueue.add(new SpeechItem(callingApp, duration, params));
- if (!mIsSpeaking) {
- processSpeechQueue();
- }
- return TextToSpeech.SUCCESS;
- }
-
- private void silence(final SpeechItem speechItem) {
- class SilenceThread implements Runnable {
- public void run() {
- String utteranceId = "";
- if (speechItem.mParams != null){
- for (int i = 0; i < speechItem.mParams.size() - 1; i = i + 2){
- String param = speechItem.mParams.get(i);
- if (param.equals(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID)){
- utteranceId = speechItem.mParams.get(i+1);
- }
- }
- }
- try {
- Thread.sleep(speechItem.mDuration);
- } catch (InterruptedException e) {
- e.printStackTrace();
- } finally {
- if (utteranceId.length() > 0){
- dispatchUtteranceCompletedCallback(utteranceId, speechItem.mCallingApp);
- }
- processSpeechQueue();
- }
- }
- }
- Thread slnc = (new Thread(new SilenceThread()));
- slnc.setPriority(Thread.MIN_PRIORITY);
- slnc.start();
- }
-
- private void speakInternalOnly(final SpeechItem speechItem) {
- class SynthThread implements Runnable {
- public void run() {
- boolean synthAvailable = false;
- String utteranceId = "";
- try {
- synthAvailable = synthesizerLock.tryLock();
- if (!synthAvailable) {
- mSynthBusy = true;
- Thread.sleep(100);
- Thread synth = (new Thread(new SynthThread()));
- synth.start();
- mSynthBusy = false;
- return;
- }
- int streamType = DEFAULT_STREAM_TYPE;
- String language = "";
- String country = "";
- String variant = "";
- String speechRate = "";
- String engine = "";
- String pitch = "";
- float volume = TextToSpeech.Engine.DEFAULT_VOLUME;
- float pan = TextToSpeech.Engine.DEFAULT_PAN;
- if (speechItem.mParams != null){
- for (int i = 0; i < speechItem.mParams.size() - 1; i = i + 2){
- String param = speechItem.mParams.get(i);
- if (param != null) {
- if (param.equals(TextToSpeech.Engine.KEY_PARAM_RATE)) {
- speechRate = speechItem.mParams.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_LANGUAGE)){
- language = speechItem.mParams.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_COUNTRY)){
- country = speechItem.mParams.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_VARIANT)){
- variant = speechItem.mParams.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID)){
- utteranceId = speechItem.mParams.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_STREAM)) {
- try {
- streamType
- = Integer.parseInt(speechItem.mParams.get(i + 1));
- } catch (NumberFormatException e) {
- streamType = DEFAULT_STREAM_TYPE;
- }
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_ENGINE)) {
- engine = speechItem.mParams.get(i + 1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_PITCH)) {
- pitch = speechItem.mParams.get(i + 1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_VOLUME)) {
- try {
- volume = Float.parseFloat(speechItem.mParams.get(i + 1));
- } catch (NumberFormatException e) {
- volume = TextToSpeech.Engine.DEFAULT_VOLUME;
- }
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_PAN)) {
- try {
- pan = Float.parseFloat(speechItem.mParams.get(i + 1));
- } catch (NumberFormatException e) {
- pan = TextToSpeech.Engine.DEFAULT_PAN;
- }
- }
- }
- }
- }
- // Only do the synthesis if it has not been killed by a subsequent utterance.
- if (mKillList.get(speechItem) == null) {
- if (engine.length() > 0) {
- setEngine(engine);
- } else {
- setEngine(getDefaultEngine());
- }
- if (language.length() > 0){
- setLanguage("", language, country, variant);
- } else {
- setLanguage("", getDefaultLanguage(), getDefaultCountry(),
- getDefaultLocVariant());
- }
- if (speechRate.length() > 0){
- setSpeechRate("", Integer.parseInt(speechRate));
- } else {
- setSpeechRate("", getDefaultRate());
- }
- if (pitch.length() > 0){
- setPitch("", Integer.parseInt(pitch));
- } else {
- setPitch("", getDefaultPitch());
- }
- try {
- sNativeSynth.speak(speechItem.mText, streamType, volume, pan);
- } catch (NullPointerException e) {
- // synth will become null during onDestroy()
- Log.v(SERVICE_TAG, " null synth, can't speak");
- }
- }
- } catch (InterruptedException e) {
- Log.e(SERVICE_TAG, "TTS speakInternalOnly(): tryLock interrupted");
- e.printStackTrace();
- } finally {
- // This check is needed because finally will always run;
- // even if the
- // method returns somewhere in the try block.
- if (utteranceId.length() > 0){
- dispatchUtteranceCompletedCallback(utteranceId, speechItem.mCallingApp);
- }
- if (synthAvailable) {
- synthesizerLock.unlock();
- processSpeechQueue();
- }
- }
- }
- }
- Thread synth = (new Thread(new SynthThread()));
- synth.setPriority(Thread.MAX_PRIORITY);
- synth.start();
- }
-
- private void synthToFileInternalOnly(final SpeechItem speechItem) {
- class SynthThread implements Runnable {
- public void run() {
- boolean synthAvailable = false;
- String utteranceId = "";
- Log.i(SERVICE_TAG, "Synthesizing to " + speechItem.mFilename);
- try {
- synthAvailable = synthesizerLock.tryLock();
- if (!synthAvailable) {
- synchronized (this) {
- mSynthBusy = true;
- }
- Thread.sleep(100);
- Thread synth = (new Thread(new SynthThread()));
- synth.start();
- synchronized (this) {
- mSynthBusy = false;
- }
- return;
- }
- String language = "";
- String country = "";
- String variant = "";
- String speechRate = "";
- String engine = "";
- String pitch = "";
- if (speechItem.mParams != null){
- for (int i = 0; i < speechItem.mParams.size() - 1; i = i + 2){
- String param = speechItem.mParams.get(i);
- if (param != null) {
- if (param.equals(TextToSpeech.Engine.KEY_PARAM_RATE)) {
- speechRate = speechItem.mParams.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_LANGUAGE)){
- language = speechItem.mParams.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_COUNTRY)){
- country = speechItem.mParams.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_VARIANT)){
- variant = speechItem.mParams.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID)){
- utteranceId = speechItem.mParams.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_ENGINE)) {
- engine = speechItem.mParams.get(i + 1);
- } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_PITCH)) {
- pitch = speechItem.mParams.get(i + 1);
- }
- }
- }
- }
- // Only do the synthesis if it has not been killed by a subsequent utterance.
- if (mKillList.get(speechItem) == null){
- if (engine.length() > 0) {
- setEngine(engine);
- } else {
- setEngine(getDefaultEngine());
- }
- if (language.length() > 0){
- setLanguage("", language, country, variant);
- } else {
- setLanguage("", getDefaultLanguage(), getDefaultCountry(),
- getDefaultLocVariant());
- }
- if (speechRate.length() > 0){
- setSpeechRate("", Integer.parseInt(speechRate));
- } else {
- setSpeechRate("", getDefaultRate());
- }
- if (pitch.length() > 0){
- setPitch("", Integer.parseInt(pitch));
- } else {
- setPitch("", getDefaultPitch());
- }
- try {
- sNativeSynth.synthesizeToFile(speechItem.mText, speechItem.mFilename);
- } catch (NullPointerException e) {
- // synth will become null during onDestroy()
- Log.v(SERVICE_TAG, " null synth, can't synthesize to file");
- }
- }
- } catch (InterruptedException e) {
- Log.e(SERVICE_TAG, "TTS synthToFileInternalOnly(): tryLock interrupted");
- e.printStackTrace();
- } finally {
- // This check is needed because finally will always run;
- // even if the
- // method returns somewhere in the try block.
- if (utteranceId.length() > 0){
- dispatchUtteranceCompletedCallback(utteranceId, speechItem.mCallingApp);
- }
- if (synthAvailable) {
- synthesizerLock.unlock();
- processSpeechQueue();
- }
- }
- }
- }
- Thread synth = (new Thread(new SynthThread()));
- synth.setPriority(Thread.MAX_PRIORITY);
- synth.start();
- }
-
- private SoundResource getSoundResource(SpeechItem speechItem) {
- SoundResource sr = null;
- String text = speechItem.mText;
- if (speechItem.mType == SpeechItem.SILENCE) {
- // Do nothing if this is just silence
- } else if (speechItem.mType == SpeechItem.EARCON) {
- sr = mEarcons.get(text);
- } else {
- sr = mUtterances.get(text);
- }
- return sr;
- }
-
- private void broadcastTtsQueueProcessingCompleted(){
- Intent i = new Intent(TextToSpeech.ACTION_TTS_QUEUE_PROCESSING_COMPLETED);
- sendBroadcast(i);
- }
-
-
- private void dispatchUtteranceCompletedCallback(String utteranceId, String packageName) {
- ITtsCallback cb = mCallbacksMap.get(packageName);
- if (cb == null){
- return;
- }
- Log.v(SERVICE_TAG, "TTS callback: dispatch started");
- // Broadcast to all clients the new value.
- final int N = mCallbacks.beginBroadcast();
- try {
- cb.utteranceCompleted(utteranceId);
- } catch (RemoteException e) {
- // The RemoteCallbackList will take care of removing
- // the dead object for us.
- }
- mCallbacks.finishBroadcast();
- Log.v(SERVICE_TAG, "TTS callback: dispatch completed to " + N);
- }
-
- private SpeechItem splitCurrentTextIfNeeded(SpeechItem currentSpeechItem){
- if (currentSpeechItem.mText.length() < MAX_SPEECH_ITEM_CHAR_LENGTH){
- return currentSpeechItem;
- } else {
- String callingApp = currentSpeechItem.mCallingApp;
- ArrayList<SpeechItem> splitItems = new ArrayList<SpeechItem>();
- int start = 0;
- int end = start + MAX_SPEECH_ITEM_CHAR_LENGTH - 1;
- String splitText;
- SpeechItem splitItem;
- while (end < currentSpeechItem.mText.length()){
- splitText = currentSpeechItem.mText.substring(start, end);
- splitItem = new SpeechItem(callingApp, splitText, null, SpeechItem.TEXT);
- splitItems.add(splitItem);
- start = end;
- end = start + MAX_SPEECH_ITEM_CHAR_LENGTH - 1;
- }
- splitText = currentSpeechItem.mText.substring(start);
- splitItem = new SpeechItem(callingApp, splitText, null, SpeechItem.TEXT);
- splitItems.add(splitItem);
- mSpeechQueue.remove(0);
- for (int i = splitItems.size() - 1; i >= 0; i--){
- mSpeechQueue.add(0, splitItems.get(i));
- }
- return mSpeechQueue.get(0);
- }
- }
-
- private void processSpeechQueue() {
- boolean speechQueueAvailable = false;
- synchronized (this) {
- if (mSynthBusy){
- // There is already a synth thread waiting to run.
- return;
- }
- }
- try {
- speechQueueAvailable =
- speechQueueLock.tryLock(SPEECHQUEUELOCK_TIMEOUT, TimeUnit.MILLISECONDS);
- if (!speechQueueAvailable) {
- Log.e(SERVICE_TAG, "processSpeechQueue - Speech queue is unavailable.");
- return;
- }
- if (mSpeechQueue.size() < 1) {
- mIsSpeaking = false;
- mKillList.clear();
- broadcastTtsQueueProcessingCompleted();
- return;
- }
-
- mCurrentSpeechItem = mSpeechQueue.get(0);
- mIsSpeaking = true;
- SoundResource sr = getSoundResource(mCurrentSpeechItem);
- // Synth speech as needed - synthesizer should call
- // processSpeechQueue to continue running the queue
- // Log.v(SERVICE_TAG, "TTS processing: " + mCurrentSpeechItem.mText);
- if (sr == null) {
- if (mCurrentSpeechItem.mType == SpeechItem.TEXT) {
- mCurrentSpeechItem = splitCurrentTextIfNeeded(mCurrentSpeechItem);
- speakInternalOnly(mCurrentSpeechItem);
- } else if (mCurrentSpeechItem.mType == SpeechItem.TEXT_TO_FILE) {
- synthToFileInternalOnly(mCurrentSpeechItem);
- } else {
- // This is either silence or an earcon that was missing
- silence(mCurrentSpeechItem);
- }
- } else {
- cleanUpPlayer();
- if (sr.mSourcePackageName == PKGNAME) {
- // Utterance is part of the TTS library
- mPlayer = MediaPlayer.create(this, sr.mResId);
- } else if (sr.mSourcePackageName != null) {
- // Utterance is part of the app calling the library
- Context ctx;
- try {
- ctx = this.createPackageContext(sr.mSourcePackageName, 0);
- } catch (NameNotFoundException e) {
- e.printStackTrace();
- mSpeechQueue.remove(0); // Remove it from the queue and
- // move on
- mIsSpeaking = false;
- return;
- }
- mPlayer = MediaPlayer.create(ctx, sr.mResId);
- } else {
- // Utterance is coming from a file
- mPlayer = MediaPlayer.create(this, Uri.parse(sr.mFilename));
- }
-
- // Check if Media Server is dead; if it is, clear the queue and
- // give up for now - hopefully, it will recover itself.
- if (mPlayer == null) {
- mSpeechQueue.clear();
- mIsSpeaking = false;
- return;
- }
- mPlayer.setOnCompletionListener(this);
- try {
- mPlayer.setAudioStreamType(getStreamTypeFromParams(mCurrentSpeechItem.mParams));
- mPlayer.start();
- } catch (IllegalStateException e) {
- mSpeechQueue.clear();
- mIsSpeaking = false;
- cleanUpPlayer();
- return;
- }
- }
- if (mSpeechQueue.size() > 0) {
- mSpeechQueue.remove(0);
- }
- } catch (InterruptedException e) {
- Log.e(SERVICE_TAG, "TTS processSpeechQueue: tryLock interrupted");
- e.printStackTrace();
- } finally {
- // This check is needed because finally will always run; even if the
- // method returns somewhere in the try block.
- if (speechQueueAvailable) {
- speechQueueLock.unlock();
- }
- }
- }
-
- private int getStreamTypeFromParams(ArrayList<String> paramList) {
- int streamType = DEFAULT_STREAM_TYPE;
- if (paramList == null) {
- return streamType;
- }
- for (int i = 0; i < paramList.size() - 1; i = i + 2) {
- String param = paramList.get(i);
- if ((param != null) && (param.equals(TextToSpeech.Engine.KEY_PARAM_STREAM))) {
- try {
- streamType = Integer.parseInt(paramList.get(i + 1));
- } catch (NumberFormatException e) {
- streamType = DEFAULT_STREAM_TYPE;
- }
- }
- }
- return streamType;
- }
-
- private void cleanUpPlayer() {
- if (mPlayer != null) {
- mPlayer.release();
- mPlayer = null;
- }
- }
-
- /**
- * Synthesizes the given text to a file using the specified parameters.
- *
- * @param text
- * The String of text that should be synthesized
- * @param params
- * An ArrayList of parameters. The first element of this array
- * controls the type of voice to use.
- * @param filename
- * The string that gives the full output filename; it should be
- * something like "/sdcard/myappsounds/mysound.wav".
- * @return A boolean that indicates if the synthesis can be started
- */
- private boolean synthesizeToFile(String callingApp, String text, ArrayList<String> params,
- String filename) {
- // Don't allow a filename that is too long
- if (filename.length() > MAX_FILENAME_LENGTH) {
- return false;
- }
- // Don't allow anything longer than the max text length; since this
- // is synthing to a file, don't even bother splitting it.
- if (text.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH){
- return false;
- }
- // Check that the output file can be created
- try {
- File tempFile = new File(filename);
- if (tempFile.exists()) {
- Log.v("TtsService", "File " + filename + " exists, deleting.");
- tempFile.delete();
- }
- if (!tempFile.createNewFile()) {
- Log.e("TtsService", "Unable to synthesize to file: can't create " + filename);
- return false;
- }
- tempFile.delete();
- } catch (IOException e) {
- Log.e("TtsService", "Can't create " + filename + " due to exception " + e);
- return false;
- }
- mSpeechQueue.add(new SpeechItem(callingApp, text, params, SpeechItem.TEXT_TO_FILE, filename));
- if (!mIsSpeaking) {
- processSpeechQueue();
- }
- return true;
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- if (ACTION.equals(intent.getAction())) {
- for (String category : intent.getCategories()) {
- if (category.equals(CATEGORY)) {
- return mBinder;
- }
- }
- }
- return null;
- }
-
- private final android.speech.tts.ITts.Stub mBinder = new Stub() {
-
- public int registerCallback(String packageName, ITtsCallback cb) {
- if (cb != null) {
- mCallbacks.register(cb);
- mCallbacksMap.put(packageName, cb);
- return TextToSpeech.SUCCESS;
- }
- return TextToSpeech.ERROR;
- }
-
- public int unregisterCallback(String packageName, ITtsCallback cb) {
- if (cb != null) {
- mCallbacksMap.remove(packageName);
- mCallbacks.unregister(cb);
- return TextToSpeech.SUCCESS;
- }
- return TextToSpeech.ERROR;
- }
-
- /**
- * Speaks the given text using the specified queueing mode and
- * parameters.
- *
- * @param text
- * The text that should be spoken
- * @param queueMode
- * TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances)
- * TextToSpeech.TTS_QUEUE_ADD for queued
- * @param params
- * An ArrayList of parameters. The first element of this
- * array controls the type of voice to use.
- */
- public int speak(String callingApp, String text, int queueMode, String[] params) {
- ArrayList<String> speakingParams = new ArrayList<String>();
- if (params != null) {
- speakingParams = new ArrayList<String>(Arrays.asList(params));
- }
- return mSelf.speak(callingApp, text, queueMode, speakingParams);
- }
-
- /**
- * Plays the earcon using the specified queueing mode and parameters.
- *
- * @param earcon
- * The earcon that should be played
- * @param queueMode
- * TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances)
- * TextToSpeech.TTS_QUEUE_ADD for queued
- * @param params
- * An ArrayList of parameters.
- */
- public int playEarcon(String callingApp, String earcon, int queueMode, String[] params) {
- ArrayList<String> speakingParams = new ArrayList<String>();
- if (params != null) {
- speakingParams = new ArrayList<String>(Arrays.asList(params));
- }
- return mSelf.playEarcon(callingApp, earcon, queueMode, speakingParams);
- }
-
- /**
- * Plays the silence using the specified queueing mode and parameters.
- *
- * @param duration
- * The duration of the silence that should be played
- * @param queueMode
- * TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances)
- * TextToSpeech.TTS_QUEUE_ADD for queued
- * @param params
- * An ArrayList of parameters.
- */
- public int playSilence(String callingApp, long duration, int queueMode, String[] params) {
- ArrayList<String> speakingParams = new ArrayList<String>();
- if (params != null) {
- speakingParams = new ArrayList<String>(Arrays.asList(params));
- }
- return mSelf.playSilence(callingApp, duration, queueMode, speakingParams);
- }
-
- /**
- * Stops all speech output and removes any utterances still in the
- * queue.
- */
- public int stop(String callingApp) {
- return mSelf.stop(callingApp);
- }
-
- /**
- * Returns whether or not the TTS is speaking.
- *
- * @return Boolean to indicate whether or not the TTS is speaking
- */
- public boolean isSpeaking() {
- return (mSelf.mIsSpeaking && (mSpeechQueue.size() < 1));
- }
-
- /**
- * Adds a sound resource to the TTS.
- *
- * @param text
- * The text that should be associated with the sound resource
- * @param packageName
- * The name of the package which has the sound resource
- * @param resId
- * The resource ID of the sound within its package
- */
- public void addSpeech(String callingApp, String text, String packageName, int resId) {
- mSelf.addSpeech(callingApp, text, packageName, resId);
- }
-
- /**
- * Adds a sound resource to the TTS.
- *
- * @param text
- * The text that should be associated with the sound resource
- * @param filename
- * The filename of the sound resource. This must be a
- * complete path like: (/sdcard/mysounds/mysoundbite.mp3).
- */
- public void addSpeechFile(String callingApp, String text, String filename) {
- mSelf.addSpeech(callingApp, text, filename);
- }
-
- /**
- * Adds a sound resource to the TTS as an earcon.
- *
- * @param earcon
- * The text that should be associated with the sound resource
- * @param packageName
- * The name of the package which has the sound resource
- * @param resId
- * The resource ID of the sound within its package
- */
- public void addEarcon(String callingApp, String earcon, String packageName, int resId) {
- mSelf.addEarcon(callingApp, earcon, packageName, resId);
- }
-
- /**
- * Adds a sound resource to the TTS as an earcon.
- *
- * @param earcon
- * The text that should be associated with the sound resource
- * @param filename
- * The filename of the sound resource. This must be a
- * complete path like: (/sdcard/mysounds/mysoundbite.mp3).
- */
- public void addEarconFile(String callingApp, String earcon, String filename) {
- mSelf.addEarcon(callingApp, earcon, filename);
- }
-
- /**
- * Sets the speech rate for the TTS. Note that this will only have an
- * effect on synthesized speech; it will not affect pre-recorded speech.
- *
- * @param speechRate
- * The speech rate that should be used
- */
- public int setSpeechRate(String callingApp, int speechRate) {
- return mSelf.setSpeechRate(callingApp, speechRate);
- }
-
- /**
- * Sets the pitch for the TTS. Note that this will only have an
- * effect on synthesized speech; it will not affect pre-recorded speech.
- *
- * @param pitch
- * The pitch that should be used for the synthesized voice
- */
- public int setPitch(String callingApp, int pitch) {
- return mSelf.setPitch(callingApp, pitch);
- }
-
- /**
- * Returns the level of support for the specified language.
- *
- * @param lang the three letter ISO language code.
- * @param country the three letter ISO country code.
- * @param variant the variant code associated with the country and language pair.
- * @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE,
- * TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE as defined in
- * android.speech.tts.TextToSpeech.
- */
- public int isLanguageAvailable(String lang, String country, String variant,
- String[] params) {
- for (int i = 0; i < params.length - 1; i = i + 2){
- String param = params[i];
- if (param != null) {
- if (param.equals(TextToSpeech.Engine.KEY_PARAM_ENGINE)) {
- mSelf.setEngine(params[i + 1]);
- break;
- }
- }
- }
- return mSelf.isLanguageAvailable(lang, country, variant);
- }
-
- /**
- * Returns the currently set language / country / variant strings representing the
- * language used by the TTS engine.
- * @return null is no language is set, or an array of 3 string containing respectively
- * the language, country and variant.
- */
- public String[] getLanguage() {
- return mSelf.getLanguage();
- }
-
- /**
- * Sets the speech rate for the TTS, which affects the synthesized voice.
- *
- * @param lang the three letter ISO language code.
- * @param country the three letter ISO country code.
- * @param variant the variant code associated with the country and language pair.
- */
- public int setLanguage(String callingApp, String lang, String country, String variant) {
- return mSelf.setLanguage(callingApp, lang, country, variant);
- }
-
- /**
- * Synthesizes the given text to a file using the specified
- * parameters.
- *
- * @param text
- * The String of text that should be synthesized
- * @param params
- * An ArrayList of parameters. The first element of this
- * array controls the type of voice to use.
- * @param filename
- * The string that gives the full output filename; it should
- * be something like "/sdcard/myappsounds/mysound.wav".
- * @return A boolean that indicates if the synthesis succeeded
- */
- public boolean synthesizeToFile(String callingApp, String text, String[] params,
- String filename) {
- ArrayList<String> speakingParams = new ArrayList<String>();
- if (params != null) {
- speakingParams = new ArrayList<String>(Arrays.asList(params));
- }
- return mSelf.synthesizeToFile(callingApp, text, speakingParams, filename);
- }
-
- /**
- * Sets the speech synthesis engine for the TTS by specifying its packagename
- *
- * @param packageName the packageName of the speech synthesis engine (ie, "com.svox.pico")
- *
- * @return SUCCESS or ERROR as defined in android.speech.tts.TextToSpeech.
- */
- public int setEngineByPackageName(String packageName) {
- return mSelf.setEngine(packageName);
- }
-
- /**
- * Returns the packagename of the default speech synthesis engine.
- *
- * @return Packagename of the TTS engine that the user has chosen as their default.
- */
- public String getDefaultEngine() {
- return mSelf.getDefaultEngine();
- }
-
- /**
- * Returns whether or not the user is forcing their defaults to override the
- * Text-To-Speech settings set by applications.
- *
- * @return Whether or not defaults are enforced.
- */
- public boolean areDefaultsEnforced() {
- return mSelf.isDefaultEnforced();
- }
-
- };
-
-}