From 18d807d680ad1fbefea27a8c7895efd85e79a4ee Mon Sep 17 00:00:00 2001 From: John Bowler Date: Thu, 29 Apr 2021 08:23:53 -0700 Subject: [PATCH 1/2] Prevent compile-phonemes use of phontab, en_dict espeak-ng --compile-phonemes generates phontab, but during the generation it starts by reading the old phontab. If works just fine, apart from an error message, if this doesn't exist but if it does exist it is not clear whether it might end up using old or bad data (e.g. possibly crashing if phontab is corrupted making it very non-obvious how to fix the problem...) The same applies to en_dict as a result of espeak-ng defaulting to the voice 'en'; again the phontab build works if en_dict is absent but a spurious message is output. It's not clear what would happen if someone tried to build a reduced espeak-ng configuration with just one (non 'en') language. The fix is to change espeak_ng_CompilePhonemeDataPath to call LoadVoice with a new flag to indicate the voice structure is just required to compile phoneme data (I changed the error message as well to make it clear where it comes from - there are two identical error messages, the other one is in voices.c) Within LoadVoice the flag causes LoadVoice to not set a default language or voicename and to skip loading the old phontab or dictionary (both of which fail in a git clean -dfx build). I checked the result by doing a compile from-scratch build sequence with and without the patch, i.e. I did: git clean -dfx ./autogen.sh ./configure --prefix=a-test-directory make make install Then I compared the two copies of "a-test-directory" as well as the log file output: $ diff -r logs/* diff -r logs/original/make.log logs/patched/make.log 175,176d174 < Unknown phoneme table: 'en' < Can't read dictionary file: '/home/jbowler/src/espeak-ng/master/espeak-ng-data/en_dict' $ diff --no-dereference -r local.original local.patched Binary files local.original/bin/speak-ng and local.patched/bin/speak-ng differ Binary files local.original/lib/libespeak-ng.a and local.patched/lib/libespeak-ng.a differ Binary files local.original/lib/libespeak-ng.so.1.1.51 and local.patched/lib/libespeak-ng.so.1.1.51 differ So the change has not altered phontab or any of the other generated files, only the binaries as would be expected. (Note that I am comparing truely clean builds; what you get if you download the source and build from scratch. This is the standard approach for people who build for distribution.) Signed-off-by: John Bowler --- src/libespeak-ng/compiledata.c | 4 ++-- src/libespeak-ng/voices.c | 33 +++++++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/libespeak-ng/compiledata.c b/src/libespeak-ng/compiledata.c index 54f0716d..77621474 100644 --- a/src/libespeak-ng/compiledata.c +++ b/src/libespeak-ng/compiledata.c @@ -1853,7 +1853,7 @@ static PHONEME_TAB_LIST *FindPhonemeTable(const char *string) if (strcmp(phoneme_tab_list2[ix].name, string) == 0) return &phoneme_tab_list2[ix]; } - error("Unknown phoneme table: '%s'", string); + error("compile: unknown phoneme table: '%s'", string); return NULL; } @@ -2541,7 +2541,7 @@ espeak_ng_CompilePhonemeDataPath(long rate, samplerate_native = samplerate = rate; LoadPhData(NULL, NULL); - if (LoadVoice("", 0) == NULL) + if (LoadVoice("", 8/*compiling phonemes*/) == NULL) return ENS_VOICE_NOT_FOUND; WavegenInit(rate, 0); diff --git a/src/libespeak-ng/voices.c b/src/libespeak-ng/voices.c index 36a4d13e..16bb8a5c 100644 --- a/src/libespeak-ng/voices.c +++ b/src/libespeak-ng/voices.c @@ -477,6 +477,9 @@ voice_t *LoadVoice(const char *vname, int control) // bit 1 1 = change tone only, not language // bit 2 1 = don't report error on LoadDictionary // bit 4 1 = vname = full path + // bit 8 1 = INTERNAL: compiling phonemes; do not try to + // load the phoneme table + // bit 16 1 = UNDOCUMENTED FILE *f_voice = NULL; char *p; @@ -526,7 +529,7 @@ voice_t *LoadVoice(const char *vname, int control) if (GetFileLength(buf) <= 0) return NULL; } else { - if (voicename[0] == 0) + if (voicename[0] == 0 && !(control & 8)/*compiling phonemes*/) strcpy(voicename, ESPEAKNG_DEFAULT_VOICE); sprintf(path_voices, "%s%cvoices%c", path_home, PATHSEP, PATHSEP); @@ -540,7 +543,11 @@ voice_t *LoadVoice(const char *vname, int control) f_voice = fopen(buf, "r"); - language_type = "en"; // default + if (!(control & 8)/*compiling phonemes*/) + language_type = "en"; // default + else + language_type = ""; + if (f_voice == NULL) { if (control & 3) return NULL; // can't open file @@ -867,18 +874,28 @@ voice_t *LoadVoice(const char *vname, int control) if (tone_only) new_translator = translator; else { - if ((ix = SelectPhonemeTableName(phonemes_name)) < 0) { + if (!!(control & 8/*compiling phonemes*/)) { + /* Set by espeak_ng_CompilePhonemeDataPath when it + * calls LoadVoice("", 8) to set up a dummy(?) voice. + * As phontab may not yet exist this avoids the spurious + * error message and guarantees consistent results by + * not actually reading a potentially bogus phontab... + */ + ix = 0; + } else if ((ix = SelectPhonemeTableName(phonemes_name)) < 0) { fprintf(stderr, "Unknown phoneme table: '%s'\n", phonemes_name); ix = 0; } voice->phoneme_tab_ix = ix; new_translator->phoneme_tab_ix = ix; new_translator->dict_min_size = dict_min; - LoadDictionary(new_translator, new_dictionary, control & 4); - if (dictionary_name[0] == 0) { - DeleteTranslator(new_translator); - return NULL; // no dictionary loaded - } + if (!(control & 8/*compiling phonemes*/)) { + LoadDictionary(new_translator, new_dictionary, control & 4); + if (dictionary_name[0] == 0) { + DeleteTranslator(new_translator); + return NULL; // no dictionary loaded + } + } new_translator->dict_condition = conditional_rules; From 8954354ba12f17de63f7731379f0ce464378e43a Mon Sep 17 00:00:00 2001 From: John Bowler Date: Thu, 29 Apr 2021 10:43:56 -0700 Subject: [PATCH 2/2] Avoid reuse of old dictionary when building new. And simplify the _dict pattern rule. If a build had completed in the directory before and "make distclean" had not been run ??_dict dictionaries still existed, if one of these was rebuilt espeak-ng loaded the old (out of date) one first resulting in inconsistent execution of the build the second time round. The change removes the target before building it thus ensuring that old, possibly damaged, data is not used. I also changed the extraction of the language code; the GNU make % (pattern) extension along with the long-standing $* extension (which precedes GNU make) allow the match to the '%' to be used directly in the command line. The cd .. at the end of the command is unnecessary; make (all versions) execute each command line using a single system() call, so the cd never happens inside make. Signed-off-by: John Bowler --- Makefile.am | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index 8ecda713..7230fa83 100644 --- a/Makefile.am +++ b/Makefile.am @@ -484,10 +484,16 @@ dictsource/%_emoji: dictsource/%_extra: touch $@ +# NOTE: % (pattern) rules are GNU make specific we can extract the 'stem' that +# matched the % in the target ($@) using $(*F) (which isn't GNU make specific, +# just this interpretation is.) +# +# The rm $@ stops espeak-ng trying to load the old dictionary when it is run +# (at the cost of a spurious error message.) espeak-ng-data/%_dict: src/espeak-ng phsource/phonemes.stamp @echo " DICT $@" - @cd dictsource && ESPEAK_DATA_PATH=$(CURDIR) LD_LIBRARY_PATH=../src:${LD_LIBRARY_PATH} ../src/espeak-ng \ - --compile=`echo $@ | sed -e 's,espeak-ng-data/,,g' -e 's,_dict,,g'` && cd .. + rm -f $@ + cd dictsource && ESPEAK_DATA_PATH=$(CURDIR) ../src/espeak-ng --compile="$(*F)" dictionaries: \ espeak-ng-data/af_dict \