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
@@ -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); | |||
@@ -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); | |||
@@ -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("<speak")); |