--- servers/espeak | 72 +++++-------- servers/linux-espeak/tclespeak.cpp | 200 +++++++++++++++++++------------------ 2 files changed, 129 insertions(+), 143 deletions(-) diff --git a/servers/espeak b/servers/espeak index c339fb7..53881b2 100755 --- a/servers/espeak +++ b/servers/espeak @@ -44,10 +44,7 @@ source $wd/tts-lib.tcl # Language switching # -# langsynth: available languages of the voice synthesis -# This variable is set by atcleci -# For example: langsynth(0)=3 -# 3 is the atcleci code for the finnish language +# langsynth: current and max index into table of synth languages. # langsynth(current): current synthesis language, # Gives the code of the current synth language. @@ -59,21 +56,19 @@ source $wd/tts-lib.tcl # For example, if there are three available languages: # langsynth(top)=2 -# langlabel: what will be announced -# e.g. langlabel(0)="finnish" +# voicename: name of the current voice for announcements # This variable is set by tclespeak # langcode: language identifier # e.g. langcode(0)="fi" # This variable is set by tclespeak -# langalias converts a code language ("en", "en_GB",...) to its index in the langsynth array. +# langalias converts a code language ("en", "en_GB",...) to its index in the language table. # e.g. langalias(fi)=3 could mean "fi_FI" will be used if "fi" is required. -set langsynth(0) 0 set langsynth(current) 0 set langsynth(top) 0 -set langlabel(0) "english" +set voicename "default" set langcode(0) "en-uk" set langcode(current) "en-uk" set mswindows [expr { $tcl_platform(platform) == "windows" } ] @@ -82,31 +77,29 @@ set mswindows [expr { $tcl_platform(platform) == "windows" } ] proc set_next_lang {say_it} { global langsynth global langalias - global langlabel + global voicename global langcode - set langsynthkey 0 set index 0 while { $index <= $langsynth(top) } { - if { $langsynth($index) == $langsynth(current) } { - set langsynthkey $index + if { $index == $langsynth(current) } { break } incr index } - if { $langsynthkey >= $langsynth(top) } { - set langsynthkey 0 + if { $index >= $langsynth(top) } { + set index 0 } else { - incr langsynthkey + incr index } - set langsynth(current) $langsynth($langsynthkey) - set langcode(current) $langcode($langsynthkey) + set langsynth(current) $index + set langcode(current) $langcode($index) setLanguage $langsynth(current) if { [info exists say_it]} { - tts_say "$langlabel($langsynthkey) " + tts_say "$voicename" } } @@ -114,30 +107,28 @@ proc set_next_lang {say_it} { proc set_previous_lang {say_it} { global langsynth global langalias - global langlabel + global voicename global langcode - set langsynthkey 0 set index 0 while { $index <= $langsynth(top) } { - if { $langsynth($index) == $langsynth(current) } { - set langsynthkey $index + if { $index == $langsynth(current) } { break } incr index } - if { $langsynthkey <= 0 } { - set langsynthkey $langsynth(top) + if { $index <= 0 } { + set index $langsynth(top) } else { - incr langsynthkey -1 + incr index -1 } - set langsynth(current) $langsynth($langsynthkey) - set langcode(current) $langcode($langsynthkey) + set langsynth(current) $index + set langcode(current) $langcode($index) setLanguage $langsynth(current) if { [info exists say_it]} { - tts_say "$langlabel($langsynthkey) " + tts_say "$voicename " } } @@ -147,7 +138,7 @@ proc set_lang {{name "en"} {say_it "nil"}} { global langsynth global langalias global langcode -global langlabel +global voicename if { ![info exists langalias($name)]} { return } @@ -157,21 +148,11 @@ global langlabel } set langsynth(current) $langalias($name) - set langcode(current) $langcode($langsynthkey) + set langcode(current) $langcode($langalias($name)) setLanguage $langsynth(current) - set langsynthkey 0 - set index 0 - while { $index <= $langsynth(top) } { - if { $langsynth($index) == $langsynth(current) } { - set langsynthkey $index - break - } - incr index - } - if { $say_it == "t"} { - tts_say "$langlabel($langsynthkey) " + tts_say "$voicename" } } @@ -188,8 +169,8 @@ proc set_preferred_lang {alias lang} { #debug proc list_lang {} { - global langsynth - echo [ array get langsynth ] + global langcode + echo [ array get langcode ] } proc list_langalias {} { @@ -238,7 +219,6 @@ proc tts_set_character_scale {factor} { proc tts_say {text} { global tts global langcode - global langsynth service set la $langcode(current) @@ -253,7 +233,6 @@ proc tts_say {text} { proc l {text} { global tts global langcode - global langsynth set la $langcode(current) set prefix "<voice xml:lang=\"$la\" gender=\"male\" variant=\"1\">" @@ -462,7 +441,6 @@ proc service {} { proc speech_task {} { global queue tts global langcode - global langsynth set tts(talking?) 1 set tts(not_stopped) 1 diff --git a/servers/linux-espeak/tclespeak.cpp b/servers/linux-espeak/tclespeak.cpp index e67b07f..9943633 100644 --- a/servers/linux-espeak/tclespeak.cpp +++ b/servers/linux-espeak/tclespeak.cpp @@ -48,8 +48,12 @@ #define ESPEAK_API_REVISION 1 #endif +#include <set> #include <string> +#include <vector> +using std::set; using std::string; +using std::vector; #define PACKAGENAME "tts" #define PACKAGEVERSION "1.0" @@ -76,50 +80,7 @@ int Pause(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]); int Resume(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]); static void initLanguage(Tcl_Interp *interp); -static int getLangIndex(Tcl_Interp *interp, int *theIndex); - -//> -//< preferred languages - -// Uncomment below your preferred languages - -static const char *ThePreferredLanguages[] = { - // "af", // afrikaans - // "cs", // czech-test - // "cy", // welsh-test - // "de", // german - // "el", // greek_test - // "en-r", // en-rhotic - // "en-sc", // en-scottish - "en-uk", // english - // "en-uk-north", // lancashire - // "en-uk-rp", // english_rp - // "en-uk-wmids", // english_wmids - // "eo", // esperanto - // "es", // spanish - // "fi" // finnish - "fr", // french-test - // "fr-ca", // quebec-test - // "hi", // hindi-test - // "hu", // hungarian - // "it", // italian - // "nl", // dutch-test - // "no", // norwegian-test - // "pl", // polish_test - // "pt", // brazil - // "pt-pt", // portugal - // "ro", // romanian - // "ro", // romanian-mbrola - // "ru", // russian_test - // "sk", // slovak-test - // "sv", // swedish-test - // "sw", // swahihi-test - // "vi", // vietnam-test - // "zh", // cantonese-test -}; - -#define MaxPreferredLang \ - (int)(sizeof(ThePreferredLanguages) / sizeof(ThePreferredLanguages[0])) +static int getLangIndex(Tcl_Interp *interp, unsigned long *theIndex); //> //<TclEspeakFree @@ -465,16 +426,27 @@ int getTTSVersion(ClientData handle, Tcl_Interp *interp, int objc, //> //<SetLanguage +static vector<string> available_languages; + +static void SetLanguageHelper(Tcl_Interp *interp, size_t aIndex) { + espeak_VOICE *current_voice = NULL; + espeak_VOICE a_voice; + memset(&a_voice, 0, sizeof(espeak_VOICE)); + a_voice.languages = (char *)available_languages[aIndex].c_str(); + a_voice.gender = 1; + espeak_SetVoiceByProperties(&a_voice); + current_voice = espeak_GetCurrentVoice(); + Tcl_SetVar(interp, "voicename", current_voice->name, 0); + // But what if we couldn't set the voice? Need some better error handling. + return; +} + int SetLanguage(ClientData eciHandle, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - int aIndex = 0; + unsigned long aIndex = 0; if (getLangIndex(interp, &aIndex)) { - espeak_VOICE a_voice; - memset(&a_voice, 0, sizeof(espeak_VOICE)); - a_voice.languages = (char *)(ThePreferredLanguages[aIndex]); - a_voice.gender = 1; - espeak_SetVoiceByProperties(&a_voice); + SetLanguageHelper(interp, aIndex); } return TCL_OK; } @@ -482,71 +454,107 @@ int SetLanguage(ClientData eciHandle, Tcl_Interp *interp, int objc, //> //<initLanguage, getLangIndex +static vector<string> ParseLanguages(const char *lang_str) { + vector<string> voice_langs; + const char *p = lang_str; + // The languages string is a string of (priority-byte, language-name) + // pairs. Each language name ends with a NUL byte, and the whole string + // ends with a NUL. So in BNF: + // (priority-byte text NUL-byte)* NUL-byte + // We can ignore the priority byte for now. Revisit it later? + while(*p) { + voice_langs.push_back(string(p+1)); + p += strlen(p + 1) + 2; + } + return voice_langs; +} + static void initLanguage(Tcl_Interp *interp) { // List the available languages + set<string> unique_languages; int i = 0; - int j = 0; - char *aDefaultLang = (char *)getenv("LANGUAGE"); - if (aDefaultLang == NULL) { - aDefaultLang = (char *)getenv("LANG"); - if (aDefaultLang == NULL) { - aDefaultLang = (char *)"en"; + unsigned long ui = 0; + char *envDefaultLang = (char *)getenv("LANGUAGE"); + if (envDefaultLang == NULL) { + envDefaultLang = (char *)getenv("LANG"); + if (envDefaultLang == NULL) { + envDefaultLang = (char *)"en"; } } + string aDefaultLang = envDefaultLang; + size_t remove = aDefaultLang.find('.', 0); - Tcl_SetVar2(interp, "langsynth", "current", "0", 0); + // Snip off everything following a period. So en-us.utf8 becomes en-us. + if (remove != string::npos) { + aDefaultLang.erase(aDefaultLang.begin() + remove, aDefaultLang.end()); + } + // And replace _ with -, E.G. en_US becomes en-US. + for (string::iterator it = aDefaultLang.begin(); + it != aDefaultLang.end(); it++) { + if (*it == '_') { + *it = '-'; + } + } const espeak_VOICE **voices = espeak_ListVoices(NULL); - int langInfoMax = 0; - for (i = 0; voices[i] != NULL; i++) { - char buffer_i[3]; - snprintf(buffer_i, 3, "%d", i); - Tcl_SetVar2(interp, "langalias", (char *)(voices[i]->languages), buffer_i, - 0); + for (i = 0; voices[i] != 0; i++) { + vector<string> voice_langs = ParseLanguages(voices[i]->languages); + unique_languages.insert(voice_langs.begin(), voice_langs.end()); } - - langInfoMax = i; - - int aLang; - for (aLang = 0; aLang < MaxPreferredLang; aLang++) { - char buffer_i[3]; - char buffer_j[3]; - - for (i = 0; i < langInfoMax; i++) { - if (voices[i] && voices[i]->languages && - (strcmp(1 + voices[i]->languages, ThePreferredLanguages[aLang]) == 0)) - break; + available_languages.assign(unique_languages.begin(), unique_languages.end()); + vector<string>::iterator it; + size_t lang_count = available_languages.size(); + size_t english_index = lang_count; + size_t default_index = lang_count; + char buffer[256]; + for (ui = 0; ui < lang_count; ui++) { + const char *aLangCode = available_languages[ui].c_str(); + snprintf(buffer, sizeof(buffer), "%lu", ui); + Tcl_SetVar2(interp, "langalias", aLangCode, buffer, 0); + Tcl_SetVar2(interp, "langcode", buffer, aLangCode, 0); + if (default_index == lang_count) { + if (strcasecmp(aDefaultLang.c_str(), aLangCode) == 0) { + Tcl_SetVar2(interp, "langsynth", "current", buffer, 0); + Tcl_SetVar2(interp, "langcode", "current", (char *)aLangCode, 0); + default_index = ui; + } } - - if (i == langInfoMax) { - continue; + if (strcmp(aLangCode, "en") == 0) { + english_index = ui; } - - const char *aLangCode = 1 + voices[i]->languages; - snprintf(buffer_i, 3, "%d", aLang); - snprintf(buffer_j, 3, "%d", j++); - Tcl_SetVar2(interp, (char *)"langsynth", buffer_j, buffer_i, 0); - - if (strncmp(aDefaultLang, aLangCode, 2) == 0) { - Tcl_SetVar2(interp, "langsynth", "current", buffer_i, 0); - Tcl_SetVar2(interp, "langcode", "current", (char *)aLangCode, 0); - } - - Tcl_SetVar2(interp, "langlabel", buffer_j, (char *)(voices[i]->name), 0); - Tcl_SetVar2(interp, "langcode", buffer_j, (char *)aLangCode, 0); - Tcl_SetVar2(interp, "langsynth", "top", buffer_j, 0); } + if ((default_index == lang_count) && (english_index == lang_count)) { + fprintf(stderr, "Could not find your default language, and English\n"); + fprintf(stderr, "doesn't seem to be available either. Bailing now.\n"); + exit(1); + } + fprintf(stderr, "default_index %d\n", default_index); + if(default_index == lang_count) { + default_index = english_index; + fprintf(stderr, "Couldn't find your default language, using English.\n"); + snprintf(buffer, sizeof(buffer), "%lu", english_index); + Tcl_SetVar2(interp, "langsynth", "current", buffer, 0); + Tcl_SetVar2(interp, "langcode", "current", "en", 0); + } + SetLanguageHelper(interp, default_index); + // Presumably we have at least one language, namely English, + // so no chance of underflowing size_t with this subtraction: + snprintf(buffer, sizeof(buffer), "%lu", lang_count - 1); + Tcl_SetVar2(interp, "langsynth", "top", buffer, 0); } -static int getLangIndex(Tcl_Interp *interp, int *theIndex) { +static int getLangIndex(Tcl_Interp *interp, unsigned long *theIndex) { int aStatus = 0; const char *aInfo = Tcl_GetVar2(interp, "langsynth", "current", 0); + char *end = NULL; if (aInfo) { - *theIndex = atoi(aInfo); + *theIndex = strtoul(aInfo, &end, 10); - if ((*theIndex > 0) && (*theIndex < MaxPreferredLang)) { - aStatus = 1; + if (end && !*end) { + if ((*theIndex > 0) && (*theIndex < available_languages.size())) { + aStatus = 1; + } } } return aStatus; -- 2.10.1
|All Past Years |Current Year|
If you have questions about this archive or had problems using it, please contact us.