The implications of this are large for using eSpeak-ng as a phonemizer. It means that it's now possible to get accurate mappings between the phonemized word and the original word (up till now it was pretty much impossible). This is huge for many applications that use it as a side-tool.master
@@ -11,6 +11,9 @@ | |||
.project | |||
.settings | |||
# VSCode project files: | |||
.vscode | |||
# intermediate build output: | |||
*.o |
@@ -9,6 +9,8 @@ The espeak-ng project is a fork of the espeak project. | |||
### 1.52 (In Development) | |||
* Add stress marks to phoneme events (Rotem Dan) | |||
android: | |||
* Added directBoot support -- beqabeqa473 | |||
@@ -523,6 +523,40 @@ char *WritePhMnemonic(char *phon_out, PHONEME_TAB *ph, PHONEME_LIST *plist, int | |||
return phon_out; | |||
} | |||
//// Extension: write phone mnemonic with stress | |||
char *WritePhMnemonicWithStress(char *phon_out, PHONEME_TAB *ph, PHONEME_LIST *plist, int use_ipa, int *flags) { | |||
if (plist->synthflags & SFLAG_SYLLABLE) { | |||
unsigned char stress = plist->stresslevel; | |||
if (stress > 1) { | |||
int c = 0; | |||
if (stress > STRESS_IS_PRIORITY) { | |||
stress = STRESS_IS_PRIORITY; | |||
} | |||
if (use_ipa) { | |||
c = 0x2cc; // ipa, secondary stress | |||
if (stress > STRESS_IS_SECONDARY) { | |||
c = 0x02c8; // ipa, primary stress | |||
} | |||
} else { | |||
const char stress_chars[] = "==,,''"; | |||
c = stress_chars[stress]; | |||
} | |||
if (c != 0) { | |||
phon_out += utf8_out(c, phon_out); | |||
} | |||
} | |||
} | |||
return WritePhMnemonic(phon_out, ph, plist, use_ipa, flags); | |||
} | |||
//// | |||
const char *GetTranslatedPhonemeString(int phoneme_mode) | |||
{ | |||
/* Called after a clause has been translated into phonemes, in order | |||
@@ -1195,7 +1229,7 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags, | |||
} | |||
break; | |||
case STRESSPOSN_EU: // LANG=eu. If more than 2 syllables: primary stress in second syllable and secondary on last. | |||
case STRESSPOSN_EU: // LANG=eu. If more than 2 syllables: primary stress in second syllable and secondary on last. | |||
if ((stressed_syllable == 0) && (vowel_count > 2)) { | |||
for (ix = 1; ix < vowel_count; ix++) { | |||
vowel_stress[ix] = STRESS_IS_DIMINISHED; |
@@ -38,6 +38,7 @@ int HashDictionary(const char *string); | |||
const char *EncodePhonemes(const char *p, char *outptr, int *bad_phoneme); | |||
void DecodePhonemes(const char *inptr, char *outptr); | |||
char *WritePhMnemonic(char *phon_out, PHONEME_TAB *ph, PHONEME_LIST *plist, int use_ipa, int *flags); | |||
char *WritePhMnemonicWithStress(char *phon_out, PHONEME_TAB *ph, PHONEME_LIST *plist, int use_ipa, int *flags); | |||
const char *GetTranslatedPhonemeString(int phoneme_mode); | |||
int GetVowelStress(Translator *tr, unsigned char *phonemes, signed char *vowel_stress, int *vowel_count, int *stressed_syllable, int control); | |||
int IsVowel(Translator *tr, int letter); |
@@ -961,7 +961,7 @@ int DoSpect2(PHONEME_TAB *this_ph, int which, FMT_PARAMS *fmt_params, PHONEME_L | |||
if ((fmt_params->wav_addr != 0) && ((frame1->frflags & FRFLAG_DEFER_WAV) == 0)) { | |||
// there is a wave file to play along with this synthesis | |||
seq_len_adjust = 0; | |||
int wavefile_amp; | |||
if (fmt_params->wav_amp == 0) | |||
wavefile_amp = 32; | |||
@@ -1171,12 +1171,14 @@ int Generate(PHONEME_LIST *phoneme_list, int *n_ph, bool resume) | |||
while ((ix < (*n_ph)) && (ix < N_PHONEME_LIST-2)) { | |||
p = &phoneme_list[ix]; | |||
if(output_hooks && output_hooks->outputPhoSymbol) | |||
{ | |||
char buf[30]; | |||
int dummy=0; | |||
WritePhMnemonic(buf, p->ph, p, 0, &dummy); | |||
//WritePhMnemonic(buf, p->ph, p, 0, &dummy); | |||
WritePhMnemonicWithStress(buf, p->ph, p, 0, &dummy); | |||
DoPhonemeAlignment(strdup(buf),p->type); | |||
} | |||
@@ -1226,7 +1228,9 @@ int Generate(PHONEME_LIST *phoneme_list, int *n_ph, bool resume) | |||
if ((p->type == phVOWEL) && (prev->type == phLIQUID || prev->type == phNASAL)) { | |||
// For vowels following a liquid or nasal, do the phoneme event after the vowel-start | |||
} else { | |||
WritePhMnemonic(phoneme_name, p->ph, p, use_ipa, NULL); | |||
//WritePhMnemonic(phoneme_name, p->ph, p, use_ipa, NULL); | |||
WritePhMnemonicWithStress(phoneme_name, p->ph, p, use_ipa, NULL); | |||
DoPhonemeMarker(espeakEVENT_PHONEME, sourceix, 0, phoneme_name); | |||
done_phoneme_marker = true; | |||
} | |||
@@ -1490,7 +1494,9 @@ int Generate(PHONEME_LIST *phoneme_list, int *n_ph, bool resume) | |||
} | |||
if ((option_phoneme_events) && (done_phoneme_marker == false)) { | |||
WritePhMnemonic(phoneme_name, p->ph, p, use_ipa, NULL); | |||
//WritePhMnemonic(phoneme_name, p->ph, p, use_ipa, NULL); | |||
WritePhMnemonicWithStress(phoneme_name, p->ph, p, use_ipa, NULL); | |||
DoPhonemeMarker(espeakEVENT_PHONEME, sourceix, 0, phoneme_name); | |||
} | |||
@@ -610,9 +610,9 @@ test_espeak_ng_phoneme_events(int enabled, int ipa) { | |||
TEST_ASSERT(espeak_ng_Synchronize() == ENS_OK); | |||
if (enabled) { | |||
if (ipa) { | |||
TEST_ASSERT(strncmp(phoneme_events, "t ɛ s t ", sizeof(phoneme_events)) == 0); | |||
TEST_ASSERT(strncmp(phoneme_events, "t ˈɛ s t ", sizeof(phoneme_events)) == 0); | |||
} else { | |||
TEST_ASSERT(strncmp(phoneme_events, "t E s t _: _", sizeof(phoneme_events)) == 0); | |||
TEST_ASSERT(strncmp(phoneme_events, "t 'E s t _: _", sizeof(phoneme_events)) == 0); | |||
} | |||
} else { | |||
TEST_ASSERT(phoneme_events[0] == 0); |