From cc728256828583179cdfd2429970a47710bb4cde Mon Sep 17 00:00:00 2001 From: "Reece H. Dunn" Date: Thu, 2 May 2013 18:06:41 +0100 Subject: [PATCH] SpeechSynthesis.java: expose the different voice options from eSpeak This makes the following changes: 1. setVoice takes a Voice object instead of a language string. This is to make the API cleaner and allows SpeechSynthesis to pass the correct parameter (identifier for SetVoiceByName, or name for SetVoiceByProperties) to espeak. 2. The espeak_SetVoiceByName API is also exposed to support passing the voice variant to use. 3. The age parameter has been re-added with the constants: * SpeechSynthesis.AGE_ANY * SpeechSynthesis.AGE_YOUNG * SpeechSynthesis.AGE_OLD based on the behaviour of the eSpeak voice selection algorithm. NOTE: Due to the way that voice selection is implemented in eSpeak, if variant is specified, the age and gender cannot be specified and vice versa. --- android/jni/jni/eSpeakService.cpp | 28 +++++++++++++++++-- .../com/reecedunn/espeak/SpeechSynthesis.java | 18 ++++++++++-- .../src/com/reecedunn/espeak/TtsService.java | 2 +- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/android/jni/jni/eSpeakService.cpp b/android/jni/jni/eSpeakService.cpp index 673fb73d..32c9d79a 100644 --- a/android/jni/jni/eSpeakService.cpp +++ b/android/jni/jni/eSpeakService.cpp @@ -241,16 +241,38 @@ JNICALL Java_com_reecedunn_espeak_SpeechSynthesis_nativeGetAvailableVoices( } JNIEXPORT jboolean -JNICALL Java_com_reecedunn_espeak_SpeechSynthesis_nativeSetVoice( - JNIEnv *env, jobject object, jstring language, jint gender) { +JNICALL Java_com_reecedunn_espeak_SpeechSynthesis_nativeSetVoiceByName( + JNIEnv *env, jobject object, jstring name) { + const char *c_name = name ? env->GetStringUTFChars(name, NULL) : NULL; + + if (DEBUG) LOGV("%s(name=%s)", __FUNCTION__, c_name); + + const espeak_ERROR result = espeak_SetVoiceByName(c_name); + + if (c_name) env->ReleaseStringUTFChars(name, c_name); + + switch (result) { + case EE_OK: return JNI_TRUE; + case EE_INTERNAL_ERROR: LOGE("espeak_SetVoiceByName: internal error."); break; + case EE_BUFFER_FULL: LOGE("espeak_SetVoiceByName: buffer full."); break; + case EE_NOT_FOUND: LOGE("espeak_SetVoiceByName: not found."); break; + } + + return JNI_FALSE; +} + +JNIEXPORT jboolean +JNICALL Java_com_reecedunn_espeak_SpeechSynthesis_nativeSetVoiceByProperties( + JNIEnv *env, jobject object, jstring language, jint gender, jint age) { const char *c_language = language ? env->GetStringUTFChars(language, NULL) : NULL; - if (DEBUG) LOGV("%s(language=%s, gender=%d)", __FUNCTION__, c_language, gender); + if (DEBUG) LOGV("%s(language=%s, gender=%d, age=%d)", __FUNCTION__, c_language, gender, age); espeak_VOICE voice_select; memset(&voice_select, 0, sizeof(espeak_VOICE)); voice_select.languages = c_language; voice_select.gender = (int) gender; + voice_select.age = (int) age; const espeak_ERROR result = espeak_SetVoiceByProperties(&voice_select); diff --git a/android/src/com/reecedunn/espeak/SpeechSynthesis.java b/android/src/com/reecedunn/espeak/SpeechSynthesis.java index c515a577..7efc5ddf 100644 --- a/android/src/com/reecedunn/espeak/SpeechSynthesis.java +++ b/android/src/com/reecedunn/espeak/SpeechSynthesis.java @@ -45,6 +45,10 @@ public class SpeechSynthesis { public static final int GENDER_MALE = 1; public static final int GENDER_FEMALE = 2; + public static final int AGE_ANY = 0; + public static final int AGE_YOUNG = 12; + public static final int AGE_OLD = 60; + static { System.loadLibrary("ttsespeak"); @@ -169,8 +173,14 @@ public class SpeechSynthesis { return voices; } - public void setVoice(String language, int gender) { - nativeSetVoice(language, gender); + public void setVoice(Voice voice, String variant, int gender, int age) { + // NOTE: espeak_SetVoiceByProperties does not support specifying the + // voice variant (e.g. klatt), but espeak_SetVoiceByName does. + if (variant == null) { + nativeSetVoiceByProperties(voice.name, gender, age); + } else { + nativeSetVoiceByName(voice.identifier + "+" + variant); + } } public void setRate(int rate) { @@ -300,7 +310,9 @@ public class SpeechSynthesis { private native final String[] nativeGetAvailableVoices(); - private native final boolean nativeSetVoice(String language, int gender); + private native final boolean nativeSetVoiceByName(String name); + + private native final boolean nativeSetVoiceByProperties(String language, int gender, int age); private native final boolean nativeSetRate(int rate); diff --git a/android/src/com/reecedunn/espeak/TtsService.java b/android/src/com/reecedunn/espeak/TtsService.java index 2bdacc32..36e8dac2 100644 --- a/android/src/com/reecedunn/espeak/TtsService.java +++ b/android/src/com/reecedunn/espeak/TtsService.java @@ -221,7 +221,7 @@ public class TtsService extends TextToSpeechService { mCallback.start(mEngine.getSampleRate(), mEngine.getAudioFormat(), mEngine.getChannelCount()); - mEngine.setVoice(mMatchingVoice.name, gender); + mEngine.setVoice(mMatchingVoice, null, gender, SpeechSynthesis.AGE_ANY); mEngine.setRate(rate); mEngine.setPitch(pitch); mEngine.synthesize(text, text.startsWith("