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
.project | .project | ||||
.settings | .settings | ||||
# VSCode project files: | |||||
.vscode | |||||
# intermediate build output: | # intermediate build output: | ||||
*.o | *.o |
### 1.52 (In Development) | ### 1.52 (In Development) | ||||
* Add stress marks to phoneme events (Rotem Dan) | |||||
android: | android: | ||||
* Added directBoot support -- beqabeqa473 | * Added directBoot support -- beqabeqa473 | ||||
return phon_out; | 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) | const char *GetTranslatedPhonemeString(int phoneme_mode) | ||||
{ | { | ||||
/* Called after a clause has been translated into phonemes, in order | /* Called after a clause has been translated into phonemes, in order | ||||
} | } | ||||
break; | 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)) { | if ((stressed_syllable == 0) && (vowel_count > 2)) { | ||||
for (ix = 1; ix < vowel_count; ix++) { | for (ix = 1; ix < vowel_count; ix++) { | ||||
vowel_stress[ix] = STRESS_IS_DIMINISHED; | vowel_stress[ix] = STRESS_IS_DIMINISHED; |
const char *EncodePhonemes(const char *p, char *outptr, int *bad_phoneme); | const char *EncodePhonemes(const char *p, char *outptr, int *bad_phoneme); | ||||
void DecodePhonemes(const char *inptr, char *outptr); | void DecodePhonemes(const char *inptr, char *outptr); | ||||
char *WritePhMnemonic(char *phon_out, PHONEME_TAB *ph, PHONEME_LIST *plist, int use_ipa, int *flags); | 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); | 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 GetVowelStress(Translator *tr, unsigned char *phonemes, signed char *vowel_stress, int *vowel_count, int *stressed_syllable, int control); | ||||
int IsVowel(Translator *tr, int letter); | int IsVowel(Translator *tr, int letter); |
if ((fmt_params->wav_addr != 0) && ((frame1->frflags & FRFLAG_DEFER_WAV) == 0)) { | if ((fmt_params->wav_addr != 0) && ((frame1->frflags & FRFLAG_DEFER_WAV) == 0)) { | ||||
// there is a wave file to play along with this synthesis | // there is a wave file to play along with this synthesis | ||||
seq_len_adjust = 0; | seq_len_adjust = 0; | ||||
int wavefile_amp; | int wavefile_amp; | ||||
if (fmt_params->wav_amp == 0) | if (fmt_params->wav_amp == 0) | ||||
wavefile_amp = 32; | wavefile_amp = 32; | ||||
while ((ix < (*n_ph)) && (ix < N_PHONEME_LIST-2)) { | while ((ix < (*n_ph)) && (ix < N_PHONEME_LIST-2)) { | ||||
p = &phoneme_list[ix]; | p = &phoneme_list[ix]; | ||||
if(output_hooks && output_hooks->outputPhoSymbol) | if(output_hooks && output_hooks->outputPhoSymbol) | ||||
{ | { | ||||
char buf[30]; | char buf[30]; | ||||
int dummy=0; | 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); | DoPhonemeAlignment(strdup(buf),p->type); | ||||
} | } | ||||
if ((p->type == phVOWEL) && (prev->type == phLIQUID || prev->type == phNASAL)) { | 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 | // For vowels following a liquid or nasal, do the phoneme event after the vowel-start | ||||
} else { | } 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); | DoPhonemeMarker(espeakEVENT_PHONEME, sourceix, 0, phoneme_name); | ||||
done_phoneme_marker = true; | done_phoneme_marker = true; | ||||
} | } | ||||
} | } | ||||
if ((option_phoneme_events) && (done_phoneme_marker == false)) { | 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); | DoPhonemeMarker(espeakEVENT_PHONEME, sourceix, 0, phoneme_name); | ||||
} | } | ||||
TEST_ASSERT(espeak_ng_Synchronize() == ENS_OK); | TEST_ASSERT(espeak_ng_Synchronize() == ENS_OK); | ||||
if (enabled) { | if (enabled) { | ||||
if (ipa) { | 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 { | } 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 { | } else { | ||||
TEST_ASSERT(phoneme_events[0] == 0); | TEST_ASSERT(phoneme_events[0] == 0); |