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")); |