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

@@ -11,6 +11,9 @@
.project
.settings

# VSCode project files:
.vscode

# intermediate build output:

*.o

+ 2
- 0
ChangeLog.md View File

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


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

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

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

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

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

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


+ 2
- 2
tests/api.c View File

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

Loading…
Cancel
Save