return acc; | return acc; | ||||
} | } | ||||
unsigned int StringToWord(const char *string) | |||||
{ | |||||
// Pack 4 characters into a word | |||||
int ix; | |||||
unsigned char c; | |||||
unsigned int word; | |||||
if (string == NULL) | |||||
return 0; | |||||
word = 0; | |||||
for (ix = 0; ix < 4; ix++) { | |||||
if (string[ix] == 0) break; | |||||
c = string[ix]; | |||||
word |= (c << (ix*8)); | |||||
} | |||||
return word; | |||||
} | |||||
int towlower2(unsigned int c, Translator *translator) | int towlower2(unsigned int c, Translator *translator) | ||||
{ | { | ||||
// check for non-standard upper to lower case conversions | // check for non-standard upper to lower case conversions |
int isspace2(unsigned int c); | int isspace2(unsigned int c); | ||||
int is_str_totally_null(const char* str, int size); // Tests if all bytes of str up to size are null | int is_str_totally_null(const char* str, int size); // Tests if all bytes of str up to size are null | ||||
int Read4Bytes(FILE *f); | int Read4Bytes(FILE *f); | ||||
unsigned int StringToWord(const char *string); | |||||
int towlower2(unsigned int c, Translator *translator); // Supports Turkish I | int towlower2(unsigned int c, Translator *translator); // Supports Turkish I | ||||
ESPEAK_NG_API int utf8_in(int *c, const char *buf); | ESPEAK_NG_API int utf8_in(int *c, const char *buf); |
#include <espeak-ng/speak_lib.h> | #include <espeak-ng/speak_lib.h> | ||||
#include <espeak-ng/encoding.h> | #include <espeak-ng/encoding.h> | ||||
#include "common.h" // for GetFileLength, strncpy0, ... | |||||
#include "common.h" // for GetFileLength, strncpy0, ...c | |||||
#include "error.h" // for create_file_error_context | #include "error.h" // for create_file_error_context | ||||
#include "mnemonics.h" // for LookupMnemName, MNEM_TAB | #include "mnemonics.h" // for LookupMnemName, MNEM_TAB | ||||
#include "phoneme.h" // for PHONEME_TAB, PHONEME_TAB_LIST | #include "phoneme.h" // for PHONEME_TAB, PHONEME_TAB_LIST | ||||
error("%s.", message); | error("%s.", message); | ||||
} | } | ||||
static unsigned int StringToWord(const char *string) | |||||
{ | |||||
// Pack 4 characters into a word | |||||
int ix; | |||||
unsigned char c; | |||||
unsigned int word; | |||||
if (string == NULL) | |||||
return 0; | |||||
word = 0; | |||||
for (ix = 0; ix < 4; ix++) { | |||||
if (string[ix] == 0) break; | |||||
c = string[ix]; | |||||
word |= (c << (ix*8)); | |||||
} | |||||
return word; | |||||
} | |||||
static const MNEM_TAB reserved_phonemes[] = { | static const MNEM_TAB reserved_phonemes[] = { | ||||
{ "_\001", phonCONTROL }, // NOT USED | { "_\001", phonCONTROL }, // NOT USED | ||||
{ "%", phonSTRESS_U }, | { "%", phonSTRESS_U }, |
#include "mbrola.h" | #include "mbrola.h" | ||||
#include "error.h" // for create_file_error_context | #include "error.h" // for create_file_error_context | ||||
#include "common.h" // for StringToWord | |||||
#include "mbrola.h" // for MBROLA_TAB | #include "mbrola.h" // for MBROLA_TAB | ||||
#include "phoneme.h" // for N_PHONEME_TAB | #include "phoneme.h" // for N_PHONEME_TAB | ||||
#include "speech.h" // for path_home | #include "speech.h" // for path_home | ||||
return current == filename ? current : current + 1; | return current == filename ? current : current + 1; | ||||
} | } | ||||
static unsigned int StringToWord(const char *string) | |||||
{ | |||||
// Pack 4 characters into a word | |||||
int ix; | |||||
unsigned char c; | |||||
unsigned int word; | |||||
if (string == NULL) | |||||
return 0; | |||||
word = 0; | |||||
for (ix = 0; ix < 4; ix++) { | |||||
if (string[ix] == 0) break; | |||||
c = string[ix]; | |||||
word |= (c << (ix*8)); | |||||
} | |||||
return word; | |||||
} | |||||
#pragma GCC visibility push(default) | #pragma GCC visibility push(default) | ||||
espeak_ng_STATUS espeak_ng_CompileMbrolaVoice(const char *filepath, FILE *log, espeak_ng_ERROR_CONTEXT *context) | espeak_ng_STATUS espeak_ng_CompileMbrolaVoice(const char *filepath, FILE *log, espeak_ng_ERROR_CONTEXT *context) | ||||
{ | { |
int ix; | int ix; | ||||
int match_type; // left, right, or consume | int match_type; // left, right, or consume | ||||
int failed; | |||||
int unpron_ignore; | |||||
int consumed; // number of letters consumed from input | |||||
int syllable_count; | int syllable_count; | ||||
int vowel; | int vowel; | ||||
int letter_group; | int letter_group; | ||||
int distance_right; | |||||
int distance_left; | |||||
int lg_pts; | int lg_pts; | ||||
int n_bytes; | int n_bytes; | ||||
int add_points; | int add_points; | ||||
int command; | int command; | ||||
bool check_atstart; | |||||
unsigned int *flags; | unsigned int *flags; | ||||
MatchRecord match; | MatchRecord match; | ||||
// search through dictionary rules | // search through dictionary rules | ||||
while (rule[0] != RULE_GROUP_END) { | while (rule[0] != RULE_GROUP_END) { | ||||
unpron_ignore = word_flags & FLAG_UNPRON_TEST; | |||||
bool check_atstart = false; | |||||
int consumed = 0; // number of letters consumed from input | |||||
int distance_left = -2; | |||||
int distance_right = -6; // used to reduce points for matches further away the current letter | |||||
int failed = 0; | |||||
int unpron_ignore = word_flags & FLAG_UNPRON_TEST; | |||||
match_type = 0; | match_type = 0; | ||||
consumed = 0; | |||||
letter_w = 0; | letter_w = 0; | ||||
distance_right = -6; // used to reduce points for matches further away the current letter | |||||
distance_left = -2; | |||||
check_atstart = false; | |||||
match.points = 1; | match.points = 1; | ||||
match.end_type = 0; | match.end_type = 0; | ||||
// work through next rule until end, or until no-match proved | // work through next rule until end, or until no-match proved | ||||
rule_start = rule; | rule_start = rule; | ||||
failed = 0; | |||||
while (!failed) { | while (!failed) { | ||||
rb = *rule++; | rb = *rule++; | ||||
add_points = 0; | |||||
if (rb <= RULE_LINENUM) { | if (rb <= RULE_LINENUM) { | ||||
switch (rb) | switch (rb) | ||||
continue; | continue; | ||||
} | } | ||||
add_points = 0; | |||||
switch (match_type) | switch (match_type) | ||||
{ | { | ||||
case 0: | case 0: | ||||
unsigned char c, c2; | unsigned char c, c2; | ||||
unsigned int c12; | unsigned int c12; | ||||
int wc = 0; | int wc = 0; | ||||
int wc_bytes; | |||||
char *p2; // copy of p for use in double letter chain match | char *p2; // copy of p for use in double letter chain match | ||||
int found; | int found; | ||||
int g; // group chain number | int g; // group chain number | ||||
int g1; // first group for this letter | int g1; // first group for this letter | ||||
int n; | |||||
int letter; | int letter; | ||||
int any_alpha = 0; | int any_alpha = 0; | ||||
int ix; | int ix; | ||||
end_phonemes[0] = 0; | end_phonemes[0] = 0; | ||||
while (((c = *p) != ' ') && (c != 0)) { | while (((c = *p) != ' ') && (c != 0)) { | ||||
wc_bytes = utf8_in(&wc, p); | |||||
int wc_bytes = utf8_in(&wc, p); | |||||
if (IsAlpha(wc)) | if (IsAlpha(wc)) | ||||
any_alpha++; | any_alpha++; | ||||
n = tr->groups2_count[c]; | |||||
int n = tr->groups2_count[c]; | |||||
if (IsDigit(wc) && ((tr->langopts.tone_numbers == 0) || !any_alpha)) { | if (IsDigit(wc) && ((tr->langopts.tone_numbers == 0) || !any_alpha)) { | ||||
// lookup the number in *_list not *_rules | // lookup the number in *_list not *_rules | ||||
char string[8]; | char string[8]; | ||||
// return: number of bytes, bit 6: 1=used compression | // return: number of bytes, bit 6: 1=used compression | ||||
int c; | int c; | ||||
int c2; | |||||
int ix; | |||||
int offset; | int offset; | ||||
int min; | int min; | ||||
int max; | int max; | ||||
char *p = text; | char *p = text; | ||||
char *p2; | char *p2; | ||||
bool all_alpha = true; | bool all_alpha = true; | ||||
int bits; | |||||
int acc; | |||||
int pairs_start; | int pairs_start; | ||||
const short *pairs_list; | |||||
int bufix; | int bufix; | ||||
char buf[N_WORD_BYTES+1]; | char buf[N_WORD_BYTES+1]; | ||||
if (all_alpha) { | if (all_alpha) { | ||||
// compress to 6 bits per character | // compress to 6 bits per character | ||||
acc = 0; | |||||
bits = 0; | |||||
int ix; | |||||
int acc = 0; | |||||
int bits = 0; | |||||
p = buf; | p = buf; | ||||
p2 = buf; | p2 = buf; | ||||
while ((c = *p++) != 0) { | while ((c = *p++) != 0) { | ||||
const short *pairs_list; | |||||
if ((pairs_list = tr->frequent_pairs) != NULL) { | if ((pairs_list = tr->frequent_pairs) != NULL) { | ||||
c2 = c + (*p << 8); | |||||
int c2 = c + (*p << 8); | |||||
for (ix = 0; c2 >= pairs_list[ix]; ix++) { | for (ix = 0; c2 >= pairs_list[ix]; ix++) { | ||||
if (c2 == pairs_list[ix]) { | if (c2 == pairs_list[ix]) { | ||||
// found an encoding for a 2-character pair | // found an encoding for a 2-character pair | ||||
int flags0; | int flags0; | ||||
unsigned int flags[2]; | unsigned int flags[2]; | ||||
int say_as; | |||||
char *word1 = (char *)word; | char *word1 = (char *)word; | ||||
char text[80]; | |||||
flags[0] = 0; | flags[0] = 0; | ||||
flags[1] = FLAG_LOOKUP_SYMBOL; | flags[1] = FLAG_LOOKUP_SYMBOL; | ||||
flags0 = flags[0]; | flags0 = flags[0]; | ||||
if (flags[0] & FLAG_TEXTMODE) { | if (flags[0] & FLAG_TEXTMODE) { | ||||
say_as = option_sayas; | |||||
int say_as = option_sayas; | |||||
option_sayas = 0; // don't speak replacement word as letter names | option_sayas = 0; // don't speak replacement word as letter names | ||||
// NOTE: TranslateRoman checks text[-2] and IsLetterGroup looks | // NOTE: TranslateRoman checks text[-2] and IsLetterGroup looks | ||||
// for a heading \0, so pad the start of text to prevent | // for a heading \0, so pad the start of text to prevent | ||||
// it reading data on the stack. | // it reading data on the stack. | ||||
char text[80]; | |||||
text[0] = 0; | text[0] = 0; | ||||
text[1] = ' '; | text[1] = ' '; | ||||
text[2] = ' '; | text[2] = ' '; | ||||
char *word_end; | char *word_end; | ||||
int len_ending; | int len_ending; | ||||
int end_flags; | int end_flags; | ||||
const char *p; | |||||
int len; | |||||
char ending[50] = {0}; | char ending[50] = {0}; | ||||
// these lists are language specific, but are only relevant if the 'e' suffix flag is used | // these lists are language specific, but are only relevant if the 'e' suffix flag is used | ||||
if (IsLetter(tr, word_end[-1], LETTERGP_VOWEL2) && IsLetter(tr, word_end[0], 1)) { | if (IsLetter(tr, word_end[-1], LETTERGP_VOWEL2) && IsLetter(tr, word_end[0], 1)) { | ||||
// vowel(incl.'y') + hard.consonant | // vowel(incl.'y') + hard.consonant | ||||
const char *p; | |||||
for (i = 0; (p = add_e_exceptions[i]) != NULL; i++) { | for (i = 0; (p = add_e_exceptions[i]) != NULL; i++) { | ||||
len = strlen(p); | |||||
int len = strlen(p); | |||||
if (memcmp(p, &word_end[1-len], len) == 0) | if (memcmp(p, &word_end[1-len], len) == 0) | ||||
break; | break; | ||||
} | } | ||||
if (p == NULL) | if (p == NULL) | ||||
end_flags |= FLAG_SUFX_E_ADDED; // no exception found | end_flags |= FLAG_SUFX_E_ADDED; // no exception found | ||||
} else { | } else { | ||||
const char *p; | |||||
for (i = 0; (p = add_e_additions[i]) != NULL; i++) { | for (i = 0; (p = add_e_additions[i]) != NULL; i++) { | ||||
len = strlen(p); | |||||
int len = strlen(p); | |||||
if (memcmp(p, &word_end[1-len], len) == 0) { | if (memcmp(p, &word_end[1-len], len) == 0) { | ||||
end_flags |= FLAG_SUFX_E_ADDED; | end_flags |= FLAG_SUFX_E_ADDED; | ||||
break; | break; |
if ((cmd == WCMD_WAVE) || (cmd == WCMD_PAUSE)) | if ((cmd == WCMD_WAVE) || (cmd == WCMD_PAUSE)) | ||||
break; // next is not from spectrum, so continue until end of wave cycle | break; // next is not from spectrum, so continue until end of wave cycle | ||||
} | } | ||||
} | |||||
if (control & 1) { | |||||
for (ix = 1; ix < 6; ix++) { | for (ix = 1; ix < 6; ix++) { | ||||
if (prev_fr.ffreq[ix] != fr1->ffreq[ix]) { | if (prev_fr.ffreq[ix] != fr1->ffreq[ix]) { | ||||
// Discontinuity in formants. | // Discontinuity in formants. |
strcpy(fname_temp, tmpnam(NULL)); | strcpy(fname_temp, tmpnam(NULL)); | ||||
#endif | #endif | ||||
sprintf(command, "sox \"%s\" -r %d -c1 -b 16 -t wav %s\n", fname, samplerate, fname_temp); | |||||
if (system(command) == 0) | |||||
// sprintf(command, "sox \"%s\" -r %d -c1 -b 16 -t wav %s\n", fname, samplerate, fname_temp); | |||||
// if (system(command) == 0) | |||||
fname = fname_temp; | fname = fname_temp; | ||||
} | } | ||||
} | } |
#include "voice.h" // for FreeVoiceList, VoiceReset, current_... | #include "voice.h" // for FreeVoiceList, VoiceReset, current_... | ||||
#include "wavegen.h" // for WavegenFill, WavegenInit, WcmdqUsed | #include "wavegen.h" // for WavegenFill, WavegenInit, WcmdqUsed | ||||
static espeak_ng_STATUS StatusCreateTerminatedMsg(t_espeak_command *c1, unsigned int *unique_identifier, void *user_data); | |||||
unsigned char *outbuf = NULL; | unsigned char *outbuf = NULL; | ||||
int outbuf_size = 0; | int outbuf_size = 0; | ||||
unsigned char *out_start; | unsigned char *out_start; | ||||
*unique_identifier = c1->u.my_text.unique_identifier; | *unique_identifier = c1->u.my_text.unique_identifier; | ||||
} | } | ||||
// Create the "terminated msg" command (same uid) | |||||
t_espeak_command *c2 = create_espeak_terminated_msg(*unique_identifier, user_data); | |||||
// Try to add these 2 commands (single transaction) | |||||
if (c1 && c2) { | |||||
espeak_ng_STATUS status = fifo_add_commands(c1, c2); | |||||
if (status != ENS_OK) { | |||||
delete_espeak_command(c1); | |||||
delete_espeak_command(c2); | |||||
} | |||||
return status; | |||||
} | |||||
delete_espeak_command(c1); | |||||
delete_espeak_command(c2); | |||||
return ENOMEM; | |||||
return StatusCreateTerminatedMsg(c1, unique_identifier, user_data); | |||||
#else | #else | ||||
return sync_espeak_Synth(0, text, position, position_type, end_position, flags, user_data); | return sync_espeak_Synth(0, text, position, position_type, end_position, flags, user_data); | ||||
#endif | #endif | ||||
*unique_identifier = c1->u.my_mark.unique_identifier; | *unique_identifier = c1->u.my_mark.unique_identifier; | ||||
} | } | ||||
// Create the "terminated msg" command (same uid) | |||||
t_espeak_command *c2 = create_espeak_terminated_msg(*unique_identifier, user_data); | |||||
// Try to add these 2 commands (single transaction) | |||||
if (c1 && c2) { | |||||
espeak_ng_STATUS status = fifo_add_commands(c1, c2); | |||||
if (status != ENS_OK) { | |||||
delete_espeak_command(c1); | |||||
delete_espeak_command(c2); | |||||
} | |||||
return status; | |||||
} | |||||
delete_espeak_command(c1); | |||||
delete_espeak_command(c2); | |||||
return ENOMEM; | |||||
return StatusCreateTerminatedMsg(c1, unique_identifier, user_data); | |||||
#else | #else | ||||
return sync_espeak_Synth_Mark(0, text, index_mark, end_position, flags, user_data); | return sync_espeak_Synth_Mark(0, text, index_mark, end_position, flags, user_data); | ||||
#endif | #endif | ||||
return version_string; | return version_string; | ||||
} | } | ||||
#pragma GCC visibility pop | #pragma GCC visibility pop | ||||
static espeak_ng_STATUS StatusCreateTerminatedMsg(t_espeak_command *c1, unsigned int *unique_identifier, void *user_data) { | |||||
// Create the "terminated msg" command (same uid) | |||||
t_espeak_command *c2 = create_espeak_terminated_msg(*unique_identifier, user_data); | |||||
// Try to add these 2 commands (single transaction) | |||||
if (c1 && c2) { | |||||
espeak_ng_STATUS status = fifo_add_commands(c1, c2); | |||||
if (status != ENS_OK) { | |||||
delete_espeak_command(c1); | |||||
delete_espeak_command(c2); | |||||
} | |||||
return status; | |||||
} | |||||
delete_espeak_command(c1); | |||||
delete_espeak_command(c2); | |||||
return ENOMEM; | |||||
} |
espeak_ng_STATUS LoadPhData(int *srate, espeak_ng_ERROR_CONTEXT *context) | espeak_ng_STATUS LoadPhData(int *srate, espeak_ng_ERROR_CONTEXT *context) | ||||
{ | { | ||||
int ix; | int ix; | ||||
int n_phonemes; | |||||
int version; | int version; | ||||
int length = 0; | int length = 0; | ||||
int rate; | int rate; | ||||
p += 4; | p += 4; | ||||
for (ix = 0; ix < n_phoneme_tables; ix++) { | for (ix = 0; ix < n_phoneme_tables; ix++) { | ||||
n_phonemes = p[0]; | |||||
int n_phonemes = p[0]; | |||||
phoneme_tab_list[ix].n_phonemes = p[0]; | phoneme_tab_list[ix].n_phonemes = p[0]; | ||||
phoneme_tab_list[ix].includes = p[1]; | phoneme_tab_list[ix].includes = p[1]; | ||||
p += 4; | p += 4; | ||||
int LookupPhonemeString(const char *string) | int LookupPhonemeString(const char *string) | ||||
{ | { | ||||
int ix; | int ix; | ||||
unsigned char c; | |||||
unsigned int mnem; | unsigned int mnem; | ||||
// Pack up to 4 characters into a word | // Pack up to 4 characters into a word | ||||
mnem = 0; | mnem = 0; | ||||
for (ix = 0; ix < 4; ix++) { | for (ix = 0; ix < 4; ix++) { | ||||
if (string[ix] == 0) break; | if (string[ix] == 0) break; | ||||
c = string[ix]; | |||||
unsigned char c = string[ix]; | |||||
mnem |= (c << (ix*8)); | mnem |= (c << (ix*8)); | ||||
} | } | ||||
int seq_break; | int seq_break; | ||||
frameref_t *frames; | frameref_t *frames; | ||||
int length1; | int length1; | ||||
int length_std; | |||||
int length_factor; | |||||
SPECT_SEQ *seq, *seq2; | SPECT_SEQ *seq, *seq2; | ||||
SPECT_SEQK *seqk, *seqk2; | SPECT_SEQK *seqk, *seqk2; | ||||
frame_t *frame; | frame_t *frame; | ||||
} | } | ||||
if (length1 > 0) { | if (length1 > 0) { | ||||
int length_factor; | |||||
if (which == 2) { | if (which == 2) { | ||||
// adjust the length of the main part to match the standard length specified for the vowel | // adjust the length of the main part to match the standard length specified for the vowel | ||||
// less the front part of the vowel and any added suffix | // less the front part of the vowel and any added suffix | ||||
length_std = fmt_params->std_length + seq_len_adjust - 45; | |||||
int length_std = fmt_params->std_length + seq_len_adjust - 45; | |||||
if (length_std < 10) | if (length_std < 10) | ||||
length_std = 10; | length_std = 10; | ||||
if (plist->synthflags & SFLAG_LENGTHEN) | if (plist->synthflags & SFLAG_LENGTHEN) | ||||
{ | { | ||||
int ix; | int ix; | ||||
int includes; | int includes; | ||||
int ph_code; | |||||
PHONEME_TAB *phtab; | PHONEME_TAB *phtab; | ||||
if ((includes = phoneme_tab_list[number].includes) > 0) { | if ((includes = phoneme_tab_list[number].includes) > 0) { | ||||
// now add the phonemes from this table | // now add the phonemes from this table | ||||
phtab = phoneme_tab_list[number].phoneme_tab_ptr; | phtab = phoneme_tab_list[number].phoneme_tab_ptr; | ||||
for (ix = 0; ix < phoneme_tab_list[number].n_phonemes; ix++) { | for (ix = 0; ix < phoneme_tab_list[number].n_phonemes; ix++) { | ||||
ph_code = phtab[ix].code; | |||||
int ph_code = phtab[ix].code; | |||||
phoneme_tab[ph_code] = &phtab[ix]; | phoneme_tab[ph_code] = &phtab[ix]; | ||||
if (ph_code > n_phoneme_tab) { | if (ph_code > n_phoneme_tab) { | ||||
memset(&phoneme_tab[n_phoneme_tab+1], 0, (ph_code - (n_phoneme_tab+1)) * sizeof(*phoneme_tab)); | memset(&phoneme_tab[n_phoneme_tab+1], 0, (ph_code - (n_phoneme_tab+1)) * sizeof(*phoneme_tab)); | ||||
static bool InterpretCondition(Translator *tr, int control, PHONEME_LIST *plist, unsigned short *p_prog, WORD_PH_DATA *worddata) | static bool InterpretCondition(Translator *tr, int control, PHONEME_LIST *plist, unsigned short *p_prog, WORD_PH_DATA *worddata) | ||||
{ | { | ||||
int which; | |||||
int ix; | |||||
unsigned int data; | unsigned int data; | ||||
int instn; | int instn; | ||||
int instn2; | int instn2; | ||||
bool check_endtype = false; | |||||
PHONEME_TAB *ph; | |||||
PHONEME_LIST *plist_this; | |||||
// instruction: 2xxx, 3xxx | // instruction: 2xxx, 3xxx | ||||
instn2 = instn >> 8; | instn2 = instn >> 8; | ||||
if (instn2 < 14) { | if (instn2 < 14) { | ||||
PHONEME_LIST *plist_this; | |||||
plist_this = plist; | plist_this = plist; | ||||
which = (instn2) % 7; | |||||
int which = (instn2) % 7; | |||||
if (which == 6) { | if (which == 6) { | ||||
// the 'which' code is in the next instruction | // the 'which' code is in the next instruction | ||||
return false; | return false; | ||||
} | } | ||||
bool check_endtype = false; | |||||
switch (which) | switch (which) | ||||
{ | { | ||||
case 0: // prevPh | case 0: // prevPh | ||||
check_endtype = true; | check_endtype = true; | ||||
break; | break; | ||||
case 9: // next3PhW | case 9: // next3PhW | ||||
for (ix = 1; ix <= 3; ix++) { | |||||
for (int ix = 1; ix <= 3; ix++) { | |||||
if (plist[ix].sourceix) | if (plist[ix].sourceix) | ||||
return false; | return false; | ||||
} | } | ||||
// "change phonemes" pass | // "change phonemes" pass | ||||
plist->ph = phoneme_tab[plist->phcode]; | plist->ph = phoneme_tab[plist->phcode]; | ||||
} | } | ||||
PHONEME_TAB *ph; | |||||
ph = plist->ph; | ph = plist->ph; | ||||
if (instn2 < 7) { | if (instn2 < 7) { | ||||
static void SwitchOnVowelType(PHONEME_LIST *plist, PHONEME_DATA *phdata, unsigned short **p_prog, int instn_type) | static void SwitchOnVowelType(PHONEME_LIST *plist, PHONEME_DATA *phdata, unsigned short **p_prog, int instn_type) | ||||
{ | { | ||||
unsigned short *prog; | |||||
int voweltype; | int voweltype; | ||||
signed char x; | |||||
if (instn_type == 2) { | if (instn_type == 2) { | ||||
phdata->pd_control |= pd_FORNEXTPH; | phdata->pd_control |= pd_FORNEXTPH; | ||||
voweltype -= phonVOWELTYPES; | voweltype -= phonVOWELTYPES; | ||||
if ((voweltype >= 0) && (voweltype < 6)) { | if ((voweltype >= 0) && (voweltype < 6)) { | ||||
unsigned short *prog; | |||||
signed char x; | |||||
prog = *p_prog + voweltype*2; | prog = *p_prog + voweltype*2; | ||||
phdata->sound_addr[instn_type] = (((prog[1] & 0xf) << 16) + prog[2]) * 4; | phdata->sound_addr[instn_type] = (((prog[1] & 0xf) << 16) + prog[2]) * 4; | ||||
x = (prog[1] >> 4) & 0xff; | x = (prog[1] >> 4) & 0xff; | ||||
PHONEME_TAB *ph; | PHONEME_TAB *ph; | ||||
unsigned short *prog; | unsigned short *prog; | ||||
unsigned short instn; | |||||
int instn2; | |||||
int or_flag; | int or_flag; | ||||
bool truth; | bool truth; | ||||
bool truth2; | bool truth2; | ||||
end_flag = 0; | end_flag = 0; | ||||
for (prog = &phoneme_index[ph->program]; end_flag != 1; prog++) { | for (prog = &phoneme_index[ph->program]; end_flag != 1; prog++) { | ||||
unsigned short instn; | |||||
int instn2; | |||||
instn = *prog; | instn = *prog; | ||||
instn2 = (instn >> 8) & 0xf; | instn2 = (instn >> 8) & 0xf; | ||||
// control = 1, less shortening at fast speeds | // control = 1, less shortening at fast speeds | ||||
unsigned int len; | unsigned int len; | ||||
int srate2; | |||||
if (length == 0) | if (length == 0) | ||||
len = 0; | len = 0; | ||||
if (len < 90000) | if (len < 90000) | ||||
len = (len * samplerate) / 1000; // convert from mS to number of samples | len = (len * samplerate) / 1000; // convert from mS to number of samples | ||||
else { | else { | ||||
srate2 = samplerate / 25; // avoid overflow | |||||
int srate2 = samplerate / 25; // avoid overflow | |||||
len = (len * srate2) / 40; | len = (len * srate2) / 40; | ||||
} | } | ||||
} | } | ||||
// RMS just adjust the formant amplitudes by the appropriate ratio | // RMS just adjust the formant amplitudes by the appropriate ratio | ||||
int x; | int x; | ||||
int h; | |||||
int ix; | int ix; | ||||
static const short sqrt_tab[200] = { | static const short sqrt_tab[200] = { | ||||
x = sqrt_tab[x]; // sqrt(new_rms/fr->rms)*0x200; | x = sqrt_tab[x]; // sqrt(new_rms/fr->rms)*0x200; | ||||
for (ix = 0; ix < 8; ix++) { | for (ix = 0; ix < 8; ix++) { | ||||
int h; | |||||
h = fr->fheight[ix] * x; | h = fr->fheight[ix] * x; | ||||
fr->fheight[ix] = h/0x200; | fr->fheight[ix] = h/0x200; | ||||
} | } | ||||
static void formants_reduce_hf(frame_t *fr, int level) | static void formants_reduce_hf(frame_t *fr, int level) | ||||
{ | { | ||||
// change height of peaks 2 to 8, percentage | // change height of peaks 2 to 8, percentage | ||||
int ix; | |||||
int x; | |||||
if (voice->klattv[0]) | if (voice->klattv[0]) | ||||
return; | return; | ||||
for (ix = 2; ix < 8; ix++) { | |||||
for (int ix = 2; ix < 8; ix++) { | |||||
int x; | |||||
x = fr->fheight[ix] * level; | x = fr->fheight[ix] * level; | ||||
fr->fheight[ix] = x/100; | fr->fheight[ix] = x/100; | ||||
} | } | ||||
int FormantTransition2(frameref_t *seq, int *n_frames, unsigned int data1, unsigned int data2, PHONEME_TAB *other_ph, int which) | int FormantTransition2(frameref_t *seq, int *n_frames, unsigned int data1, unsigned int data2, PHONEME_TAB *other_ph, int which) | ||||
{ | { | ||||
int ix; | |||||
int formant; | |||||
int next_rms; | |||||
int len; | int len; | ||||
int rms; | int rms; | ||||
int f1; | int f1; | ||||
seq[0].frflags |= FRFLAG_LEN_MOD2; // reduce length modification | seq[0].frflags |= FRFLAG_LEN_MOD2; // reduce length modification | ||||
fr->frflags |= FRFLAG_LEN_MOD2; | fr->frflags |= FRFLAG_LEN_MOD2; | ||||
next_rms = seq[1].frame->rms; | |||||
int next_rms = seq[1].frame->rms; | |||||
if (voice->klattv[0]) | if (voice->klattv[0]) | ||||
fr->klattp[KLATT_AV] = seq[1].frame->klattp[KLATT_AV] - 4; | fr->klattp[KLATT_AV] = seq[1].frame->klattp[KLATT_AV] - 4; | ||||
set_frame_rms(fr, rms); | set_frame_rms(fr, rms); | ||||
if ((vcolour > 0) && (vcolour <= N_VCOLOUR)) { | if ((vcolour > 0) && (vcolour <= N_VCOLOUR)) { | ||||
for (ix = 0; ix < *n_frames; ix++) { | |||||
for (int ix = 0; ix < *n_frames; ix++) { | |||||
fr = CopyFrame(seq[ix].frame, 0); | fr = CopyFrame(seq[ix].frame, 0); | ||||
seq[ix].frame = fr; | seq[ix].frame = fr; | ||||
for (formant = 1; formant <= 5; formant++) { | |||||
for (int formant = 1; formant <= 5; formant++) { | |||||
int x; | int x; | ||||
x = fr->ffreq[formant] * vcolouring[vcolour-1][formant-1]; | x = fr->ffreq[formant] * vcolouring[vcolour-1][formant-1]; | ||||
fr->ffreq[formant] = x / 256; | fr->ffreq[formant] = x / 256; | ||||
int frameix; | int frameix; | ||||
frame_t *frame1; | frame_t *frame1; | ||||
frame_t *frame2; | frame_t *frame2; | ||||
frame_t *fr; | |||||
int ix; | |||||
intptr_t *q; | intptr_t *q; | ||||
int len; | int len; | ||||
int frame_length; | |||||
int length_factor; | |||||
int length_mod; | int length_mod; | ||||
int length_sum; | int length_sum; | ||||
int length_min; | int length_min; | ||||
if (last_frame->frflags & FRFLAG_BREAK_LF) { | if (last_frame->frflags & FRFLAG_BREAK_LF) { | ||||
// but flag indicates keep HF peaks in last segment | // but flag indicates keep HF peaks in last segment | ||||
frame_t *fr; | |||||
fr = CopyFrame(frame1, 1); | fr = CopyFrame(frame1, 1); | ||||
for (ix = 3; ix < 8; ix++) { | |||||
for (int ix = 3; ix < 8; ix++) { | |||||
if (ix < 7) | if (ix < 7) | ||||
fr->ffreq[ix] = last_frame->ffreq[ix]; | fr->ffreq[ix] = last_frame->ffreq[ix]; | ||||
fr->fheight[ix] = last_frame->fheight[ix]; | fr->fheight[ix] = last_frame->fheight[ix]; | ||||
length_sum = 0; | length_sum = 0; | ||||
for (frameix = 1; frameix < n_frames; frameix++) { | for (frameix = 1; frameix < n_frames; frameix++) { | ||||
length_factor = length_mod; | |||||
int length_factor = length_mod; | |||||
if (frames[frameix-1].frflags & FRFLAG_LEN_MOD) // reduce effect of length mod | if (frames[frameix-1].frflags & FRFLAG_LEN_MOD) // reduce effect of length mod | ||||
length_factor = (length_mod*(256-speed.lenmod_factor) + 256*speed.lenmod_factor)/256; | length_factor = (length_mod*(256-speed.lenmod_factor) + 256*speed.lenmod_factor)/256; | ||||
else if (frames[frameix-1].frflags & FRFLAG_LEN_MOD2) // reduce effect of length mod, used for the start of a vowel | else if (frames[frameix-1].frflags & FRFLAG_LEN_MOD2) // reduce effect of length mod, used for the start of a vowel | ||||
length_factor = (length_mod*(256-speed.lenmod2_factor) + 256*speed.lenmod2_factor)/256; | length_factor = (length_mod*(256-speed.lenmod2_factor) + 256*speed.lenmod2_factor)/256; | ||||
frame_length = frames[frameix-1].length; | |||||
int frame_length = frames[frameix-1].length; | |||||
len = (frame_length * samplerate)/1000; | len = (frame_length * samplerate)/1000; | ||||
len = (len * length_factor)/256; | len = (len * length_factor)/256; | ||||
length_sum += len; | length_sum += len; | ||||
{ | { | ||||
// There were embedded commands in the text at this point | // There were embedded commands in the text at this point | ||||
unsigned int word; // bit 7=last command for this word, bits 5,6 sign, bits 0-4 command | unsigned int word; // bit 7=last command for this word, bits 5,6 sign, bits 0-4 command | ||||
unsigned int value; | |||||
int command; | |||||
do { | do { | ||||
unsigned int value; | |||||
int command; | |||||
word = embedded_list[*embix]; | word = embedded_list[*embix]; | ||||
value = word >> 8; | value = word >> 8; | ||||
command = word & 0x7f; | command = word & 0x7f; | ||||
static int ix; | static int ix; | ||||
static int embedded_ix; | static int embedded_ix; | ||||
static int word_count; | static int word_count; | ||||
PHONEME_LIST *prev; | |||||
PHONEME_LIST *next; | |||||
PHONEME_LIST *next2; | |||||
PHONEME_LIST *p; | PHONEME_LIST *p; | ||||
bool released; | bool released; | ||||
int stress; | int stress; | ||||
unsigned char *amp_env; | unsigned char *amp_env; | ||||
PHONEME_TAB *ph; | PHONEME_TAB *ph; | ||||
int use_ipa = 0; | int use_ipa = 0; | ||||
bool done_phoneme_marker; | |||||
int vowelstart_prev; | int vowelstart_prev; | ||||
char phoneme_name[16]; | char phoneme_name[16]; | ||||
static int sourceix = 0; | static int sourceix = 0; | ||||
if (WcmdqFree() <= free_min) | if (WcmdqFree() <= free_min) | ||||
return 1; // wait | return 1; // wait | ||||
PHONEME_LIST *prev; | |||||
PHONEME_LIST *next; | |||||
PHONEME_LIST *next2; | |||||
prev = &phoneme_list[ix-1]; | prev = &phoneme_list[ix-1]; | ||||
next = &phoneme_list[ix+1]; | next = &phoneme_list[ix+1]; | ||||
next2 = &phoneme_list[ix+2]; | next2 = &phoneme_list[ix+2]; | ||||
if ((p->prepause > 0) && !(p->ph->phflags & phPREVOICE)) | if ((p->prepause > 0) && !(p->ph->phflags & phPREVOICE)) | ||||
DoPause(p->prepause, 1); | DoPause(p->prepause, 1); | ||||
done_phoneme_marker = false; | |||||
bool done_phoneme_marker = false; | |||||
if (option_phoneme_events && (p->ph->code != phonEND_WORD)) { | if (option_phoneme_events && (p->ph->code != phonEND_WORD)) { | ||||
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 | ||||
int clause_tone; | int clause_tone; | ||||
char *voice_change; | char *voice_change; | ||||
const char *phon_out; | |||||
if (control == 2) { | if (control == 2) { | ||||
// stop speaking | // stop speaking | ||||
CalcLengths(translator); | CalcLengths(translator); | ||||
if ((option_phonemes & 0xf) || (phoneme_callback != NULL)) { | if ((option_phonemes & 0xf) || (phoneme_callback != NULL)) { | ||||
const char *phon_out; | |||||
phon_out = GetTranslatedPhonemeString(option_phonemes); | phon_out = GetTranslatedPhonemeString(option_phonemes); | ||||
if (option_phonemes & 0xf) | if (option_phonemes & 0xf) | ||||
fprintf(f_trans, "%s\n", phon_out); | fprintf(f_trans, "%s\n", phon_out); |
nmatched++; | nmatched++; | ||||
} | } | ||||
if (*from == 0 && matched) { | |||||
if (matched) { | |||||
*ignore_next_n = nmatched; | *ignore_next_n = nmatched; | ||||
return from + 1; | return from + 1; | ||||
} | } |
nbytes += 2; // delete the final dot (eg. u.s.a.'s) | nbytes += 2; // delete the final dot (eg. u.s.a.'s) | ||||
ok = 2; | ok = 2; | ||||
} | } | ||||
} else if ((count > 0) && (word[nbytes] == ' ')) | |||||
} else if ((count > 0)) | |||||
ok = 2; | ok = 2; | ||||
} | } | ||||
#include "translate.h" // for LANGUAGE_OPTIONS, DeleteTranslator | #include "translate.h" // for LANGUAGE_OPTIONS, DeleteTranslator | ||||
#include "wavegen.h" // for InitBreath | #include "wavegen.h" // for InitBreath | ||||
static int AddToVoicesList(const char *fname, int len_path_voices, int is_language_file); | |||||
static const MNEM_TAB genders[] = { | static const MNEM_TAB genders[] = { | ||||
static void GetVoices(const char *path, int len_path_voices, int is_language_file) | static void GetVoices(const char *path, int len_path_voices, int is_language_file) | ||||
{ | { | ||||
FILE *f_voice; | |||||
espeak_VOICE *voice_data; | |||||
int ftype; | |||||
char fname[sizeof(path_home)+100]; | char fname[sizeof(path_home)+100]; | ||||
#ifdef PLATFORM_WINDOWS | #ifdef PLATFORM_WINDOWS | ||||
if (FindFileData.cFileName[0] != '.') { | if (FindFileData.cFileName[0] != '.') { | ||||
sprintf(fname, "%s%c%s", path, PATHSEP, FindFileData.cFileName); | sprintf(fname, "%s%c%s", path, PATHSEP, FindFileData.cFileName); | ||||
ftype = GetFileLength(fname); | |||||
if (ftype == -EISDIR) { | |||||
// a sub-directory | |||||
GetVoices(fname, len_path_voices, is_language_file); | |||||
} else if (ftype > 0) { | |||||
// a regular file, add it to the voices list | |||||
if ((f_voice = fopen(fname, "r")) == NULL) | |||||
continue; | |||||
// pass voice file name within the voices directory | |||||
voice_data = ReadVoiceFile(f_voice, fname+len_path_voices, is_language_file); | |||||
fclose(f_voice); | |||||
if (voice_data != NULL) | |||||
voices_list[n_voices_list++] = voice_data; | |||||
if (AddToVoicesList(fname, len_path_voices, is_language_file) != 0) { | |||||
continue; | |||||
} | } | ||||
} | } | ||||
} while (FindNextFileA(hFind, &FindFileData) != 0); | } while (FindNextFileA(hFind, &FindFileData) != 0); | ||||
if (ent->d_name[0] == '.') | if (ent->d_name[0] == '.') | ||||
continue; | continue; | ||||
sprintf(fname, "%s%c%s", path, PATHSEP, ent->d_name); | |||||
ftype = GetFileLength(fname); | |||||
if (ftype == -EISDIR) { | |||||
// a sub-directory | |||||
GetVoices(fname, len_path_voices, is_language_file); | |||||
} else if (ftype > 0) { | |||||
// a regular file, add it to the voices list | |||||
if ((f_voice = fopen(fname, "r")) == NULL) | |||||
sprintf(fname, "%s%c%s", path, PATHSEP, ent->d_name); | |||||
if (AddToVoicesList(fname, len_path_voices, is_language_file) != 0) { | |||||
continue; | continue; | ||||
} | |||||
// pass voice file name within the voices directory | |||||
voice_data = ReadVoiceFile(f_voice, fname+len_path_voices, is_language_file); | |||||
fclose(f_voice); | |||||
if (voice_data != NULL) | |||||
voices_list[n_voices_list++] = voice_data; | |||||
} | |||||
} | } | ||||
closedir(dir); | closedir(dir); | ||||
#endif | #endif | ||||
} | } | ||||
#pragma GCC visibility pop | #pragma GCC visibility pop | ||||
static int AddToVoicesList(const char *fname, int len_path_voices, int is_language_file) { | |||||
int ftype = GetFileLength(fname); | |||||
if (ftype == -EISDIR) { | |||||
// a sub-directory | |||||
GetVoices(fname, len_path_voices, is_language_file); | |||||
} else if (ftype > 0) { | |||||
// a regular file, add it to the voices list | |||||
FILE *f_voice; | |||||
if ((f_voice = fopen(fname, "r")) == NULL) | |||||
return 1; | |||||
// pass voice file name within the voices directory | |||||
espeak_VOICE *voice_data; | |||||
voice_data = ReadVoiceFile(f_voice, fname+len_path_voices, is_language_file); | |||||
fclose(f_voice); | |||||
if (voice_data != NULL) | |||||
voices_list[n_voices_list++] = voice_data; | |||||
return 0; | |||||
} | |||||
return 0; | |||||
} |
int hmax; | int hmax; | ||||
int hmax_samplerate; // highest harmonic allowed for the samplerate | int hmax_samplerate; // highest harmonic allowed for the samplerate | ||||
int x; | int x; | ||||
int ix; | |||||
int h1; | int h1; | ||||
// initialise as much of *out as we will need | // initialise as much of *out as we will need | ||||
x = htab[h] >> 15; | x = htab[h] >> 15; | ||||
htab[h] = (x * x) >> 8; | htab[h] = (x * x) >> 8; | ||||
int ix; | |||||
if ((ix = (f >> 19)) < N_TONE_ADJUST) | if ((ix = (f >> 19)) < N_TONE_ADJUST) | ||||
htab[h] = (htab[h] * wvoice->tone_adjust[ix]) >> 13; // index tone_adjust with Hz/8 | htab[h] = (htab[h] * wvoice->tone_adjust[ix]) >> 13; // index tone_adjust with Hz/8 | ||||
} | } | ||||
int value = 0; | int value = 0; | ||||
int noise; | int noise; | ||||
int ix; | int ix; | ||||
int amp; | |||||
// use two random numbers, for alternate formants | // use two random numbers, for alternate formants | ||||
noise = (rand() & 0x3fff) - 0x2000; | noise = (rand() & 0x3fff) - 0x2000; | ||||
for (ix = 1; ix < N_PEAKS; ix++) { | for (ix = 1; ix < N_PEAKS; ix++) { | ||||
int amp; | |||||
if ((amp = wvoice->breath[ix]) != 0) { | if ((amp = wvoice->breath[ix]) != 0) { | ||||
amp *= (peaks[ix].height >> 14); | amp *= (peaks[ix].height >> 14); | ||||
value += (int)resonator(&rbreath[ix], noise) * amp; | value += (int)resonator(&rbreath[ix], noise) * amp; | ||||
static int PlaySilence(int length, bool resume) | static int PlaySilence(int length, bool resume) | ||||
{ | { | ||||
static int n_samples; | static int n_samples; | ||||
int value = 0; | |||||
nsamples = 0; | nsamples = 0; | ||||
samplecount = 0; | samplecount = 0; | ||||
if (resume == false) | if (resume == false) | ||||
n_samples = length; | n_samples = length; | ||||
int value = 0; | |||||
while (n_samples-- > 0) { | while (n_samples-- > 0) { | ||||
value = (echo_buf[echo_tail++] * echo_amp) >> 8; | value = (echo_buf[echo_tail++] * echo_amp) >> 8; | ||||
void SetPitch2(voice_t *voice, int pitch1, int pitch2, int *pitch_base, int *pitch_range) | void SetPitch2(voice_t *voice, int pitch1, int pitch2, int *pitch_base, int *pitch_range) | ||||
{ | { | ||||
int x; | |||||
int base; | int base; | ||||
int range; | int range; | ||||
int pitch_value; | int pitch_value; | ||||
if (pitch1 > pitch2) { | if (pitch1 > pitch2) { | ||||
int x; | |||||
x = pitch1; // swap values | x = pitch1; // swap values | ||||
pitch1 = pitch2; | pitch1 = pitch2; | ||||
pitch2 = x; | pitch2 = x; | ||||
int length2; | int length2; | ||||
int length4; | int length4; | ||||
int qix; | int qix; | ||||
int cmd; | |||||
static const int glottal_reduce_tab1[4] = { 0x30, 0x30, 0x40, 0x50 }; // vowel before [?], amp * 1/256 | static const int glottal_reduce_tab1[4] = { 0x30, 0x30, 0x40, 0x50 }; // vowel before [?], amp * 1/256 | ||||
static const int glottal_reduce_tab2[4] = { 0x90, 0xa0, 0xb0, 0xc0 }; // vowel after [?], amp * 1/256 | static const int glottal_reduce_tab2[4] = { 0x90, 0xa0, 0xb0, 0xc0 }; // vowel after [?], amp * 1/256 | ||||
if (qix >= N_WCMDQ) qix = 0; | if (qix >= N_WCMDQ) qix = 0; | ||||
if (qix == wcmdq_tail) break; | if (qix == wcmdq_tail) break; | ||||
cmd = wcmdq[qix][0]; | |||||
int cmd = wcmdq[qix][0]; | |||||
if (cmd == WCMD_SPECT) { | if (cmd == WCMD_SPECT) { | ||||
end_wave = 0; // next wave generation is from another spectrum | end_wave = 0; // next wave generation is from another spectrum | ||||
break; | break; |