Browse Source

Add stress marks to phoneme events (#1700)

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
Rotem Dan 5 months ago
parent
commit
7ad80656e5
No account linked to committer's email address
6 changed files with 54 additions and 8 deletions
  1. 3
    0
      .gitignore
  2. 2
    0
      ChangeLog.md
  3. 35
    1
      src/libespeak-ng/dictionary.c
  4. 1
    0
      src/libespeak-ng/dictionary.h
  5. 11
    5
      src/libespeak-ng/synthesize.c
  6. 2
    2
      tests/api.c

+ 3
- 0
.gitignore View File

.project .project
.settings .settings


# VSCode project files:
.vscode

# intermediate build output: # intermediate build output:


*.o *.o

+ 2
- 0
ChangeLog.md View File



### 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



+ 35
- 1
src/libespeak-ng/dictionary.c View File

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;

+ 1
- 0
src/libespeak-ng/dictionary.h View File

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);

+ 11
- 5
src/libespeak-ng/synthesize.c View File

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);
} }



+ 2
- 2
tests/api.c View File

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);

Loading…
Cancel
Save