diff options
| -rwxr-xr-x | core/java/android/speech/tts/ITts.aidl | 6 | ||||
| -rw-r--r-- | include/tts/TtsEngine.h | 41 | ||||
| -rw-r--r-- | packages/TtsService/jni/android_tts_SynthProxy.cpp | 32 | ||||
| -rwxr-xr-x | packages/TtsService/src/android/tts/SynthProxy.java | 10 | ||||
| -rwxr-xr-x | packages/TtsService/src/android/tts/TtsService.java | 136 |
5 files changed, 201 insertions, 24 deletions
diff --git a/core/java/android/speech/tts/ITts.aidl b/core/java/android/speech/tts/ITts.aidl index 75c3b30d5ab4..47976e5cb39f 100755 --- a/core/java/android/speech/tts/ITts.aidl +++ b/core/java/android/speech/tts/ITts.aidl @@ -33,6 +33,8 @@ interface ITts { void speak(in String text, in int queueMode, in String[] params);
+ void speakIpa(in String ipaText, in int queueMode, in String[] params);
+
boolean isSpeaking();
void stop();
@@ -43,7 +45,9 @@ interface ITts { void setLanguage(in String language, in String country, in String variant);
- boolean synthesizeToFile(in String text, in String[] params, in String outputDirectory);
+ boolean synthesizeToFile(in String text, in String[] params, in String outputDirectory); +
+ boolean synthesizeIpaToFile(in String ipaText, in String[] params, in String outputDirectory);
void playEarcon(in String earcon, in int queueMode, in String[] params);
diff --git a/include/tts/TtsEngine.h b/include/tts/TtsEngine.h index 848653232dd6..ca50a5edd212 100644 --- a/include/tts/TtsEngine.h +++ b/include/tts/TtsEngine.h @@ -69,6 +69,14 @@ enum tts_result { 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: @@ -86,19 +94,32 @@ public: // @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 values are based on the Android - // conventions for localization as described in the Android platform - // documentation on internationalization. This implies that language - // data is specified in the format xx-rYY, where xx is a two letter - // ISO 639-1 language code in lowercase and rYY is a two letter - // ISO 3166-1-alpha-2 language code in uppercase preceded by a - // lowercase "r". - // @param value pointer to the language value - // @param size length of the language value + // 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 *value, const size_t size); + 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 diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp index c356acb50683..8537cae28c89 100644 --- a/packages/TtsService/jni/android_tts_SynthProxy.cpp +++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp @@ -297,8 +297,32 @@ android_tts_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData, variantNativeString); } env->ReleaseStringUTFChars(language, langNativeString); - env->ReleaseStringUTFChars(language, countryNativeString); - env->ReleaseStringUTFChars(language, variantNativeString); + env->ReleaseStringUTFChars(country, countryNativeString); + env->ReleaseStringUTFChars(variant, variantNativeString); +} + + +static void +android_tts_SynthProxy_loadLanguage(JNIEnv *env, jobject thiz, jint jniData, + jstring language, jstring country, jstring variant) +{ + if (jniData == 0) { + LOGE("android_tts_SynthProxy_loadLanguage(): invalid JNI data"); + return; + } + + 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); + // TODO check return codes + if (pSynthData->mNativeSynthInterface) { + pSynthData->mNativeSynthInterface->loadLanguage(langNativeString, countryNativeString, + variantNativeString); + } + env->ReleaseStringUTFChars(language, langNativeString); + env->ReleaseStringUTFChars(country, countryNativeString); + env->ReleaseStringUTFChars(variant, variantNativeString); } @@ -567,6 +591,10 @@ static JNINativeMethod gMethods[] = { "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", (void*)android_tts_SynthProxy_setLanguage }, + { "native_loadLanguage", + "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", + (void*)android_tts_SynthProxy_loadLanguage + }, { "native_setSpeechRate", "(II)V", (void*)android_tts_SynthProxy_setSpeechRate diff --git a/packages/TtsService/src/android/tts/SynthProxy.java b/packages/TtsService/src/android/tts/SynthProxy.java index 3bdff376429c..a8eaaa43f21f 100755 --- a/packages/TtsService/src/android/tts/SynthProxy.java +++ b/packages/TtsService/src/android/tts/SynthProxy.java @@ -73,6 +73,13 @@ public class SynthProxy { public void setLanguage(String language, String country, String variant) { native_setLanguage(mJniData, language, country, variant); } + + /** + * Loads the language: it's not set, but prepared for use later. + */ + public void loadLanguage(String language, String country, String variant) { + native_loadLanguage(mJniData, language, country, variant); + } /** * Sets the speech rate @@ -149,6 +156,9 @@ public class SynthProxy { private native final void native_setLanguage(int jniData, String language, String country, String variant); + + private native final void native_loadLanguage(int jniData, String language, String country, + String variant); private native final void native_setSpeechRate(int jniData, int speechRate); diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java index 3c931b819be2..421b2ca6d675 100755 --- a/packages/TtsService/src/android/tts/TtsService.java +++ b/packages/TtsService/src/android/tts/TtsService.java @@ -47,12 +47,13 @@ import java.util.concurrent.locks.ReentrantLock; public class TtsService extends Service implements OnCompletionListener { private static class SpeechItem { - public static final int SPEECH = 0; - public static final int EARCON = 1; - public static final int SILENCE = 2; + public static final int TEXT = 0; + public static final int IPA = 1; + public static final int EARCON = 2; + public static final int SILENCE = 3; public String mText = null; public ArrayList<String> mParams = null; - public int mType = SPEECH; + public int mType = TEXT; public long mDuration = 0; public SpeechItem(String text, ArrayList<String> params, int itemType) { @@ -297,7 +298,29 @@ public class TtsService extends Service implements OnCompletionListener { if (queueMode == 0) { stop(); } - mSpeechQueue.add(new SpeechItem(text, params, SpeechItem.SPEECH)); + mSpeechQueue.add(new SpeechItem(text, params, SpeechItem.TEXT)); + if (!mIsSpeaking) { + processSpeechQueue(); + } + } + + /** + * Speaks the given IPA text using the specified queueing mode and parameters. + * + * @param ipaText + * The IPA text that should be spoken + * @param queueMode + * 0 for no queue (interrupts all previous utterances), 1 for + * queued + * @param params + * An ArrayList of parameters. This is not implemented for all + * engines. + */ + private void speakIpa(String ipaText, int queueMode, ArrayList<String> params) { + if (queueMode == 0) { + stop(); + } + mSpeechQueue.add(new SpeechItem(ipaText, params, SpeechItem.IPA)); if (!mIsSpeaking) { processSpeechQueue(); } @@ -455,13 +478,13 @@ public class TtsService extends Service implements OnCompletionListener { SpeechItem splitItem; while (end < currentSpeechItem.mText.length()){ splitText = currentSpeechItem.mText.substring(start, end); - splitItem = new SpeechItem(splitText, null, SpeechItem.SPEECH); + splitItem = new SpeechItem(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(splitText, null, SpeechItem.SPEECH); + splitItem = new SpeechItem(splitText, null, SpeechItem.TEXT); splitItems.add(splitItem); mSpeechQueue.remove(0); for (int i = splitItems.size() - 1; i >= 0; i--){ @@ -491,10 +514,12 @@ public class TtsService extends Service implements OnCompletionListener { // processSpeechQueue to continue running the queue Log.i("TTS processing: ", currentSpeechItem.mText); if (sr == null) { - if (currentSpeechItem.mType == SpeechItem.SPEECH) { + if (currentSpeechItem.mType == SpeechItem.TEXT) { currentSpeechItem = splitCurrentTextIfNeeded(currentSpeechItem); speakInternalOnly(currentSpeechItem.mText, currentSpeechItem.mParams); + } else if (currentSpeechItem.mType == SpeechItem.IPA) { + // TODO Implement IPA support } else { // This is either silence or an earcon that was missing silence(currentSpeechItem.mDuration); @@ -560,8 +585,7 @@ public class TtsService extends Service implements OnCompletionListener { } /** - * Synthesizes the given text using the specified queuing mode and - * parameters. + * Synthesizes the given text to a file using the specified parameters. * * @param text * The String of text that should be synthesized @@ -606,6 +630,52 @@ public class TtsService extends Service implements OnCompletionListener { return true; } + /** + * Synthesizes the given IPA text to a file using the specified parameters. + * + * @param ipaText + * The String of IPA 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 + */ + private boolean synthesizeIpaToFile(String ipaText, ArrayList<String> params, + String filename, boolean calledFromApi) { + // Only stop everything if this is a call made by an outside app trying + // to + // use the API. Do NOT stop if this is a call from within the service as + // clearing the speech queue here would be a mistake. + if (calledFromApi) { + stop(); + } + Log.i("TTS", "Synthesizing IPA to " + filename); + boolean synthAvailable = false; + try { + synthAvailable = synthesizerLock.tryLock(); + if (!synthAvailable) { + return false; + } + // Don't allow a filename that is too long + // TODO use platform constant + if (filename.length() > 250) { + return false; + } + // TODO: Add nativeSynth.synthesizeIpaToFile(text, filename); + } finally { + // This check is needed because finally will always run; even if the + // method returns somewhere in the try block. + if (synthAvailable) { + synthesizerLock.unlock(); + } + } + Log.i("TTS", "Completed synthesis for " + filename); + return true; + } + @Override public IBinder onBind(Intent intent) { if (ACTION.equals(intent.getAction())) { @@ -652,6 +722,27 @@ public class TtsService extends Service implements OnCompletionListener { } /** + * Speaks the given IPA text using the specified queueing mode and + * parameters. + * + * @param ipaText + * The IPA text that should be spoken + * @param queueMode + * 0 for no queue (interrupts all previous utterances), 1 for + * queued + * @param params + * An ArrayList of parameters. The first element of this + * array controls the type of voice to use. + */ + public void speakIpa(String ipaText, int queueMode, String[] params) { + ArrayList<String> speakingParams = new ArrayList<String>(); + if (params != null) { + speakingParams = new ArrayList<String>(Arrays.asList(params)); + } + mSelf.speakIpa(ipaText, queueMode, speakingParams); + } + + /** * Plays the earcon using the specified queueing mode and parameters. * * @param earcon @@ -794,7 +885,7 @@ public class TtsService extends Service implements OnCompletionListener { } /** - * Speaks the given text using the specified queueing mode and + * Synthesizes the given text to a file using the specified * parameters. * * @param text @@ -815,6 +906,29 @@ public class TtsService extends Service implements OnCompletionListener { } return mSelf.synthesizeToFile(text, speakingParams, filename, true); } + + /** + * Synthesizes the given IPA text to a file using the specified + * parameters. + * + * @param ipaText + * The String of IPA 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 synthesizeIpaToFile(String ipaText, String[] params, + String filename) { + ArrayList<String> speakingParams = new ArrayList<String>(); + if (params != null) { + speakingParams = new ArrayList<String>(Arrays.asList(params)); + } + return mSelf.synthesizeIpaToFile(ipaText, speakingParams, filename, true); + } }; } |