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.
master
| } | } | ||||
| JNIEXPORT jboolean | 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; | 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; | espeak_VOICE voice_select; | ||||
| memset(&voice_select, 0, sizeof(espeak_VOICE)); | memset(&voice_select, 0, sizeof(espeak_VOICE)); | ||||
| voice_select.languages = c_language; | voice_select.languages = c_language; | ||||
| voice_select.gender = (int) gender; | voice_select.gender = (int) gender; | ||||
| voice_select.age = (int) age; | |||||
| const espeak_ERROR result = espeak_SetVoiceByProperties(&voice_select); | const espeak_ERROR result = espeak_SetVoiceByProperties(&voice_select); | ||||
| public static final int GENDER_MALE = 1; | public static final int GENDER_MALE = 1; | ||||
| public static final int GENDER_FEMALE = 2; | 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 { | static { | ||||
| System.loadLibrary("ttsespeak"); | System.loadLibrary("ttsespeak"); | ||||
| return voices; | 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) { | public void setRate(int rate) { | ||||
| private native final String[] nativeGetAvailableVoices(); | 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); | private native final boolean nativeSetRate(int rate); | ||||
| mCallback.start(mEngine.getSampleRate(), mEngine.getAudioFormat(), | mCallback.start(mEngine.getSampleRate(), mEngine.getAudioFormat(), | ||||
| mEngine.getChannelCount()); | mEngine.getChannelCount()); | ||||
| mEngine.setVoice(mMatchingVoice.name, gender); | |||||
| mEngine.setVoice(mMatchingVoice, null, gender, SpeechSynthesis.AGE_ANY); | |||||
| mEngine.setRate(rate); | mEngine.setRate(rate); | ||||
| mEngine.setPitch(pitch); | mEngine.setPitch(pitch); | ||||
| mEngine.synthesize(text, text.startsWith("<speak")); | mEngine.synthesize(text, text.startsWith("<speak")); |