Browse Source

Check and fix source code formatting.

master
Reece H. Dunn 9 years ago
parent
commit
3e99752d0c

+ 33
- 36
src/espeak-ng.c View File



static void Write4Bytes(FILE *f, int value) static void Write4Bytes(FILE *f, int value)
{ {
// Write 4 bytes to a file, least significant first
// Write 4 bytes to a file, least significant first
int ix; int ix;


for (ix = 0; ix < 4; ix++) { for (ix = 0; ix < 4; ix++) {


fclose(f_wavfile); fclose(f_wavfile);
f_wavfile = NULL; f_wavfile = NULL;

} }


static int SynthCallback(short *wav, int numsamples, espeak_EVENT *events) static int SynthCallback(short *wav, int numsamples, espeak_EVENT *events)
{ {
char fname[210]; char fname[210];


if (quiet) return 0; // -q quiet mode
if (quiet) return 0; // -q quiet mode


if (wav == NULL) { if (wav == NULL) {
CloseWavFile(); CloseWavFile();
sprintf(fname, "%s_%.2d%s", wavefile, wavefile_count+1, filetype); sprintf(fname, "%s_%.2d%s", wavefile, wavefile_count+1, filetype);
if (OpenWavFile(fname, samplerate) != 0) if (OpenWavFile(fname, samplerate) != 0)
return 1; return 1;
} else {
if (OpenWavFile(wavefile, samplerate) != 0)
return 1;
}
} else if (OpenWavFile(wavefile, samplerate) != 0)
return 1;
} }


if (numsamples > 0) { if (numsamples > 0) {
}; };
int optind; int optind;
static int optional_argument; static int optional_argument;
static const char *arg_opts = "abfgklpsvw"; // which options have arguments
static const char *arg_opts = "abfgklpsvw"; // which options have arguments
static char *opt_string = ""; static char *opt_string = "";
#define no_argument 0 #define no_argument 0
#define required_argument 1 #define required_argument 1
FILE *f_text = NULL; FILE *f_text = NULL;
char *p_text = NULL; char *p_text = NULL;
FILE *f_phonemes_out = stdout; FILE *f_phonemes_out = stdout;
char *data_path = NULL; // use default path for espeak-data
char *data_path = NULL; // use default path for espeak-data


int option_index = 0; int option_index = 0;
int c; int c;
espeak_VOICE voice_select; espeak_VOICE voice_select;
char filename[200]; char filename[200];
char voicename[40]; char voicename[40];
#define N_PUNCTLIST 100
#define N_PUNCTLIST 100
wchar_t option_punctlist[N_PUNCTLIST]; wchar_t option_punctlist[N_PUNCTLIST];


voicename[0] = 0; voicename[0] = 0;


if (c == '-') { if (c == '-') {
if (p[0] == 0) if (p[0] == 0)
break; // -- means don't interpret further - as commands
break; // -- means don't interpret further - as commands


opt_string = ""; opt_string = "";
for (ix = 0;; ix++) { for (ix = 0;; ix++) {
c = getopt_long(argc, argv, "a:b:f:g:hk:l:mp:qs:v:w:xXz", c = getopt_long(argc, argv, "a:b:f:g:hk:l:mp:qs:v:w:xXz",
long_options, &option_index); long_options, &option_index);


/* Detect the end of the options. */
// Detect the end of the options.
if (c == -1) if (c == -1)
break; break;
optarg2 = optarg; optarg2 = optarg;
option_waveout = 1; option_waveout = 1;
strncpy0(wavefile, optarg2, sizeof(filename)); strncpy0(wavefile, optarg2, sizeof(filename));
break; break;
case 'z': // remove pause from the end of a sentence
case 'z': // remove pause from the end of a sentence
synth_flags &= ~espeakENDPAUSE; synth_flags &= ~espeakENDPAUSE;
break; break;
case 0x100: // --stdin
case 0x100: // --stdin
flag_stdin = 1; flag_stdin = 1;
break; break;
case 0x105: // --stdout
case 0x105: // --stdout
option_waveout = 1; option_waveout = 1;
strcpy(wavefile, "stdout"); strcpy(wavefile, "stdout");
break; break;
case 0x101: // --compile-debug
case 0x102: // --compile
case 0x101: // --compile-debug
case 0x102: // --compile
strncpy0(voicename, optarg2, sizeof(voicename)); strncpy0(voicename, optarg2, sizeof(voicename));
flag_compile = c; flag_compile = c;
quiet = 1; quiet = 1;
break; break;
case 0x103: // --punct
case 0x103: // --punct
option_punctuation = 1; option_punctuation = 1;
if (optarg2 != NULL) { if (optarg2 != NULL) {
ix = 0; ix = 0;
option_punctuation = 2; option_punctuation = 2;
} }
break; break;
case 0x104: // --voices
case 0x104: // --voices
espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS, 0, data_path, 0); espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS, 0, data_path, 0);
DisplayVoices(stdout, optarg2); DisplayVoices(stdout, optarg2);
exit(0); exit(0);
case 0x106: // -- split
case 0x106: // -- split
if (optarg2 == NULL) if (optarg2 == NULL)
samples_split_seconds = 30 * 60; // default 30 minutes
samples_split_seconds = 30 * 60; // default 30 minutes
else else
samples_split_seconds = atoi(optarg2) * 60; samples_split_seconds = atoi(optarg2) * 60;
break; break;
case 0x107: // --path
case 0x107: // --path
data_path = optarg2; data_path = optarg2;
break; break;
case 0x108: // --phonout
case 0x108: // --phonout
if ((f_phonemes_out = fopen(optarg2, "w")) == NULL) if ((f_phonemes_out = fopen(optarg2, "w")) == NULL)
fprintf(stderr, "Can't write to: %s\n", optarg2); fprintf(stderr, "Can't write to: %s\n", optarg2);
break; break;
case 0x109: // --pho
case 0x109: // --pho
phoneme_options |= espeakPHONEMES_MBROLA; phoneme_options |= espeakPHONEMES_MBROLA;
break; break;
case 0x10a: // --ipa
case 0x10a: // --ipa
phoneme_options |= espeakPHONEMES_IPA; phoneme_options |= espeakPHONEMES_IPA;
if (optarg2 != NULL) { if (optarg2 != NULL) {
// deprecated and obsolete // deprecated and obsolete
phoneme_options |= espeakPHONEMES_TIE; phoneme_options |= espeakPHONEMES_TIE;
break; break;
case 3: case 3:
phonemes_separator = 0x200d; // ZWJ
phonemes_separator = 0x200d; // ZWJ
phoneme_options |= espeakPHONEMES_TIE; phoneme_options |= espeakPHONEMES_TIE;
break; break;
} }


} }
break; break;
case 0x10b: // --version
case 0x10b: // --version
PrintVersion(); PrintVersion();
exit(0); exit(0);
case 0x10c: // --sep
case 0x10c: // --sep
phoneme_options |= espeakPHONEMES_SHOW; phoneme_options |= espeakPHONEMES_SHOW;
if (optarg2 == 0) if (optarg2 == 0)
phonemes_separator = ' '; phonemes_separator = ' ';
else else
utf8_in(&phonemes_separator, optarg2); utf8_in(&phonemes_separator, optarg2);
if (phonemes_separator == 'z') if (phonemes_separator == 'z')
phonemes_separator = 0x200c; // ZWNJ
phonemes_separator = 0x200c; // ZWNJ
break; break;
case 0x10d: // --tie
case 0x10d: // --tie
phoneme_options |= (espeakPHONEMES_SHOW | espeakPHONEMES_TIE); phoneme_options |= (espeakPHONEMES_SHOW | espeakPHONEMES_TIE);
if (optarg2 == 0) if (optarg2 == 0)
phonemes_separator = 0x0361; // default: combining-double-inverted-breve
phonemes_separator = 0x0361; // default: combining-double-inverted-breve
else else
utf8_in(&phonemes_separator, optarg2); utf8_in(&phonemes_separator, optarg2);
if (phonemes_separator == 'z') if (phonemes_separator == 'z')
phonemes_separator = 0x200d; // ZWJ
phonemes_separator = 0x200d; // ZWJ
break; break;
case 0x10e: // --compile-mbrola
case 0x10e: // --compile-mbrola
samplerate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, data_path, 0); samplerate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, data_path, 0);
espeak_ng_CompileMbrolaVoice(optarg2, stdout); espeak_ng_CompileMbrolaVoice(optarg2, stdout);
exit(0); exit(0);
case 0x10f: // --compile-intonations
case 0x10f: // --compile-intonations
samplerate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, data_path, espeakINITIALIZE_PATH_ONLY); samplerate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, data_path, espeakINITIALIZE_PATH_ONLY);
espeak_ng_CompileIntonation(stdout); espeak_ng_CompileIntonation(stdout);
exit(0); exit(0);
case 0x110: // --compile-phonemes
case 0x110: // --compile-phonemes
samplerate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, data_path, espeakINITIALIZE_PATH_ONLY); samplerate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, data_path, espeakINITIALIZE_PATH_ONLY);
espeak_ng_CompilePhonemeData(22050, stdout); espeak_ng_CompilePhonemeData(22050, stdout);
exit(0); exit(0);
} }


if (f_phonemes_out != stdout) if (f_phonemes_out != stdout)
fclose(f_phonemes_out); // needed for WinCE
fclose(f_phonemes_out);
return 0; return 0;
} }

+ 293
- 290
src/libespeak-ng/compiledata.c
File diff suppressed because it is too large
View File


+ 132
- 128
src/libespeak-ng/compiledict.c View File

static char letterGroupsDefined[N_LETTER_GROUPS]; static char letterGroupsDefined[N_LETTER_GROUPS];


MNEM_TAB mnem_rules[] = { MNEM_TAB mnem_rules[] = {
{ "unpr", DOLLAR_UNPR },
{ "unpr", DOLLAR_UNPR },
{ "noprefix", DOLLAR_NOPREFIX }, // rule fails if a prefix has been removed { "noprefix", DOLLAR_NOPREFIX }, // rule fails if a prefix has been removed
{ "list", DOLLAR_LIST }, // a pronunciation is given in the *_list file
{ "list", DOLLAR_LIST }, // a pronunciation is given in the *_list file


{ "w_alt1", 0x11 }, { "w_alt1", 0x11 },
{ "w_alt2", 0x12 }, { "w_alt2", 0x12 },
{ "w_alt4", 0x14 }, { "w_alt4", 0x14 },
{ "w_alt5", 0x15 }, { "w_alt5", 0x15 },
{ "w_alt6", 0x16 }, { "w_alt6", 0x16 },
{ "w_alt", 0x11 }, // note: put longer names before their sub-strings
{ "w_alt", 0x11 }, // note: put longer names before their sub-strings


{ "p_alt1", 0x21 }, { "p_alt1", 0x21 },
{ "p_alt2", 0x22 }, { "p_alt2", 0x22 },
{ "p_alt4", 0x24 }, { "p_alt4", 0x24 },
{ "p_alt5", 0x25 }, { "p_alt5", 0x25 },
{ "p_alt6", 0x26 }, { "p_alt6", 0x26 },
{ "p_alt", 0x21 },
{ "p_alt", 0x21 },

{ NULL, -1 } { NULL, -1 }
}; };


MNEM_TAB mnem_flags[] = { MNEM_TAB mnem_flags[] = {
// these in the first group put a value in bits0-3 of dictionary_flags // these in the first group put a value in bits0-3 of dictionary_flags
{ "$1", 0x41 }, // stress on 1st syllable
{ "$2", 0x42 }, // stress on 2nd syllable
{ "$3", 0x43 },
{ "$4", 0x44 },
{ "$5", 0x45 },
{ "$6", 0x46 },
{ "$7", 0x47 },
{ "$u", 0x48 }, // reduce to unstressed
{ "$u1", 0x49 },
{ "$u2", 0x4a },
{ "$u3", 0x4b },
{ "$u+", 0x4c }, // reduce to unstressed, but stress at end of clause
{ "$1", 0x41 }, // stress on 1st syllable
{ "$2", 0x42 }, // stress on 2nd syllable
{ "$3", 0x43 },
{ "$4", 0x44 },
{ "$5", 0x45 },
{ "$6", 0x46 },
{ "$7", 0x47 },
{ "$u", 0x48 }, // reduce to unstressed
{ "$u1", 0x49 },
{ "$u2", 0x4a },
{ "$u3", 0x4b },
{ "$u+", 0x4c }, // reduce to unstressed, but stress at end of clause
{ "$u1+", 0x4d }, { "$u1+", 0x4d },
{ "$u2+", 0x4e }, { "$u2+", 0x4e },
{ "$u3+", 0x4f }, { "$u3+", 0x4f },


// these set the corresponding numbered bit if dictionary_flags // these set the corresponding numbered bit if dictionary_flags
{ "$pause", 8 }, // ensure pause before this word
{ "$strend", 9 }, // full stress if at end of clause
{ "$strend2", 10 }, // full stress if at end of clause, or only followed by unstressed
{ "$unstressend", 11 }, // reduce stress at end of clause
{ "$pause", 8 }, // ensure pause before this word
{ "$strend", 9 }, // full stress if at end of clause
{ "$strend2", 10 }, // full stress if at end of clause, or only followed by unstressed
{ "$unstressend", 11 }, // reduce stress at end of clause
{ "$accent_before", 12 }, // used with accent names, say this accent name before the letter name { "$accent_before", 12 }, // used with accent names, say this accent name before the letter name
{ "$abbrev", 13 }, // use this pronuciation rather than split into letters
// language specific
{ "$double", 14 }, // IT double the initial consonant of next word
{ "$alt", 15 }, // use alternative pronunciation
{ "$alt1", 15 }, // synonym for $alt
{ "$alt2", 16 },
{ "$alt3", 17 },
{ "$alt4", 18 },
{ "$alt5", 19 },
{ "$alt6", 20 },
{ "$alt7", 21 },
{ "$combine", 23 }, // Combine with the next word
{ "$dot", 24 }, // ignore '.' after this word (abbreviation)
{ "$hasdot", 25 }, // use this pronunciation if there is a dot after the word
{ "$max3", 27 }, // limit to 3 repetitions
{ "$brk", 28 }, // a shorter $pause
{ "$text", 29 }, // word translates to replcement text, not phonemes
// flags in dictionary word 2
{ "$verbf", 0x20 }, // verb follows
{ "$verbsf", 0x21 }, // verb follows, allow -s suffix
{ "$nounf", 0x22 }, // noun follows
{ "$pastf", 0x23 }, // past tense follows
{ "$verb", 0x24 }, // use this pronunciation when its a verb
{ "$noun", 0x25 }, // use this pronunciation when its a noun
{ "$past", 0x26 }, // use this pronunciation when its past tense
{ "$abbrev", 13 }, // use this pronuciation rather than split into letters
// language specific
{ "$double", 14 }, // IT double the initial consonant of next word
{ "$alt", 15 }, // use alternative pronunciation
{ "$alt1", 15 }, // synonym for $alt
{ "$alt2", 16 },
{ "$alt3", 17 },
{ "$alt4", 18 },
{ "$alt5", 19 },
{ "$alt6", 20 },
{ "$alt7", 21 },
{ "$combine", 23 }, // Combine with the next word
{ "$dot", 24 }, // ignore '.' after this word (abbreviation)
{ "$hasdot", 25 }, // use this pronunciation if there is a dot after the word
{ "$max3", 27 }, // limit to 3 repetitions
{ "$brk", 28 }, // a shorter $pause
{ "$text", 29 }, // word translates to replcement text, not phonemes
// flags in dictionary word 2
{ "$verbf", 0x20 }, // verb follows
{ "$verbsf", 0x21 }, // verb follows, allow -s suffix
{ "$nounf", 0x22 }, // noun follows
{ "$pastf", 0x23 }, // past tense follows
{ "$verb", 0x24 }, // use this pronunciation when its a verb
{ "$noun", 0x25 }, // use this pronunciation when its a noun
{ "$past", 0x26 }, // use this pronunciation when its past tense
{ "$verbextend", 0x28 }, // extend influence of 'verb follows' { "$verbextend", 0x28 }, // extend influence of 'verb follows'
{ "$capital", 0x29 }, // use this pronunciation if initial letter is upper case
{ "$allcaps", 0x2a }, // use this pronunciation if initial letter is upper case
{ "$accent", 0x2b }, // character name is base-character name + accent name
{ "$sentence", 0x2d }, // only if this clause is a sentence (i.e. terminator is {. ? !} not {, ; :}
{ "$only", 0x2e }, // only match on this word without suffix
{ "$onlys", 0x2f }, // only match with none, or with 's' suffix
{ "$stem", 0x30 }, // must have a suffix
{ "$atend", 0x31 }, // use this pronunciation if at end of clause
{ "$atstart", 0x32 }, // use this pronunciation at start of clause
{ "$native", 0x33 }, // not if we've switched translators
{ "$capital", 0x29 }, // use this pronunciation if initial letter is upper case
{ "$allcaps", 0x2a }, // use this pronunciation if initial letter is upper case
{ "$accent", 0x2b }, // character name is base-character name + accent name
{ "$sentence", 0x2d }, // only if this clause is a sentence (i.e. terminator is {. ? !} not {, ; :}
{ "$only", 0x2e }, // only match on this word without suffix
{ "$onlys", 0x2f }, // only match with none, or with 's' suffix
{ "$stem", 0x30 }, // must have a suffix
{ "$atend", 0x31 }, // use this pronunciation if at end of clause
{ "$atstart", 0x32 }, // use this pronunciation at start of clause
{ "$native", 0x33 }, // not if we've switched translators


// doesn't set dictionary_flags // doesn't set dictionary_flags
{ "$?", 100 }, // conditional rule, followed by byte giving the condition number
{ "$?", 100 }, // conditional rule, followed by byte giving the condition number


{ "$textmode", 200 },
{ "$textmode", 200 },
{ "$phonememode", 201 }, { "$phonememode", 201 },
{ NULL, -1 }

{ NULL, -1 }
}; };


#define LEN_GROUP_NAME 12 #define LEN_GROUP_NAME 12


int isspace2(unsigned int c) int isspace2(unsigned int c)
{ {
// can't use isspace() because on Windows, isspace(0xe1) gives TRUE !
// can't use isspace() because on Windows, isspace(0xe1) gives TRUE !
int c2; int c2;


if (((c2 = (c & 0xff)) == 0) || (c > ' ')) if (((c2 = (c & 0xff)) == 0) || (c > ' '))


static FILE *fopen_log(const char *fname, const char *access) static FILE *fopen_log(const char *fname, const char *access)
{ {
// performs fopen, but produces error message to f_log if it fails
// performs fopen, but produces error message to f_log if it fails
FILE *f; FILE *f;


if ((f = fopen(fname, access)) == NULL) { if ((f = fopen(fname, access)) == NULL) {
return f; return f;
} }


/* Lookup a mnemonic string in a table, return its name */
// Lookup a mnemonic string in a table, return its name
const char *LookupMnemName(MNEM_TAB *table, const int value) const char *LookupMnemName(MNEM_TAB *table, const int value)
{ {
while (table->mnem != NULL) { while (table->mnem != NULL) {
return table->mnem; return table->mnem;
table++; table++;
} }
return ""; /* not found */
return ""; // not found
} }


void print_dictionary_flags(unsigned int *flags, char *buf, int buf_len) void print_dictionary_flags(unsigned int *flags, char *buf, int buf_len)


char *DecodeRule(const char *group_chars, int group_length, char *rule, int control) char *DecodeRule(const char *group_chars, int group_length, char *rule, int control)
{ {
/* Convert compiled match template to ascii */
// Convert compiled match template to ascii


unsigned char rb; unsigned char rb;
unsigned char c; unsigned char c;
char suffix[20]; char suffix[20];
static char output[80]; static char output[80];


static char symbols[] =
{ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
'&', '%', '+', '#', 'S', 'D', 'Z', 'A', 'L', '!', ' ', '@', '?', 'J', 'N', 'K', 'V', '?', 'T', 'X', '?', 'W' };
static char symbols[] = {
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
'&', '%', '+', '#', 'S', 'D', 'Z', 'A', 'L', '!',
' ', '@', '?', 'J', 'N', 'K', 'V', '?', 'T', 'X',
'?', 'W'
};


static char symbols_lg[] = { 'A', 'B', 'C', 'H', 'F', 'G', 'Y' }; static char symbols_lg[] = { 'A', 'B', 'C', 'H', 'F', 'G', 'Y' };


finished = 1; finished = 1;
break; break;
case RULE_PRE_ATSTART: case RULE_PRE_ATSTART:
at_start = 1; // drop through to next case
at_start = 1;
// fallthrough:
case RULE_PRE: case RULE_PRE:
match_type = RULE_PRE; match_type = RULE_PRE;
*p = 0; *p = 0;
case RULE_PH_COMMON: case RULE_PH_COMMON:
break; break;
case RULE_CONDITION: case RULE_CONDITION:
/* conditional rule, next byte gives condition number */
// conditional rule, next byte gives condition number
condition_num = *rule++; condition_num = *rule++;
break; break;
case RULE_LINENUM: case RULE_LINENUM:
} }
*p = 0; *p = 0;


buf[p_end - p] = 0; // prevent overflow in output[]
buf[p_end - p] = 0; // prevent overflow in output[]
strcat(p, buf); strcat(p, buf);
ix = strlen(output); ix = strlen(output);
while (ix < 8) while (ix < 8)


static int compile_line(char *linebuf, char *dict_line, int *hash) static int compile_line(char *linebuf, char *dict_line, int *hash)
{ {
// Compile a line in the language_list file
// Compile a line in the language_list file
unsigned char c; unsigned char c;
char *p; char *p;
char *word; char *word;


int len_word; int len_word;
int len_phonetic; int len_phonetic;
int text_not_phonemes; // this word specifies replacement text, not phonemes
int text_not_phonemes; // this word specifies replacement text, not phonemes
unsigned int wc; unsigned int wc;
int all_upper_case; int all_upper_case;


} }


if ((c == '$') && isalnum(p[1])) { if ((c == '$') && isalnum(p[1])) {
/* read keyword parameter */
// read keyword parameter
mnemptr = p; mnemptr = p;
while (!isspace2(c = *p)) p++; while (!isspace2(c = *p)) p++;
*p = 0; *p = 0;
} }
} }


if ((c == '/') && (p[1] == '/') && (multiple_words == 0)) {
c = '\n'; /* "//" treat comment as end of line */
}
if ((c == '/') && (p[1] == '/') && (multiple_words == 0))
c = '\n'; // "//" treat comment as end of line


switch (step) switch (step)
{ {
c = ' '; c = ' ';
} }
if (isspace2(c)) { if (isspace2(c)) {
p[0] = 0; /* terminate english word */
p[0] = 0; // terminate english word


if (multiple_words) { if (multiple_words) {
multiple_string = multiple_string_end = p+1; multiple_string = multiple_string_end = p+1;
if (isspace2(c)) if (isspace2(c))
multiple_words++; multiple_words++;
else if (c == ')') { else if (c == ')') {
p[0] = ' '; // terminate extra string
p[0] = ' '; // terminate extra string
multiple_string_end = p+1; multiple_string_end = p+1;
step = 3; step = 3;
} }
break; break;
case 4: case 4:
if (isspace2(c)) { if (isspace2(c)) {
p[0] = 0; /* terminate phonetic */
p[0] = 0; // terminate phonetic
step = 5; step = 5;
} }
break; break;
} }


if (word[0] == 0) if (word[0] == 0)
return 0; /* blank line */
return 0; // blank line


if (text_mode) if (text_mode)
text_not_phonemes = 1; text_not_phonemes = 1;
if (text_not_phonemes) { if (text_not_phonemes) {
if (word[0] == '_') { if (word[0] == '_') {
// This is a special word, used by eSpeak. Translate this into phonemes now // This is a special word, used by eSpeak. Translate this into phonemes now
strcat(phonetic, " "); // need a space to indicate word-boundary
strcat(phonetic, " "); // need a space to indicate word-boundary


// PROBLEM vowel reductions are not applied to the translated phonemes // PROBLEM vowel reductions are not applied to the translated phonemes
// condition rules are not applied // condition rules are not applied
if (text_not_phonemes != translator->langopts.textmode) if (text_not_phonemes != translator->langopts.textmode)
flag_codes[n_flag_codes++] = BITNUM_FLAG_TEXTMODE; flag_codes[n_flag_codes++] = BITNUM_FLAG_TEXTMODE;



if (sscanf(word, "U+%x", &wc) == 1) { if (sscanf(word, "U+%x", &wc) == 1) {
// Character code // Character code
ix = utf8_out(wc, word); ix = utf8_out(wc, word);
*hash = HashDictionary(word); *hash = HashDictionary(word);
len_phonetic = strlen(encoded_ph); len_phonetic = strlen(encoded_ph);


dict_line[1] = len_word; // bit 6 indicates whether the word has been compressed
dict_line[1] = len_word; // bit 6 indicates whether the word has been compressed
len_word &= 0x3f; len_word &= 0x3f;


memcpy(&dict_line[2], word, len_word); memcpy(&dict_line[2], word, len_word);


static void compile_dictlist_start(void) static void compile_dictlist_start(void)
{ {
// initialise dictionary list
// initialise dictionary list
int ix; int ix;
char *p; char *p;
char *p2; char *p2;


static void compile_dictlist_end(FILE *f_out) static void compile_dictlist_end(FILE *f_out)
{ {
// Write out the compiled dictionary list
// Write out the compiled dictionary list
int hash; int hash;
int length; int length;
char *p; char *p;
linenum++; linenum++;


length = compile_line(buf, dict_line, &hash); length = compile_line(buf, dict_line, &hash);
if (length == 0) continue; /* blank line */
if (length == 0) continue; // blank line


hash_counts[hash]++; hash_counts[hash]++;


static char group_name[LEN_GROUP_NAME+1]; static char group_name[LEN_GROUP_NAME+1];
static int group3_ix; static int group3_ix;


#define N_RULES 3000 // max rules for each group
#define N_RULES 3000 // max rules for each group


int isHexDigit(int c) int isHexDigit(int c)
{ {


static void copy_rule_string(char *string, int *state_out) static void copy_rule_string(char *string, int *state_out)
{ {
// state 0: conditional, 1=pre, 2=match, 3=post, 4=phonemes
// state 0: conditional, 1=pre, 2=match, 3=post, 4=phonemes
static char *outbuf[5] = { rule_cond, rule_pre, rule_match, rule_post, rule_phonemes }; static char *outbuf[5] = { rule_cond, rule_pre, rule_match, rule_post, rule_phonemes };
static int next_state[5] = { 2, 2, 4, 4, 4 }; static int next_state[5] = { 2, 2, 4, 4, 4 };
char *output; char *output;
rule_phonemes[len++] = ' '; rule_phonemes[len++] = ' ';
output = &rule_phonemes[len]; output = &rule_phonemes[len];
} }
sxflags = 0x808000; // to ensure non-zero bytes
sxflags = 0x808000; // to ensure non-zero bytes


for (p = string, ix = 0;;) { for (p = string, ix = 0;;) {
literal = 0; literal = 0;
p += 2; p += 2;
} }
if (c == '\\') { if (c == '\\') {
c = *p++; // treat next character literally
c = *p++; // treat next character literally
if ((c >= '0') && (c <= '3') && (p[0] >= '0') && (p[0] <= '7') && (p[1] >= '0') && (p[1] <= '7')) { if ((c >= '0') && (c <= '3') && (p[0] >= '0') && (p[0] <= '7') && (p[1] >= '0') && (p[1] <= '7')) {
// character code given by 3 digit octal value; // character code given by 3 digit octal value;
c = (c-'0')*64 + (p[0]-'0')*8 + (p[1]-'0'); c = (c-'0')*64 + (p[0]-'0')*8 + (p[1]-'0');
break; break;


case 'Y': case 'Y':
c = 'I'; // drop through to next case
case 'A': // vowel
c = 'I';
// fallthrough:
case 'A': // vowel
case 'B': case 'B':
case 'C': case 'C':
case 'H': case 'H':
error_count++; error_count++;
} }
break; break;
case 'P':
sxflags |= SUFX_P; // Prefix, now drop through to Suffix
case 'P': // Prefix
sxflags |= SUFX_P;
// fallthrough // fallthrough
case 'S':
case 'S': // Suffix
output[ix++] = RULE_ENDING; output[ix++] = RULE_ENDING;
value = 0; value = 0;
while (!isspace2(c = *p++) && (c != 0)) { while (!isspace2(c = *p++) && (c != 0)) {
case 'i': case 'i':
sxflags |= SUFX_I; sxflags |= SUFX_I;
break; break;
case 'p': // obsolete, replaced by 'P' above
case 'p': // obsolete, replaced by 'P' above
sxflags |= SUFX_P; sxflags |= SUFX_P;
break; break;
case 'v': case 'v':


switch (c = input[ix]) switch (c = input[ix])
{ {
case ')': // end of prefix section
case ')': // end of prefix section
*p = 0; *p = 0;
state = 1; state = 1;
copy_rule_string(buf, &state); copy_rule_string(buf, &state);
p = buf; p = buf;
break; break;
case '(': // start of suffix section
case '(': // start of suffix section
*p = 0; *p = 0;
state = 2; state = 2;
copy_rule_string(buf, &state); copy_rule_string(buf, &state);
error_count++; error_count++;
} }
break; break;
case '\n': // end of line
case '\n': // end of line
case '\r': case '\r':
case 0: // end of line
case 0: // end of line
*p = 0; *p = 0;
copy_rule_string(buf, &state); copy_rule_string(buf, &state);
finish = 1; finish = 1;
break; break;
case '\t': // end of section section
case '\t': // end of section section
case ' ': case ' ':
*p = 0; *p = 0;
copy_rule_string(buf, &state); copy_rule_string(buf, &state);


static int __cdecl rgroup_sorter(RGROUP *a, RGROUP *b) static int __cdecl rgroup_sorter(RGROUP *a, RGROUP *b)
{ {
// Sort long names before short names
// Sort long names before short names
int ix; int ix;
ix = strlen(b->name) - strlen(a->name); ix = strlen(b->name) - strlen(a->name);
if (ix != 0) return ix; if (ix != 0) return ix;
qsort((void *)rules, n_rules, sizeof(char *), (int(__cdecl *)(const void *, const void *))string_sorter); qsort((void *)rules, n_rules, sizeof(char *), (int(__cdecl *)(const void *, const void *))string_sorter);


if (strcmp(name, "9") == 0) if (strcmp(name, "9") == 0)
len_name = 0; // don't remove characters from numeric match strings
len_name = 0; // don't remove characters from numeric match strings


for (ix = 0; ix < n_rules; ix++) { for (ix = 0; ix < n_rules; ix++) {
p = rules[ix]; p = rules[ix];
len1 = strlen(p) + 1; // phoneme string
len1 = strlen(p) + 1; // phoneme string
p3 = &p[len1]; p3 = &p[len1];
p2 = p3 + len_name; // remove group name from start of match string
p2 = p3 + len_name; // remove group name from start of match string
len2 = strlen(p2); len2 = strlen(p2);


nextchar_count[(unsigned char)(p2[0])]++; // the next byte after the group name
nextchar_count[(unsigned char)(p2[0])]++; // the next byte after the group name


if ((common[0] != 0) && (strcmp(p, common) == 0)) { if ((common[0] != 0) && (strcmp(p, common) == 0)) {
fwrite(p2, len2, 1, f_out); fwrite(p2, len2, 1, f_out);
fputc(0, f_out); // no phoneme string, it's the same as previous rule
fputc(0, f_out); // no phoneme string, it's the same as previous rule
} else { } else {
if ((ix < n_rules-1) && (strcmp(p, rules[ix+1]) == 0)) { if ((ix < n_rules-1) && (strcmp(p, rules[ix+1]) == 0)) {
common = rules[ix]; // phoneme string is same as next, set as common
common = rules[ix]; // phoneme string is same as next, set as common
fputc(RULE_PH_COMMON, f_out); fputc(RULE_PH_COMMON, f_out);
} }


int length; int length;
int max_length = 0; int max_length = 0;


#define N_LETTERGP_ITEMS 200
#define N_LETTERGP_ITEMS 200
char *items[N_LETTERGP_ITEMS]; char *items[N_LETTERGP_ITEMS];
char item_length[N_LETTERGP_ITEMS]; char item_length[N_LETTERGP_ITEMS];




items[n_items] = p_start = p; items[n_items] = p_start = p;
while ((*p & 0xff) > ' ') { while ((*p & 0xff) > ' ') {
if (*p == '_') *p = ' '; // allow '_' for word break
if (*p == '_') *p = ' '; // allow '_' for word break
p++; p++;
} }
*p++ = 0; *p++ = 0;
// write out the items, longest first // write out the items, longest first
while (max_length > 1) { while (max_length > 1) {
for (ix = 0; ix < n_items; ix++) { for (ix = 0; ix < n_items; ix++) {
if (item_length[ix] == max_length) {
if (item_length[ix] == max_length)
fwrite(items[ix], 1, max_length, f_out); fwrite(items[ix], 1, max_length, f_out);
}
} }
max_length--; max_length--;
} }
if ((p = (unsigned char *)strstr(buf, "//")) != NULL) if ((p = (unsigned char *)strstr(buf, "//")) != NULL)
*p = 0; *p = 0;


if (buf[0] == '\r') buf++; // ignore extra \r in \r\n
if (buf[0] == '\r') buf++; // ignore extra \r in \r\n
} }


if ((buf == NULL) || (buf[0] == '.')) { if ((buf == NULL) || (buf[0] == '.')) {


if (compile_mode == 2) { if (compile_mode == 2) {
// end of the character replacements section // end of the character replacements section
fwrite(&n_rules, 1, 4, f_out); // write a zero word to terminate the replacemenmt list
fwrite(&n_rules, 1, 4, f_out); // write a zero word to terminate the replacemenmt list
compile_mode = 0; compile_mode = 0;
} }


if (buf == NULL) break; // end of file
if (buf == NULL) break; // end of file


if (memcmp(buf, ".L", 2) == 0) { if (memcmp(buf, ".L", 2) == 0) {
compile_lettergroup(&buf[2], f_out); compile_lettergroup(&buf[2], f_out);
compile_mode = 1; compile_mode = 1;


p = (unsigned char *)&buf[6]; p = (unsigned char *)&buf[6];
while ((p[0] == ' ') || (p[0] == '\t')) p++; // Note: Windows isspace(0xe1) gives TRUE !
while ((p[0] == ' ') || (p[0] == '\t')) p++; // Note: Windows isspace(0xe1) gives TRUE !
ix = 0; ix = 0;
while ((*p > ' ') && (ix < LEN_GROUP_NAME)) while ((*p > ' ') && (ix < LEN_GROUP_NAME))
group_name[ix++] = *p++; group_name[ix++] = *p++;
if (translator->letter_bits_offset > 0) { if (translator->letter_bits_offset > 0) {
utf8_in(&wc, group_name); utf8_in(&wc, group_name);
if (((ix = (wc - translator->letter_bits_offset)) >= 0) && (ix < 128)) if (((ix = (wc - translator->letter_bits_offset)) >= 0) && (ix < 128))
group3_ix = ix+1; // not zero
group3_ix = ix+1; // not zero
} }
} }




switch (compile_mode) switch (compile_mode)
{ {
case 1: // .group
case 1: // .group
prule = compile_rule(buf); prule = compile_rule(buf);
if (prule != NULL) { if (prule != NULL) {
if (n_rules < N_RULES) if (n_rules < N_RULES)


} }
break; break;
case 2: // .replace
case 2: // .replace
{ {
int replace1; int replace1;
int replace2; int replace2;
ix += 16; ix += 16;
} }
if (replace1 != 0) { if (replace1 != 0) {
Write4Bytes(f_out, replace1); // write as little-endian
Write4Bytes(f_out, replace2); // if big-endian, reverse the bytes in LoadDictionary()
Write4Bytes(f_out, replace1); // write as little-endian
Write4Bytes(f_out, replace2); // if big-endian, reverse the bytes in LoadDictionary()
} }
} }
break;
break;
} }
} }
fclose(f_temp); fclose(f_temp);


int CompileDictionary(const char *dsource, const char *dict_name, FILE *log, char *fname_err, int flags) int CompileDictionary(const char *dsource, const char *dict_name, FILE *log, char *fname_err, int flags)
{ {
// fname: space to write the filename in case of error
// flags: bit 0: include source line number information, for debug purposes.
// fname: space to write the filename in case of error
// flags: bit 0: include source line number information, for debug purposes.


FILE *f_in; FILE *f_in;
FILE *f_out; FILE *f_out;

+ 3
- 3
src/libespeak-ng/compilembrola.c View File

char name2[40]; char name2[40];
char mbrola_voice[40]; char mbrola_voice[40];
char buf[sizeof(path_home)+30]; char buf[sizeof(path_home)+30];
int mbrola_ctrl = 20; // volume in 1/16 ths
int mbrola_ctrl = 20; // volume in 1/16 ths
MBROLA_TAB data[N_PHONEME_TAB]; MBROLA_TAB data[N_PHONEME_TAB];


strcpy(buf, filepath); strcpy(buf, filepath);
buf[sizeof(phoneme)-1] = 0; buf[sizeof(phoneme)-1] = 0;


if ((p = strstr(buf, "//")) != NULL) if ((p = strstr(buf, "//")) != NULL)
*p = 0; // truncate line at comment
*p = 0; // truncate line at comment


if (memcmp(buf, "volume", 6) == 0) { if (memcmp(buf, "volume", 6) == 0) {
mbrola_ctrl = atoi(&buf[6]); mbrola_ctrl = atoi(&buf[6]);
return ENE_WRITE_ERROR; return ENE_WRITE_ERROR;
} }


data[count].name = 0; // list terminator
data[count].name = 0; // list terminator
Write4Bytes(f_out, mbrola_ctrl); Write4Bytes(f_out, mbrola_ctrl);


pw_end = (int *)(&data[count+1]); pw_end = (int *)(&data[count+1]);

+ 225
- 228
src/libespeak-ng/dictionary.c
File diff suppressed because it is too large
View File


+ 2
- 2
src/libespeak-ng/espeak_command.h View File



typedef enum { typedef enum {
CS_UNDEFINED, // The command has just been created CS_UNDEFINED, // The command has just been created
CS_PENDING, // stored in the fifo
CS_PROCESSED // processed
CS_PENDING, // stored in the fifo
CS_PROCESSED // processed
} t_command_state; } t_command_state;


typedef struct { typedef struct {

+ 19
- 18
src/libespeak-ng/event.c View File

static t_espeak_callback *my_callback = NULL; static t_espeak_callback *my_callback = NULL;
static int my_event_is_running = 0; static int my_event_is_running = 0;


enum { MIN_TIMEOUT_IN_MS = 10,
ACTIVITY_TIMEOUT = 50, // in ms, check that the stream is active
MAX_ACTIVITY_CHECK = 6 };
enum {
MIN_TIMEOUT_IN_MS = 10,
ACTIVITY_TIMEOUT = 50, // in ms, check that the stream is active
MAX_ACTIVITY_CHECK = 6
};


typedef struct t_node { typedef struct t_node {
void *data; void *data;
for (i = 0; i < MAX_ACTIVITY_CHECK && (*stop_is_required == 0); i++) { for (i = 0; i < MAX_ACTIVITY_CHECK && (*stop_is_required == 0); i++) {
err = wave_get_remaining_time(sample, time_in_ms); err = wave_get_remaining_time(sample, time_in_ms);


if (err || wave_is_busy(NULL) || (*time_in_ms == 0)) { // if err, stream not available: quit
// if wave is busy, time_in_ms is known: quit
// if wave is not busy but remaining time == 0, event is reached: quit
if (err || // if err, stream not available: quit
wave_is_busy(NULL) || // if wave is busy, time_in_ms is known: quit
(*time_in_ms == 0)) { // if wave is not busy but remaining time == 0, event is reached: quit
break; break;
} }


SHOW_TIME("polling_thread > unlocked\n"); SHOW_TIME("polling_thread > unlocked\n");


a_stop_is_required = 0; a_stop_is_required = 0;
a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required); // NOTE: may set a_stop_is_required to -1
a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required); // NOTE: may set a_stop_is_required to -1
if ((a_status == 0) && (a_stop_is_required > 0)) { if ((a_status == 0) && (a_stop_is_required > 0)) {
SHOW("polling_thread > stop required (%d)\n", __LINE__); SHOW("polling_thread > stop required (%d)\n", __LINE__);
while (0 == sem_trywait(&my_sem_stop_is_required)) {
}
;
while (0 == sem_trywait(&my_sem_stop_is_required))
;
} else } else
a_stop_is_required = 0; a_stop_is_required = 0;


// In this loop, my_event_is_running = 1 // In this loop, my_event_is_running = 1
while (head && (a_stop_is_required <= 0)) { while (head && (a_stop_is_required <= 0)) {
SHOW_TIME("polling_thread > check head\n"); SHOW_TIME("polling_thread > check head\n");
while (0 == sem_trywait(&my_sem_start_is_required)) {
}
while (0 == sem_trywait(&my_sem_start_is_required))
;


espeak_EVENT *event = (espeak_EVENT *)(head->data); espeak_EVENT *event = (espeak_EVENT *)(head->data);
assert(event); assert(event);


if ((a_status == 0) && (a_stop_is_required > 0)) { if ((a_status == 0) && (a_stop_is_required > 0)) {
SHOW("polling_thread > stop required (%d)\n", __LINE__); SHOW("polling_thread > stop required (%d)\n", __LINE__);
while (0 == sem_trywait(&my_sem_stop_is_required)) {
}
while (0 == sem_trywait(&my_sem_stop_is_required))
;
} else } else
a_stop_is_required = 0; a_stop_is_required = 0;
} else // The event will be notified soon: sleep until timeout or stop request } else // The event will be notified soon: sleep until timeout or stop request
a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required); a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required);
if ((a_status == 0) && (a_stop_is_required > 0)) { if ((a_status == 0) && (a_stop_is_required > 0)) {
SHOW("polling_thread > stop required (%d)\n", __LINE__); SHOW("polling_thread > stop required (%d)\n", __LINE__);
while (0 == sem_trywait(&my_sem_stop_is_required)) {
}
while (0 == sem_trywait(&my_sem_stop_is_required))
;
} else } else
a_stop_is_required = 0; a_stop_is_required = 0;
} }
{ {
ENTER("event > init"); ENTER("event > init");


while (event_delete((espeak_EVENT *)pop())) {
}
while (event_delete((espeak_EVENT *)pop()))
;


node_counter = 0; node_counter = 0;
} }

+ 10
- 9
src/libespeak-ng/fifo.c View File

static pthread_mutex_t my_mutex; static pthread_mutex_t my_mutex;
static int my_command_is_running = 0; static int my_command_is_running = 0;
static int my_stop_is_required = 0; static int my_stop_is_required = 0;
// + fifo
//


// my_thread: reads commands from the fifo, and runs them. // my_thread: reads commands from the fifo, and runs them.
static pthread_t my_thread; static pthread_t my_thread;
static t_espeak_command *pop(); static t_espeak_command *pop();
static void init(int process_parameters); static void init(int process_parameters);
static int node_counter = 0; static int node_counter = 0;
enum { MAX_NODE_COUNTER = 400,
INACTIVITY_TIMEOUT = 50, // in ms, check that the stream is inactive
MAX_INACTIVITY_CHECK = 2 };

enum {
MAX_NODE_COUNTER = 400,
INACTIVITY_TIMEOUT = 50, // in ms, check that the stream is inactive
MAX_INACTIVITY_CHECK = 2
};


void fifo_init() void fifo_init()
{ {
display_espeak_command(a_command); display_espeak_command(a_command);
// purge start semaphore // purge start semaphore
SHOW_TIME("say_thread > purge my_sem_start_is_required\n"); SHOW_TIME("say_thread > purge my_sem_start_is_required\n");
while (0 == sem_trywait(&my_sem_start_is_required)) {
}
while (0 == sem_trywait(&my_sem_start_is_required))
;


if (my_stop_is_required) { if (my_stop_is_required) {
SHOW_TIME("say_thread > my_command_is_running = 0\n"); SHOW_TIME("say_thread > my_command_is_running = 0\n");


// purge start semaphore // purge start semaphore
SHOW_TIME("say_thread > purge my_sem_start_is_required\n"); SHOW_TIME("say_thread > purge my_sem_start_is_required\n");
while (0 == sem_trywait(&my_sem_start_is_required)) {
}
while (0 == sem_trywait(&my_sem_start_is_required))
;


// acknowledge the stop request // acknowledge the stop request
SHOW_TIME("say_thread > post my_sem_stop_is_acknowledged\n"); SHOW_TIME("say_thread > post my_sem_stop_is_acknowledged\n");

+ 105
- 115
src/libespeak-ng/intonation.c View File

typedef struct { typedef struct {
char stress; char stress;
char env; char env;
char flags; // bit 0=pitch rising, bit1=emnphasized, bit2=end of clause
char flags; // bit 0=pitch rising, bit1=emnphasized, bit2=end of clause
char nextph_type; char nextph_type;
unsigned char pitch1; unsigned char pitch1;
unsigned char pitch2; unsigned char pitch2;


static SYLLABLE *syllable_tab; static SYLLABLE *syllable_tab;


static int tone_pitch_env; /* used to return pitch envelope */
static int tone_pitch_env; // used to return pitch envelope


/* Pitch data for tone types */ /* Pitch data for tone types */
/*****************************/ /*****************************/


#define PITCHfall 0
#define PITCHrise 2
#define PITCHfrise 4 // and 3 must be for the variant preceded by 'r'
#define PITCHfrise2 6 // and 5 must be the 'r' variant
#define PITCHrisefall 8
#define PITCHfall 0
#define PITCHrise 2
#define PITCHfrise 4 // and 3 must be for the variant preceded by 'r'
#define PITCHfrise2 6 // and 5 must be the 'r' variant
#define PITCHrisefall 8


/* 0 fall */
unsigned char env_fall[128] = { unsigned char env_fall[128] = {
0xff, 0xfd, 0xfa, 0xf8, 0xf6, 0xf4, 0xf2, 0xf0, 0xee, 0xec, 0xea, 0xe8, 0xe6, 0xe4, 0xe2, 0xe0, 0xff, 0xfd, 0xfa, 0xf8, 0xf6, 0xf4, 0xf2, 0xf0, 0xee, 0xec, 0xea, 0xe8, 0xe6, 0xe4, 0xe2, 0xe0,
0xde, 0xdc, 0xda, 0xd8, 0xd6, 0xd4, 0xd2, 0xd0, 0xce, 0xcc, 0xca, 0xc8, 0xc6, 0xc4, 0xc2, 0xc0, 0xde, 0xdc, 0xda, 0xd8, 0xd6, 0xd4, 0xd2, 0xd0, 0xce, 0xcc, 0xca, 0xc8, 0xc6, 0xc4, 0xc2, 0xc0,
0x1e, 0x1c, 0x1a, 0x18, 0x16, 0x14, 0x12, 0x10, 0x0e, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x00 0x1e, 0x1c, 0x1a, 0x18, 0x16, 0x14, 0x12, 0x10, 0x0e, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x00
}; };


/* 1 rise */
unsigned char env_rise[128] = { unsigned char env_rise[128] = {
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
env_risefallrise, env_risefallrise env_risefallrise, env_risefallrise
}; };


/* indexed by stress */
// indexed by stress
static int min_drop[] = { 6, 7, 9, 9, 20, 20, 20, 25 }; static int min_drop[] = { 6, 7, 9, 9, 20, 20, 20, 25 };


// pitch change during the main part of the clause // pitch change during the main part of the clause
static signed char oflow_emf[] = { 10, 52, 32, 20, 10 }; static signed char oflow_emf[] = { 10, 52, 32, 20, 10 };
static signed char oflow_less[] = { 6, 38, 24, 14, 4 }; static signed char oflow_less[] = { 6, 38, 24, 14, 4 };


#define N_TONE_HEAD_TABLE 13
#define N_TONE_NUCLEUS_TABLE 13
#define N_TONE_HEAD_TABLE 13
#define N_TONE_NUCLEUS_TABLE 13


typedef struct { typedef struct {
unsigned char pre_start; unsigned char pre_start;
} TONE_HEAD; } TONE_HEAD;


typedef struct { typedef struct {
unsigned char pitch_env0; /* pitch envelope, tonic syllable at end */
unsigned char pitch_env0; // pitch envelope, tonic syllable at end
unsigned char tonic_max0; unsigned char tonic_max0;
unsigned char tonic_min0; unsigned char tonic_min0;


unsigned char pitch_env1; /* followed by unstressed */
unsigned char pitch_env1; // followed by unstressed
unsigned char tonic_max1; unsigned char tonic_max1;
unsigned char tonic_min1; unsigned char tonic_min1;


#define T_EMPH 1 #define T_EMPH 1


static TONE_HEAD tone_head_table[N_TONE_HEAD_TABLE] = { static TONE_HEAD tone_head_table[N_TONE_HEAD_TABLE] = {
{ 46, 57, 78, 50, drops_0, 3, 7, 5, oflow }, // 0 statement
{ 46, 57, 78, 46, drops_0, 3, 7, 5, oflow }, // 1 comma
{ 46, 57, 78, 46, drops_0, 3, 7, 5, oflow }, // 2 question
{ 46, 57, 90, 50, drops_0, 3, 9, 5, oflow_emf }, // 3 exclamation
{ 46, 57, 78, 50, drops_0, 3, 7, 5, oflow }, // 4 statement, emphatic
{ 46, 57, 74, 55, drops_0, 4, 7, 5, oflow_less },// 5 statement, less intonation
{ 46, 57, 74, 55, drops_0, 4, 7, 5, oflow_less },// 6 comma, less intonation
{ 46, 57, 74, 55, drops_0, 4, 7, 5, oflow_less },// 7 comma, less intonation, less rise
{ 46, 57, 78, 50, drops_0, 3, 7, 5, oflow }, // 8 pitch raises at end of sentence
{ 46, 57, 78, 46, drops_0, 3, 7, 5, oflow }, // 9 comma
{ 46, 57, 78, 50, drops_0, 3, 7, 5, oflow }, // 10 question
{ 34, 41, 41, 32, drops_0, 3, 7, 5, oflow_less }, // 11 test
{ 46, 57, 55, 50, drops_0, 3, 7, 5, oflow_less }, // 12 test
{ 46, 57, 78, 50, drops_0, 3, 7, 5, oflow }, // 0 statement
{ 46, 57, 78, 46, drops_0, 3, 7, 5, oflow }, // 1 comma
{ 46, 57, 78, 46, drops_0, 3, 7, 5, oflow }, // 2 question
{ 46, 57, 90, 50, drops_0, 3, 9, 5, oflow_emf }, // 3 exclamation
{ 46, 57, 78, 50, drops_0, 3, 7, 5, oflow }, // 4 statement, emphatic
{ 46, 57, 74, 55, drops_0, 4, 7, 5, oflow_less }, // 5 statement, less intonation
{ 46, 57, 74, 55, drops_0, 4, 7, 5, oflow_less }, // 6 comma, less intonation
{ 46, 57, 74, 55, drops_0, 4, 7, 5, oflow_less }, // 7 comma, less intonation, less rise
{ 46, 57, 78, 50, drops_0, 3, 7, 5, oflow }, // 8 pitch raises at end of sentence
{ 46, 57, 78, 46, drops_0, 3, 7, 5, oflow }, // 9 comma
{ 46, 57, 78, 50, drops_0, 3, 7, 5, oflow }, // 10 question
{ 34, 41, 41, 32, drops_0, 3, 7, 5, oflow_less }, // 11 test
{ 46, 57, 55, 50, drops_0, 3, 7, 5, oflow_less }, // 12 test
}; };


static TONE_NUCLEUS tone_nucleus_table[N_TONE_NUCLEUS_TABLE] = { static TONE_NUCLEUS tone_nucleus_table[N_TONE_NUCLEUS_TABLE] = {
{ PITCHfall, 64, 8, PITCHfall, 70, 18, NULL, 24, 12, 0 }, // 0 statement
{ PITCHfrise, 80, 18, PITCHfrise2, 78, 22, NULL, 34, 52, 0 }, // 1 comma
{ PITCHfrise, 88, 22, PITCHfrise2, 82, 22, NULL, 34, 64, 0 }, // 2 question
{ PITCHfall, 92, 8, PITCHfall, 92, 80, NULL, 76, 8, T_EMPH }, // 3 exclamation

{ PITCHfall, 86, 4, PITCHfall, 94, 66, NULL, 34, 10, 0 }, // 4 statement, emphatic
{ PITCHfall, 62, 10, PITCHfall, 62, 20, NULL, 28, 16, 0 }, // 5 statement, less intonation
{ PITCHfrise, 68, 18, PITCHfrise2, 68, 22, NULL, 30, 44, 0 }, // 6 comma, less intonation
{ PITCHfrise2, 64, 16, PITCHfall, 66, 32, NULL, 32, 18, 0 }, // 7 comma, less intonation, less rise
{ PITCHrise, 68, 46, PITCHfall, 42, 32, NULL, 46, 58, 0 }, // 8 pitch raises at end of sentence
{ PITCHfrise, 78, 24, PITCHfrise2, 72, 22, NULL, 42, 52, 0 }, // 9 comma
{ PITCHfrise, 88, 34, PITCHfall, 64, 32, NULL, 46, 82, 0 }, // 10 question
{ PITCHfall, 56, 12, PITCHfall, 56, 20, NULL, 24, 12, 0 }, // 11 test
{ PITCHfall, 70, 18, PITCHfall, 70, 24, NULL, 32, 20, 0 }, // 12 test
{ PITCHfall, 64, 8, PITCHfall, 70, 18, NULL, 24, 12, 0 }, // 0 statement
{ PITCHfrise, 80, 18, PITCHfrise2, 78, 22, NULL, 34, 52, 0 }, // 1 comma
{ PITCHfrise, 88, 22, PITCHfrise2, 82, 22, NULL, 34, 64, 0 }, // 2 question
{ PITCHfall, 92, 8, PITCHfall, 92, 80, NULL, 76, 8, T_EMPH }, // 3 exclamation
{ PITCHfall, 86, 4, PITCHfall, 94, 66, NULL, 34, 10, 0 }, // 4 statement, emphatic
{ PITCHfall, 62, 10, PITCHfall, 62, 20, NULL, 28, 16, 0 }, // 5 statement, less intonation
{ PITCHfrise, 68, 18, PITCHfrise2, 68, 22, NULL, 30, 44, 0 }, // 6 comma, less intonation
{ PITCHfrise2, 64, 16, PITCHfall, 66, 32, NULL, 32, 18, 0 }, // 7 comma, less intonation, less rise
{ PITCHrise, 68, 46, PITCHfall, 42, 32, NULL, 46, 58, 0 }, // 8 pitch raises at end of sentence
{ PITCHfrise, 78, 24, PITCHfrise2, 72, 22, NULL, 42, 52, 0 }, // 9 comma
{ PITCHfrise, 88, 34, PITCHfall, 64, 32, NULL, 46, 82, 0 }, // 10 question
{ PITCHfall, 56, 12, PITCHfall, 56, 20, NULL, 24, 12, 0 }, // 11 test
{ PITCHfall, 70, 18, PITCHfall, 70, 24, NULL, 32, 20, 0 }, // 12 test
}; };


/* index by 0=. 1=, 2=?, 3=! 4=none, 5=emphasized */
// index by 0=. 1=, 2=?, 3=! 4=none, 5=emphasized
unsigned char punctuation_to_tone[INTONATION_TYPES][PUNCT_INTONATIONS] = { unsigned char punctuation_to_tone[INTONATION_TYPES][PUNCT_INTONATIONS] = {
{ 0, 1, 2, 3, 0, 4 },
{ 0, 1, 2, 3, 0, 4 },
{ 5, 6, 2, 3, 0, 4 },
{ 5, 7, 1, 3, 0, 4 },
{ 8, 9, 10, 3, 0, 0 },
{ 8, 8, 10, 3, 0, 0 },
{ 11, 11, 11, 11, 0, 0 }, // 6 test
{ 0, 1, 2, 3, 0, 4 },
{ 0, 1, 2, 3, 0, 4 },
{ 5, 6, 2, 3, 0, 4 },
{ 5, 7, 1, 3, 0, 4 },
{ 8, 9, 10, 3, 0, 0 },
{ 8, 8, 10, 3, 0, 0 },
{ 11, 11, 11, 11, 0, 0 }, // 6 test
{ 12, 12, 12, 12, 0, 0 } { 12, 12, 12, 12, 0, 0 }
}; };


int n_tunes = 0; int n_tunes = 0;
TUNE *tunes = NULL; TUNE *tunes = NULL;


#define SECONDARY 3
#define PRIMARY 4
#define SECONDARY 3
#define PRIMARY 4
#define PRIMARY_STRESSED 6 #define PRIMARY_STRESSED 6
#define PRIMARY_LAST 7
#define PRIMARY_LAST 7


static int number_pre; static int number_pre;
static int number_body; static int number_body;
int ix; int ix;
int stress; int stress;
int max_stress = 0; int max_stress = 0;
int max_stress_posn = 0; // last syllable ot the highest stress
int max_stress_posn2 = 0; // penuntimate syllable of the highest stress
int max_stress_posn = 0; // last syllable ot the highest stress
int max_stress_posn2 = 0; // penuntimate syllable of the highest stress


number_pre = -1; /* number of vowels before 1st primary stress */
number_pre = -1; // number of vowels before 1st primary stress
number_body = 0; number_body = 0;
number_tail = 0; /* number between tonic syllable and next primary */
number_tail = 0; // number between tonic syllable and next primary
last_primary = -1; last_primary = -1;


for (ix = start; ix < end; ix++) { for (ix = start; ix < end; ix++) {
stress = syllable_tab[ix].stress; /* marked stress level */
stress = syllable_tab[ix].stress; // marked stress level


if (stress >= max_stress) { if (stress >= max_stress) {
if (stress > max_stress) if (stress > max_stress)
tone_posn = max_stress_posn; tone_posn = max_stress_posn;
tone_posn2 = max_stress_posn2; tone_posn2 = max_stress_posn2;


if (no_tonic) {
tone_posn = tone_posn2 = end; // next position after the end of the truncated clause
} else if (last_primary >= 0) {
if (no_tonic)
tone_posn = tone_posn2 = end; // next position after the end of the truncated clause
else if (last_primary >= 0) {
if (end == clause_end) if (end == clause_end)
syllable_tab[last_primary].stress = PRIMARY_LAST; syllable_tab[last_primary].stress = PRIMARY_LAST;
} else { } else {
} }
} }


/* Count number of primary stresses up to tonic syllable or body_reset */
// Count number of primary stresses up to tonic syllable or body_reset
static int count_increments(int ix, int end_ix, int min_stress) static int count_increments(int ix, int end_ix, int min_stress)
{ {
int count = 0; int count = 0;
int pitch = 0; int pitch = 0;
int increment = 0; int increment = 0;
int n_steps = 0; int n_steps = 0;
int stage; // onset, head, last
int stage; // onset, head, last
int initial; int initial;
int overflow_ix = 0; int overflow_ix = 0;
int pitch_range; int pitch_range;
int unstressed_inc; int unstressed_inc;
int used_onset = 0; int used_onset = 0;
int head_final = end_ix; int head_final = end_ix;
int secondary = 2; // 2
int secondary = 2;


pitch_range = (tune->head_end - tune->head_start) << 8; pitch_range = (tune->head_end - tune->head_start) << 8;
pitch_range_abs = abs(pitch_range); pitch_range_abs = abs(pitch_range);
drops = drops_0; // this should be controled by tune->head_drops
drops = drops_0; // this should be controled by tune->head_drops
initial = 1; initial = 1;


stage = 0; stage = 0;
if (tune->onset == 255) if (tune->onset == 255)
stage = 1; // no onset specified
stage = 1; // no onset specified


if (tune->head_last != 255) { if (tune->head_last != 255) {
// find the last primary stress in the body // find the last primary stress in the body
} else if (stress >= SECONDARY) } else if (stress >= SECONDARY)
set_pitch(syl, (pitch >> 8), drops[stress]); set_pitch(syl, (pitch >> 8), drops[stress]);
else { else {
/* unstressed, drop pitch if preceded by PRIMARY */
// unstressed, drop pitch if preceded by PRIMARY
if ((syllable_tab[ix-1].stress & 0x3f) >= SECONDARY) if ((syllable_tab[ix-1].stress & 0x3f) >= SECONDARY)
set_pitch(syl, (pitch >> 8) - th->body_lower_u, drops[stress]); set_pitch(syl, (pitch >> 8) - th->body_lower_u, drops[stress]);
else else


static void SetPitchGradient(int start_ix, int end_ix, int start_pitch, int end_pitch) static void SetPitchGradient(int start_ix, int end_ix, int start_pitch, int end_pitch)
{ {
// Set a linear pitch change over a number of syllables.
// Used for pre-head, unstressed syllables in the body, and the tail
// Set a linear pitch change over a number of syllables.
// Used for pre-head, unstressed syllables in the body, and the tail


int ix; int ix;
int stress; int stress;
tune = &tunes[tune_number]; tune = &tunes[tune_number];
ix = start; ix = start;


/* vowels before the first primary stress */
/******************************************/
// vowels before the first primary stress


SetPitchGradient(ix, ix+number_pre, tune->prehead_start, tune->prehead_end); SetPitchGradient(ix, ix+number_pre, tune->prehead_start, tune->prehead_end);
ix += number_pre; ix += number_pre;


/* body of tonic segment */
/*************************/
// body of tonic segment


if (option_tone_flags & OPTION_EMPHASIZE_PENULTIMATE) if (option_tone_flags & OPTION_EMPHASIZE_PENULTIMATE)
tone_posn = tone_posn2; // put tone on the penultimate stressed word
tone_posn = tone_posn2; // put tone on the penultimate stressed word
ix = SetHeadIntonation(tune, ix, tone_posn, 0); ix = SetHeadIntonation(tune, ix, tone_posn, 0);


if (no_tonic) if (no_tonic)
return 0; return 0;


/* tonic syllable */
/******************/
// tonic syllable


if (number_tail == 0) { if (number_tail == 0) {
tone_pitch_env = tune->nucleus0_env; tone_pitch_env = tune->nucleus0_env;
if (syllable_tab[tone_posn].stress == PRIMARY) if (syllable_tab[tone_posn].stress == PRIMARY)
syllable_tab[tone_posn].stress = PRIMARY_STRESSED; syllable_tab[tone_posn].stress = PRIMARY_STRESSED;


/* tail, after the tonic syllable */
/**********************************/
// tail, after the tonic syllable


SetPitchGradient(ix, end, tune->tail_start, tune->tail_end); SetPitchGradient(ix, end, tune->tail_start, tune->tail_end);


tn = &tone_nucleus_table[tune_number]; tn = &tone_nucleus_table[tune_number];
ix = start; ix = start;


/* vowels before the first primary stress */
/******************************************/
// vowels before the first primary stress


SetPitchGradient(ix, ix+number_pre, th->pre_start, th->pre_end); SetPitchGradient(ix, ix+number_pre, th->pre_start, th->pre_end);
ix += number_pre; ix += number_pre;


/* body of tonic segment */
/*************************/
// body of tonic segment


if (option_tone_flags & OPTION_EMPHASIZE_PENULTIMATE) if (option_tone_flags & OPTION_EMPHASIZE_PENULTIMATE)
tone_posn = tone_posn2; // put tone on the penultimate stressed word
tone_posn = tone_posn2; // put tone on the penultimate stressed word
ix = calc_pitch_segment(ix, tone_posn, th, tn, PRIMARY, continuing); ix = calc_pitch_segment(ix, tone_posn, th, tn, PRIMARY, continuing);


if (no_tonic) if (no_tonic)
return 0; return 0;


/* tonic syllable */
/******************/
// tonic syllable


if (tn->flags & T_EMPH) {
if (tn->flags & T_EMPH)
syllable_tab[ix].flags |= SYL_EMPHASIS; syllable_tab[ix].flags |= SYL_EMPHASIS;
}


if (number_tail == 0) { if (number_tail == 0) {
tone_pitch_env = tn->pitch_env0; tone_pitch_env = tn->pitch_env0;
if (syllable_tab[tone_posn].stress == PRIMARY) if (syllable_tab[tone_posn].stress == PRIMARY)
syllable_tab[tone_posn].stress = PRIMARY_STRESSED; syllable_tab[tone_posn].stress = PRIMARY_STRESSED;


/* tail, after the tonic syllable */
/**********************************/
// tail, after the tonic syllable


SetPitchGradient(ix, end, tn->tail_start, tn->tail_end); SetPitchGradient(ix, end, tn->tail_start, tn->tail_end);




static void CalcPitches_Tone(Translator *tr, int clause_tone) static void CalcPitches_Tone(Translator *tr, int clause_tone)
{ {
// clause_tone: 0=. 1=, 2=?, 3=! 4=none
// clause_tone: 0=. 1=, 2=?, 3=! 4=none

PHONEME_LIST *p; PHONEME_LIST *p;
int ix; int ix;
int count_stressed = 0; int count_stressed = 0;
int pause; int pause;
int tone_promoted; int tone_promoted;
PHONEME_TAB *tph; PHONEME_TAB *tph;
PHONEME_TAB *prev_tph; // forget across word boundary
PHONEME_TAB *prevw_tph; // remember across word boundary
PHONEME_TAB *prev_tph; // forget across word boundary
PHONEME_TAB *prevw_tph; // remember across word boundary
PHONEME_LIST *prev_p; PHONEME_LIST *prev_p;


int pitch_adjust = 0; // pitch gradient through the clause - inital value
int pitch_decrement = 0; // decrease by this for each stressed syllable
int pitch_low = 0; // until it drops to this
int pitch_high = 0; // then reset to this
int pitch_adjust = 0; // pitch gradient through the clause - inital value
int pitch_decrement = 0; // decrease by this for each stressed syllable
int pitch_low = 0; // until it drops to this
int pitch_high = 0; // then reset to this


// count number of stressed syllables // count number of stressed syllables
p = &phoneme_list[0]; p = &phoneme_list[0];
// LANG=vi // LANG=vi
p = &phoneme_list[final_stressed]; p = &phoneme_list[final_stressed];
if (p->tone_ph == 0) if (p->tone_ph == 0)
p->tone_ph = PhonemeCode('7'); // change default tone (tone 1) to falling tone at end of clause
p->tone_ph = PhonemeCode('7'); // change default tone (tone 1) to falling tone at end of clause
} }


pause = 1; pause = 1;
// perform tone sandhi // perform tone sandhi
for (ix = 0; ix < n_phoneme_list; ix++, p++) { for (ix = 0; ix < n_phoneme_list; ix++, p++) {
if ((p->type == phPAUSE) && (p->ph->std_length > 50)) { if ((p->type == phPAUSE) && (p->ph->std_length > 50)) {
pause = 1; // there is a pause since the previous vowel
prevw_tph = phoneme_tab[phonPAUSE]; // forget previous tone
pause = 1; // there is a pause since the previous vowel
prevw_tph = phoneme_tab[phonPAUSE]; // forget previous tone
} }


if (p->newword) if (p->newword)
prev_tph = phoneme_tab[phonPAUSE]; // forget across word boundaries
prev_tph = phoneme_tab[phonPAUSE]; // forget across word boundaries


if (p->synthflags & SFLAG_SYLLABLE) { if (p->synthflags & SFLAG_SYLLABLE) {
tone_ph = p->tone_ph; tone_ph = p->tone_ph;
if (tr->translator_name == L('z', 'h')) { if (tr->translator_name == L('z', 'h')) {
if (tone_ph == 0) { if (tone_ph == 0) {
if (pause || tone_promoted) { if (pause || tone_promoted) {
tone_ph = PhonemeCode2('5', '5'); // no previous vowel, use tone 1
tone_ph = PhonemeCode2('5', '5'); // no previous vowel, use tone 1
tone_promoted = 1; tone_promoted = 1;
} else } else
tone_ph = PhonemeCode2('1', '1'); // default tone 5
tone_ph = PhonemeCode2('1', '1'); // default tone 5


p->tone_ph = tone_ph; p->tone_ph = tone_ph;
tph = phoneme_tab[tone_ph]; tph = phoneme_tab[tone_ph];
} }


if (prevw_tph->mnemonic == 0x343132) { // [214] if (prevw_tph->mnemonic == 0x343132) { // [214]
if (tph->mnemonic == 0x343132) // [214]
if (tph->mnemonic == 0x343132) // [214]
prev_p->tone_ph = PhonemeCode2('3', '5'); prev_p->tone_ph = PhonemeCode2('3', '5');
else else
prev_p->tone_ph = PhonemeCode2('2', '1'); prev_p->tone_ph = PhonemeCode2('2', '1');
} }
if ((prev_tph->mnemonic == 0x3135) && (tph->mnemonic == 0x3135)) // [51] + [51]
if ((prev_tph->mnemonic == 0x3135) && (tph->mnemonic == 0x3135)) // [51] + [51]
prev_p->tone_ph = PhonemeCode2('5', '3'); prev_p->tone_ph = PhonemeCode2('5', '3');


if (tph->mnemonic == 0x3131) { // [11] Tone 5 if (tph->mnemonic == 0x3131) { // [11] Tone 5
p->tone_ph = PhonemeCode2('4', '4'); p->tone_ph = PhonemeCode2('4', '4');


// tone 5 is unstressed (shorter) // tone 5 is unstressed (shorter)
p->stresslevel = 0; // diminished stress
p->stresslevel = 0; // diminished stress
} }
} }


} }


if (tone_ph == 0) { if (tone_ph == 0) {
tone_ph = phonDEFAULTTONE; // no tone specified, use default tone 1
tone_ph = phonDEFAULTTONE; // no tone specified, use default tone 1
p->tone_ph = tone_ph; p->tone_ph = tone_ph;
} }
p->pitch1 = pitch_adjust + phoneme_tab[tone_ph]->start_type; p->pitch1 = pitch_adjust + phoneme_tab[tone_ph]->start_type;


void CalcPitches(Translator *tr, int clause_type) void CalcPitches(Translator *tr, int clause_type)
{ {
// clause_type: 0=. 1=, 2=?, 3=! 4=none
// clause_type: 0=. 1=, 2=?, 3=! 4=none

PHONEME_LIST *p; PHONEME_LIST *p;
SYLLABLE *syl; SYLLABLE *syl;
int ix; int ix;


SYLLABLE syllable_tab2[N_PHONEME_LIST]; SYLLABLE syllable_tab2[N_PHONEME_LIST];


syllable_tab = syllable_tab2; // don't use permanent storage. it's only needed during the call of CalcPitches()
syllable_tab = syllable_tab2; // don't use permanent storage. it's only needed during the call of CalcPitches()
n_st = 0; n_st = 0;
n_primary = 0; n_primary = 0;
for (ix = 0; ix < (n_phoneme_list-1); ix++) { for (ix = 0; ix < (n_phoneme_list-1); ix++) {
} else if ((p->ph->code == phonPAUSE_CLAUSE) && (n_st > 0)) } else if ((p->ph->code == phonPAUSE_CLAUSE) && (n_st > 0))
syllable_tab[n_st-1].flags |= SYL_END_CLAUSE; syllable_tab[n_st-1].flags |= SYL_END_CLAUSE;
} }
syllable_tab[n_st].stress = 0; // extra 0 entry at the end
syllable_tab[n_st].stress = 0; // extra 0 entry at the end


if (n_st == 0) if (n_st == 0)
return; // nothing to do
return; // nothing to do


if (tr->langopts.tone_language == 1) { if (tr->langopts.tone_language == 1) {
CalcPitches_Tone(tr, clause_type); CalcPitches_Tone(tr, clause_type);
group_tone_comma = tr->langopts.tunes[1]; group_tone_comma = tr->langopts.tunes[1];
} else { } else {
group_tone = tr->punct_to_tone[option][clause_type]; group_tone = tr->punct_to_tone[option][clause_type];
group_tone_comma = tr->punct_to_tone[option][1]; // emphatic form of statement
group_tone_comma = tr->punct_to_tone[option][1]; // emphatic form of statement
} }


if (clause_type == 4) if (clause_type == 4)
no_tonic = 1; /* incomplete clause, used for abbreviations such as Mr. Dr. Mrs. */
no_tonic = 1; // incomplete clause, used for abbreviations such as Mr. Dr. Mrs.
else else
no_tonic = 0; no_tonic = 0;




count_pitch_vowels(st_start, ix, n_st); count_pitch_vowels(st_start, ix, n_st);
if ((ix < n_st) || (clause_type == 0)) { if ((ix < n_st) || (clause_type == 0)) {
calc_pitches(option, st_start, ix, group_tone); // split into > 1 tone groups
calc_pitches(option, st_start, ix, group_tone); // split into > 1 tone groups


if ((clause_type == 1) || (clause_type == 2)) if ((clause_type == 1) || (clause_type == 2))
group_tone = tr->langopts.tunes[1]; // , or ? remainder has comma-tone
group_tone = tr->langopts.tunes[1]; // , or ? remainder has comma-tone
else else
group_tone = tr->langopts.tunes[0]; // . or ! remainder has statement tone
group_tone = tr->langopts.tunes[0]; // . or ! remainder has statement tone
} else } else
calc_pitches(option, st_start, ix, group_tone); calc_pitches(option, st_start, ix, group_tone);


} }


if (syl->flags & SYL_EMPHASIS) if (syl->flags & SYL_EMPHASIS)
p->stresslevel |= 8; // emphasized
p->stresslevel |= 8; // emphasized


st_ix++; st_ix++;
} }

+ 151
- 174
src/libespeak-ng/klatt.c View File

#define getrandom(min, max) ((rand()%(long)(((max)+1)-(min)))+(min)) #define getrandom(min, max) ((rand()%(long)(((max)+1)-(min)))+(min))
#endif #endif


/* function prototypes for functions private to this file */
// function prototypes for functions private to this file


static void flutter(klatt_frame_ptr); static void flutter(klatt_frame_ptr);
static double sampled_source(int); static double sampled_source(int);


#define NUMBER_OF_SAMPLES 100 #define NUMBER_OF_SAMPLES 100


static int scale_wav_tab[] = { 45, 38, 45, 45, 55 }; // scale output from different voicing sources
static int scale_wav_tab[] = { 45, 38, 45, 45, 55 }; // scale output from different voicing sources


// For testing, this can be overwritten in KlattInit() // For testing, this can be overwritten in KlattInit()
static short natural_samples2[256] = { static short natural_samples2[256] = {
2583, 2516, 2450, 2384, 2319, 2254, 2191, 2127,
2067, 2005, 1946, 1890, 1832, 1779, 1726, 1675,
1626, 1579, 1533, 1491, 1449, 1409, 1372, 1336,
1302, 1271, 1239, 1211, 1184, 1158, 1134, 1111,
1089, 1069, 1049, 1031, 1013, 996, 980, 965,
950, 936, 921, 909, 895, 881, 869, 855,
843, 830, 818, 804, 792, 779, 766, 754,
740, 728, 715, 702, 689, 676, 663, 651,
637, 626, 612, 601, 588, 576, 564, 552,
540, 530, 517, 507, 496, 485, 475, 464,
454, 443, 434, 424, 414, 404, 394, 385,
375, 366, 355, 347, 336, 328, 317, 308,
299, 288, 280, 269, 260, 250, 240, 231,
220, 212, 200, 192, 181, 172, 161, 152,
142, 133, 123, 113, 105, 94, 86, 76,
67, 57, 49, 39, 30, 22, 11, 4,
-5, -14, -23, -32, -41, -50, -60, -69,
-78, -87, -96, -107, -115, -126, -134, -144,
-154, -164, -174, -183, -193, -203, -213, -222,
-233, -242, -252, -262, -271, -281, -291, -301,
-310, -320, -330, -339, -349, -357, -368, -377,
-387, -397, -406, -417, -426, -436, -446, -456,
-467, -477, -487, -499, -509, -521, -532, -543,
-555, -567, -579, -591, -603, -616, -628, -641,
-653, -666, -679, -692, -705, -717, -732, -743,
-758, -769, -783, -795, -808, -820, -834, -845,
-860, -872, -885, -898, -911, -926, -939, -955,
-968, -986, -999, -1018, -1034, -1054, -1072, -1094,
2583, 2516, 2450, 2384, 2319, 2254, 2191, 2127,
2067, 2005, 1946, 1890, 1832, 1779, 1726, 1675,
1626, 1579, 1533, 1491, 1449, 1409, 1372, 1336,
1302, 1271, 1239, 1211, 1184, 1158, 1134, 1111,
1089, 1069, 1049, 1031, 1013, 996, 980, 965,
950, 936, 921, 909, 895, 881, 869, 855,
843, 830, 818, 804, 792, 779, 766, 754,
740, 728, 715, 702, 689, 676, 663, 651,
637, 626, 612, 601, 588, 576, 564, 552,
540, 530, 517, 507, 496, 485, 475, 464,
454, 443, 434, 424, 414, 404, 394, 385,
375, 366, 355, 347, 336, 328, 317, 308,
299, 288, 280, 269, 260, 250, 240, 231,
220, 212, 200, 192, 181, 172, 161, 152,
142, 133, 123, 113, 105, 94, 86, 76,
67, 57, 49, 39, 30, 22, 11, 4,
-5, -14, -23, -32, -41, -50, -60, -69,
-78, -87, -96, -107, -115, -126, -134, -144,
-154, -164, -174, -183, -193, -203, -213, -222,
-233, -242, -252, -262, -271, -281, -291, -301,
-310, -320, -330, -339, -349, -357, -368, -377,
-387, -397, -406, -417, -426, -436, -446, -456,
-467, -477, -487, -499, -509, -521, -532, -543,
-555, -567, -579, -591, -603, -616, -628, -641,
-653, -666, -679, -692, -705, -717, -732, -743,
-758, -769, -783, -795, -808, -820, -834, -845,
-860, -872, -885, -898, -911, -926, -939, -955,
-968, -986, -999, -1018, -1034, -1054, -1072, -1094,
-1115, -1138, -1162, -1188, -1215, -1244, -1274, -1307, -1115, -1138, -1162, -1188, -1215, -1244, -1274, -1307,
-1340, -1377, -1415, -1453, -1496, -1538, -1584, -1631, -1340, -1377, -1415, -1453, -1496, -1538, -1584, -1631,
-1680, -1732, -1783, -1839, -1894, -1952, -2010, -2072, -1680, -1732, -1783, -1839, -1894, -1952, -2010, -2072,
-2133, -2196, -2260, -2325, -2390, -2456, -2522, -2589, -2133, -2196, -2260, -2325, -2390, -2456, -2522, -2589,
}; };
static short natural_samples[100] = { static short natural_samples[100] = {
-310, -400, 530, 356, 224, 89, 23, -10, -58, -16, 461, 599, 536, 701, 770,
605, 497, 461, 560, 404, 110, 224, 131, 104, -97, 155, 278, -154, -1165,
-598, 737, 125, -592, 41, 11, -247, -10, 65, 92, 80, -304, 71, 167, -1, 122,
233, 161, -43, 278, 479, 485, 407, 266, 650, 134, 80, 236, 68, 260, 269, 179,
53, 140, 275, 293, 296, 104, 257, 152, 311, 182, 263, 245, 125, 314, 140, 44,
203, 230, -235, -286, 23, 107, 92, -91, 38, 464, 443, 176, 98, -784, -2449,
-310, -400, 530, 356, 224, 89, 23, -10, -58, -16, 461, 599, 536, 701, 770,
605, 497, 461, 560, 404, 110, 224, 131, 104, -97, 155, 278, -154, -1165,
-598, 737, 125, -592, 41, 11, -247, -10, 65, 92, 80, -304, 71, 167, -1, 122,
233, 161, -43, 278, 479, 485, 407, 266, 650, 134, 80, 236, 68, 260, 269, 179,
53, 140, 275, 293, 296, 104, 257, 152, 311, 182, 263, 245, 125, 314, 140, 44,
203, 230, -235, -286, 23, 107, 92, -91, 38, 464, 443, 176, 98, -784, -2449,
-1891, -1045, -1600, -1462, -1384, -1261, -949, -730 -1891, -1045, -1600, -1462, -1384, -1261, -949, -730
}; };




fla = (double)kt_globals.f0_flutter / 50; fla = (double)kt_globals.f0_flutter / 50;
flb = (double)kt_globals.original_f0 / 100; flb = (double)kt_globals.original_f0 / 100;
flc = sin(PI*12.7*time_count); // because we are calling flutter() more frequently, every 2.9mS
flc = sin(PI*12.7*time_count); // because we are calling flutter() more frequently, every 2.9mS
fld = sin(PI*7.1*time_count); fld = sin(PI*7.1*time_count);
fle = sin(PI*4.7*time_count); fle = sin(PI*4.7*time_count);
delta_f0 = fla * flb * (flc + fld + fle) * 10; delta_f0 = fla * flb * (flc + fld + fle) * 10;
static double sourc; static double sourc;
int ix; int ix;


flutter(frame); /* add f0 flutter */
flutter(frame); // add f0 flutter


/* MAIN LOOP, for each output sample of current frame: */
// MAIN LOOP, for each output sample of current frame:


for (kt_globals.ns = 0; kt_globals.ns < kt_globals.nspfr; kt_globals.ns++) { for (kt_globals.ns = 0; kt_globals.ns < kt_globals.nspfr; kt_globals.ns++) {
/* Get low-passed random number for aspiration and frication noise */
// Get low-passed random number for aspiration and frication noise
noise = gen_noise(noise); noise = gen_noise(noise);


/*
Amplitude modulate noise (reduce noise amplitude during
second half of glottal period) if voicing simultaneously present.
*/
// Amplitude modulate noise (reduce noise amplitude during
// second half of glottal period) if voicing simultaneously present.


if (kt_globals.nper > kt_globals.nmod) if (kt_globals.nper > kt_globals.nmod)
noise *= (double)0.5; noise *= (double)0.5;


/* Compute frication noise */
// Compute frication noise
frics = kt_globals.amp_frica * noise; frics = kt_globals.amp_frica * noise;


/*
Compute voicing waveform. Run glottal source simulation at 4
times normal sample rate to minimize quantization noise in
period of female voice.
*/
// Compute voicing waveform. Run glottal source simulation at 4
// times normal sample rate to minimize quantization noise in
// period of female voice.


for (n4 = 0; n4 < 4; n4++) { for (n4 = 0; n4 < 4; n4++) {
switch (kt_globals.glsource) switch (kt_globals.glsource)
break; break;
} }


/* Reset period when counter 'nper' reaches T0 */
// Reset period when counter 'nper' reaches T0
if (kt_globals.nper >= kt_globals.T0) { if (kt_globals.nper >= kt_globals.T0) {
kt_globals.nper = 0; kt_globals.nper = 0;
pitch_synch_par_reset(frame); pitch_synch_par_reset(frame);
} }


/*
Low-pass filter voicing waveform before downsampling from 4*samrate
to samrate samples/sec. Resonator f=.09*samrate, bw=.06*samrate
*/
// Low-pass filter voicing waveform before downsampling from 4*samrate
// to samrate samples/sec. Resonator f=.09*samrate, bw=.06*samrate


voice = resonator(&(kt_globals.rsn[RLP]), voice); voice = resonator(&(kt_globals.rsn[RLP]), voice);


/* Increment counter that keeps track of 4*samrate samples per sec */
// Increment counter that keeps track of 4*samrate samples per sec
kt_globals.nper++; kt_globals.nper++;
} }


/*
Tilt spectrum of voicing source down by soft low-pass filtering, amount
of tilt determined by TLTdb
*/
// Tilt spectrum of voicing source down by soft low-pass filtering, amount
// of tilt determined by TLTdb


voice = (voice * kt_globals.onemd) + (vlast * kt_globals.decay); voice = (voice * kt_globals.onemd) + (vlast * kt_globals.decay);
vlast = voice; vlast = voice;


/*
Add breathiness during glottal open phase. Amount of breathiness
determined by parameter Aturb Use nrand rather than noise because
noise is low-passed.
*/

// Add breathiness during glottal open phase. Amount of breathiness
// determined by parameter Aturb Use nrand rather than noise because
// noise is low-passed.


if (kt_globals.nper < kt_globals.nopen) if (kt_globals.nper < kt_globals.nopen)
voice += kt_globals.amp_breth * kt_globals.nrand; voice += kt_globals.amp_breth * kt_globals.nrand;


/* Set amplitude of voicing */
// Set amplitude of voicing
glotout = kt_globals.amp_voice * voice; glotout = kt_globals.amp_voice * voice;
par_glotout = kt_globals.par_amp_voice * voice; par_glotout = kt_globals.par_amp_voice * voice;


/* Compute aspiration amplitude and add to voicing source */
// Compute aspiration amplitude and add to voicing source
aspiration = kt_globals.amp_aspir * noise; aspiration = kt_globals.amp_aspir * noise;
glotout += aspiration; glotout += aspiration;


par_glotout += aspiration; par_glotout += aspiration;


/*
Cascade vocal tract, excited by laryngeal sources.
Nasal antiresonator, then formants FNP, F5, F4, F3, F2, F1
*/
// Cascade vocal tract, excited by laryngeal sources.
// Nasal antiresonator, then formants FNP, F5, F4, F3, F2, F1


out = 0; out = 0;
if (kt_globals.synthesis_model != ALL_PARALLEL) { if (kt_globals.synthesis_model != ALL_PARALLEL) {
out = resonator2(&(kt_globals.rsn[R1c]), casc_next_in); out = resonator2(&(kt_globals.rsn[R1c]), casc_next_in);
} }


/* Excite parallel F1 and FNP by voicing waveform */
sourc = par_glotout; /* Source is voicing plus aspiration */
// Excite parallel F1 and FNP by voicing waveform
sourc = par_glotout; // Source is voicing plus aspiration


/*
Standard parallel vocal tract Formants F6,F5,F4,F3,F2,
outputs added with alternating sign. Sound source for other
parallel resonators is frication plus first difference of
voicing waveform.
*/
// Standard parallel vocal tract Formants F6,F5,F4,F3,F2,
// outputs added with alternating sign. Sound source for other
// parallel resonators is frication plus first difference of
// voicing waveform.


out += resonator(&(kt_globals.rsn[R1p]), sourc); out += resonator(&(kt_globals.rsn[R1p]), sourc);
out += resonator(&(kt_globals.rsn[Rnpp]), sourc); out += resonator(&(kt_globals.rsn[Rnpp]), sourc);
out = outbypas - out; out = outbypas - out;


out = resonator(&(kt_globals.rsn[Rout]), out); out = resonator(&(kt_globals.rsn[Rout]), out);
temp = (int)(out * wdata.amplitude * kt_globals.amp_gain0); /* Convert back to integer */

temp = (int)(out * wdata.amplitude * kt_globals.amp_gain0); // Convert back to integer


// mix with a recorded WAV if required for this phoneme // mix with a recorded WAV if required for this phoneme
{
int z2;
signed char c;
int sample;

z2 = 0;
if (wdata.mix_wavefile_ix < wdata.n_mix_wavefile) {
if (wdata.mix_wave_scale == 0) {
// a 16 bit sample
c = wdata.mix_wavefile[wdata.mix_wavefile_ix+1];
sample = wdata.mix_wavefile[wdata.mix_wavefile_ix] + (c * 256);
wdata.mix_wavefile_ix += 2;
} else {
// a 8 bit sample, scaled
sample = (signed char)wdata.mix_wavefile[wdata.mix_wavefile_ix++] * wdata.mix_wave_scale;
}
z2 = sample * wdata.amplitude_v / 1024;
z2 = (z2 * wdata.mix_wave_amp)/40;
temp += z2;
int z2;
signed char c;
int sample;

z2 = 0;
if (wdata.mix_wavefile_ix < wdata.n_mix_wavefile) {
if (wdata.mix_wave_scale == 0) {
// a 16 bit sample
c = wdata.mix_wavefile[wdata.mix_wavefile_ix+1];
sample = wdata.mix_wavefile[wdata.mix_wavefile_ix] + (c * 256);
wdata.mix_wavefile_ix += 2;
} else {
// a 8 bit sample, scaled
sample = (signed char)wdata.mix_wavefile[wdata.mix_wavefile_ix++] * wdata.mix_wave_scale;
} }
z2 = sample * wdata.amplitude_v / 1024;
z2 = (z2 * wdata.mix_wave_amp)/40;
temp += z2;
} }


// if fadeout is set, fade to zero over 64 samples, to avoid clicks at end of synthesis // if fadeout is set, fade to zero over 64 samples, to avoid clicks at end of synthesis
kt_globals.minus_pi_t = -PI / kt_globals.samrate; kt_globals.minus_pi_t = -PI / kt_globals.samrate;
kt_globals.two_pi_t = -2.0 * kt_globals.minus_pi_t; kt_globals.two_pi_t = -2.0 * kt_globals.minus_pi_t;
setabc(kt_globals.FLPhz, kt_globals.BLPhz, &(kt_globals.rsn[RLP])); setabc(kt_globals.FLPhz, kt_globals.BLPhz, &(kt_globals.rsn[RLP]));

} }


if (control > 0) { if (control > 0) {
Gain0_tmp = 57; Gain0_tmp = 57;
kt_globals.amp_gain0 = DBtoLIN(Gain0_tmp) / kt_globals.scale_wav; kt_globals.amp_gain0 = DBtoLIN(Gain0_tmp) / kt_globals.scale_wav;


/* Set coefficients of variable cascade resonators */
// Set coefficients of variable cascade resonators
for (ix = 1; ix <= 9; ix++) { for (ix = 1; ix <= 9; ix++) {
// formants 1 to 8, plus nasal pole // formants 1 to 8, plus nasal pole
setabc(frame->Fhz[ix], frame->Bhz[ix], &(kt_globals.rsn[ix])); setabc(frame->Fhz[ix], frame->Bhz[ix], &(kt_globals.rsn[ix]));
kt_globals.rsn[F_NZ].b_inc = (kt_globals.rsn_next[F_NZ].b - kt_globals.rsn[F_NZ].b) / 64.0; kt_globals.rsn[F_NZ].b_inc = (kt_globals.rsn_next[F_NZ].b - kt_globals.rsn[F_NZ].b) / 64.0;
kt_globals.rsn[F_NZ].c_inc = (kt_globals.rsn_next[F_NZ].c - kt_globals.rsn[F_NZ].c) / 64.0; kt_globals.rsn[F_NZ].c_inc = (kt_globals.rsn_next[F_NZ].c - kt_globals.rsn[F_NZ].c) / 64.0;


/* Set coefficients of parallel resonators, and amplitude of outputs */
// Set coefficients of parallel resonators, and amplitude of outputs


for (ix = 0; ix <= 6; ix++) { for (ix = 0; ix <= 6; ix++) {
setabc(frame->Fhz[ix], frame->Bphz[ix], &(kt_globals.rsn[Rparallel+ix])); setabc(frame->Fhz[ix], frame->Bphz[ix], &(kt_globals.rsn[Rparallel+ix]));
kt_globals.rsn[Rparallel+ix].a *= amp_par[ix]; kt_globals.rsn[Rparallel+ix].a *= amp_par[ix];
} }


/* output low-pass filter */
// output low-pass filter


setabc((long)0.0, (long)(kt_globals.samrate/2), &(kt_globals.rsn[Rout])); setabc((long)0.0, (long)(kt_globals.samrate/2), &(kt_globals.rsn[Rout]));
} }
static long skew; static long skew;
static short B0[224] = { static short B0[224] = {
1200, 1142, 1088, 1038, 991, 948, 907, 869, 833, 799, 768, 738, 710, 683, 658, 1200, 1142, 1088, 1038, 991, 948, 907, 869, 833, 799, 768, 738, 710, 683, 658,
634, 612, 590, 570, 551, 533, 515, 499, 483, 468, 454, 440, 427, 415, 403,
391, 380, 370, 360, 350, 341, 332, 323, 315, 307, 300, 292, 285, 278, 272,
265, 259, 253, 247, 242, 237, 231, 226, 221, 217, 212, 208, 204, 199, 195,
192, 188, 184, 180, 177, 174, 170, 167, 164, 161, 158, 155, 153, 150, 147,
145, 142, 140, 137, 135, 133, 131, 128, 126, 124, 122, 120, 119, 117, 115,
113, 111, 110, 108, 106, 105, 103, 102, 100, 99, 97, 96, 95, 93, 92, 91, 90,
88, 87, 86, 85, 84, 83, 82, 80, 79, 78, 77, 76, 75, 75, 74, 73, 72, 71,
70, 69, 68, 68, 67, 66, 65, 64, 64, 63, 62, 61, 61, 60, 59, 59, 58, 57,
57, 56, 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, 49, 49, 48, 48,
47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 42, 42, 41, 41, 41, 41, 40, 40,
39, 39, 38, 38, 38, 38, 37, 37, 36, 36, 36, 36, 35, 35, 35, 35, 34, 34, 33,
33, 33, 33, 32, 32, 32, 32, 31, 31, 31, 31, 30, 30, 30, 30, 29, 29, 29, 29,
28, 28, 28, 28, 27, 27
634, 612, 590, 570, 551, 533, 515, 499, 483, 468, 454, 440, 427, 415, 403,
391, 380, 370, 360, 350, 341, 332, 323, 315, 307, 300, 292, 285, 278, 272,
265, 259, 253, 247, 242, 237, 231, 226, 221, 217, 212, 208, 204, 199, 195,
192, 188, 184, 180, 177, 174, 170, 167, 164, 161, 158, 155, 153, 150, 147,
145, 142, 140, 137, 135, 133, 131, 128, 126, 124, 122, 120, 119, 117, 115,
113, 111, 110, 108, 106, 105, 103, 102, 100, 99, 97, 96, 95, 93, 92, 91, 90,
88, 87, 86, 85, 84, 83, 82, 80, 79, 78, 77, 76, 75, 75, 74, 73, 72, 71,
70, 69, 68, 68, 67, 66, 65, 64, 64, 63, 62, 61, 61, 60, 59, 59, 58, 57,
57, 56, 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, 49, 49, 48, 48,
47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 42, 42, 41, 41, 41, 41, 40, 40,
39, 39, 38, 38, 38, 38, 37, 37, 36, 36, 36, 36, 35, 35, 35, 35, 34, 34, 33,
33, 33, 33, 32, 32, 32, 32, 31, 31, 31, 31, 30, 30, 30, 30, 29, 29, 29, 29,
28, 28, 28, 28, 27, 27
}; };


if (frame->F0hz10 > 0) { if (frame->F0hz10 > 0) {
/* T0 is 4* the number of samples in one pitch period */
// T0 is 4* the number of samples in one pitch period


kt_globals.T0 = (40 * kt_globals.samrate) / frame->F0hz10; kt_globals.T0 = (40 * kt_globals.samrate) / frame->F0hz10;



kt_globals.amp_voice = DBtoLIN(frame->AVdb_tmp); kt_globals.amp_voice = DBtoLIN(frame->AVdb_tmp);


/* Duration of period before amplitude modulation */
// Duration of period before amplitude modulation


kt_globals.nmod = kt_globals.T0; kt_globals.nmod = kt_globals.T0;
if (frame->AVdb_tmp > 0) if (frame->AVdb_tmp > 0)
kt_globals.nmod >>= 1; kt_globals.nmod >>= 1;


/* Breathiness of voicing waveform */
// Breathiness of voicing waveform


kt_globals.amp_breth = DBtoLIN(frame->Aturb) * 0.1; kt_globals.amp_breth = DBtoLIN(frame->Aturb) * 0.1;


/* Set open phase of glottal period where 40 <= open phase <= 263 */
// Set open phase of glottal period where 40 <= open phase <= 263


kt_globals.nopen = 4 * frame->Kopen; kt_globals.nopen = 4 * frame->Kopen;


kt_globals.nopen = kt_globals.T0 - 2; kt_globals.nopen = kt_globals.T0 - 2;


if (kt_globals.nopen < 40) { if (kt_globals.nopen < 40) {
/* F0 max = 1000 Hz */
// F0 max = 1000 Hz
kt_globals.nopen = 40; kt_globals.nopen = 40;
} }


/* Reset a & b, which determine shape of "natural" glottal waveform */
// Reset a & b, which determine shape of "natural" glottal waveform


kt_globals.pulse_shape_b = B0[kt_globals.nopen-40]; kt_globals.pulse_shape_b = B0[kt_globals.nopen-40];
kt_globals.pulse_shape_a = (kt_globals.pulse_shape_b * kt_globals.nopen) * 0.333; kt_globals.pulse_shape_a = (kt_globals.pulse_shape_b * kt_globals.nopen) * 0.333;


/* Reset width of "impulsive" glottal pulse */
// Reset width of "impulsive" glottal pulse


temp = kt_globals.samrate / kt_globals.nopen; temp = kt_globals.samrate / kt_globals.nopen;


setabc((long)0, temp, &(kt_globals.rsn[RGL])); setabc((long)0, temp, &(kt_globals.rsn[RGL]));


/* Make gain at F1 about constant */
// Make gain at F1 about constant


temp1 = kt_globals.nopen *.00833; temp1 = kt_globals.nopen *.00833;
kt_globals.rsn[RGL].a *= temp1 * temp1; kt_globals.rsn[RGL].a *= temp1 * temp1;


/*
Truncate skewness so as not to exceed duration of closed phase
of glottal period.
*/

// Truncate skewness so as not to exceed duration of closed phase
// of glottal period.


temp = kt_globals.T0 - kt_globals.nopen; temp = kt_globals.T0 - kt_globals.nopen;
if (frame->Kskew > temp) if (frame->Kskew > temp)
else else
skew = -frame->Kskew; skew = -frame->Kskew;


/* Add skewness to closed portion of voicing period */
// Add skewness to closed portion of voicing period
kt_globals.T0 = kt_globals.T0 + skew; kt_globals.T0 = kt_globals.T0 + skew;
skew = -skew; skew = -skew;
} else { } else {
kt_globals.T0 = 4; /* Default for f0 undefined */
kt_globals.T0 = 4; // Default for f0 undefined
kt_globals.amp_voice = 0.0; kt_globals.amp_voice = 0.0;
kt_globals.nmod = kt_globals.T0; kt_globals.nmod = kt_globals.T0;
kt_globals.amp_breth = 0.0; kt_globals.amp_breth = 0.0;
kt_globals.pulse_shape_b = 0.0; kt_globals.pulse_shape_b = 0.0;
} }


/* Reset these pars pitch synchronously or at update rate if f0=0 */
// Reset these pars pitch synchronously or at update rate if f0=0


if ((kt_globals.T0 != 4) || (kt_globals.ns == 0)) { if ((kt_globals.T0 != 4) || (kt_globals.ns == 0)) {
/* Set one-pole low-pass filter that tilts glottal source */
// Set one-pole low-pass filter that tilts glottal source


kt_globals.decay = (0.033 * frame->TLTdb); kt_globals.decay = (0.033 * frame->TLTdb);


double r; double r;
double arg; double arg;


/* Let r = exp(-pi bw t) */
// Let r = exp(-pi bw t)
arg = kt_globals.minus_pi_t * bw; arg = kt_globals.minus_pi_t * bw;
r = exp(arg); r = exp(arg);


/* Let c = -r**2 */
// Let c = -r**2
rp->c = -(r * r); rp->c = -(r * r);


/* Let b = r * 2*cos(2 pi f t) */
// Let b = r * 2*cos(2 pi f t)
arg = kt_globals.two_pi_t * f; arg = kt_globals.two_pi_t * f;
rp->b = r * cos(arg) * 2.0; rp->b = r * cos(arg) * 2.0;


/* Let a = 1.0 - b - c */
// Let a = 1.0 - b - c
rp->a = 1.0 - rp->b - rp->c; rp->a = 1.0 - rp->b - rp->c;
} }




f = -f; f = -f;


/* First compute ordinary resonator coefficients */
/* Let r = exp(-pi bw t) */
// First compute ordinary resonator coefficients
// Let r = exp(-pi bw t)
arg = kt_globals.minus_pi_t * bw; arg = kt_globals.minus_pi_t * bw;
r = exp(arg); r = exp(arg);


/* Let c = -r**2 */
// Let c = -r**2
rp->c = -(r * r); rp->c = -(r * r);


/* Let b = r * 2*cos(2 pi f t) */
// Let b = r * 2*cos(2 pi f t)
arg = kt_globals.two_pi_t * f; arg = kt_globals.two_pi_t * f;
rp->b = r * cos(arg) * 2.; rp->b = r * cos(arg) * 2.;


/* Let a = 1.0 - b - c */
// Let a = 1.0 - b - c
rp->a = 1.0 - rp->b - rp->c; rp->a = 1.0 - rp->b - rp->c;


/* Now convert to antiresonator coefficients (a'=1/a, b'=b/a, c'=c/a) */
/* If f == 0 then rp->a gets set to 0 which makes a'=1/a set a', b' and c' to
* INF, causing an audible sound spike when triggered (e.g. apiration with the
* nasal register set to f=0, bw=0).
*/
// Now convert to antiresonator coefficients (a'=1/a, b'=b/a, c'=c/a)
// If f == 0 then rp->a gets set to 0 which makes a'=1/a set a', b' and c' to
// INF, causing an audible sound spike when triggered (e.g. apiration with the
// nasal register set to f=0, bw=0).
if (rp->a != 0) { if (rp->a != 0) {
/* Now convert to antiresonator coefficients (a'=1/a, b'=b/a, c'=c/a) */
// Now convert to antiresonator coefficients (a'=1/a, b'=b/a, c'=c/a)
rp->a = 1.0 / rp->a; rp->a = 1.0 / rp->a;
rp->c *= -rp->a; rp->c *= -rp->a;
rp->b *= -rp->a; rp->b *= -rp->a;
static double DBtoLIN(long dB) static double DBtoLIN(long dB)
{ {
static short amptable[88] = { static short amptable[88] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7,
8, 9, 10, 11, 13, 14, 16, 18, 20, 22, 25, 28, 32,
35, 40, 45, 51, 57, 64, 71, 80, 90, 101, 114, 128,
142, 159, 179, 202, 227, 256, 284, 318, 359, 405,
455, 512, 568, 638, 719, 881, 911, 1024, 1137, 1276,
1438, 1622, 1823, 2048, 2273, 2552, 2875, 3244, 3645,
4096, 4547, 5104, 5751, 6488, 7291, 8192, 9093, 10207,
11502, 12976, 14582, 16384, 18350, 20644, 23429,
26214, 29491, 32767
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7,
8, 9, 10, 11, 13, 14, 16, 18, 20, 22, 25, 28, 32,
35, 40, 45, 51, 57, 64, 71, 80, 90, 101, 114, 128,
142, 159, 179, 202, 227, 256, 284, 318, 359, 405,
455, 512, 568, 638, 719, 881, 911, 1024, 1137, 1276,
1438, 1622, 1823, 2048, 2273, 2552, 2875, 3244, 3645,
4096, 4547, 5104, 5751, 6488, 7291, 8192, 9093, 10207,
11502, 12976, 14582, 16384, 18350, 20644, 23429,
26214, 29491, 32767
}; };


if ((dB < 0) || (dB > 87)) if ((dB < 0) || (dB > 87))
if (kt_globals.nspfr > STEPSIZE) if (kt_globals.nspfr > STEPSIZE)
kt_globals.nspfr = STEPSIZE; kt_globals.nspfr = STEPSIZE;


frame_init(&kt_frame); /* get parameters for next frame of speech */
frame_init(&kt_frame); // get parameters for next frame of speech


if (parwave(&kt_frame) == 1) if (parwave(&kt_frame) == 1)
return 1; // output buffer is full
return 1; // output buffer is full
} }


if (end_wave > 0) { if (end_wave > 0) {
fade = 64; // not followd by formant synthesis
fade = 64; // not followed by formant synthesis


// fade out to avoid a click // fade out to avoid a click
kt_globals.fadeout = fade; kt_globals.fadeout = fade;
sample_count -= fade; sample_count -= fade;
kt_globals.nspfr = fade; kt_globals.nspfr = fade;
if (parwave(&kt_frame) == 1) if (parwave(&kt_frame) == 1)
return 1; // output buffer is full
return 1; // output buffer is full
} }


return 0; return 0;


end_wave = 0; end_wave = 0;
if (control & 2) if (control & 2)
end_wave = 1; // fadeout at the end
end_wave = 1; // fadeout at the end
if (control & 1) { if (control & 1) {
end_wave = 1; end_wave = 1;
for (qix = wcmdq_head+1;; qix++) { for (qix = wcmdq_head+1;; qix++) {


cmd = wcmdq[qix][0]; cmd = wcmdq[qix][0];
if (cmd == WCMD_KLATT) { if (cmd == WCMD_KLATT) {
end_wave = 0; // next wave generation is from another spectrum
end_wave = 0; // next wave generation is from another spectrum


fr3 = (frame_t *)wcmdq[qix][2]; fr3 = (frame_t *)wcmdq[qix][2];
for (ix = 1; ix < 6; ix++) { for (ix = 1; ix < 6; ix++) {
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
} }
} }


// nasal zero frequency // nasal zero frequency
peaks[0].freq1 = fr1->klattp[KLATT_FNZ] * 2; peaks[0].freq1 = fr1->klattp[KLATT_FNZ] * 2;
if (peaks[0].freq1 == 0) if (peaks[0].freq1 == 0)
peaks[0].freq1 = kt_frame.Fhz[F_NP]; // if no nasal zero, set it to same freq as nasal pole
peaks[0].freq1 = kt_frame.Fhz[F_NP]; // if no nasal zero, set it to same freq as nasal pole


peaks[0].freq = (int)peaks[0].freq1; peaks[0].freq = (int)peaks[0].freq1;
next = fr2->klattp[KLATT_FNZ] * 2; next = fr2->klattp[KLATT_FNZ] * 2;
if (fr1->frflags & FRFLAG_KLATT) { if (fr1->frflags & FRFLAG_KLATT) {
// the frame contains additional parameters for parallel resonators // the frame contains additional parameters for parallel resonators
for (ix = 1; ix < 7; ix++) { for (ix = 1; ix < 7; ix++) {
peaks[ix].bp1 = fr1->klatt_bp[ix] * 4; // parallel bandwidth
peaks[ix].bp1 = fr1->klatt_bp[ix] * 4; // parallel bandwidth
peaks[ix].bp = (int)peaks[ix].bp1; peaks[ix].bp = (int)peaks[ix].bp1;
next = fr2->klatt_bp[ix] * 4; next = fr2->klatt_bp[ix] * 4;
peaks[ix].bp_inc = ((next - peaks[ix].bp1) * STEPSIZE) / length; peaks[ix].bp_inc = ((next - peaks[ix].bp1) * STEPSIZE) / length;


peaks[ix].ap1 = fr1->klatt_ap[ix]; // parallal amplitude
peaks[ix].ap1 = fr1->klatt_ap[ix]; // parallal amplitude
peaks[ix].ap = (int)peaks[ix].ap1; peaks[ix].ap = (int)peaks[ix].ap1;
next = fr2->klatt_ap[ix]; next = fr2->klatt_ap[ix];
peaks[ix].ap_inc = ((next - peaks[ix].ap1) * STEPSIZE) / length; peaks[ix].ap_inc = ((next - peaks[ix].ap1) * STEPSIZE) / length;
kt_globals.synthesis_model = CASCADE_PARALLEL; kt_globals.synthesis_model = CASCADE_PARALLEL;
kt_globals.samrate = 22050; kt_globals.samrate = 22050;


kt_globals.glsource = IMPULSIVE; // IMPULSIVE, NATURAL, SAMPLED
kt_globals.glsource = IMPULSIVE;
kt_globals.scale_wav = scale_wav_tab[kt_globals.glsource]; kt_globals.scale_wav = scale_wav_tab[kt_globals.glsource];
kt_globals.natural_samples = natural_samples; kt_globals.natural_samples = natural_samples;
kt_globals.num_samples = NUMBER_OF_SAMPLES; kt_globals.num_samples = NUMBER_OF_SAMPLES;
kt_frame.Bhz_next[F_NZ] = bandwidth[F_NZ]; kt_frame.Bhz_next[F_NZ] = bandwidth[F_NZ];


kt_frame.F0hz10 = 1000; kt_frame.F0hz10 = 1000;
kt_frame.AVdb = 59; // 59
kt_frame.AVdb = 59;
kt_frame.ASP = 0; kt_frame.ASP = 0;
kt_frame.Kopen = 40; // 40
kt_frame.Kopen = 40;
kt_frame.Aturb = 0; kt_frame.Aturb = 0;
kt_frame.TLTdb = 0; kt_frame.TLTdb = 0;
kt_frame.AF = 50; kt_frame.AF = 50;
kt_frame.Kskew = 0; kt_frame.Kskew = 0;
kt_frame.AB = 0; kt_frame.AB = 0;
kt_frame.AVpdb = 0; kt_frame.AVpdb = 0;
kt_frame.Gain0 = 62; // 60
kt_frame.Gain0 = 62;
} }

+ 10
- 11
src/libespeak-ng/mbrowrap.c View File

struct datablock *next; struct datablock *next;
int done; int done;
int size; int size;
char buffer[1]; /* 1 or more, dynamically allocated */
char buffer[1]; // 1 or more, dynamically allocated
}; };


static struct datablock *mbr_pending_data_head, *mbr_pending_data_tail; static struct datablock *mbr_pending_data_head, *mbr_pending_data_tail;
} }


if (result == 0) { if (result == 0) {
/* EOF on stderr, assume mbrola died. */
// EOF on stderr, assume mbrola died.
return mbrola_died(); return mbrola_died();
} }


buf_ptr[result] = 0; buf_ptr[result] = 0;


for (; (lf = strchr(buf_ptr, '\n')); buf_ptr = lf + 1) { for (; (lf = strchr(buf_ptr, '\n')); buf_ptr = lf + 1) {
/* inhibit the reset signal messages */
// inhibit the reset signal messages
if (strncmp(buf_ptr, "Got a reset signal", 18) == 0 || if (strncmp(buf_ptr, "Got a reset signal", 18) == 0 ||
strncmp(buf_ptr, "Input Flush Signal", 18) == 0) strncmp(buf_ptr, "Input Flush Signal", 18) == 0)
continue; continue;
*lf = 0; *lf = 0;
log("mbrola: %s", buf_ptr); log("mbrola: %s", buf_ptr);
/* is this the last line? */
// is this the last line?
if (lf == &buf_ptr[result - 1]) { if (lf == &buf_ptr[result - 1]) {
snprintf(mbr_errorbuf, sizeof(mbr_errorbuf), snprintf(mbr_errorbuf, sizeof(mbr_errorbuf),
"%s", buf_ptr); "%s", buf_ptr);
/* don't consider this fatal at this point */
// don't consider this fatal at this point
return 0; return 0;
} }
} }
static int mbrola_is_idle(void) static int mbrola_is_idle(void)
{ {
char *p; char *p;
char buffer[20]; /* looking for "12345 (mbrola) S" so 20 is plenty*/
char buffer[20]; // looking for "12345 (mbrola) S" so 20 is plenty


/* look in /proc to determine if mbrola is still running or sleeping */
// look in /proc to determine if mbrola is still running or sleeping
if (lseek(mbr_proc_stat, 0, SEEK_SET) != 0) if (lseek(mbr_proc_stat, 0, SEEK_SET) != 0)
return 0; return 0;
if (read(mbr_proc_stat, buffer, sizeof(buffer)) != sizeof(buffer)) if (read(mbr_proc_stat, buffer, sizeof(buffer)) != sizeof(buffer))
return -1; return -1;
} }


/* we should actually be getting only 44 bytes */
// we should actually be getting only 44 bytes
result = receive_from_mbrola(wavhdr, 45); result = receive_from_mbrola(wavhdr, 45);
if (result != 44) { if (result != 44) {
if (result >= 0) if (result >= 0)
return -1; return -1;
} }


/* parse wavhdr to get mbrola voice samplerate */
// parse wavhdr to get mbrola voice samplerate
if (memcmp(wavhdr, "RIFF", 4) != 0 || if (memcmp(wavhdr, "RIFF", 4) != 0 ||
memcmp(wavhdr+8, "WAVEfmt ", 8) != 0) { memcmp(wavhdr+8, "WAVEfmt ", 8) != 0) {
err("mbrola did not return a .wav header"); err("mbrola did not return a .wav header");
} }
mbr_samplerate = wavhdr[24] + (wavhdr[25]<<8) + mbr_samplerate = wavhdr[24] + (wavhdr[25]<<8) +
(wavhdr[26]<<16) + (wavhdr[27]<<24); (wavhdr[26]<<16) + (wavhdr[27]<<24);
// log("mbrowrap: voice samplerate = %d", mbr_samplerate);


/* remember the voice path for setVolumeRatio_MBR() */
// remember the voice path for setVolumeRatio_MBR()
if (mbr_voice_path != voice_path) { if (mbr_voice_path != voice_path) {
free(mbr_voice_path); free(mbr_voice_path);
mbr_voice_path = strdup(voice_path); mbr_voice_path = strdup(voice_path);

+ 163
- 157
src/libespeak-ng/numbers.c View File

#define M_RETROFLEX 20 #define M_RETROFLEX 20
#define M_HOOK 21 #define M_HOOK 21


#define M_MIDDLE_DOT M_DOT_ABOVE // duplicate of M_DOT_ABOVE
#define M_MIDDLE_DOT M_DOT_ABOVE // duplicate of M_DOT_ABOVE
#define M_IMPLOSIVE M_HOOK #define M_IMPLOSIVE M_HOOK


static int n_digit_lookup; static int n_digit_lookup;
#define LETTER(ch, mod1, mod2) (ch-59)+(mod1 << 6)+(mod2 << 11) #define LETTER(ch, mod1, mod2) (ch-59)+(mod1 << 6)+(mod2 << 11)
#define LIGATURE(ch1, ch2, mod1) (ch1-59)+((ch2-59) << 6)+(mod1 << 12)+M_LIGATURE #define LIGATURE(ch1, ch2, mod1) (ch1-59)+((ch2-59) << 6)+(mod1 << 12)+M_LIGATURE


#define L_ALPHA 60 // U+3B1
#define L_SCHWA 61 // U+259
#define L_OPEN_E 62 // U+25B
#define L_GAMMA 63 // U+3B3
#define L_IOTA 64 // U+3B9
#define L_OE 65 // U+153
#define L_OMEGA 66 // U+3C9


#define L_ALPHA 60 // U+3B1
#define L_SCHWA 61 // U+259
#define L_OPEN_E 62 // U+25B
#define L_GAMMA 63 // U+3B3
#define L_IOTA 64 // U+3B9
#define L_OE 65 // U+153
#define L_OMEGA 66 // U+3C9

#define L_PHI 67 // U+3C6
#define L_ESH 68 // U+283
#define L_PHI 67 // U+3C6
#define L_ESH 68 // U+283
#define L_UPSILON 69 // U+3C5 #define L_UPSILON 69 // U+3C5
#define L_EZH 70 // U+292 #define L_EZH 70 // U+292
#define L_GLOTTAL 71 // U+294 #define L_GLOTTAL 71 // U+294
#define L_RLONG 73 // U+27C #define L_RLONG 73 // U+27C


static const short non_ascii_tab[] = { static const short non_ascii_tab[] = {
0, 0x3b1, 0x259, 0x25b, 0x3b3, 0x3b9, 0x153, 0x3c9,
0,
0x3b1, 0x259, 0x25b, 0x3b3, 0x3b9, 0x153, 0x3c9,
0x3c6, 0x283, 0x3c5, 0x292, 0x294, 0x27e, 0x27c 0x3c6, 0x283, 0x3c5, 0x292, 0x294, 0x27e, 0x27c
}; };


// characters U+00e0 to U+017f // characters U+00e0 to U+017f
static const unsigned short letter_accents_0e0[] = { static const unsigned short letter_accents_0e0[] = {
LETTER('a', M_GRAVE, 0), // U+00e0
LETTER('a', M_GRAVE, 0), // U+00e0
LETTER('a', M_ACUTE, 0), LETTER('a', M_ACUTE, 0),
LETTER('a', M_CIRCUMFLEX, 0), LETTER('a', M_CIRCUMFLEX, 0),
LETTER('a', M_TILDE, 0), LETTER('a', M_TILDE, 0),
LETTER('i', M_ACUTE, 0), LETTER('i', M_ACUTE, 0),
LETTER('i', M_CIRCUMFLEX, 0), LETTER('i', M_CIRCUMFLEX, 0),
LETTER('i', M_DIAERESIS, 0), LETTER('i', M_DIAERESIS, 0),
LETTER('d', M_NAME, 0), // eth // U+00f0
LETTER('d', M_NAME, 0), // eth U+00f0
LETTER('n', M_TILDE, 0), LETTER('n', M_TILDE, 0),
LETTER('o', M_GRAVE, 0), LETTER('o', M_GRAVE, 0),
LETTER('o', M_ACUTE, 0), LETTER('o', M_ACUTE, 0),
LETTER('o', M_CIRCUMFLEX, 0), LETTER('o', M_CIRCUMFLEX, 0),
LETTER('o', M_TILDE, 0), LETTER('o', M_TILDE, 0),
LETTER('o', M_DIAERESIS, 0), LETTER('o', M_DIAERESIS, 0),
0, // division sign
0, // division sign
LETTER('o', M_STROKE, 0), LETTER('o', M_STROKE, 0),
LETTER('u', M_GRAVE, 0), LETTER('u', M_GRAVE, 0),
LETTER('u', M_ACUTE, 0), LETTER('u', M_ACUTE, 0),
LETTER('u', M_CIRCUMFLEX, 0), LETTER('u', M_CIRCUMFLEX, 0),
LETTER('u', M_DIAERESIS, 0), LETTER('u', M_DIAERESIS, 0),
LETTER('y', M_ACUTE, 0), LETTER('y', M_ACUTE, 0),
LETTER('t', M_NAME, 0), // thorn
LETTER('t', M_NAME, 0), // thorn
LETTER('y', M_DIAERESIS, 0), LETTER('y', M_DIAERESIS, 0),
CAPITAL, // U+0100
CAPITAL, // U+0100
LETTER('a', M_MACRON, 0), LETTER('a', M_MACRON, 0),
CAPITAL, CAPITAL,
LETTER('a', M_BREVE, 0), LETTER('a', M_BREVE, 0),
LETTER('c', M_CARON, 0), LETTER('c', M_CARON, 0),
CAPITAL, CAPITAL,
LETTER('d', M_CARON, 0), LETTER('d', M_CARON, 0),
CAPITAL, // U+0110
CAPITAL, // U+0110
LETTER('d', M_STROKE, 0), LETTER('d', M_STROKE, 0),
CAPITAL, CAPITAL,
LETTER('e', M_MACRON, 0), LETTER('e', M_MACRON, 0),
LETTER('g', M_CIRCUMFLEX, 0), LETTER('g', M_CIRCUMFLEX, 0),
CAPITAL, CAPITAL,
LETTER('g', M_BREVE, 0), LETTER('g', M_BREVE, 0),
CAPITAL, // U+0120
CAPITAL, // U+0120
LETTER('g', M_DOT_ABOVE, 0), LETTER('g', M_DOT_ABOVE, 0),
CAPITAL, CAPITAL,
LETTER('g', M_CEDILLA, 0), LETTER('g', M_CEDILLA, 0),
LETTER('i', M_BREVE, 0), LETTER('i', M_BREVE, 0),
CAPITAL, CAPITAL,
LETTER('i', M_OGONEK, 0), LETTER('i', M_OGONEK, 0),
CAPITAL, // U+0130
CAPITAL, // U+0130
LETTER('i', M_NAME, 0), // dotless i LETTER('i', M_NAME, 0), // dotless i
CAPITAL, CAPITAL,
LIGATURE('i', 'j', 0), LIGATURE('i', 'j', 0),
LETTER('j', M_CIRCUMFLEX, 0), LETTER('j', M_CIRCUMFLEX, 0),
CAPITAL, CAPITAL,
LETTER('k', M_CEDILLA, 0), LETTER('k', M_CEDILLA, 0),
LETTER('k', M_NAME, 0), // kra
LETTER('k', M_NAME, 0), // kra
CAPITAL, CAPITAL,
LETTER('l', M_ACUTE, 0), LETTER('l', M_ACUTE, 0),
CAPITAL, CAPITAL,
CAPITAL, CAPITAL,
LETTER('l', M_CARON, 0), LETTER('l', M_CARON, 0),
CAPITAL, CAPITAL,
LETTER('l', M_MIDDLE_DOT, 0), // U+0140
LETTER('l', M_MIDDLE_DOT, 0), // U+0140
CAPITAL, CAPITAL,
LETTER('l', M_STROKE, 0), LETTER('l', M_STROKE, 0),
CAPITAL, CAPITAL,
LETTER('n', M_CEDILLA, 0), LETTER('n', M_CEDILLA, 0),
CAPITAL, CAPITAL,
LETTER('n', M_CARON, 0), LETTER('n', M_CARON, 0),
LETTER('n', M_NAME, 0), // apostrophe n
LETTER('n', M_NAME, 0), // apostrophe n
CAPITAL, CAPITAL,
LETTER('n', M_NAME, 0), // eng
LETTER('n', M_NAME, 0), // eng
CAPITAL, CAPITAL,
LETTER('o', M_MACRON, 0), LETTER('o', M_MACRON, 0),
CAPITAL, CAPITAL,
LETTER('o', M_BREVE, 0), LETTER('o', M_BREVE, 0),
CAPITAL, // U+0150
CAPITAL, // U+0150
LETTER('o', M_DOUBLE_ACUTE, 0), LETTER('o', M_DOUBLE_ACUTE, 0),
CAPITAL, CAPITAL,
LIGATURE('o', 'e', 0), LIGATURE('o', 'e', 0),
LETTER('s', M_CIRCUMFLEX, 0), LETTER('s', M_CIRCUMFLEX, 0),
CAPITAL, CAPITAL,
LETTER('s', M_CEDILLA, 0), LETTER('s', M_CEDILLA, 0),
CAPITAL, // U+0160
CAPITAL, // U+0160
LETTER('s', M_CARON, 0), LETTER('s', M_CARON, 0),
CAPITAL, CAPITAL,
LETTER('t', M_CEDILLA, 0), LETTER('t', M_CEDILLA, 0),
LETTER('u', M_BREVE, 0), LETTER('u', M_BREVE, 0),
CAPITAL, CAPITAL,
LETTER('u', M_RING, 0), LETTER('u', M_RING, 0),
CAPITAL, // U+0170
CAPITAL, // U+0170
LETTER('u', M_DOUBLE_ACUTE, 0), LETTER('u', M_DOUBLE_ACUTE, 0),
CAPITAL, CAPITAL,
LETTER('u', M_OGONEK, 0), LETTER('u', M_OGONEK, 0),
LETTER('w', M_CIRCUMFLEX, 0), LETTER('w', M_CIRCUMFLEX, 0),
CAPITAL, CAPITAL,
LETTER('y', M_CIRCUMFLEX, 0), LETTER('y', M_CIRCUMFLEX, 0),
CAPITAL, // Y-DIAERESIS
CAPITAL, // Y-DIAERESIS
CAPITAL, CAPITAL,
LETTER('z', M_ACUTE, 0), LETTER('z', M_ACUTE, 0),
CAPITAL, CAPITAL,
LETTER('z', M_DOT_ABOVE, 0), LETTER('z', M_DOT_ABOVE, 0),
CAPITAL, CAPITAL,
LETTER('z', M_CARON, 0), LETTER('z', M_CARON, 0),
LETTER('s', M_NAME, 0), // long-s // U+17f
LETTER('s', M_NAME, 0), // long-s U+17f
}; };


// characters U+0250 to U+029F // characters U+0250 to U+029F
static const unsigned short letter_accents_250[] = { static const unsigned short letter_accents_250[] = {
LETTER('a', M_TURNED, 0), // U+250
LETTER('a', M_TURNED, 0), // U+250
LETTER(L_ALPHA, 0, 0), LETTER(L_ALPHA, 0, 0),
LETTER(L_ALPHA, M_TURNED, 0), LETTER(L_ALPHA, M_TURNED, 0),
LETTER('b', M_IMPLOSIVE, 0), LETTER('b', M_IMPLOSIVE, 0),
0, // open-o
0, // open-o
LETTER('c', M_CURL, 0), LETTER('c', M_CURL, 0),
LETTER('d', M_RETROFLEX, 0), LETTER('d', M_RETROFLEX, 0),
LETTER('d', M_IMPLOSIVE, 0), LETTER('d', M_IMPLOSIVE, 0),
LETTER('e', M_REVERSED, 0), // U+258
0, // schwa
LETTER('e', M_REVERSED, 0), // U+258
0, // schwa
LETTER(L_SCHWA, M_HOOK, 0), LETTER(L_SCHWA, M_HOOK, 0),
0, // open-e
0, // open-e
LETTER(L_OPEN_E, M_REVERSED, 0), LETTER(L_OPEN_E, M_REVERSED, 0),
LETTER(L_OPEN_E, M_HOOK, M_REVERSED), LETTER(L_OPEN_E, M_HOOK, M_REVERSED),
0, 0,
LETTER('j', M_BAR, 0), LETTER('j', M_BAR, 0),
LETTER('g', M_IMPLOSIVE, 0), // U+260
LETTER('g', M_IMPLOSIVE, 0), // U+260
LETTER('g', 0, 0), LETTER('g', 0, 0),
LETTER('g', M_SMALLCAP, 0), LETTER('g', M_SMALLCAP, 0),
LETTER(L_GAMMA, 0, 0), LETTER(L_GAMMA, 0, 0),
0, // ramshorn
0, // ramshorn
LETTER('h', M_TURNED, 0), LETTER('h', M_TURNED, 0),
LETTER('h', M_HOOK, 0), LETTER('h', M_HOOK, 0),
0, 0,
LETTER('i', M_BAR, 0), // U+268
LETTER('i', M_BAR, 0), // U+268
LETTER(L_IOTA, 0, 0), LETTER(L_IOTA, 0, 0),
LETTER('i', M_SMALLCAP, 0), LETTER('i', M_SMALLCAP, 0),
LETTER('l', M_TILDE, 0), LETTER('l', M_TILDE, 0),
LETTER('o', M_BAR, 0), LETTER('o', M_BAR, 0),
LIGATURE('o', 'e', M_SMALLCAP), LIGATURE('o', 'e', M_SMALLCAP),
0, 0,
LETTER(L_PHI, 0, 0), // U+278
LETTER(L_PHI, 0, 0), // U+278
LETTER('r', M_TURNED, 0), LETTER('r', M_TURNED, 0),
LETTER(L_RLONG, M_TURNED, 0), LETTER(L_RLONG, M_TURNED, 0),
LETTER('r', M_RETROFLEX, M_TURNED), LETTER('r', M_RETROFLEX, M_TURNED),
0, 0,
LETTER('r', M_RETROFLEX, 0), LETTER('r', M_RETROFLEX, 0),
0, // r-tap
0, // r-tap
LETTER(L_RTAP, M_REVERSED, 0), LETTER(L_RTAP, M_REVERSED, 0),
LETTER('r', M_SMALLCAP, 0), // U+280
LETTER('r', M_SMALLCAP, 0), // U+280
LETTER('r', M_TURNED, M_SMALLCAP), LETTER('r', M_TURNED, M_SMALLCAP),
LETTER('s', M_RETROFLEX, 0), LETTER('s', M_RETROFLEX, 0),
0, // esh
0, // esh
LETTER('j', M_HOOK, 0), LETTER('j', M_HOOK, 0),
LETTER(L_ESH, M_REVERSED, 0), LETTER(L_ESH, M_REVERSED, 0),
LETTER(L_ESH, M_CURL, 0), LETTER(L_ESH, M_CURL, 0),
LETTER('t', M_TURNED, 0), LETTER('t', M_TURNED, 0),
LETTER('t', M_RETROFLEX, 0), // U+288
LETTER('t', M_RETROFLEX, 0), // U+288
LETTER('u', M_BAR, 0), LETTER('u', M_BAR, 0),
LETTER(L_UPSILON, 0, 0), LETTER(L_UPSILON, 0, 0),
LETTER('v', M_HOOK, 0), LETTER('v', M_HOOK, 0),
LETTER('w', M_TURNED, 0), LETTER('w', M_TURNED, 0),
LETTER('y', M_TURNED, 0), LETTER('y', M_TURNED, 0),
LETTER('y', M_SMALLCAP, 0), LETTER('y', M_SMALLCAP, 0),
LETTER('z', M_RETROFLEX, 0), // U+290
LETTER('z', M_RETROFLEX, 0), // U+290
LETTER('z', M_CURL, 0), LETTER('z', M_CURL, 0),
0, // ezh
0, // ezh
LETTER(L_EZH, M_CURL, 0), LETTER(L_EZH, M_CURL, 0),
0, // glottal stop
0, // glottal stop
LETTER(L_GLOTTAL, M_REVERSED, 0), LETTER(L_GLOTTAL, M_REVERSED, 0),
LETTER(L_GLOTTAL, M_TURNED, 0), LETTER(L_GLOTTAL, M_TURNED, 0),
0, 0,
0, // bilabial click // U+298
0, // bilabial click U+298
LETTER('b', M_SMALLCAP, 0), LETTER('b', M_SMALLCAP, 0),
0, 0,
LETTER('g', M_IMPLOSIVE, M_SMALLCAP), LETTER('g', M_IMPLOSIVE, M_SMALLCAP),
LETTER('j', M_CURL, 0), LETTER('j', M_CURL, 0),
LETTER('k', M_TURNED, 0), LETTER('k', M_TURNED, 0),
LETTER('l', M_SMALLCAP, 0), LETTER('l', M_SMALLCAP, 0),
LETTER('q', M_HOOK, 0), // U+2a0
LETTER('q', M_HOOK, 0), // U+2a0
LETTER(L_GLOTTAL, M_STROKE, 0), LETTER(L_GLOTTAL, M_STROKE, 0),
LETTER(L_GLOTTAL, M_STROKE, M_REVERSED), LETTER(L_GLOTTAL, M_STROKE, M_REVERSED),
LIGATURE('d', 'z', 0), LIGATURE('d', 'z', 0),
0, // dezh
0, // dezh
LIGATURE('d', 'z', M_CURL), LIGATURE('d', 'z', M_CURL),
LIGATURE('t', 's', 0), LIGATURE('t', 's', 0),
0, // tesh
0, // tesh
LIGATURE('t', 's', M_CURL), LIGATURE('t', 's', M_CURL),
}; };




void LookupLetter(Translator *tr, unsigned int letter, int next_byte, char *ph_buf1, int control) void LookupLetter(Translator *tr, unsigned int letter, int next_byte, char *ph_buf1, int control)
{ {
// control, bit 0: not the first letter of a word
// control, bit 0: not the first letter of a word


int len; int len;
static char single_letter[10] = { 0, 0 }; static char single_letter[10] = { 0, 0 };


single_letter[1] = '_'; single_letter[1] = '_';
if (Lookup(tr, &single_letter[1], ph_buf3) != 0) if (Lookup(tr, &single_letter[1], ph_buf3) != 0)
return; // the character is specified as _* so ignore it when speaking normal text
return; // the character is specified as _* so ignore it when speaking normal text


// check whether this character is specified for English // check whether this character is specified for English
if (tr->translator_name == L('e', 'n')) if (tr->translator_name == L('e', 'n'))
return; // we are already using English
return; // we are already using English


SetTranslator2("en"); SetTranslator2("en");
if (Lookup(translator2, &single_letter[2], ph_buf3) != 0) { if (Lookup(translator2, &single_letter[2], ph_buf3) != 0) {
// yes, switch to English and re-translate the word // yes, switch to English and re-translate the word
sprintf(ph_buf1, "%c", phonSWITCH); sprintf(ph_buf1, "%c", phonSWITCH);
} }
SelectPhonemeTable(voice->phoneme_tab_ix); // revert to original phoneme table
SelectPhonemeTable(voice->phoneme_tab_ix); // revert to original phoneme table
return; return;
} }




if (next_byte != ' ') if (next_byte != ' ')
next_byte = RULE_SPELLING; next_byte = RULE_SPELLING;
single_letter[3+len] = next_byte; // follow by space-space if the end of the word, or space-31
single_letter[3+len] = next_byte; // follow by space-space if the end of the word, or space-31


single_letter[1] = '_'; single_letter[1] = '_';


SetWordStress(tr, ph_buf1, dict_flags, -1, control & 1); SetWordStress(tr, ph_buf1, dict_flags, -1, control & 1);
} }


// unicode ranges for non-ascii digits 0-9
// unicode ranges for non-ascii digits 0-9 (these must be in ascending order)
static const int number_ranges[] = { static const int number_ranges[] = {
0x660, 0x6f0, // arabic
0x966, 0x9e6, 0xa66, 0xae6, 0xb66, 0xbe6, 0xc66, 0xce6, 0xd66, // indic
0x660, 0x6f0, // arabic
0x966, 0x9e6, 0xa66, 0xae6, 0xb66, 0xbe6, 0xc66, 0xce6, 0xd66, // indic
0xe50, 0xed0, 0xf20, 0x1040, 0x1090, 0xe50, 0xed0, 0xf20, 0x1040, 0x1090,
0 0
}; // these must be in ascending order
};


int NonAsciiNumber(int letter) int NonAsciiNumber(int letter)
{ {
// Change non-ascii digit into ascii digit '0' to '9', (or -1 if not)
// Change non-ascii digit into ascii digit '0' to '9', (or -1 if not)
const int *p; const int *p;
int base; int base;


for (p = number_ranges; (base = *p) != 0; p++) { for (p = number_ranges; (base = *p) != 0; p++) {
if (letter < base) if (letter < base)
break; // not found
break; // not found
if (letter < (base+10)) if (letter < (base+10))
return letter-base+'0'; return letter-base+'0';
} }
return -1; return -1;
} }


#define L_SUB 0x4000 // subscript
#define L_SUP 0x8000 // superscript
#define L_SUB 0x4000 // subscript
#define L_SUP 0x8000 // superscript


static const char *modifiers[] = { NULL, "_sub", "_sup", NULL }; static const char *modifiers[] = { NULL, "_sub", "_sup", NULL };


0, 0 0, 0
}; };


static const char *hex_letters[] = { "'e:j", "b'i:", "s'i:", "d'i:", "'i:", "'ef" }; // names, using phonemes available to all languages
// names, using phonemes available to all languages
static const char *hex_letters[] = {
"'e:j",
"b'i:",
"s'i:",
"d'i:",
"'i:",
"'ef"
};


int IsSuperscript(int letter) int IsSuperscript(int letter)
{ {
// is this a subscript or superscript letter ?
// is this a subscript or superscript letter ?
int ix; int ix;
int c; int c;




int TranslateLetter(Translator *tr, char *word, char *phonemes, int control) int TranslateLetter(Translator *tr, char *word, char *phonemes, int control)
{ {
// get pronunciation for an isolated letter
// return number of bytes used by the letter
// control bit 0: a non-initial letter in a word
// bit 1: say 'capital'
// bit 2: say character code for unknown letters
// get pronunciation for an isolated letter
// return number of bytes used by the letter
// control bit 0: a non-initial letter in a word
// bit 1: say 'capital'
// bit 2: say character code for unknown letters


int n_bytes; int n_bytes;
int letter; int letter;
n_bytes = utf8_in(&letter, word); n_bytes = utf8_in(&letter, word);


if ((letter & 0xfff00) == 0x0e000) if ((letter & 0xfff00) == 0x0e000)
letter &= 0xff; // uncode private usage area
letter &= 0xff; // uncode private usage area


if (control & 2) { if (control & 2) {
// include CAPITAL information // include CAPITAL information
// don't say "superscript" during normal text reading // don't say "superscript" during normal text reading
Lookup(tr, modifier, capital); Lookup(tr, modifier, capital);
if (capital[0] == 0) { if (capital[0] == 0) {
capital[2] = SetTranslator2("en"); // overwrites previous contents of translator2
capital[2] = SetTranslator2("en"); // overwrites previous contents of translator2
Lookup(translator2, modifier, &capital[3]); Lookup(translator2, modifier, &capital[3]);
if (capital[3] != 0) { if (capital[3] != 0) {
capital[0] = phonPAUSE; capital[0] = phonPAUSE;
ph_buf2[0] = 0; ph_buf2[0] = 0;
if (Lookup(translator, alphabet->name, ph_alphabet) == 0) { // the original language for the current voice if (Lookup(translator, alphabet->name, ph_alphabet) == 0) { // the original language for the current voice
// Can't find the local name for this alphabet, use the English name // Can't find the local name for this alphabet, use the English name
ph_alphabet[2] = SetTranslator2("en"); // overwrites previous contents of translator2
ph_alphabet[2] = SetTranslator2("en"); // overwrites previous contents of translator2
Lookup(translator2, alphabet->name, ph_buf2); Lookup(translator2, alphabet->name, ph_buf2);
} else if (translator != tr) { } else if (translator != tr) {
phontab_1 = tr->phoneme_tab_ix; phontab_1 = tr->phoneme_tab_ix;
} }
} }


// caution: SetWordStress() etc don't expect phonSWITCH + phoneme table number
// caution: SetWordStress() etc don't expect phonSWITCH + phoneme table number


if (ph_buf[0] == 0) { if (ph_buf[0] == 0) {
if ((al_offset != 0) && (al_offset == translator->langopts.alt_alphabet)) if ((al_offset != 0) && (al_offset == translator->langopts.alt_alphabet))
if ((initial = (code/28)/21) != 11) { if ((initial = (code/28)/21) != 11) {
p3 += utf8_out(initial + 0x1100, p3); p3 += utf8_out(initial + 0x1100, p3);
} }
utf8_out(((code/28) % 21) + 0x1161, p3); // medial
utf8_out((code % 28) + 0x11a7, &p3[3]); // final
utf8_out(((code/28) % 21) + 0x1161, p3); // medial
utf8_out((code % 28) + 0x11a7, &p3[3]); // final
p3[6] = ' '; p3[6] = ' ';
p3[7] = 0; p3[7] = 0;
ph_buf[3] = 0; ph_buf[3] = 0;
LookupLetter(translator2, letter, word[n_bytes], &ph_buf[3], control & 1); LookupLetter(translator2, letter, word[n_bytes], &ph_buf[3], control & 1);
} }


SelectPhonemeTable(voice->phoneme_tab_ix); // revert to original phoneme table
SelectPhonemeTable(voice->phoneme_tab_ix); // revert to original phoneme table


if (ph_buf[3] != 0) { if (ph_buf[3] != 0) {
ph_buf[0] = phonPAUSE; ph_buf[0] = phonPAUSE;
ph_buf[1] = phonSWITCH; ph_buf[1] = phonSWITCH;
len = strlen(&ph_buf[3]) + 3; len = strlen(&ph_buf[3]) + 3;
ph_buf[len] = phonSWITCH; // switch back
ph_buf[len] = phonSWITCH; // switch back
ph_buf[len+1] = tr->phoneme_tab_ix; ph_buf[len+1] = tr->phoneme_tab_ix;
ph_buf[len+2] = 0; ph_buf[len+2] = 0;
} }
if (tr->langopts.accents & 2) // 'capital' before or after the word ? if (tr->langopts.accents & 2) // 'capital' before or after the word ?
sprintf(ph_buf2, "%c%s%s%s", 0xff, ph_alphabet, ph_buf, capital); sprintf(ph_buf2, "%c%s%s%s", 0xff, ph_alphabet, ph_buf, capital);
else else
sprintf(ph_buf2, "%c%s%s%s", 0xff, ph_alphabet, capital, ph_buf); // the 0xff marker will be removed or replaced in SetSpellingStress()
if ((len + strlen(ph_buf2)) < N_WORD_PHONEMES) {
sprintf(ph_buf2, "%c%s%s%s", 0xff, ph_alphabet, capital, ph_buf); // the 0xff marker will be removed or replaced in SetSpellingStress()
if ((len + strlen(ph_buf2)) < N_WORD_PHONEMES)
strcpy(&phonemes[len], ph_buf2); strcpy(&phonemes[len], ph_buf2);
}
return n_bytes; return n_bytes;
} }


void SetSpellingStress(Translator *tr, char *phonemes, int control, int n_chars) void SetSpellingStress(Translator *tr, char *phonemes, int control, int n_chars)
{ {
// Individual letter names, reduce the stress of some.
// Individual letter names, reduce the stress of some.
int ix; int ix;
unsigned int c; unsigned int c;
int n_stress = 0; int n_stress = 0;
} else { } else {
if (count != n_stress) { if (count != n_stress) {
if (((count % 3) != 0) || (count == n_stress-1)) if (((count % 3) != 0) || (count == n_stress-1))
c = phonSTRESS_3; // reduce to secondary stress
c = phonSTRESS_3; // reduce to secondary stress
} }
} }
} else if (c == 0xff) { } else if (c == 0xff) {
if ((control < 2) || (ix == 0)) if ((control < 2) || (ix == 0))
continue; // don't insert pauses
continue; // don't insert pauses


if (control == 4) if (control == 4)
c = phonPAUSE; // pause after each character
c = phonPAUSE; // pause after each character
if (((count % 3) == 0) || (control > 2)) if (((count % 3) == 0) || (control > 2))
c = phonPAUSE_NOLINK; // pause following a primary stress
c = phonPAUSE_NOLINK; // pause following a primary stress
else else
c = phonPAUSE_VSHORT; c = phonPAUSE_VSHORT;
} }
nextflags = TranslateWord(tr, &word_end[2], 0, NULL, NULL); nextflags = TranslateWord(tr, &word_end[2], 0, NULL, NULL);


if ((tr->prev_dict_flags[0] & FLAG_ALT_TRANS) && ((c2 == 0) || (wtab[0].flags & FLAG_COMMA_AFTER) || iswdigit(c2))) if ((tr->prev_dict_flags[0] & FLAG_ALT_TRANS) && ((c2 == 0) || (wtab[0].flags & FLAG_COMMA_AFTER) || iswdigit(c2)))
ordinal = 0; // TEST 09.02.10
ordinal = 0; // TEST 09.02.10


if (nextflags & FLAG_ALT_TRANS) if (nextflags & FLAG_ALT_TRANS)
ordinal = 0; ordinal = 0;


if (nextflags & FLAG_ALT3_TRANS) { if (nextflags & FLAG_ALT3_TRANS) {
if (word[-2] == '-') if (word[-2] == '-')
ordinal = 0; // eg. december 2-5. között
ordinal = 0; // e.g. december 2-5. között


if (tr->prev_dict_flags[0] & (FLAG_ALT_TRANS | FLAG_ALT3_TRANS)) if (tr->prev_dict_flags[0] & (FLAG_ALT_TRANS | FLAG_ALT3_TRANS))
ordinal = 0x22; ordinal = 0x22;


static int hu_number_e(const char *word, int thousandplex, int value) static int hu_number_e(const char *word, int thousandplex, int value)
{ {
// lang-hu: variant form of numbers when followed by hyphen and a suffix starting with 'a' or 'e' (but not a, e, az, ez, azt, ezt, att. ett
// lang-hu: variant form of numbers when followed by hyphen and a suffix starting with 'a' or 'e' (but not a, e, az, ez, azt, ezt, att. ett


if ((word[0] == 'a') || (word[0] == 'e')) { if ((word[0] == 'a') || (word[0] == 'e')) {
if ((word[1] == ' ') || (word[1] == 'z') || ((word[1] == 't') && (word[2] == 't'))) if ((word[1] == ' ') || (word[1] == 'z') || ((word[1] == 't') && (word[2] == 't')))
return 0; return 0;


if (((thousandplex == 1) || ((value % 1000) == 0)) && (word[1] == 'l')) if (((thousandplex == 1) || ((value % 1000) == 0)) && (word[1] == 'l'))
return 0; // 1000-el
return 0; // 1000-el


return 1; return 1;
} }
flags[1] = 0; flags[1] = 0;


if (((tr->langopts.numbers & NUM_ROMAN_CAPITALS) && !(wtab[0].flags & FLAG_ALL_UPPER)) || IsDigit09(word[-2])) if (((tr->langopts.numbers & NUM_ROMAN_CAPITALS) && !(wtab[0].flags & FLAG_ALL_UPPER)) || IsDigit09(word[-2]))
return 0; // not '2xx'
return 0; // not '2xx'


if (word[1] == ' ') { if (word[1] == ' ') {
if ((tr->langopts.numbers & (NUM_ROMAN_CAPITALS | NUM_ROMAN_ORDINAL | NUM_ORDINAL_DOT)) && (wtab[0].flags & FLAG_HAS_DOT)) { if ((tr->langopts.numbers & (NUM_ROMAN_CAPITALS | NUM_ROMAN_ORDINAL | NUM_ORDINAL_DOT)) && (wtab[0].flags & FLAG_HAS_DOT)) {
// allow single letter Roman ordinal followed by dot. // allow single letter Roman ordinal followed by dot.
} else } else
return 0; // only one letter, don't speak as a Roman Number
return 0; // only one letter, don't speak as a Roman Number
} }


word_start = word; word_start = word;
} }


if (IsDigit09(word[0])) if (IsDigit09(word[0]))
return 0; // eg. 'xx2'
return 0; // e.g. 'xx2'


acc += prev; acc += prev;
if (acc < tr->langopts.min_roman) if (acc < tr->langopts.min_roman)
if (acc > tr->langopts.max_roman) if (acc > tr->langopts.max_roman)
return 0; return 0;


Lookup(tr, "_roman", ph_roman); // precede by "roman" if _rom is defined in *_list
Lookup(tr, "_roman", ph_roman); // precede by "roman" if _rom is defined in *_list
p = &ph_out[0]; p = &ph_out[0];


if ((tr->langopts.numbers & NUM_ROMAN_AFTER) == 0) { if ((tr->langopts.numbers & NUM_ROMAN_AFTER) == 0) {


switch ((translator->langopts.numbers2 >> 6) & 0x7) switch ((translator->langopts.numbers2 >> 6) & 0x7)
{ {
case 1: // lang=ru use singular for xx1 except for x11
case 1: // lang=ru use singular for xx1 except for x11
if ((teens == 0) && ((value % 10) == 1)) if ((teens == 0) && ((value % 10) == 1))
return "1M"; return "1M";
break; break;
case 2: // lang=cs,sk
case 2: // lang=cs,sk
if ((value >= 2) && (value <= 4)) if ((value >= 2) && (value <= 4))
return "0MA"; return "0MA";
break; break;
case 3: // lang=pl
case 3: // lang=pl
if ((teens == 0) && (((value % 10) >= 2) && ((value % 10) <= 4))) if ((teens == 0) && (((value % 10) >= 2) && ((value % 10) <= 4)))
return "0MA"; return "0MA";
break; break;
case 4: // lang=lt
case 4: // lang=lt
if ((teens == 1) || ((value % 10) == 0)) if ((teens == 1) || ((value % 10) == 0))
return "0MB"; return "0MB";
if ((value % 10) == 1) if ((value % 10) == 1)
return "0MA"; return "0MA";
break; break;
case 5: // lang=bs,hr,sr
case 5: // lang=bs,hr,sr
if (teens == 0) { if (teens == 0) {
if ((value % 10) == 1) if ((value % 10) == 1)
return "1M"; return "1M";


static int LookupThousands(Translator *tr, int value, int thousandplex, int thousands_exact, char *ph_out) static int LookupThousands(Translator *tr, int value, int thousandplex, int thousands_exact, char *ph_out)
{ {
// thousands_exact: bit 0 no hundreds,tens,or units, bit 1 ordinal numberr
// thousands_exact: bit 0 no hundreds,tens,or units, bit 1 ordinal numberr
int found; int found;
int found_value = 0; int found_value = 0;
char string[12]; char string[12];


static int LookupNum2(Translator *tr, int value, int thousandplex, const int control, char *ph_out) static int LookupNum2(Translator *tr, int value, int thousandplex, const int control, char *ph_out)
{ {
// Lookup a 2 digit number
// control bit 0: ordinal number
// control bit 1: final tens and units (not number of thousands) (use special form of '1', LANG=de "eins")
// control bit 2: tens and units only, no higher digits
// control bit 3: use feminine form of '2' (for thousands
// control bit 4: speak zero tens
// control bit 5: variant of ordinal number (lang=hu)
// bit 8 followed by decimal fraction
// bit 9: use #f form for both tens and units (lang=ml)
// Lookup a 2 digit number
// control bit 0: ordinal number
// control bit 1: final tens and units (not number of thousands) (use special form of '1', LANG=de "eins")
// control bit 2: tens and units only, no higher digits
// control bit 3: use feminine form of '2' (for thousands
// control bit 4: speak zero tens
// control bit 5: variant of ordinal number (lang=hu)
// bit 8 followed by decimal fraction
// bit 9: use #f form for both tens and units (lang=ml)


int found; int found;
int ix; int ix;
int found_ordinal = 0; int found_ordinal = 0;
int next_phtype; int next_phtype;
int ord_type = 'o'; int ord_type = 'o';
char string[12]; // for looking up entries in *_list
char string[12]; // for looking up entries in *_list
char ph_ordinal[20]; char ph_ordinal[20];
char ph_tens[50]; char ph_tens[50];
char ph_digits[50]; char ph_digits[50];
strcpy(ph_ordinal, ph_ordinal2); strcpy(ph_ordinal, ph_ordinal2);


if (control & 4) { if (control & 4) {
sprintf(string, "_%d%cx", value, ord_type); // LANG=hu, special word for 1. 2. when there are no higher digits
sprintf(string, "_%d%cx", value, ord_type); // LANG=hu, special word for 1. 2. when there are no higher digits
if ((found = Lookup(tr, string, ph_digits)) != 0) { if ((found = Lookup(tr, string, ph_digits)) != 0) {
if (ph_ordinal2x[0] != 0) if (ph_ordinal2x[0] != 0)
strcpy(ph_ordinal, ph_ordinal2x); // alternate pronunciation (lang=an)
strcpy(ph_ordinal, ph_ordinal2x); // alternate pronunciation (lang=an)
} }
} }
if (found == 0) { if (found == 0) {
} else { } else {
// followed by hundreds or thousands etc // followed by hundreds or thousands etc
if ((tr->langopts.numbers2 & NUM2_ORDINAL_AND_THOUSANDS) && (thousandplex <= 1)) if ((tr->langopts.numbers2 & NUM2_ORDINAL_AND_THOUSANDS) && (thousandplex <= 1))
sprintf(string, "_%do", value); // LANG=TA
sprintf(string, "_%do", value); // LANG=TA
else else
sprintf(string, "_%da", value); sprintf(string, "_%da", value);
found = Lookup(tr, string, ph_digits); found = Lookup(tr, string, ph_digits);
// speak leading zero // speak leading zero
Lookup(tr, "_0", ph_tens); Lookup(tr, "_0", ph_tens);
} else { } else {
if (found) {
if (found)
ph_tens[0] = 0; ph_tens[0] = 0;
} else {
else {
if (is_ordinal) { if (is_ordinal) {
sprintf(string, "_%dX%c", tens, ord_type); sprintf(string, "_%dX%c", tens, ord_type);
if (Lookup(tr, string, ph_tens) != 0) { if (Lookup(tr, string, ph_tens) != 0) {
if ((is_ordinal) && ((tr->langopts.numbers & NUM_SWAP_TENS) == 0)) { if ((is_ordinal) && ((tr->langopts.numbers & NUM_SWAP_TENS) == 0)) {
// ordinal // ordinal
sprintf(string, "_%d%c", units, ord_type); sprintf(string, "_%d%c", units, ord_type);
if ((found = Lookup(tr, string, ph_digits)) != 0) {
if ((found = Lookup(tr, string, ph_digits)) != 0)
found_ordinal = 1; found_ordinal = 1;
}
} }
if (found == 0) { if (found == 0) {
if ((number_control & 1) && (control & 2)) { if ((number_control & 1) && (control & 2)) {


static int LookupNum3(Translator *tr, int value, char *ph_out, int suppress_null, int thousandplex, int control) static int LookupNum3(Translator *tr, int value, char *ph_out, int suppress_null, int thousandplex, int control)
{ {
// Translate a 3 digit number
// control bit 0, previous thousands
// bit 1, ordinal number
// bit 5 variant form of ordinal number
// bit 8 followed by decimal fraction
// Translate a 3 digit number
// control bit 0, previous thousands
// bit 1, ordinal number
// bit 5 variant form of ordinal number
// bit 8 followed by decimal fraction

int found; int found;
int hundreds; int hundreds;
int tensunits; int tensunits;
int tplex; int tplex;
int say_zero_hundred = 0; int say_zero_hundred = 0;
int say_one_hundred; int say_one_hundred;
char string[12]; // for looking up entries in **_list
char string[12]; // for looking up entries in **_list
char buf1[100]; char buf1[100];
char buf2[100]; char buf2[100];
char ph_100[20]; char ph_100[20];
ph_thousands[0] = 0; ph_thousands[0] = 0;
ph_thousand_and[0] = 0; ph_thousand_and[0] = 0;


if ((tr->langopts.numbers & NUM_ZERO_HUNDRED) && ((control & 1) || (hundreds >= 10))) {
say_zero_hundred = 1; // lang=vi
}
if ((tr->langopts.numbers & NUM_ZERO_HUNDRED) && ((control & 1) || (hundreds >= 10)))
say_zero_hundred = 1; // lang=vi


if ((hundreds > 0) || say_zero_hundred) { if ((hundreds > 0) || say_zero_hundred) {
found = 0; found = 0;
if (LookupThousands(tr, hundreds / 10, tplex, exact | ordinal, ph_10T) == 0) { if (LookupThousands(tr, hundreds / 10, tplex, exact | ordinal, ph_10T) == 0) {
x = 0; x = 0;
if (tr->langopts.numbers2 & (1 << tplex)) if (tr->langopts.numbers2 & (1 << tplex))
x = 8; // use variant (feminine) for before thousands and millions
x = 8; // use variant (feminine) for before thousands and millions
if (tr->translator_name == L('m', 'l')) if (tr->translator_name == L('m', 'l'))
x = 0x208; x = 0x208;
LookupNum2(tr, hundreds/10, thousandplex, x, ph_digits); LookupNum2(tr, hundreds/10, thousandplex, x, ph_digits);
} }


if (tr->langopts.numbers2 & 0x200) if (tr->langopts.numbers2 & 0x200)
sprintf(ph_thousands, "%s%c%s%c", ph_10T, phonEND_WORD, ph_digits, phonEND_WORD); // say "thousands" before its number, not after
sprintf(ph_thousands, "%s%c%s%c", ph_10T, phonEND_WORD, ph_digits, phonEND_WORD); // say "thousands" before its number, not after
else else
sprintf(ph_thousands, "%s%c%s%c", ph_digits, phonEND_WORD, ph_10T, phonEND_WORD); sprintf(ph_thousands, "%s%c%s%c", ph_digits, phonEND_WORD, ph_10T, phonEND_WORD);


if ((tensunits != 0) || (suppress_null == 0)) { if ((tensunits != 0) || (suppress_null == 0)) {
x = 0; x = 0;
if (thousandplex == 0) { if (thousandplex == 0) {
x = 2; // allow "eins" for 1 rather than "ein"
x = 2; // allow "eins" for 1 rather than "ein"
if (ordinal) if (ordinal)
x = 3; // ordinal number
x = 3; // ordinal number
if ((value < 100) && !(control & 1)) if ((value < 100) && !(control & 1))
x |= 4; // tens and units only, no higher digits
x |= 4; // tens and units only, no higher digits
if (ordinal & 0x20) if (ordinal & 0x20)
x |= 0x20; // variant form of ordinal number
x |= 0x20; // variant form of ordinal number
} else if (tr->langopts.numbers2 & (1 << thousandplex)) } else if (tr->langopts.numbers2 & (1 << thousandplex))
x = 8; // use variant (feminine) for before thousands and millions
x = 8; // use variant (feminine) for before thousands and millions


if ((tr->translator_name == L('m', 'l')) && (thousandplex == 1)) if ((tr->translator_name == L('m', 'l')) && (thousandplex == 1))
x |= 0x208; // use #f form for both tens and units
x |= 0x208; // use #f form for both tens and units


if ((tr->langopts.numbers2 & NUM2_ZERO_TENS) && ((control & 1) || (hundreds > 0))) { if ((tr->langopts.numbers2 & NUM2_ZERO_TENS) && ((control & 1) || (hundreds > 0))) {
// LANG=zh, // LANG=zh,


if (LookupNum2(tr, tensunits, thousandplex, x | (control & 0x100), buf2) != 0) { if (LookupNum2(tr, tensunits, thousandplex, x | (control & 0x100), buf2) != 0) {
if (tr->langopts.numbers & NUM_SINGLE_AND) if (tr->langopts.numbers & NUM_SINGLE_AND)
ph_hundred_and[0] = 0; // don't put 'and' after 'hundred' if there's 'and' between tens and units
ph_hundred_and[0] = 0; // don't put 'and' after 'hundred' if there's 'and' between tens and units
} }
} else { } else {
if (ph_ordinal2[0] != 0) { if (ph_ordinal2[0] != 0) {
ix = strlen(buf1); ix = strlen(buf1);
if ((ix > 0) && (buf1[ix-1] == phonPAUSE_SHORT)) if ((ix > 0) && (buf1[ix-1] == phonPAUSE_SHORT))
buf1[ix-1] = 0; // remove pause before addding ordinal suffix
buf1[ix-1] = 0; // remove pause before addding ordinal suffix
strcpy(buf2, ph_ordinal2); strcpy(buf2, ph_ordinal2);
} }
} }


bool CheckThousandsGroup(char *word, int group_len) bool CheckThousandsGroup(char *word, int group_len)
{ {
// Is this a group of 3 digits which looks like a thousands group?
// Is this a group of 3 digits which looks like a thousands group?
int ix; int ix;


if (IsDigit09(word[group_len]) || IsDigit09(-1)) if (IsDigit09(word[group_len]) || IsDigit09(-1))


static int TranslateNumber_1(Translator *tr, char *word, char *ph_out, unsigned int *flags, WORD_TAB *wtab, int control) static int TranslateNumber_1(Translator *tr, char *word, char *ph_out, unsigned int *flags, WORD_TAB *wtab, int control)
{ {
// Number translation with various options
// the "word" may be up to 4 digits
// "words" of 3 digits may be preceded by another number "word" for thousands or millions
// Number translation with various options
// the "word" may be up to 4 digits
// "words" of 3 digits may be preceded by another number "word" for thousands or millions


int n_digits; int n_digits;
int value; int value;
int group_len; int group_len;
int len; int len;
char *p; char *p;
char string[32]; // for looking up entries in **_list
char string[32]; // for looking up entries in **_list
char buf1[100]; char buf1[100];
char ph_append[50]; char ph_append[50];
char ph_buf[200]; char ph_buf[200];
char ph_buf2[50]; char ph_buf2[50];
char ph_zeros[50]; char ph_zeros[50];
char suffix[30]; // string[] must be long enough for sizeof(suffix)+2
char suffix[30]; // string[] must be long enough for sizeof(suffix)+2
char buf_digit_lookup[50]; char buf_digit_lookup[50];


static const char str_pause[2] = { phonPAUSE_NOLINK, 0 }; static const char str_pause[2] = { phonPAUSE_NOLINK, 0 };
group_len = 4; group_len = 4;


// is there a previous thousands part (as a previous "word") ? // is there a previous thousands part (as a previous "word") ?
if ((n_digits == group_len) && (word[-2] == tr->langopts.thousands_sep) && IsDigit09(word[-3])) {
if ((n_digits == group_len) && (word[-2] == tr->langopts.thousands_sep) && IsDigit09(word[-3]))
prev_thousands = 1; prev_thousands = 1;
} else if ((tr->langopts.thousands_sep == ' ') || (tr->langopts.numbers & NUM_ALLOW_SPACE)) {
else if ((tr->langopts.thousands_sep == ' ') || (tr->langopts.numbers & NUM_ALLOW_SPACE)) {
// thousands groups can be separated by spaces // thousands groups can be separated by spaces
if ((n_digits == 3) && !(wtab->flags & FLAG_MULTIPLE_SPACES) && IsDigit09(word[-2])) if ((n_digits == 3) && !(wtab->flags & FLAG_MULTIPLE_SPACES) && IsDigit09(word[-2]))
prev_thousands = 1; prev_thousands = 1;
} }


if ((ordinal == 0) || (tr->translator_name == L('h', 'u'))) { if ((ordinal == 0) || (tr->translator_name == L('h', 'u'))) {
// NOTE lang=hu, allow both dot and ordinal suffix, eg. "december 21.-én"
// NOTE lang=hu, allow both dot and ordinal suffix, eg. "december 21.-én"
// look for an ordinal number suffix after the number // look for an ordinal number suffix after the number
ix++; ix++;
p = suffix; p = suffix;
if (suffix[0] != 0) { if (suffix[0] != 0) {
if ((tr->langopts.ordinal_indicator != NULL) && (strcmp(suffix, tr->langopts.ordinal_indicator) == 0)) if ((tr->langopts.ordinal_indicator != NULL) && (strcmp(suffix, tr->langopts.ordinal_indicator) == 0))
ordinal = 2; ordinal = 2;
else if (!IsDigit09(suffix[0])) { // not _#9 (tab)
else if (!IsDigit09(suffix[0])) { // not _#9 (tab)
sprintf(string, "_#%s", suffix); sprintf(string, "_#%s", suffix);
if (Lookup(tr, string, ph_ordinal2)) { if (Lookup(tr, string, ph_ordinal2)) {
// this is an ordinal suffix // this is an ordinal suffix
flags[0] |= FLAG_SKIPWORDS; flags[0] |= FLAG_SKIPWORDS;
skipwords = 1; skipwords = 1;
sprintf(string, "_x#%s", suffix); sprintf(string, "_x#%s", suffix);
Lookup(tr, string, ph_ordinal2x); // is there an alternate pronunciation?
Lookup(tr, string, ph_ordinal2x); // is there an alternate pronunciation?
} }
} }
} }
} else { } else {
if (n_digits > 3) { if (n_digits > 3) {
flags[0] &= ~FLAG_SKIPWORDS; flags[0] &= ~FLAG_SKIPWORDS;
return 0; // long number string with leading zero, speak as individual digits
return 0; // long number string with leading zero, speak as individual digits
} }


// speak leading zeros // speak leading zeros
if (tr->translator_name == L('h', 'u')) { if (tr->translator_name == L('h', 'u')) {
// variant form of numbers when followed by hyphen and a suffix starting with 'a' or 'e' (but not a, e, az, ez, azt, ezt // variant form of numbers when followed by hyphen and a suffix starting with 'a' or 'e' (but not a, e, az, ez, azt, ezt
if ((wtab[thousandplex].flags & FLAG_HYPHEN_AFTER) && (thousands_exact == 1) && hu_number_e(&word[suffix_ix], thousandplex, value)) if ((wtab[thousandplex].flags & FLAG_HYPHEN_AFTER) && (thousands_exact == 1) && hu_number_e(&word[suffix_ix], thousandplex, value))
number_control |= 1; // use _1e variant of number
number_control |= 1; // use _1e variant of number
} }


if ((word[n_digits] == tr->langopts.decimal_sep) && IsDigit09(word[n_digits+1])) { if ((word[n_digits] == tr->langopts.decimal_sep) && IsDigit09(word[n_digits+1])) {
char *p2; char *p2;
// look for combinations of the number with the next word // look for combinations of the number with the next word
p = word; p = word;
while (IsDigit09(p[1])) p++; // just use the last digit
while (IsDigit09(p[1])) p++; // just use the last digit
if (IsDigit09(p[-1])) { if (IsDigit09(p[-1])) {
p2 = p - 1; p2 = p - 1;
if (LookupDictList(tr, &p2, buf_digit_lookup, flags, FLAG_SUFX, wtab)) // lookup 2 digits if (LookupDictList(tr, &p2, buf_digit_lookup, flags, FLAG_SUFX, wtab)) // lookup 2 digits


LookupNum3(tr, value, ph_buf, suppress_null, thousandplex, prev_thousands | ordinal | decimal_point); LookupNum3(tr, value, ph_buf, suppress_null, thousandplex, prev_thousands | ordinal | decimal_point);
if ((thousandplex > 0) && (tr->langopts.numbers2 & 0x200)) if ((thousandplex > 0) && (tr->langopts.numbers2 & 0x200))
sprintf(ph_out, "%s%s%c%s%s", ph_zeros, ph_append, phonEND_WORD, ph_buf2, ph_buf); // say "thousands" before its number
sprintf(ph_out, "%s%s%c%s%s", ph_zeros, ph_append, phonEND_WORD, ph_buf2, ph_buf); // say "thousands" before its number
else else
sprintf(ph_out, "%s%s%s%c%s", ph_zeros, ph_buf2, ph_buf, phonEND_WORD, ph_append); sprintf(ph_out, "%s%s%s%c%s", ph_zeros, ph_buf2, ph_buf, phonEND_WORD, ph_append);


{ {
case NUM_DFRACTION_4: case NUM_DFRACTION_4:
max_decimal_count = 5; max_decimal_count = 5;
// fallthrough
// fallthrough:
case NUM_DFRACTION_2: case NUM_DFRACTION_2:
// French/Polish decimal fraction // French/Polish decimal fraction
while (word[n_digits] == '0') { while (word[n_digits] == '0') {
n_digits += decimal_count; n_digits += decimal_count;
} }
break; break;
case NUM_DFRACTION_1: // italian, say "hundredths" if leading zero
case NUM_DFRACTION_5: // hungarian, always say "tenths" etc.
case NUM_DFRACTION_6: // kazakh, always say "tenths" etc, before the decimal fraction
case NUM_DFRACTION_1: // italian, say "hundredths" if leading zero
case NUM_DFRACTION_5: // hungarian, always say "tenths" etc.
case NUM_DFRACTION_6: // kazakh, always say "tenths" etc, before the decimal fraction
LookupNum3(tr, atoi(&word[n_digits]), ph_buf, 0, 0, 0); LookupNum3(tr, atoi(&word[n_digits]), ph_buf, 0, 0, 0);
if ((word[n_digits] == '0') || (decimal_mode != NUM_DFRACTION_1)) { if ((word[n_digits] == '0') || (decimal_mode != NUM_DFRACTION_1)) {
// decimal part has leading zeros, so add a "hundredths" or "thousandths" suffix // decimal part has leading zeros, so add a "hundredths" or "thousandths" suffix
sprintf(string, "_0Z%d", decimal_count); sprintf(string, "_0Z%d", decimal_count);
if (Lookup(tr, string, buf1) == 0) if (Lookup(tr, string, buf1) == 0)
break; // revert to speaking single digits
break; // revert to speaking single digits


if (decimal_mode == NUM_DFRACTION_6) if (decimal_mode == NUM_DFRACTION_6)
strcat(ph_out, buf1); strcat(ph_out, buf1);
utf8_in(&next_char, p); utf8_in(&next_char, p);


if (!iswalpha2(next_char) && (thousands_exact == 0)) if (!iswalpha2(next_char) && (thousands_exact == 0))
strcat(ph_out, str_pause); // don't add pause for 100s, 6th, etc.
strcat(ph_out, str_pause); // don't add pause for 100s, 6th, etc.
} }


*flags |= FLAG_FOUND; *flags |= FLAG_FOUND;
int TranslateNumber(Translator *tr, char *word1, char *ph_out, unsigned int *flags, WORD_TAB *wtab, int control) int TranslateNumber(Translator *tr, char *word1, char *ph_out, unsigned int *flags, WORD_TAB *wtab, int control)
{ {
if ((option_sayas == SAYAS_DIGITS1) || (wtab[0].flags & FLAG_INDIVIDUAL_DIGITS)) if ((option_sayas == SAYAS_DIGITS1) || (wtab[0].flags & FLAG_INDIVIDUAL_DIGITS))
return 0; // speak digits individually
return 0; // speak digits individually


if (tr->langopts.numbers != 0) if (tr->langopts.numbers != 0)
return TranslateNumber_1(tr, word1, ph_out, flags, wtab, control); return TranslateNumber_1(tr, word1, ph_out, flags, wtab, control);

+ 0
- 10
src/libespeak-ng/phoneme.h View File

{ {
#endif #endif



// phoneme types // phoneme types
#define phPAUSE 0 #define phPAUSE 0
#define phSTRESS 1 #define phSTRESS 1
#define phDELETED 14 #define phDELETED 14
#define phINVALID 15 #define phINVALID 15



// phoneme properties // phoneme properties
// bits 16-19 give place of articulation // bits 16-19 give place of articulation
#define phARTICULATION 0xf0000 #define phARTICULATION 0xf0000
unsigned char end_type; unsigned char end_type;
unsigned char std_length; // for vowels, in mS/2; for phSTRESS phonemes, this is the stress/tone type unsigned char std_length; // for vowels, in mS/2; for phSTRESS phonemes, this is the stress/tone type
unsigned char length_mod; // a length_mod group number, used to access length_mod_tab unsigned char length_mod; // a length_mod group number, used to access length_mod_tab

} PHONEME_TAB; } PHONEME_TAB;




// Several phoneme tables may be loaded into memory. phoneme_tab points to // Several phoneme tables may be loaded into memory. phoneme_tab points to
// one for the current voice // one for the current voice
extern int n_phoneme_tab; extern int n_phoneme_tab;
int equivalence_tables; // lists of equivalent phonemes to match other languages, byte index into phondata int equivalence_tables; // lists of equivalent phonemes to match other languages, byte index into phondata
} PHONEME_TAB_LIST; } PHONEME_TAB_LIST;




// table of phonemes to be replaced with different phonemes, for the current voice // table of phonemes to be replaced with different phonemes, for the current voice
#define N_REPLACE_PHONEMES 60 #define N_REPLACE_PHONEMES 60
typedef struct { typedef struct {
extern int n_replace_phonemes; extern int n_replace_phonemes;
extern REPLACE_PHONEMES replace_phonemes[N_REPLACE_PHONEMES]; extern REPLACE_PHONEMES replace_phonemes[N_REPLACE_PHONEMES];



// Table of phoneme programs and lengths. Used by MakeVowelLists // Table of phoneme programs and lengths. Used by MakeVowelLists
typedef struct { typedef struct {
unsigned int addr; unsigned int addr;
unsigned int length; unsigned int length;
} PHONEME_PROG_LOG; } PHONEME_PROG_LOG;




#define PH(c1, c2) (c2<<8)+c1 // combine two characters into an integer for phoneme name #define PH(c1, c2) (c2<<8)+c1 // combine two characters into an integer for phoneme name
#define PH3(c1, c2, c3) (c3<<16)+(c2<<8)+c1 #define PH3(c1, c2, c3) (c3<<16)+(c2<<8)+c1
#define PhonemeCode2(c1, c2) PhonemeCode((c2<<8)+c1) #define PhonemeCode2(c1, c2) PhonemeCode((c2<<8)+c1)

+ 33
- 32
src/libespeak-ng/phonemelist.c View File

#include "synthesize.h" #include "synthesize.h"
#include "translate.h" #include "translate.h"


const unsigned char pause_phonemes[8] = { 0, phonPAUSE_VSHORT, phonPAUSE_SHORT, phonPAUSE, phonPAUSE_LONG, phonGLOTTALSTOP, phonPAUSE_LONG, phonPAUSE_LONG };
const unsigned char pause_phonemes[8] = {
0, phonPAUSE_VSHORT, phonPAUSE_SHORT, phonPAUSE, phonPAUSE_LONG, phonGLOTTALSTOP, phonPAUSE_LONG, phonPAUSE_LONG
};


extern int n_ph_list2; extern int n_ph_list2;
extern PHONEME_LIST2 ph_list2[N_PHONEME_LIST]; // first stage of text->phonemes
extern PHONEME_LIST2 ph_list2[N_PHONEME_LIST]; // first stage of text->phonemes


static int SubstitutePhonemes(Translator *tr, PHONEME_LIST *plist_out) static int SubstitutePhonemes(Translator *tr, PHONEME_LIST *plist_out)
{ {
// Copy the phonemes list and perform any substitutions that are required for the
// current voice
// Copy the phonemes list and perform any substitutions that are required for the
// current voice
int ix; int ix;
int k; int k;
int replace_flags; int replace_flags;
replace_flags = replace_phonemes[k].type; replace_flags = replace_phonemes[k].type;


if ((replace_flags & 1) && (word_end == 0)) if ((replace_flags & 1) && (word_end == 0))
continue; // this replacement only occurs at the end of a word
continue; // this replacement only occurs at the end of a word


if ((replace_flags & 2) && ((plist2->stresslevel & 0x7) > 3)) if ((replace_flags & 2) && ((plist2->stresslevel & 0x7) > 3))
continue; // this replacement doesn't occur in stressed syllables
continue; // this replacement doesn't occur in stressed syllables


if ((replace_flags & 4) && (plist2->sourceix == 0)) if ((replace_flags & 4) && (plist2->sourceix == 0))
continue; // this replacement only occurs at the start of a word
continue; // this replacement only occurs at the start of a word


// substitute the replacement phoneme // substitute the replacement phoneme
plist2->phcode = replace_phonemes[k].new_ph; plist2->phcode = replace_phonemes[k].new_ph;
if ((plist2->stresslevel > 1) && (phoneme_tab[plist2->phcode]->phflags & phUNSTRESSED)) if ((plist2->stresslevel > 1) && (phoneme_tab[plist2->phcode]->phflags & phUNSTRESSED))
plist2->stresslevel = 0; // the replacement must be unstressed
plist2->stresslevel = 0; // the replacement must be unstressed
break; break;
} }
} }


if (plist2->phcode == 0) if (plist2->phcode == 0)
continue; // phoneme has been replaced by NULL, so don't copy it
continue; // phoneme has been replaced by NULL, so don't copy it
} }


// copy phoneme into the output list // copy phoneme into the output list
// the last word is unstressed, look for a previous word that can be stressed // the last word is unstressed, look for a previous word that can be stressed
while (--j >= 0) { while (--j >= 0) {
if (plist2[j].synthflags & SFLAG_PROMOTE_STRESS) { // dictionary flags indicated that this stress can be promoted if (plist2[j].synthflags & SFLAG_PROMOTE_STRESS) { // dictionary flags indicated that this stress can be promoted
plist2[j].stresslevel = 4; // promote to stressed
plist2[j].stresslevel = 4; // promote to stressed
break; break;
} }
if (plist2[j].stresslevel >= 4) { if (plist2[j].stresslevel >= 4) {
stop_propagation = 0; stop_propagation = 0;
voicing = 0; voicing = 0;
if (regression & 0x100) if (regression & 0x100)
voicing = 1; // word-end devoicing
voicing = 1; // word-end devoicing
continue; continue;
} }


if ((voicing == 0) && (regression & 0xf)) if ((voicing == 0) && (regression & 0xf))
voicing = 1; voicing = 1;
else if ((voicing == 2) && (ph->end_type != 0)) // use end_type field for voicing_switch for consonants else if ((voicing == 2) && (ph->end_type != 0)) // use end_type field for voicing_switch for consonants
plist2[j].phcode = ph->end_type; // change to voiced equivalent
plist2[j].phcode = ph->end_type; // change to voiced equivalent
} else if ((type == phVSTOP) || type == (phVFRICATIVE)) { } else if ((type == phVSTOP) || type == (phVFRICATIVE)) {
if ((voicing == 0) && (regression & 0xf)) if ((voicing == 0) && (regression & 0xf))
voicing = 2; voicing = 2;
else if ((voicing == 1) && (ph->end_type != 0)) else if ((voicing == 1) && (ph->end_type != 0))
plist2[j].phcode = ph->end_type; // change to unvoiced equivalent
plist2[j].phcode = ph->end_type; // change to unvoiced equivalent
} else { } else {
if (regression & 0x8) { if (regression & 0x8) {
// LANG=Polish, propagate through liquids and nasals // LANG=Polish, propagate through liquids and nasals


nextw++; nextw++;
if (ph_list3[nextw].sourceix) if (ph_list3[nextw].sourceix)
break; // start of the next word
break; // start of the next word
} }
for (k = j; k < nextw; k++) for (k = j; k < nextw; k++)
ph_list3[k].wordstress = word_stress; ph_list3[k].wordstress = word_stress;
ph = phoneme_tab[insert_ph]; ph = phoneme_tab[insert_ph];
plist3->ph = ph; plist3->ph = ph;
insert_ph = 0; insert_ph = 0;
inserted = 1; // don't insert the same phoneme repeatedly
inserted = 1; // don't insert the same phoneme repeatedly
} else { } else {
// otherwise get the next phoneme from the list // otherwise get the next phoneme from the list
if (plist3->sourceix != 0) if (plist3->sourceix != 0)
// change phoneme table // change phoneme table
SelectPhonemeTable(plist3->tone_ph); SelectPhonemeTable(plist3->tone_ph);
} }
next = phoneme_tab[plist3[1].phcode]; // the phoneme after this one
next = phoneme_tab[plist3[1].phcode]; // the phoneme after this one
plist3[1].ph = next; plist3[1].ph = next;
} }


if (ph->type == phVOWEL) { if (ph->type == phVOWEL) {
plist3->synthflags |= SFLAG_SYLLABLE; plist3->synthflags |= SFLAG_SYLLABLE;
if (ph2->type != phVOWEL) if (ph2->type != phVOWEL)
plist3->stresslevel = 0; // change from non-vowel to vowel, make sure it's unstressed
plist3->stresslevel = 0; // change from non-vowel to vowel, make sure it's unstressed
} else } else
plist3->synthflags &= ~SFLAG_SYLLABLE; plist3->synthflags &= ~SFLAG_SYLLABLE;


plist3->phcode = alternative; plist3->phcode = alternative;


if (alternative == 1) if (alternative == 1)
deleted = 1; // NULL phoneme, discard
deleted = 1; // NULL phoneme, discard
else { else {
if (ph->type == phVOWEL) { if (ph->type == phVOWEL) {
plist3->synthflags |= SFLAG_SYLLABLE; plist3->synthflags |= SFLAG_SYLLABLE;
if (ph2->type != phVOWEL) if (ph2->type != phVOWEL)
plist3->stresslevel = 0; // change from non-vowel to vowel, make sure it's unstressed
plist3->stresslevel = 0; // change from non-vowel to vowel, make sure it's unstressed
} else } else
plist3->synthflags &= ~SFLAG_SYLLABLE; plist3->synthflags &= ~SFLAG_SYLLABLE;


// stress. But not for the last phoneme of a stressed word // stress. But not for the last phoneme of a stressed word
if ((tr->langopts.stress_flags & S_NO_DIM) || ((word_stress > 3) && ((plist3+1)->sourceix != 0))) { if ((tr->langopts.stress_flags & S_NO_DIM) || ((word_stress > 3) && ((plist3+1)->sourceix != 0))) {
// An unstressed final vowel of a stressed word // An unstressed final vowel of a stressed word
unstress_count = 1; // try again for next syllable
unstress_count = 1; // try again for next syllable
} else } else
plist3->stresslevel = 0; // change stress to 'diminished'
plist3->stresslevel = 0; // change stress to 'diminished'
} }
} }
} else {
} else
unstress_count = 0; unstress_count = 0;
}
} }


if ((plist3+1)->synthflags & SFLAG_LENGTHEN) { if ((plist3+1)->synthflags & SFLAG_LENGTHEN) {
if (deleted == 0) { if (deleted == 0) {
phlist[ix].ph = ph; phlist[ix].ph = ph;
phlist[ix].type = ph->type; phlist[ix].type = ph->type;
phlist[ix].env = PITCHfall; // default, can be changed in the "intonation" module
phlist[ix].env = PITCHfall; // default, can be changed in the "intonation" module
phlist[ix].synthflags = plist3->synthflags; phlist[ix].synthflags = plist3->synthflags;
phlist[ix].stresslevel = plist3->stresslevel & 0xf; phlist[ix].stresslevel = plist3->stresslevel & 0xf;
phlist[ix].wordstress = plist3->wordstress; phlist[ix].wordstress = plist3->wordstress;


if (plist3->sourceix != 0) { if (plist3->sourceix != 0) {
phlist[ix].sourceix = plist3->sourceix; phlist[ix].sourceix = plist3->sourceix;
phlist[ix].newword = 1; // this phoneme is the start of a word
phlist[ix].newword = 1; // this phoneme is the start of a word


if (start_sentence) { if (start_sentence) {
phlist[ix].newword = 5; // start of sentence + start of word
phlist[ix].newword = 5; // start of sentence + start of word
start_sentence = 0; start_sentence = 0;
} }
} else } else
phlist[ix].length = phdata.pd_param[i_SET_LENGTH]*2; phlist[ix].length = phdata.pd_param[i_SET_LENGTH]*2;
if ((ph->code == phonPAUSE_LONG) && (option_wordgap > 0) && (plist3[1].sourceix != 0)) { if ((ph->code == phonPAUSE_LONG) && (option_wordgap > 0) && (plist3[1].sourceix != 0)) {
phlist[ix].ph = phoneme_tab[phonPAUSE_SHORT]; phlist[ix].ph = phoneme_tab[phonPAUSE_SHORT];
phlist[ix].length = option_wordgap*14; // 10mS per unit at the default speed
phlist[ix].length = option_wordgap*14; // 10mS per unit at the default speed
} }


if (ph->type == phVOWEL || ph->type == phLIQUID || ph->type == phNASAL || ph->type == phVSTOP || ph->type == phVFRICATIVE || (ph->phflags & phPREVOICE)) { if (ph->type == phVOWEL || ph->type == phLIQUID || ph->type == phNASAL || ph->type == phVSTOP || ph->type == phVFRICATIVE || (ph->phflags & phPREVOICE)) {
phlist[ix].length = 128; // length_mod
phlist[ix].length = 128; // length_mod
phlist[ix].env = PITCHfall; phlist[ix].env = PITCHfall;
} }


phlist[ix].prepause = 0; phlist[ix].prepause = 0;
phlist[ix].amp = 20; // default, will be changed later
phlist[ix].amp = 20; // default, will be changed later
phlist[ix].pitch1 = 255; phlist[ix].pitch1 = 255;
phlist[ix].pitch2 = 255; phlist[ix].pitch2 = 255;
ix++; ix++;
} }
} }
phlist[ix].newword = 2; // end of clause
phlist[ix].newword = 2; // end of clause


phlist[ix].phcode = phonPAUSE; phlist[ix].phcode = phonPAUSE;
phlist[ix].type = phPAUSE; // terminate with 2 Pause phonemes
phlist[ix].length = post_pause; // length of the pause, depends on the punctuation
phlist[ix].type = phPAUSE; // terminate with 2 Pause phonemes
phlist[ix].length = post_pause; // length of the pause, depends on the punctuation
phlist[ix].sourceix = end_sourceix; phlist[ix].sourceix = end_sourceix;
phlist[ix].synthflags = 0; phlist[ix].synthflags = 0;
phlist[ix++].ph = phoneme_tab[phonPAUSE]; phlist[ix++].ph = phoneme_tab[phonPAUSE];

+ 358
- 346
src/libespeak-ng/readclause.c
File diff suppressed because it is too large
View File


+ 84
- 85
src/libespeak-ng/setlengths.c View File

// convert from words-per-minute to internal speed factor // convert from words-per-minute to internal speed factor
// Use this to calibrate speed for wpm 80-350 // Use this to calibrate speed for wpm 80-350
static unsigned char speed_lookup[] = { static unsigned char speed_lookup[] = {
255, 255, 255, 255, 255, // 80
253, 249, 245, 242, 238, // 85
235, 232, 228, 225, 222, // 90
218, 216, 213, 210, 207, // 95
255, 255, 255, 255, 255, // 80
253, 249, 245, 242, 238, // 85
235, 232, 228, 225, 222, // 90
218, 216, 213, 210, 207, // 95
204, 201, 198, 196, 193, // 100 204, 201, 198, 196, 193, // 100
191, 188, 186, 183, 181, // 105 191, 188, 186, 183, 181, // 105
179, 176, 174, 172, 169, // 110 179, 176, 174, 172, 169, // 110
118, 117, 115, 114, 113, // 145 118, 117, 115, 114, 113, // 145
112, 111, 110, 109, 107, // 150 112, 111, 110, 109, 107, // 150
106, 105, 104, 103, 102, // 155 106, 105, 104, 103, 102, // 155
101, 100, 99, 98, 97,// 160
96, 95, 94, 93, 92, // 165
91, 90, 89, 89, 88, // 170
87, 86, 85, 84, 83, // 175
82, 82, 81, 80, 80, // 180
79, 78, 77, 76, 76, // 185
75, 75, 74, 73, 72, // 190
71, 71, 70, 69, 69, // 195
68, 67, 67, 66, 66, // 200
65, 64, 64, 63, 62, // 205
62, 61, 61, 60, 59, // 210
59, 58, 58, 57, 57, // 215
56, 56, 55, 54, 54, // 220
53, 53, 52, 52, 52, // 225
51, 50, 50, 49, 49, // 230
48, 48, 47, 47, 46, // 235
46, 46, 45, 45, 44, // 240
44, 44, 43, 43, 42, // 245
41, 40, 40, 40, 39, // 250
39, 39, 38, 38, 38, // 255
37, 37, 37, 36, 36, // 260
35, 35, 35, 35, 34, // 265
34, 34, 33, 33, 33, // 270
32, 32, 31, 31, 31, // 275
30, 30, 30, 29, 29, // 280
29, 29, 28, 28, 27, // 285
27, 27, 27, 26, 26, // 290
26, 26, 25, 25, 25, // 295
24, 24, 24, 24, 23, // 300
23, 23, 23, 22, 22, // 305
22, 21, 21, 21, 21, // 310
20, 20, 20, 20, 19, // 315
19, 19, 18, 18, 17, // 320
17, 17, 16, 16, 16, // 325
16, 16, 16, 15, 15, // 330
15, 15, 14, 14, 14, // 335
13, 13, 13, 12, 12, // 340
12, 12, 11, 11, 11, // 345
11, 10, 10, 10, 9, // 350
9, 9, 8, 8, 8, // 355
101, 100, 99, 98, 97, // 160
96, 95, 94, 93, 92, // 165
91, 90, 89, 89, 88, // 170
87, 86, 85, 84, 83, // 175
82, 82, 81, 80, 80, // 180
79, 78, 77, 76, 76, // 185
75, 75, 74, 73, 72, // 190
71, 71, 70, 69, 69, // 195
68, 67, 67, 66, 66, // 200
65, 64, 64, 63, 62, // 205
62, 61, 61, 60, 59, // 210
59, 58, 58, 57, 57, // 215
56, 56, 55, 54, 54, // 220
53, 53, 52, 52, 52, // 225
51, 50, 50, 49, 49, // 230
48, 48, 47, 47, 46, // 235
46, 46, 45, 45, 44, // 240
44, 44, 43, 43, 42, // 245
41, 40, 40, 40, 39, // 250
39, 39, 38, 38, 38, // 255
37, 37, 37, 36, 36, // 260
35, 35, 35, 35, 34, // 265
34, 34, 33, 33, 33, // 270
32, 32, 31, 31, 31, // 275
30, 30, 30, 29, 29, // 280
29, 29, 28, 28, 27, // 285
27, 27, 27, 26, 26, // 290
26, 26, 25, 25, 25, // 295
24, 24, 24, 24, 23, // 300
23, 23, 23, 22, 22, // 305
22, 21, 21, 21, 21, // 310
20, 20, 20, 20, 19, // 315
19, 19, 18, 18, 17, // 320
17, 17, 16, 16, 16, // 325
16, 16, 16, 15, 15, // 330
15, 15, 14, 14, 14, // 335
13, 13, 13, 12, 12, // 340
12, 12, 11, 11, 11, // 345
11, 10, 10, 10, 9, // 350
9, 9, 8, 8, 8, // 355
}; };


// speed_factor1 adjustments for speeds 350 to 374: pauses // speed_factor1 adjustments for speeds 350 to 374: pauses
static unsigned char pause_factor_350[] = { static unsigned char pause_factor_350[] = {
22, 22, 22, 22, 22, 22, 22, 21, 21, 21, // 350 22, 22, 22, 22, 22, 22, 22, 21, 21, 21, // 350
21, 20, 20, 19, 19, 18, 17, 16, 15, 15, // 360 21, 20, 20, 19, 19, 18, 17, 16, 15, 15, // 360
15, 15, 15, 15, 15
}; // 370
15, 15, 15, 15, 15 // 370
};


// wav_factor adjustments for speeds 350 to 450 // wav_factor adjustments for speeds 350 to 450
// Use this to calibrate speed for wpm 350-450 // Use this to calibrate speed for wpm 350-450
111, 111, 110, 109, 108, // 365 111, 111, 110, 109, 108, // 365
107, 106, 106, 104, 103, // 370 107, 106, 106, 104, 103, // 370
103, 102, 102, 102, 101, // 375 103, 102, 102, 102, 101, // 375
101, 99, 98, 98, 97,// 380
96, 96, 95, 94, 93, // 385
91, 90, 91, 90, 89, // 390
88, 86, 85, 86, 85, // 395
85, 84, 82, 81, 80, // 400
79, 77, 78, 78, 76, // 405
77, 75, 75, 74, 73, // 410
71, 72, 70, 69, 69, // 415
69, 67, 65, 64, 63, // 420
63, 63, 61, 61, 59, // 425
59, 59, 58, 56, 57, // 430
58, 56, 54, 53, 52, // 435
52, 53, 52, 52, 50, // 440
48, 47, 47, 45, 46, // 445
45
}; // 450
101, 99, 98, 98, 97, // 380
96, 96, 95, 94, 93, // 385
91, 90, 91, 90, 89, // 390
88, 86, 85, 86, 85, // 395
85, 84, 82, 81, 80, // 400
79, 77, 78, 78, 76, // 405
77, 75, 75, 74, 73, // 410
71, 72, 70, 69, 69, // 415
69, 67, 65, 64, 63, // 420
63, 63, 61, 61, 59, // 425
59, 59, 58, 56, 57, // 430
58, 56, 54, 53, 52, // 435
52, 53, 52, 52, 50, // 440
48, 47, 47, 45, 46, // 445
45 // 450
};


static int speed1 = 130; static int speed1 = 130;
static int speed2 = 121; static int speed2 = 121;


speed.loud_consonants = 0; speed.loud_consonants = 0;
speed.min_sample_len = 450; speed.min_sample_len = 450;
speed.lenmod_factor = 110; // controls the effect of FRFLAG_LEN_MOD reduce length change
speed.lenmod_factor = 110; // controls the effect of FRFLAG_LEN_MOD reduce length change
speed.lenmod2_factor = 100; speed.lenmod2_factor = 100;
speed.min_pause = 5; speed.min_pause = 5;


s1 = (x * voice->speedf1)/256; s1 = (x * voice->speedf1)/256;


if (wpm >= 170) if (wpm >= 170)
speed.wav_factor = 110 + (150*s1)/128; // reduced speed adjustment, used for playing recorded sounds
speed.wav_factor = 110 + (150*s1)/128; // reduced speed adjustment, used for playing recorded sounds
else else
speed.wav_factor = 128 + (128*s1)/130; // = 215 at 170 wpm
speed.wav_factor = 128 + (128*s1)/130; // = 215 at 170 wpm


if (wpm >= 350) if (wpm >= 350)
speed.wav_factor = wav_factor_350[wpm-350]; speed.wav_factor = wav_factor_350[wpm-350];
speed.min_sample_len = 420 - (wpm - 440); speed.min_sample_len = 420 - (wpm - 440);
} }


// adjust for different sample rates
// adjust for different sample rates
speed.min_sample_len = (speed.min_sample_len * samplerate_native) / 22050; speed.min_sample_len = (speed.min_sample_len * samplerate_native) / 22050;


speed.pause_factor = (256 * s1)/115; // full speed adjustment, used for pause length
speed.pause_factor = (256 * s1)/115; // full speed adjustment, used for pause length
speed.clause_pause_factor = 0; speed.clause_pause_factor = 0;


if (wpm > 430) if (wpm > 430)
} }
} }


#else // not using sonic speed-up
#else


void SetSpeed(int control) void SetSpeed(int control)
{ {
// This is the earlier version of SetSpeed() before sonic speed-up was added
// This is the earlier version of SetSpeed() before sonic speed-up was added
int x; int x;
int s1; int s1;
int wpm; int wpm;


speed.loud_consonants = 0; speed.loud_consonants = 0;
speed.min_sample_len = 450; speed.min_sample_len = 450;
speed.lenmod_factor = 110; // controls the effect of FRFLAG_LEN_MOD reduce length change
speed.lenmod_factor = 110; // controls the effect of FRFLAG_LEN_MOD reduce length change
speed.lenmod2_factor = 100; speed.lenmod2_factor = 100;


wpm = embedded_value[EMBED_S]; wpm = embedded_value[EMBED_S];
s1 = (x * voice->speedf1)/256; s1 = (x * voice->speedf1)/256;


if (wpm >= 170) if (wpm >= 170)
speed.wav_factor = 110 + (150*s1)/128; // reduced speed adjustment, used for playing recorded sounds
speed.wav_factor = 110 + (150*s1)/128; // reduced speed adjustment, used for playing recorded sounds
else else
speed.wav_factor = 128 + (128*s1)/130; // = 215 at 170 wpm
speed.wav_factor = 128 + (128*s1)/130; // = 215 at 170 wpm


if (wpm >= 350) if (wpm >= 350)
speed.wav_factor = wav_factor_350[wpm-350]; speed.wav_factor = wav_factor_350[wpm-350];
speed.min_sample_len = 420 - (wpm - 440); speed.min_sample_len = 420 - (wpm - 440);
} }


speed.pause_factor = (256 * s1)/115; // full speed adjustment, used for pause length
speed.pause_factor = (256 * s1)/115; // full speed adjustment, used for pause length
speed.clause_pause_factor = 0; speed.clause_pause_factor = 0;


if (wpm > 430) if (wpm > 430)


#endif #endif



void SetParameter(int parameter, int value, int relative) void SetParameter(int parameter, int value, int relative)
{ {
// parameter: reset-all, amp, pitch, speed, linelength, expression, capitals, number grouping
// relative 0=absolute 1=relative
// parameter: reset-all, amp, pitch, speed, linelength, expression, capitals, number grouping
// relative 0=absolute 1=relative


int new_value = value; int new_value = value;
int default_value; int default_value;


if ((word & 0x1f) == EMBED_S) { if ((word & 0x1f) == EMBED_S) {
// speed // speed
SetEmbedded(word & 0x7f, word >> 8); // adjusts embedded_value[EMBED_S]
SetEmbedded(word & 0x7f, word >> 8); // adjusts embedded_value[EMBED_S]
SetSpeed(1); SetSpeed(1);
} }
} while ((word & 0x80) == 0); } while ((word & 0x80) == 0);


if ((p->ph->phflags & phSIBILANT) && next->type == phSTOP && !next->newword) { if ((p->ph->phflags & phSIBILANT) && next->type == phSTOP && !next->newword) {
if (prev->type == phVOWEL) if (prev->type == phVOWEL)
p->length = 200; // ?? should do this if it's from a prefix
p->length = 200; // ?? should do this if it's from a prefix
else else
p->length = 150; p->length = 150;
} else } else
p->prepause = 40; p->prepause = 40;


if (prev->type == phVOWEL) { if (prev->type == phVOWEL) {
p->prepause = 0; // use murmur instead to link from the preceding vowel
p->prepause = 0; // use murmur instead to link from the preceding vowel
} else if (prev->type == phPAUSE) { } else if (prev->type == phPAUSE) {
// reduce by the length of the preceding pause // reduce by the length of the preceding pause
if (prev->length < p->prepause) if (prev->length < p->prepause)
break; break;
case phLIQUID: case phLIQUID:
case phNASAL: case phNASAL:
p->amp = tr->stress_amps[0]; // unless changed later
p->length = 256; // TEMPORARY
p->amp = tr->stress_amps[0]; // unless changed later
p->length = 256; // TEMPORARY
min_drop = 0; min_drop = 0;


if (p->newword) { if (p->newword) {
length_mod = length_mod / 128; length_mod = length_mod / 128;


if (length_mod < 8) if (length_mod < 8)
length_mod = 8; // restrict how much lengths can be reduced
length_mod = 8; // restrict how much lengths can be reduced


if (stress >= 7) { if (stress >= 7) {
// tonic syllable, include a constant component so it doesn't decrease directly with speed // tonic syllable, include a constant component so it doesn't decrease directly with speed
// this is the last syllable in the clause, lengthen it - more for short vowels // this is the last syllable in the clause, lengthen it - more for short vowels
len = (p->ph->std_length * 2); len = (p->ph->std_length * 2);
if (tr->langopts.stress_flags & S_EO_CLAUSE1) if (tr->langopts.stress_flags & S_EO_CLAUSE1)
len = 200; // don't lengthen short vowels more than long vowels at end-of-clause
len = 200; // don't lengthen short vowels more than long vowels at end-of-clause
length_mod = length_mod * (256 + (280 - len)/3)/256; length_mod = length_mod * (256 + (280 - len)/3)/256;
} }




// pre-vocalic part // pre-vocalic part
// set last-pitch // set last-pitch
env2 = p->env + 1; // version for use with preceding semi-vowel
env2 = p->env + 1; // version for use with preceding semi-vowel


if (p->tone_ph != 0) { if (p->tone_ph != 0) {
InterpretPhoneme2(p->tone_ph, &phdata_tone); InterpretPhoneme2(p->tone_ph, &phdata_tone);
if (pre_sonorant || pre_voiced) { if (pre_sonorant || pre_voiced) {
// set pitch for pre-vocalic part // set pitch for pre-vocalic part
if (pitch_start == 255) if (pitch_start == 255)
last_pitch = pitch_start; // pitch is not set
last_pitch = pitch_start; // pitch is not set


if (pitch_start - last_pitch > 16) if (pitch_start - last_pitch > 16)
last_pitch = pitch_start - 16; last_pitch = pitch_start - 16;

+ 29
- 28
src/libespeak-ng/speak_lib.c View File

int (*uri_callback)(int, const char *, const char *) = NULL; int (*uri_callback)(int, const char *, const char *) = NULL;
int (*phoneme_callback)(const char *) = NULL; int (*phoneme_callback)(const char *) = NULL;


char path_home[N_PATH_HOME]; // this is the espeak-data directory
char path_home[N_PATH_HOME]; // this is the espeak-data directory
extern int saved_parameters[N_SPEECH_PARAM]; // Parameters saved on synthesis start extern int saved_parameters[N_SPEECH_PARAM]; // Parameters saved on synthesis start


void WVoiceChanged(voice_t *wvoice) void WVoiceChanged(voice_t *wvoice)
{ {
// Voice change in wavegen
// Voice change in wavegen
voice_samplerate = wvoice->samplerate; voice_samplerate = wvoice->samplerate;
} }


my_mode = output_type; my_mode = output_type;
my_audio = NULL; my_audio = NULL;
synchronous_mode = 1; synchronous_mode = 1;
option_waveout = 1; // inhibit portaudio callback from wavegen.cpp
option_waveout = 1; // inhibit portaudio callback from wavegen.cpp
out_samplerate = 0; out_samplerate = 0;


switch (my_mode) switch (my_mode)
return 0; return 0;


if (S_ISDIR(statbuf.st_mode)) if (S_ISDIR(statbuf.st_mode))
return -2; // a directory
return -2; // a directory


return statbuf.st_size; return statbuf.st_size;
} }
{ {
char *p; char *p;
if ((p = (char *)malloc(size)) == NULL) if ((p = (char *)malloc(size)) == NULL)
fprintf(stderr, "Can't allocate memory\n"); // I was told that size+1 fixes a crash on 64-bit systems
fprintf(stderr, "Can't allocate memory\n"); // I was told that size+1 fixes a crash on 64-bit systems
return p; return p;
} }


if ((env = getenv("ESPEAK_DATA_PATH")) != NULL) { if ((env = getenv("ESPEAK_DATA_PATH")) != NULL) {
sprintf(path_home, "%s/espeak-data", env); sprintf(path_home, "%s/espeak-data", env);
if (GetFileLength(path_home) == -2) if (GetFileLength(path_home) == -2)
return; // an espeak-data directory exists
return; // an espeak-data directory exists
} }


buf[0] = 0; buf[0] = 0;
if ((env = getenv("ESPEAK_DATA_PATH")) != NULL) { if ((env = getenv("ESPEAK_DATA_PATH")) != NULL) {
snprintf(path_home, sizeof(path_home), "%s/espeak-data", env); snprintf(path_home, sizeof(path_home), "%s/espeak-data", env);
if (GetFileLength(path_home) == -2) if (GetFileLength(path_home) == -2)
return; // an espeak-data directory exists
return; // an espeak-data directory exists
} }


snprintf(path_home, sizeof(path_home), "%s/espeak-data", getenv("HOME")); snprintf(path_home, sizeof(path_home), "%s/espeak-data", getenv("HOME"));
{ {
int param; int param;
int result; int result;
int srate = 22050; // default sample rate 22050 Hz
int srate = 22050; // default sample rate 22050 Hz


err = EE_OK; err = EE_OK;
LoadConfig(); LoadConfig();
#endif #endif


if ((outbuf == NULL) || (event_list == NULL)) if ((outbuf == NULL) || (event_list == NULL))
return EE_INTERNAL_ERROR; // espeak_Initialize() has not been called
return EE_INTERNAL_ERROR; // espeak_Initialize() has not been called


option_multibyte = flags & 7; option_multibyte = flags & 7;
option_ssml = flags & espeakSSML; option_ssml = flags & espeakSSML;
if (my_mode == AUDIO_OUTPUT_SYNCH_PLAYBACK) { if (my_mode == AUDIO_OUTPUT_SYNCH_PLAYBACK) {
for (;;) { for (;;) {
#ifdef PLATFORM_WINDOWS #ifdef PLATFORM_WINDOWS
Sleep(300); // 0.3s
Sleep(300); // 0.3s
#else #else
#ifdef USE_NANOSLEEP #ifdef USE_NANOSLEEP
struct timespec period; struct timespec period;
struct timespec remaining; struct timespec remaining;
period.tv_sec = 0; period.tv_sec = 0;
period.tv_nsec = 300000000; // 0.3 sec
period.tv_nsec = 300000000; // 0.3 sec
nanosleep(&period, &remaining); nanosleep(&period, &remaining);
#else #else
sleep(1); sleep(1);
} else } else
finished = synth_callback((short *)outbuf, length, event_list); finished = synth_callback((short *)outbuf, length, event_list);
if (finished) { if (finished) {
SpeakNextClause(NULL, 0, 2); // stop
SpeakNextClause(NULL, 0, 2); // stop
break; break;
} }


if (dispatch_audio(NULL, 0, NULL) < 0) // TBD: test case if (dispatch_audio(NULL, 0, NULL) < 0) // TBD: test case
return err = EE_INTERNAL_ERROR; return err = EE_INTERNAL_ERROR;
} else } else
synth_callback(NULL, 0, event_list); // NULL buffer ptr indicates end of data
synth_callback(NULL, 0, event_list); // NULL buffer ptr indicates end of data
#else #else
synth_callback(NULL, 0, event_list); // NULL buffer ptr indicates end of data
synth_callback(NULL, 0, event_list); // NULL buffer ptr indicates end of data
#endif #endif
break; break;
} }


my_unique_identifier = 0; my_unique_identifier = 0;
my_user_data = NULL; my_user_data = NULL;
Synthesize(0, key, 0); // speak key as a text string
Synthesize(0, key, 0); // speak key as a text string
} }


void sync_espeak_Char(wchar_t character) void sync_espeak_Char(wchar_t character)
ESPEAK_API void espeak_SetPhonemeTrace(int phonememode, FILE *stream) ESPEAK_API void espeak_SetPhonemeTrace(int phonememode, FILE *stream)
{ {
ENTER("espeak_SetPhonemes"); ENTER("espeak_SetPhonemes");
/* phonememode: Controls the output of phoneme symbols for the text
bits 0-2:
value=0 No phoneme output (default)
value=1 Output the translated phoneme symbols for the text
value=2 as (1), but produces IPA phoneme names rather than ascii
bit 3: output a trace of how the translation was done (showing the matching rules and list entries)
bit 4: produce pho data for mbrola
bit 7: use (bits 8-23) as a tie within multi-letter phonemes names
bits 8-23: separator character, between phoneme names

stream output stream for the phoneme symbols (and trace). If stream=NULL then it uses stdout.
*/
/* phonememode: Controls the output of phoneme symbols for the text
bits 0-2:
value=0 No phoneme output (default)
value=1 Output the translated phoneme symbols for the text
value=2 as (1), but produces IPA phoneme names rather than ascii
bit 3: output a trace of how the translation was done (showing the matching rules and list entries)
bit 4: produce pho data for mbrola
bit 7: use (bits 8-23) as a tie within multi-letter phonemes names
bits 8-23: separator character, between phoneme names

stream output stream for the phoneme symbols (and trace). If stream=NULL then it uses stdout.
*/

option_phonemes = phonememode; option_phonemes = phonememode;
f_trans = stream; f_trans = stream;
if (stream == NULL) if (stream == NULL)
wave_close(my_audio); wave_close(my_audio);
SHOW_TIME("espeak_Cancel > LEAVE"); SHOW_TIME("espeak_Cancel > LEAVE");
#endif #endif
embedded_value[EMBED_T] = 0; // reset echo for pronunciation announcements
embedded_value[EMBED_T] = 0; // reset echo for pronunciation announcements


for (int i = 0; i < N_SPEECH_PARAM; i++) for (int i = 0; i < N_SPEECH_PARAM; i++)
SetParameter(i, saved_parameters[i], 0); SetParameter(i, saved_parameters[i], 0);

+ 8
- 7
src/libespeak-ng/spect.c View File



float polint(float xa[], float ya[], int n, float x) float polint(float xa[], float ya[], int n, float x)
{ {
// General polinomial interpolation routine, xa[1...n] ya[1...n]
// General polinomial interpolation routine, xa[1...n] ya[1...n]
int i, m, ns = 1; int i, m, ns = 1;
float den, dif, dift, ho, hp, w; float den, dif, dift, ho, hp, w;
float y; // result
float y; // result
float c[9], d[9]; float c[9], d[9];


dif = fabs(x-xa[1]); dif = fabs(x-xa[1]);
hp = xa[i+m]-x; hp = xa[i+m]-x;
w = c[i+1]-d[i]; w = c[i+1]-d[i];
if ((den = ho-hp) == 0.0) if ((den = ho-hp) == 0.0)
return ya[2]; // two input xa are identical
return ya[2]; // two input xa are identical
den = w/den; den = w/den;
d[i] = hp*den; d[i] = hp*den;
c[i] = ho*den; c[i] = ho*den;
if (frame >= spect->numframes-1) return 0; if (frame >= spect->numframes-1) return 0;


for (ix = frame+1; ix < spect->numframes-1; ix++) { for (ix = frame+1; ix < spect->numframes-1; ix++) {
if (spect->frames[ix]->keyframe) break; // reached next keyframe
if (spect->frames[ix]->keyframe)
break; // reached next keyframe
adjust += spect->frames[ix]->length_adjust; adjust += spect->frames[ix]->length_adjust;
} }
return (spect->frames[ix]->time - spect->frames[frame]->time) * 1000.0 + adjust; return (spect->frames[ix]->time - spect->frames[frame]->time) * 1000.0 + adjust;
fread(&id2, sizeof(uint32_t), 1, stream); fread(&id2, sizeof(uint32_t), 1, stream);


if ((id1 == FILEID1_SPECTSEQ) && (id2 == FILEID2_SPECTSEQ)) if ((id1 == FILEID1_SPECTSEQ) && (id2 == FILEID2_SPECTSEQ))
spect->file_format = 0; // eSpeak formants
spect->file_format = 0; // eSpeak formants
else if ((id1 == FILEID1_SPECTSEQ) && (id2 == FILEID2_SPECTSEK)) else if ((id1 == FILEID1_SPECTSEQ) && (id2 == FILEID2_SPECTSEK))
spect->file_format = 1; // formants for Klatt synthesizer
spect->file_format = 1; // formants for Klatt synthesizer
else if ((id1 == FILEID1_SPECTSEQ) && (id2 == FILEID2_SPECTSQ2)) else if ((id1 == FILEID1_SPECTSEQ) && (id2 == FILEID2_SPECTSQ2))
spect->file_format = 2; // formants for Klatt synthesizer
spect->file_format = 2; // formants for Klatt synthesizer
else { else {
fprintf(stderr, "Unsupported spectral file format.\n"); fprintf(stderr, "Unsupported spectral file format.\n");
fclose(stream); fclose(stream);

+ 4
- 8
src/libespeak-ng/spect.h View File

#define MAX_DISPLAY_FREQ 9500 #define MAX_DISPLAY_FREQ 9500
#define FRAME_HEIGHT 240 #define FRAME_HEIGHT 240



#define T_ZOOMOUT 301 #define T_ZOOMOUT 301
#define T_ZOOMIN 302 #define T_ZOOMIN 302
#define T_USEPITCHENV 303 #define T_USEPITCHENV 303
#define T_KOPEN 320 #define T_KOPEN 320
#define T_FNZ 321 #define T_FNZ 321



#define FILEID1_SPECTSEQ 0x43455053 #define FILEID1_SPECTSEQ 0x43455053
#define FILEID2_SPECTSEQ 0x51455354 // for eSpeak sequence #define FILEID2_SPECTSEQ 0x51455354 // for eSpeak sequence
#define FILEID2_SPECTSEK 0x4b455354 // for Klatt sequence #define FILEID2_SPECTSEK 0x4b455354 // for Klatt sequence
#define FILEID1_PRAATSEQ 0x41415250 #define FILEID1_PRAATSEQ 0x41415250
#define FILEID2_PRAATSEQ 0x51455354 #define FILEID2_PRAATSEQ 0x51455354



typedef struct { typedef struct {
unsigned short pitch1; unsigned short pitch1;
unsigned short pitch2; unsigned short pitch2;
typedef struct { typedef struct {
short freq; short freq;
short bandw; short bandw;
} formant_t;
} formant_t;


typedef struct { typedef struct {
short pkfreq; short pkfreq;
short klt_bw; short klt_bw;
short klt_ap; short klt_ap;
short klt_bp; short klt_bp;
} peak_t;

} peak_t;


typedef struct { typedef struct {
int keyframe; int keyframe;
unsigned short nx; unsigned short nx;
short markers; short markers;
int max_y; int max_y;
USHORT *spect; // sqrt of harmonic amplitudes, 1-nx at 'pitch'
USHORT *spect; // sqrt of harmonic amplitudes, 1-nx at 'pitch'


short klatt_param[N_KLATTP2]; short klatt_param[N_KLATTP2];


formant_t formants[N_PEAKS]; // this is just the estimate given by Praat
formant_t formants[N_PEAKS]; // this is just the estimate given by Praat
peak_t peaks[N_PEAKS]; peak_t peaks[N_PEAKS];
} SpectFrame; } SpectFrame;



+ 0
- 2
src/libespeak-ng/speech.h View File

#endif #endif
#endif #endif




typedef struct { typedef struct {
const char *mnem; const char *mnem;
int value; int value;

+ 29
- 28
src/libespeak-ng/synth_mbrola.c View File



espeak_ERROR LoadMbrolaTable(const char *mbrola_voice, const char *phtrans, int *srate) espeak_ERROR LoadMbrolaTable(const char *mbrola_voice, const char *phtrans, int *srate)
{ {
// Load a phoneme name translation table from espeak-data/mbrola
// Load a phoneme name translation table from espeak-data/mbrola


int size; int size;
int ix; int ix;
close_MBR(); close_MBR();
#endif #endif
#ifdef PLATFORM_WINDOWS #ifdef PLATFORM_WINDOWS
if (load_MBR() == FALSE) { // load mbrola.dll
if (load_MBR() == FALSE) { // load mbrola.dll
fprintf(stderr, "Can't load mbrola.dll\n"); fprintf(stderr, "Can't load mbrola.dll\n");
return EE_INTERNAL_ERROR; return EE_INTERNAL_ERROR;
} }
#endif #endif


if (init_MBR(path) != 0) // initialise the required mbrola voice
if (init_MBR(path) != 0) // initialise the required mbrola voice
return EE_NOT_FOUND; return EE_NOT_FOUND;


setNoError_MBR(1); // don't stop on phoneme errors
setNoError_MBR(1); // don't stop on phoneme errors


// read eSpeak's mbrola phoneme translation data, eg. en1_phtrans // read eSpeak's mbrola phoneme translation data, eg. en1_phtrans
sprintf(path, "%s/mbrola_ph/%s", path_home, phtrans); sprintf(path, "%s/mbrola_ph/%s", path_home, phtrans);
else else
SetParameter(espeakVOICETYPE, 1, 0); SetParameter(espeakVOICETYPE, 1, 0);
strcpy(mbrola_name, mbrola_voice); strcpy(mbrola_name, mbrola_voice);
mbrola_delay = 1000; // improve synchronization of events
mbrola_delay = 1000; // improve synchronization of events
return EE_OK; return EE_OK;
} }


static int GetMbrName(PHONEME_LIST *plist, PHONEME_TAB *ph, PHONEME_TAB *ph_prev, PHONEME_TAB *ph_next, int *name2, int *split, int *control) static int GetMbrName(PHONEME_LIST *plist, PHONEME_TAB *ph, PHONEME_TAB *ph_prev, PHONEME_TAB *ph_next, int *name2, int *split, int *control)
{ {
// Look up a phoneme in the mbrola phoneme name translation table
// It may give none, 1, or 2 mbrola phonemes
// Look up a phoneme in the mbrola phoneme name translation table
// It may give none, 1, or 2 mbrola phonemes

MBROLA_TAB *pr; MBROLA_TAB *pr;
PHONEME_TAB *other_ph; PHONEME_TAB *other_ph;
int found = 0; int found = 0;
if (pr->control & 2) if (pr->control & 2)
other_ph = ph_prev; other_ph = ph_prev;
else if ((pr->control & 8) && ((plist+1)->newword)) else if ((pr->control & 8) && ((plist+1)->newword))
other_ph = phoneme_tab[phPAUSE]; // don't match the next phoneme over a word boundary
other_ph = phoneme_tab[phPAUSE]; // don't match the next phoneme over a word boundary
else else
other_ph = ph_next; other_ph = ph_next;


found = 1; found = 1;
} }


if ((pr->control & 4) && (plist->newword == 0)) // only at start of word
if ((pr->control & 4) && (plist->newword == 0)) // only at start of word
found = 0; found = 0;


if ((pr->control & 0x40) && (plist[1].newword == 0)) // only at the end of a word
if ((pr->control & 0x40) && (plist[1].newword == 0)) // only at the end of a word
found = 0; found = 0;


if ((pr->control & 0x20) && (plist->stresslevel < plist->wordstress)) if ((pr->control & 0x20) && (plist->stresslevel < plist->wordstress))
found = 0; // only in stressed syllables
found = 0; // only in stressed syllables


if (found) { if (found) {
*name2 = pr->mbr_name2; *name2 = pr->mbr_name2;


static char *WritePitch(int env, int pitch1, int pitch2, int split, int final) static char *WritePitch(int env, int pitch1, int pitch2, int split, int final)
{ {
// final=1: only give the final pitch value.
// final=1: only give the final pitch value.
int x; int x;
int ix; int ix;
int pitch_base; int pitch_base;
int min = 999; int min = 999;
int y_max = 0; int y_max = 0;
int y_min = 0; int y_min = 0;
int env100 = 80; // apply the pitch change only over this proportion of the mbrola phoneme(s)
int env100 = 80; // apply the pitch change only over this proportion of the mbrola phoneme(s)
int y2; int y2;
int y[4]; int y[4];
int env_split; int env_split;
y[3] = y[2] + (127 - y[2])/2; y[3] = y[2] + (127 - y[2])/2;


// set initial pitch // set initial pitch
p1 = ((pitch_env[0]*pitch_range)>>8) + pitch_base; // Hz << 12
p1 = ((pitch_env[0]*pitch_range)>>8) + pitch_base; // Hz << 12
p_end = ((pitch_env[127]*pitch_range)>>8) + pitch_base; p_end = ((pitch_env[127]*pitch_range)>>8) + pitch_base;


if (split >= 0) { if (split >= 0) {


int MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, int resume, FILE *f_mbrola) int MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, int resume, FILE *f_mbrola)
{ {
// Generate a mbrola pho file
// Generate a mbrola pho file
unsigned int name; unsigned int name;
int len; int len;
int len1; int len1;


if (name == 0) { if (name == 0) {
phix++; phix++;
continue; // ignore this phoneme
continue; // ignore this phoneme
} }


if ((ph->type == phPAUSE) && (name == ph->mnemonic)) { if ((ph->type == phPAUSE) && (name == ph->mnemonic)) {
case phVOWEL: case phVOWEL:
len = ph->std_length; len = ph->std_length;
if (p->synthflags & SFLAG_LENGTHEN) if (p->synthflags & SFLAG_LENGTHEN)
len += phoneme_tab[phonLENGTHEN]->std_length; // phoneme was followed by an extra : symbol
len += phoneme_tab[phonLENGTHEN]->std_length; // phoneme was followed by an extra : symbol


if (ph_next->type == phPAUSE) if (ph_next->type == phPAUSE)
len += 50; // lengthen vowels before a pause
len += 50; // lengthen vowels before a pause
len = (len * p->length)/256; len = (len * p->length)/256;


if (name2 == 0) { if (name2 == 0) {
InterpretPhoneme(NULL, 0, p, &phdata, NULL); InterpretPhoneme(NULL, 0, p, &phdata, NULL);
len = DoSample3(&phdata, 0, -1); len = DoSample3(&phdata, 0, -1);


len = (len * 1000)/samplerate; // convert to mS
len = (len * 1000)/samplerate; // convert to mS
len += PauseLength(p->prepause, 1); len += PauseLength(p->prepause, 1);
break; break;
case phVSTOP: case phVSTOP:
len = 0; len = 0;
InterpretPhoneme(NULL, 0, p, &phdata, NULL); InterpretPhoneme(NULL, 0, p, &phdata, NULL);
if (p->synthflags & SFLAG_LENGTHEN) if (p->synthflags & SFLAG_LENGTHEN)
len = DoSample3(&phdata, p->length, -1); // play it twice for [s:] etc.
len = DoSample3(&phdata, p->length, -1); // play it twice for [s:] etc.
len += DoSample3(&phdata, p->length, -1); len += DoSample3(&phdata, p->length, -1);


len = (len * 1000)/samplerate; // convert to mS
len = (len * 1000)/samplerate; // convert to mS
break; break;
case phNASAL: case phNASAL:
if (next->type != phVOWEL) { if (next->type != phVOWEL) {
} }


if (f_mbrola) if (f_mbrola)
fwrite(mbr_buf, 1, (ptr-mbr_buf), f_mbrola); // write .pho to a file
fwrite(mbr_buf, 1, (ptr-mbr_buf), f_mbrola); // write .pho to a file
else { else {
int res = write_MBR(mbr_buf); int res = write_MBR(mbr_buf);
if (res < 0) if (res < 0)
return 0; /* don't get stuck on error */
return 0; // don't get stuck on error
if (res == 0) if (res == 0)
return 1; return 1;
wcmdq[wcmdq_tail][0] = WCMD_MBROLA_DATA; wcmdq[wcmdq_tail][0] = WCMD_MBROLA_DATA;


int MbrolaFill(int length, int resume, int amplitude) int MbrolaFill(int length, int resume, int amplitude)
{ {
// Read audio data from Mbrola (length is in millisecs)
// Read audio data from Mbrola (length is in millisecs)


static int n_samples; static int n_samples;
int req_samples, result; int req_samples, result;
for (ix = 0; ix < result; ix++) { for (ix = 0; ix < result; ix++) {
value16 = out_ptr[0] + (out_ptr[1] << 8); value16 = out_ptr[0] + (out_ptr[1] << 8);
value = value16 * amplitude; value = value16 * amplitude;
value = value / 40; // adjust this constant to give a suitable amplitude for mbrola voices
value = value / 40; // adjust this constant to give a suitable amplitude for mbrola voices
if (value > 0x7fff) if (value > 0x7fff)
value = 0x7fff; value = 0x7fff;
if (value < -0x8000) if (value < -0x8000)


void MbrolaReset(void) void MbrolaReset(void)
{ {
// Reset the Mbrola engine and flush the pending audio
// Reset the Mbrola engine and flush the pending audio


reset_MBR(); reset_MBR();
} }


#else // INCLUDE_MBROLA
#else


// mbrola interface is not compiled, provide dummy functions. // mbrola interface is not compiled, provide dummy functions.


{ {
} }


#endif // INCLUDE_MBROLA
#endif

+ 71
- 77
src/libespeak-ng/synthdata.c View File

PHONEME_TAB_LIST phoneme_tab_list[N_PHONEME_TABS]; PHONEME_TAB_LIST phoneme_tab_list[N_PHONEME_TABS];
int phoneme_tab_number = 0; int phoneme_tab_number = 0;


int wavefile_ix; // a wavefile to play along with the synthesis
int wavefile_ix; // a wavefile to play along with the synthesis
int wavefile_amp; int wavefile_amp;
int wavefile_ix2; int wavefile_ix2;
int wavefile_amp2; int wavefile_amp2;
n_tunes = length / sizeof(TUNE); n_tunes = length / sizeof(TUNE);


// read the version number and sample rate from the first 8 bytes of phondata // read the version number and sample rate from the first 8 bytes of phondata
version = 0; // bytes 0-3, version number
rate = 0; // bytes 4-7, sample rate
version = 0; // bytes 0-3, version number
rate = 0; // bytes 4-7, sample rate
for (ix = 0; ix < 4; ix++) { for (ix = 0; ix < 4; ix++) {
version += (wavefile_data[ix] << (ix*8)); version += (wavefile_data[ix] << (ix*8));
rate += (wavefile_data[ix+4] << (ix*8)); rate += (wavefile_data[ix+4] << (ix*8));
if (which == 1) if (which == 1)
nf = seq_break + 1; nf = seq_break + 1;
else { else {
frames = &frames_buf[seq_break]; // body of vowel, skip past initial frames
frames = &frames_buf[seq_break]; // body of vowel, skip past initial frames
nf -= seq_break; nf -= seq_break;
} }
} }
if (length1 > 0) { if (length1 > 0) {
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; 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)
length_std += (phoneme_tab[phonLENGTHEN]->std_length * 2); // phoneme was followed by an extra : symbol

// can adjust vowel length for stressed syllables here
length_std += (phoneme_tab[phonLENGTHEN]->std_length * 2); // phoneme was followed by an extra : symbol


// can adjust vowel length for stressed syllables here


length_factor = (length_std * 256)/ length1; length_factor = (length_std * 256)/ length1;


{ {
if (index == 0) { if (index == 0) {
fprintf(stderr, "espeak: No envelope\n"); fprintf(stderr, "espeak: No envelope\n");
return envelope_data[0]; // not found, use a default envelope
return envelope_data[0]; // not found, use a default envelope
} }
return (unsigned char *)&phondata_ptr[index]; return (unsigned char *)&phondata_ptr[index];
} }
n_phoneme_tab = ph_code; n_phoneme_tab = ph_code;


if (recursing == 0) if (recursing == 0)
phoneme_tab_flags[ph_code] |= 1; // not inherited
phoneme_tab_flags[ph_code] |= 1; // not inherited
} }
} }


void SelectPhonemeTable(int number) void SelectPhonemeTable(int number)
{ {
n_phoneme_tab = 0; n_phoneme_tab = 0;
SetUpPhonemeTable(number, 0); // recursively for included phoneme tables
SetUpPhonemeTable(number, 0); // recursively for included phoneme tables
n_phoneme_tab++; n_phoneme_tab++;
current_phoneme_table = number; current_phoneme_table = number;
} }


int SelectPhonemeTableName(const char *name) int SelectPhonemeTableName(const char *name)
{ {
// Look up a phoneme set by name, and select it if it exists
// Returns the phoneme table number
// Look up a phoneme set by name, and select it if it exists
// Returns the phoneme table number
int ix; int ix;


if ((ix = LookupPhonemeTable(name)) == -1) if ((ix = LookupPhonemeTable(name)) == -1)


void LoadConfig(void) void LoadConfig(void)
{ {
// Load configuration file, if one exists
// Load configuration file, if one exists
char buf[sizeof(path_home)+10]; char buf[sizeof(path_home)+10];
FILE *f; FILE *f;
int ix; int ix;


static bool StressCondition(Translator *tr, PHONEME_LIST *plist, int condition, int control) static bool StressCondition(Translator *tr, PHONEME_LIST *plist, int condition, int control)
{ {
// condition:
// 0 if diminished, 1 if unstressed, 2 if not stressed, 3 if stressed, 4 if max stress
// condition:
// 0 if diminished, 1 if unstressed, 2 if not stressed, 3 if stressed, 4 if max stress


int stress_level; int stress_level;
PHONEME_LIST *pl; PHONEME_LIST *pl;
if (phoneme_tab[plist[1].phcode]->type == phVOWEL) if (phoneme_tab[plist[1].phcode]->type == phVOWEL)
pl = &plist[1]; pl = &plist[1];
else else
return false; // no stress elevel for this consonant
return false; // no stress elevel for this consonant
} }


stress_level = pl->stresslevel & 0xf; stress_level = pl->stresslevel & 0xf;


switch (which) switch (which)
{ {
case 0: // prevPh
case 5: // prevPhW
case 0: // prevPh
case 5: // prevPhW
plist--; plist--;
check_endtype = 1; check_endtype = 1;
break; break;
case 1: // thisPh
case 1: // thisPh
break; break;
case 2: // nextPh
case 4: // nextPhW
case 2: // nextPh
case 4: // nextPhW
plist++; plist++;
break; break;
case 3: // next2Ph
case 6: // next2PhW
case 3: // next2Ph
case 6: // next2PhW
plist += 2; plist += 2;
break; break;
case 7: case 7:
} }
} }
break; break;
case 8: // prevVowel in this word
case 8: // prevVowel in this word
if ((worddata == NULL) || (worddata->prev_vowel.ph == NULL)) if ((worddata == NULL) || (worddata->prev_vowel.ph == NULL))
return false; // no previous vowel
return false; // no previous vowel
plist = &(worddata->prev_vowel); plist = &(worddata->prev_vowel);
check_endtype = 1; check_endtype = 1;
break; break;
case 9: // next3PhW
case 9: // next3PhW
for (ix = 1; ix <= 3; ix++) { for (ix = 1; ix <= 3; ix++) {
if (plist[ix].sourceix) if (plist[ix].sourceix)
return false; return false;


// not an exact match, check for a vowel type (eg. #i ) // not an exact match, check for a vowel type (eg. #i )
if ((check_endtype) && (ph->type == phVOWEL)) if ((check_endtype) && (ph->type == phVOWEL))
return data == ph->end_type; // prevPh() match on end_type
return data == ph->start_type; // thisPh() or nextPh(), match on start_type
return data == ph->end_type; // prevPh() match on end_type
return data == ph->start_type; // thisPh() or nextPh(), match on start_type
} }


data = instn & 0x1f; data = instn & 0x1f;
case 3: case 3:
case 4: case 4:
return StressCondition(tr, plist, data, 0); return StressCondition(tr, plist, data, 0);
case 5: // isBreak, Either pause phoneme, or (stop/vstop/vfric not followed by vowel or (liquid in same word))
case 5: // isBreak, Either pause phoneme, or (stop/vstop/vfric not followed by vowel or (liquid in same word))
return (ph->type == phPAUSE) || (plist_this->synthflags & SFLAG_NEXT_PAUSE); return (ph->type == phPAUSE) || (plist_this->synthflags & SFLAG_NEXT_PAUSE);
case 6: // isWordStart
case 6: // isWordStart
return plist->sourceix != 0; return plist->sourceix != 0;
case 7: // notWordStart
case 7: // notWordStart
return plist->sourceix == 0; return plist->sourceix == 0;
case 8: // isWordEnd
case 8: // isWordEnd
return plist[1].sourceix || (plist[1].ph->type == phPAUSE); return plist[1].sourceix || (plist[1].ph->type == phPAUSE);
break; break;
case 9: // isAfterStress
case 9: // isAfterStress
if (plist->sourceix != 0) if (plist->sourceix != 0)
return false; return false;
do { do {


} while (plist->sourceix == 0); } while (plist->sourceix == 0);
break; break;
case 10: // isNotVowel
case 10: // isNotVowel
return ph->type != phVOWEL; return ph->type != phVOWEL;
case 11: // isFinalVowel
case 11: // isFinalVowel
for (;;) { for (;;) {
plist++; plist++;
// plist->ph = phoneme_tab[plist->phcode]; // Why was this line here?? It corrupts plist if we have language switching if phoneme_tab is wrong language
if (plist->sourceix != 0) if (plist->sourceix != 0)
return true; // start of next word, without finding another vowel
return true; // start of next word, without finding another vowel
if (plist->ph->type == phVOWEL) if (plist->ph->type == phVOWEL)
return false; return false;
} }
break; break;
case 12: // isVoiced
case 12: // isVoiced
return (ph->type == phVOWEL) || (ph->type == phLIQUID) || (ph->phflags & phVOICED); return (ph->type == phVOWEL) || (ph->type == phLIQUID) || (ph->phflags & phVOICED);
case 13: // isFirstVowel
case 13: // isFirstVowel
return CountVowelPosition(plist) == 1; return CountVowelPosition(plist) == 1;
case 14: // isSecondVowel
case 14: // isSecondVowel
return CountVowelPosition(plist) == 2; return CountVowelPosition(plist) == 2;
case 15: // isSeqFlag1
case 15: // isSeqFlag1
// is this preceded by a sequence if 1 or more vowels which have 'flag1' ? (lang=hi) // is this preceded by a sequence if 1 or more vowels which have 'flag1' ? (lang=hi)
if (plist->sourceix != 0) if (plist->sourceix != 0)
return false; // this is the first phoneme in the word, so no.
return false; // this is the first phoneme in the word, so no.


count = 0; count = 0;
for (;;) { for (;;) {
if (plist->ph->phflags & phFLAG1) if (plist->ph->phflags & phFLAG1)
count++; count++;
else else
break; // stop when we find a vowel without flag1
break; // stop when we find a vowel without flag1
} }
if (plist->sourceix != 0) if (plist->sourceix != 0)
break; break;
} }
return count > 0; return count > 0;
case 0x10: // isTranslationGiven
case 0x10: // isTranslationGiven
return (plist->synthflags & SFLAG_DICTIONARY) != 0; return (plist->synthflags & SFLAG_DICTIONARY) != 0;
} }
break; break;
// Other conditions // Other conditions
switch (data) switch (data)
{ {
case 1: // PreVoicing
case 1: // PreVoicing
return control & 1; return control & 1;
case 2: // KlattSynth
case 2: // KlattSynth
return voice->klattv[0] != 0; return voice->klattv[0] != 0;
case 3: // MbrolaSynth
case 3: // MbrolaSynth
return mbrola_name[0] != 0; return mbrola_name[0] != 0;
} }
} }


if (instn_type == 2) { if (instn_type == 2) {
phdata->pd_control |= pd_FORNEXTPH; phdata->pd_control |= pd_FORNEXTPH;
voweltype = plist[1].ph->start_type; // SwitchNextVowelType
voweltype = plist[1].ph->start_type; // SwitchNextVowelType
} else } else
voweltype = plist[-1].ph->end_type; // SwitchPrevVowelType
voweltype = plist[-1].ph->end_type; // SwitchPrevVowelType


voweltype -= phonVOWELTYPES; voweltype -= phonVOWELTYPES;
if ((voweltype >= 0) && (voweltype < 6)) { if ((voweltype >= 0) && (voweltype < 6)) {
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;
phdata->sound_param[instn_type] = x; // sign extend
phdata->sound_param[instn_type] = x; // sign extend
} }


*p_prog += 12; *p_prog += 12;
case 6: case 6:
type2 = (instn & 0xf00) >> 9; type2 = (instn & 0xf00) >> 9;
if ((type2 == 5) || (type2 == 6)) if ((type2 == 5) || (type2 == 6))
return 12; // switch on vowel type
return 12; // switch on vowel type
return 1; return 1;
case 2: case 2:
case 3: case 3:


void InterpretPhoneme(Translator *tr, int control, PHONEME_LIST *plist, PHONEME_DATA *phdata, WORD_PH_DATA *worddata) void InterpretPhoneme(Translator *tr, int control, PHONEME_LIST *plist, PHONEME_DATA *phdata, WORD_PH_DATA *worddata)
{ {
// control:
// bit 0: PreVoicing
// bit 8: change phonemes
// control:
// bit 0: PreVoicing
// bit 8: change phonemes

PHONEME_TAB *ph; PHONEME_TAB *ph;
USHORT *prog; USHORT *prog;
USHORT instn; USHORT instn;


#define N_RETURN 10 #define N_RETURN 10
int n_return = 0; int n_return = 0;
USHORT *return_addr[N_RETURN]; // return address stack
USHORT *return_addr[N_RETURN]; // return address stack


ph = plist->ph; ph = plist->ph;




switch (instn >> 12) switch (instn >> 12)
{ {
case 0: // 0xxx
case 0: // 0xxx
data = instn & 0xff; data = instn & 0xff;


if (instn2 == 0) { if (instn2 == 0) {
case i_RETURN: case i_RETURN:
end_flag = 1; end_flag = 1;
break; break;

case i_CONTINUE: case i_CONTINUE:
break; break;

default: default:
InvalidInstn(ph, instn); InvalidInstn(ph, instn);
break; break;
phdata->ipa_string[ix] = 0; phdata->ipa_string[ix] = 0;
} else if (instn2 < N_PHONEME_DATA_PARAM) { } else if (instn2 < N_PHONEME_DATA_PARAM) {
if (instn2 == i_CHANGE_PHONEME2) if (instn2 == i_CHANGE_PHONEME2)
phdata->pd_param[i_CHANGE_PHONEME] = data; // also set ChangePhoneme
phdata->pd_param[i_CHANGE_PHONEME] = data; // also set ChangePhoneme
phdata->pd_param[instn2] = data; phdata->pd_param[instn2] = data;
if ((instn2 == i_CHANGE_PHONEME) && (control & 0x100)) { if ((instn2 == i_CHANGE_PHONEME) && (control & 0x100)) {
// found ChangePhoneme() in PhonemeList mode, exit // found ChangePhoneme() in PhonemeList mode, exit
break; break;
case 1: case 1:
if (tr == NULL) if (tr == NULL)
break; // ignore if in synthesis stage
break; // ignore if in synthesis stage


if (instn2 < 8) { if (instn2 < 8) {
// ChangeIf // ChangeIf
if (StressCondition(tr, plist, instn2 & 7, 1) == true) { if (StressCondition(tr, plist, instn2 & 7, 1) == true) {
phdata->pd_param[i_CHANGE_PHONEME] = instn & 0xff; phdata->pd_param[i_CHANGE_PHONEME] = instn & 0xff;
end_flag = 1; // change phoneme, exit
end_flag = 1; // change phoneme, exit
} }
} }
break; break;
// instruction after a condition is not JUMP_FALSE, so skip the instruction. // instruction after a condition is not JUMP_FALSE, so skip the instruction.
prog += NumInstnWords(prog); prog += NumInstnWords(prog);
if ((prog[0] & 0xfe00) == 0x6000) if ((prog[0] & 0xfe00) == 0x6000)
prog++; // and skip ELSE jump
prog++; // and skip ELSE jump
} }
} }
prog--; prog--;
case 0: case 0:
prog += (instn & 0xff) - 1; prog += (instn & 0xff) - 1;
break; break;

case 4: case 4:
// conditional jumps should have been processed in the Condition section // conditional jumps should have been processed in the Condition section
break; break;

case 5: // NexttVowelStarts
case 5: // NexttVowelStarts
SwitchOnVowelType(plist, phdata, &prog, 2); SwitchOnVowelType(plist, phdata, &prog, 2);
break; break;

case 6: // PrevVowelTypeEndings
case 6: // PrevVowelTypeEndings
SwitchOnVowelType(plist, phdata, &prog, 3); SwitchOnVowelType(plist, phdata, &prog, 3);
break; break;
} }
break; break;
} }
break; break;
case 10: // Vowelin, Vowelout
case 10: // Vowelin, Vowelout
if (instn2 == 1) if (instn2 == 1)
ix = 0; ix = 0;
else else
phdata->vowel_transition[ix+1] = (prog[2] << 16) + prog[3]; phdata->vowel_transition[ix+1] = (prog[2] << 16) + prog[3];
prog += 3; prog += 3;
break; break;
case 11: // FMT
case 12: // WAV
case 13: // VowelStart
case 14: // VowelEnd
case 15: // addWav
case 11: // FMT
case 12: // WAV
case 13: // VowelStart
case 14: // VowelEnd
case 15: // addWav
instn2 = (instn >> 12) - 11; instn2 = (instn >> 12) - 11;
phdata->sound_addr[instn2] = ((instn & 0xf) << 18) + (prog[1] << 2); phdata->sound_addr[instn2] = ((instn & 0xf) << 18) + (prog[1] << 2);
param_sc = phdata->sound_param[instn2] = (instn >> 4) & 0xff; param_sc = phdata->sound_param[instn2] = (instn >> 4) & 0xff;


plist->std_length = phdata->pd_param[i_SET_LENGTH]; plist->std_length = phdata->pd_param[i_SET_LENGTH];
if (phdata->sound_addr[0] != 0) { if (phdata->sound_addr[0] != 0) {
plist->phontab_addr = phdata->sound_addr[0]; // FMT address
plist->phontab_addr = phdata->sound_addr[0]; // FMT address
plist->sound_param = phdata->sound_param[0]; plist->sound_param = phdata->sound_param[0];
} else { } else {
plist->phontab_addr = phdata->sound_addr[1]; // WAV address
plist->phontab_addr = phdata->sound_addr[1]; // WAV address
plist->sound_param = phdata->sound_param[1]; plist->sound_param = phdata->sound_param[1];
} }
} }


void InterpretPhoneme2(int phcode, PHONEME_DATA *phdata) void InterpretPhoneme2(int phcode, PHONEME_DATA *phdata)
{ {
// Examine the program of a single isolated phoneme
// Examine the program of a single isolated phoneme
int ix; int ix;
PHONEME_LIST plist[4]; PHONEME_LIST plist[4];
memset(plist, 0, sizeof(plist)); memset(plist, 0, sizeof(plist));

+ 96
- 96
src/libespeak-ng/synthesize.c View File



const char *WordToString(unsigned int word) const char *WordToString(unsigned int word)
{ {
// Convert a phoneme mnemonic word into a string
// Convert a phoneme mnemonic word into a string
int ix; int ix;
static char buf[5]; static char buf[5];


intptr_t *q; intptr_t *q;


last_amp_cmd = wcmdq_tail; last_amp_cmd = wcmdq_tail;
amp_length = 0; // total length of vowel with this amplitude envelope
amp_length = 0; // total length of vowel with this amplitude envelope


q = wcmdq[wcmdq_tail]; q = wcmdq[wcmdq_tail];
q[0] = WCMD_AMPLITUDE; q[0] = WCMD_AMPLITUDE;
q[1] = 0; // fill in later from amp_length
q[1] = 0; // fill in later from amp_length
q[2] = (intptr_t)amp_env; q[2] = (intptr_t)amp_env;
q[3] = amp; q[3] = amp;
WcmdqInc(); WcmdqInc();
env = envelope_data[PITCHfall]; env = envelope_data[PITCHfall];
} }
last_pitch_cmd = wcmdq_tail; last_pitch_cmd = wcmdq_tail;
pitch_length = 0; // total length of spect with this pitch envelope
pitch_length = 0; // total length of spect with this pitch envelope


if (pitch2 < 0) if (pitch2 < 0)
pitch2 = 0; pitch2 = 0;


q = wcmdq[wcmdq_tail]; q = wcmdq[wcmdq_tail];
q[0] = WCMD_PITCH; q[0] = WCMD_PITCH;
q[1] = 0; // length, fill in later from pitch_length
q[1] = 0; // length, fill in later from pitch_length
q[2] = (intptr_t)env; q[2] = (intptr_t)env;
q[3] = (pitch1 << 16) + pitch2; q[3] = (pitch1 << 16) + pitch2;
WcmdqInc(); WcmdqInc();
} else } else
len = (pause * speed.wav_factor)/256; len = (pause * speed.wav_factor)/256;


if (len < speed.min_pause) {
len = speed.min_pause; // mS, limit the amount to which pauses can be shortened
}
if (len < speed.min_pause)
len = speed.min_pause; // mS, limit the amount to which pauses can be shortened
return len; return len;
} }


static void DoPause(int length, int control) static void DoPause(int length, int control)
{ {
// length in nominal mS
// control = 1, less shortening at fast speeds
// length in nominal mS
// control = 1, less shortening at fast speeds

unsigned int len; unsigned int len;
int srate2; int srate2;


len = PauseLength(length, control); len = PauseLength(length, control);


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
srate2 = samplerate / 25; // avoid overflow
len = (len * srate2) / 40; len = (len * srate2) / 40;
} }
} }
} }
} }


extern int seq_len_adjust; // temporary fix to advance the start point for playing the wav sample
extern int seq_len_adjust; // temporary fix to advance the start point for playing the wav sample


static int DoSample2(int index, int which, int std_length, int control, int length_mod, int amp) static int DoSample2(int index, int which, int std_length, int control, int length_mod, int amp)
{ {
p = &wavefile_data[index]; p = &wavefile_data[index];
wav_scale = p[2]; wav_scale = p[2];
wav_length = (p[1] * 256); wav_length = (p[1] * 256);
wav_length += p[0]; // length in bytes
wav_length += p[0]; // length in bytes


if (wav_length == 0) if (wav_length == 0)
return 0; return 0;
min_length = speed.min_sample_len; min_length = speed.min_sample_len;


if (wav_scale == 0) if (wav_scale == 0)
min_length *= 2; // 16 bit samples
min_length *= 2; // 16 bit samples


if (std_length > 0) { if (std_length > 0) {
std_length = (std_length * samplerate)/1000; std_length = (std_length * samplerate)/1000;
last_wcmdq = wcmdq_tail; last_wcmdq = wcmdq_tail;
q = wcmdq[wcmdq_tail]; q = wcmdq[wcmdq_tail];
q[0] = WCMD_WAVE2; q[0] = WCMD_WAVE2;
q[1] = length | (wav_length << 16); // length in samples
q[1] = length | (wav_length << 16); // length in samples
q[2] = (intptr_t)(&wavefile_data[index]); q[2] = (intptr_t)(&wavefile_data[index]);
q[3] = wav_scale + (amp << 8); q[3] = wav_scale + (amp << 8);
WcmdqInc(); WcmdqInc();
last_wcmdq = wcmdq_tail; last_wcmdq = wcmdq_tail;
q = wcmdq[wcmdq_tail]; q = wcmdq[wcmdq_tail];
q[0] = WCMD_WAVE; q[0] = WCMD_WAVE;
q[1] = x; // length in samples
q[1] = x; // length in samples
q[2] = (intptr_t)(&wavefile_data[index]); q[2] = (intptr_t)(&wavefile_data[index]);
q[3] = wav_scale + (amp << 8); q[3] = wav_scale + (amp << 8);
WcmdqInc(); WcmdqInc();
last_wcmdq = wcmdq_tail; last_wcmdq = wcmdq_tail;
q = wcmdq[wcmdq_tail]; q = wcmdq[wcmdq_tail];
q[0] = WCMD_WAVE; q[0] = WCMD_WAVE;
q[1] = len4*2; // length in samples
q[1] = len4*2; // length in samples
q[2] = (intptr_t)(&wavefile_data[index+x]); q[2] = (intptr_t)(&wavefile_data[index+x]);
q[3] = wav_scale + (amp << 8); q[3] = wav_scale + (amp << 8);
WcmdqInc(); WcmdqInc();
last_wcmdq = wcmdq_tail; last_wcmdq = wcmdq_tail;
q = wcmdq[wcmdq_tail]; q = wcmdq[wcmdq_tail];
q[0] = WCMD_WAVE; q[0] = WCMD_WAVE;
q[1] = length; // length in samples
q[1] = length; // length in samples
q[2] = (intptr_t)(&wavefile_data[index+x]); q[2] = (intptr_t)(&wavefile_data[index+x]);
q[3] = wav_scale + (amp << 8); q[3] = wav_scale + (amp << 8);
WcmdqInc(); WcmdqInc();
// enough to use a round-robin without checks. // enough to use a round-robin without checks.
// Only needed for modifying spectra for blending to consonants // Only needed for modifying spectra for blending to consonants


#define N_FRAME_POOL N_WCMDQ
#define N_FRAME_POOL N_WCMDQ
static int ix = 0; static int ix = 0;
static frame_t frame_pool[N_FRAME_POOL]; static frame_t frame_pool[N_FRAME_POOL];




static void set_frame_rms(frame_t *fr, int new_rms) static void set_frame_rms(frame_t *fr, int new_rms)
{ {
// Each frame includes its RMS amplitude value, so to set a new
// RMS just adjust the formant amplitudes by the appropriate ratio
// Each frame includes its RMS amplitude value, so to set a new
// RMS just adjust the formant amplitudes by the appropriate ratio


int x; int x;
int h; int h;
int ix; int ix;


static const short sqrt_tab[200] = { static const short sqrt_tab[200] = {
0, 64, 90, 110, 128, 143, 156, 169, 181, 192, 202, 212, 221, 230, 239, 247,
0, 64, 90, 110, 128, 143, 156, 169, 181, 192, 202, 212, 221, 230, 239, 247,
256, 263, 271, 278, 286, 293, 300, 306, 313, 320, 326, 332, 338, 344, 350, 356, 256, 263, 271, 278, 286, 293, 300, 306, 313, 320, 326, 332, 338, 344, 350, 356,
362, 367, 373, 378, 384, 389, 394, 399, 404, 409, 414, 419, 424, 429, 434, 438, 362, 367, 373, 378, 384, 389, 394, 399, 404, 409, 414, 419, 424, 429, 434, 438,
443, 448, 452, 457, 461, 465, 470, 474, 478, 483, 487, 491, 495, 499, 503, 507, 443, 448, 452, 457, 461, 465, 470, 474, 478, 483, 487, 491, 495, 499, 503, 507,
return; return;
} }


if (fr->rms == 0) return; // check for divide by zero
if (fr->rms == 0) return; // check for divide by zero
x = (new_rms * 64)/fr->rms; x = (new_rms * 64)/fr->rms;
if (x >= 200) x = 199; if (x >= 200) x = 199;


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++) {
h = fr->fheight[ix] * x; h = fr->fheight[ix] * x;


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 ix;
int x; int x;




static frame_t *CopyFrame(frame_t *frame1, int copy) static frame_t *CopyFrame(frame_t *frame1, int copy)
{ {
// create a copy of the specified frame in temporary buffer
// create a copy of the specified frame in temporary buffer

frame_t *frame2; frame_t *frame2;


if ((copy == 0) && (frame1->frflags & FRFLAG_COPIED)) { if ((copy == 0) && (frame1->frflags & FRFLAG_COPIED)) {
fr->ffreq[3] += f3_adj; fr->ffreq[3] += f3_adj;


if (flags & 0x20) if (flags & 0x20)
f3_adj = -f3_adj; // . reverse direction for f4,f5 change
f3_adj = -f3_adj; // reverse direction for f4,f5 change
fr->ffreq[4] += f3_adj; fr->ffreq[4] += f3_adj;
fr->ffreq[5] += f3_adj; fr->ffreq[5] += f3_adj;




static int VowelCloseness(frame_t *fr) static int VowelCloseness(frame_t *fr)
{ {
// return a value 0-3 depending on the vowel's f1
// return a value 0-3 depending on the vowel's f1
int f1; int f1;


if ((f1 = fr->ffreq[1]) < 300) if ((f1 = fr->ffreq[1]) < 300)
int flags; int flags;
int vcolour; int vcolour;


#define N_VCOLOUR 2
// percentage change for each formant in 256ths
#define N_VCOLOUR 2
// percentage change for each formant in 256ths
static short vcolouring[N_VCOLOUR][5] = { static short vcolouring[N_VCOLOUR][5] = {
{ 243, 272, 256, 256, 256 }, // palatal consonant follows
{ 256, 256, 240, 240, 240 }, // retroflex
{ 243, 272, 256, 256, 256 }, // palatal consonant follows
{ 256, 256, 240, 240, 240 }, // retroflex
}; };


frame_t *fr = NULL; frame_t *fr = NULL;
flags |= 8; flags |= 8;


if (which == 1) { if (which == 1) {
/* entry to vowel */
// entry to vowel
fr = CopyFrame(seq[0].frame, 0); fr = CopyFrame(seq[0].frame, 0);
seq[0].frame = fr; seq[0].frame = fr;
seq[0].length = VOWEL_FRONT_LENGTH; seq[0].length = VOWEL_FRONT_LENGTH;
if (len > 0) if (len > 0)
seq[0].length = len; seq[0].length = len;
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; next_rms = seq[1].frame->rms;
if (flags & 4) if (flags & 4)
fr->frflags |= FRFLAG_FORMANT_RATE; fr->frflags |= FRFLAG_FORMANT_RATE;
if (flags & 2) if (flags & 2)
fr->frflags |= FRFLAG_BREAK; // don't merge with next frame
fr->frflags |= FRFLAG_BREAK; // don't merge with next frame
} }


if (flags & 0x40) if (flags & 0x40)
DoPause(20, 0); // add a short pause after the consonant
DoPause(20, 0); // add a short pause after the consonant


if (flags & 16) if (flags & 16)
return len; return len;
q[3] = (intptr_t)frame2; q[3] = (intptr_t)frame2;
frame1 = frame2; frame1 = frame2;
} else } else
break; // doesn't follow on from previous frame
break; // doesn't follow on from previous frame


frame = frame2 = (frame_t *)q[2]; frame = frame2 = (frame_t *)q[2];
modified = 0; modified = 0;
break; break;


if (frame->frflags & FRFLAG_FORMANT_RATE) if (frame->frflags & FRFLAG_FORMANT_RATE)
len = (len * 12)/10; // allow slightly greater rate of change for this frame (was 12/10)
len = (len * 12)/10; // allow slightly greater rate of change for this frame (was 12/10)


for (pk = 0; pk < 6; pk++) { for (pk = 0; pk < 6; pk++) {
int f1, f2; int f1, f2;
q[2] = (intptr_t)frame2; q[2] = (intptr_t)frame2;
frame1 = frame2; frame1 = frame2;
} else } else
break; // doesn't follow on from previous frame
break; // doesn't follow on from previous frame
} }


frame = frame2 = (frame_t *)q[3]; frame = frame2 = (frame_t *)q[3];
break; break;


if (frame1->frflags & FRFLAG_FORMANT_RATE) if (frame1->frflags & FRFLAG_FORMANT_RATE)
len = (len *6)/5; // allow slightly greater rate of change for this frame
len = (len *6)/5; // allow slightly greater rate of change for this frame


for (pk = 0; pk < 6; pk++) { for (pk = 0; pk < 6; pk++) {
int f1, f2; int f1, f2;
length_mod = plist->length; length_mod = plist->length;
if (length_mod == 0) length_mod = 256; if (length_mod == 0) length_mod = 256;


length_min = (samplerate/70); // greater than one cycle at low pitch (Hz)
length_min = (samplerate/70); // greater than one cycle at low pitch (Hz)
if (which == 2) { if (which == 2) {
if ((translator->langopts.param[LOPT_LONG_VOWEL_THRESHOLD] > 0) && ((this_ph->std_length >= translator->langopts.param[LOPT_LONG_VOWEL_THRESHOLD]) || (plist->synthflags & SFLAG_LENGTHEN) || (this_ph->phflags & phLONG))) if ((translator->langopts.param[LOPT_LONG_VOWEL_THRESHOLD] > 0) && ((this_ph->std_length >= translator->langopts.param[LOPT_LONG_VOWEL_THRESHOLD]) || (plist->synthflags & SFLAG_LENGTHEN) || (this_ph->phflags & phLONG)))
length_min *= 2; // ensure long vowels are longer
length_min *= 2; // ensure long vowels are longer
} }


if (which == 1) { if (which == 1) {
modn_flags = 0; modn_flags = 0;
frames = LookupSpect(this_ph, which, fmt_params, &n_frames, plist); frames = LookupSpect(this_ph, which, fmt_params, &n_frames, plist);
if (frames == NULL) if (frames == NULL)
return 0; // not found
return 0; // not found


if (fmt_params->fmt_amp != fmt_amplitude) { if (fmt_params->fmt_amp != fmt_amplitude) {
// an amplitude adjustment is specified for this sequence // an amplitude adjustment is specified for this sequence
} }


if ((this_ph->type == phVOWEL) && (which == 2)) { if ((this_ph->type == phVOWEL) && (which == 2)) {
SmoothSpect(); // process previous syllable
SmoothSpect(); // process previous syllable


// remember the point in the output queue of the centre of the vowel // remember the point in the output queue of the centre of the vowel
syllable_centre = wcmdq_tail; syllable_centre = wcmdq_tail;
length_sum = 0; length_sum = 0;
for (frameix = 1; frameix < n_frames; frameix++) { for (frameix = 1; frameix < n_frames; frameix++) {
length_factor = length_mod; 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; frame_length = frames[frameix-1].length;
len = (frame_length * samplerate)/1000; len = (frame_length * samplerate)/1000;
if (frame1->frflags & FRFLAG_MODULATE) if (frame1->frflags & FRFLAG_MODULATE)
modulation = 6; modulation = 6;
if ((frameix == n_frames-1) && (modn_flags & 0xf00)) if ((frameix == n_frames-1) && (modn_flags & 0xf00))
modulation |= modn_flags; // before or after a glottal stop
modulation |= modn_flags; // before or after a glottal stop
} }


len = frame_lengths[frameix]; len = frame_lengths[frameix];


void DoMarker(int type, int char_posn, int length, int value) void DoMarker(int type, int char_posn, int length, int value)
{ {
// This could be used to return an index to the word currently being spoken
// Type 1=word, 2=sentence, 3=named marker, 4=play audio, 5=end
// This could be used to return an index to the word currently being spoken
// Type 1=word, 2=sentence, 3=named marker, 4=play audio, 5=end

if (WcmdqFree() > 5) { if (WcmdqFree() > 5) {
wcmdq[wcmdq_tail][0] = WCMD_MARKER + (type << 8); wcmdq[wcmdq_tail][0] = WCMD_MARKER + (type << 8);
wcmdq[wcmdq_tail][1] = (char_posn & 0xffffff) | (length << 24); wcmdq[wcmdq_tail][1] = (char_posn & 0xffffff) | (length << 24);


void DoPhonemeMarker(int type, int char_posn, int length, char *name) void DoPhonemeMarker(int type, int char_posn, int length, char *name)
{ {
// This could be used to return an index to the word currently being spoken
// Type 7=phoneme
// This could be used to return an index to the word currently being spoken
// Type 7=phoneme

int *p; int *p;


if (WcmdqFree() > 5) { if (WcmdqFree() > 5) {
wcmdq[wcmdq_tail][0] = WCMD_MARKER + (type << 8); wcmdq[wcmdq_tail][0] = WCMD_MARKER + (type << 8);
wcmdq[wcmdq_tail][1] = (char_posn & 0xffffff) | (length << 24); wcmdq[wcmdq_tail][1] = (char_posn & 0xffffff) | (length << 24);
p = (int *)name; p = (int *)name;
wcmdq[wcmdq_tail][2] = p[0]; // up to 8 bytes of UTF8 characters
wcmdq[wcmdq_tail][2] = p[0]; // up to 8 bytes of UTF8 characters
wcmdq[wcmdq_tail][3] = p[1]; wcmdq[wcmdq_tail][3] = p[1];
WcmdqInc(); WcmdqInc();
} }
#if HAVE_SONIC_H #if HAVE_SONIC_H
void DoSonicSpeed(int value) void DoSonicSpeed(int value)
{ {
// value, multiplier * 1024
// value, multiplier * 1024
wcmdq[wcmdq_tail][0] = WCMD_SONIC_SPEED; wcmdq[wcmdq_tail][0] = WCMD_SONIC_SPEED;
wcmdq[wcmdq_tail][1] = value; wcmdq[wcmdq_tail][1] = value;
WcmdqInc(); WcmdqInc();


void DoVoiceChange(voice_t *v) void DoVoiceChange(voice_t *v)
{ {
// allocate memory for a copy of the voice data, and free it in wavegenfill()
// allocate memory for a copy of the voice data, and free it in wavegenfill()
voice_t *v2; voice_t *v2;


v2 = (voice_t *)malloc(sizeof(voice_t)); v2 = (voice_t *)malloc(sizeof(voice_t));
void DoEmbedded(int *embix, int sourceix) void DoEmbedded(int *embix, int sourceix)
{ {
// 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; unsigned int value;
int command; int command;


command = word & 0x7f; command = word & 0x7f;


if (command == 0) if (command == 0)
return; // error
return; // error


(*embix)++; (*embix)++;


switch (command & 0x1f) switch (command & 0x1f)
{ {
case EMBED_S: // speed
SetEmbedded((command & 0x60) + EMBED_S2, value); // adjusts embedded_value[EMBED_S2]
case EMBED_S: // speed
SetEmbedded((command & 0x60) + EMBED_S2, value); // adjusts embedded_value[EMBED_S2]
SetSpeed(2); SetSpeed(2);
break; break;
case EMBED_I: // play dynamically loaded wav data (sound icon)
case EMBED_I: // play dynamically loaded wav data (sound icon)
if ((int)value < n_soundicon_tab) { if ((int)value < n_soundicon_tab) {
if (soundicon_tab[value].length != 0) { if (soundicon_tab[value].length != 0) {
DoPause(10, 0); // ensure a break in the speech
DoPause(10, 0); // ensure a break in the speech
wcmdq[wcmdq_tail][0] = WCMD_WAVE; wcmdq[wcmdq_tail][0] = WCMD_WAVE;
wcmdq[wcmdq_tail][1] = soundicon_tab[value].length; wcmdq[wcmdq_tail][1] = soundicon_tab[value].length;
wcmdq[wcmdq_tail][2] = (intptr_t)soundicon_tab[value].data + 44; // skip WAV header
wcmdq[wcmdq_tail][3] = 0x1500; // 16 bit data, amp=21
wcmdq[wcmdq_tail][2] = (intptr_t)soundicon_tab[value].data + 44; // skip WAV header
wcmdq[wcmdq_tail][3] = 0x1500; // 16 bit data, amp=21
WcmdqInc(); WcmdqInc();
} }
} }
break; break;
case EMBED_M: // named marker
case EMBED_M: // named marker
DoMarker(espeakEVENT_MARK, (sourceix & 0x7ff) + clause_start_char, 0, value); DoMarker(espeakEVENT_MARK, (sourceix & 0x7ff) + clause_start_char, 0, value);
break; break;
case EMBED_U: // play sound
DoMarker(espeakEVENT_PLAY, count_characters+1, 0, value); // always occurs at end of clause
case EMBED_U: // play sound
DoMarker(espeakEVENT_PLAY, count_characters+1, 0, value); // always occurs at end of clause
break; break;
default: default:
DoPause(10, 0); // ensure a break in the speech
DoPause(10, 0); // ensure a break in the speech
wcmdq[wcmdq_tail][0] = WCMD_EMBEDDED; wcmdq[wcmdq_tail][0] = WCMD_EMBEDDED;
wcmdq[wcmdq_tail][1] = command; wcmdq[wcmdq_tail][1] = command;
wcmdq[wcmdq_tail][2] = value; wcmdq[wcmdq_tail][2] = value;
last_pitch_cmd = -1; last_pitch_cmd = -1;
memset(vowel_transition, 0, sizeof(vowel_transition)); memset(vowel_transition, 0, sizeof(vowel_transition));
memset(&worddata, 0, sizeof(worddata)); memset(&worddata, 0, sizeof(worddata));
DoPause(0, 0); // isolate from the previous clause
DoPause(0, 0); // isolate from the previous clause
} }


while ((ix < (*n_ph)) && (ix < N_PHONEME_LIST-2)) { while ((ix < (*n_ph)) && (ix < N_PHONEME_LIST-2)) {
if (p->type == phPAUSE) if (p->type == phPAUSE)
free_min = 10; free_min = 10;
else if (p->type != phVOWEL) else if (p->type != phVOWEL)
free_min = 15; // we need less Q space for non-vowels, and we need to generate phonemes after a vowel so that the pitch_length is filled in
free_min = 15; // we need less Q space for non-vowels, and we need to generate phonemes after a vowel so that the pitch_length is filled in
else else
free_min = MIN_WCMDQ; // 25
free_min = MIN_WCMDQ;


if (WcmdqFree() <= free_min) if (WcmdqFree() <= free_min)
return 1; // wait
return 1; // wait


prev = &phoneme_list[ix-1]; prev = &phoneme_list[ix-1];
next = &phoneme_list[ix+1]; next = &phoneme_list[ix+1];
sourceix = (p->sourceix & 0x7ff) + clause_start_char; sourceix = (p->sourceix & 0x7ff) + clause_start_char;


if (p->newword & 4) if (p->newword & 4)
DoMarker(espeakEVENT_SENTENCE, sourceix, 0, count_sentences); // start of sentence
DoMarker(espeakEVENT_SENTENCE, sourceix, 0, count_sentences); // start of sentence


if (p->newword & 1) if (p->newword & 1)
DoMarker(espeakEVENT_WORD, sourceix, p->sourceix >> 11, clause_start_word + word_count++); // NOTE, this count doesn't include multiple-word pronunciations in *_list. eg (of a)
DoMarker(espeakEVENT_WORD, sourceix, p->sourceix >> 11, clause_start_word + word_count++); // NOTE, this count doesn't include multiple-word pronunciations in *_list. eg (of a)
} }


EndAmplitude(); EndAmplitude();
InterpretPhoneme(NULL, 0, p, &phdata, &worddata); InterpretPhoneme(NULL, 0, p, &phdata, &worddata);


if (p->synthflags & SFLAG_LENGTHEN) if (p->synthflags & SFLAG_LENGTHEN)
DoSample3(&phdata, p->length, 0); // play it twice for [s:] etc.
DoSample3(&phdata, p->length, 0); // play it twice for [s:] etc.
DoSample3(&phdata, p->length, 0); DoSample3(&phdata, p->length, 0);
break; break;
case phVSTOP: case phVSTOP:
} else if (prev->type == phVOWEL && (p->synthflags & SFLAG_SEQCONTINUE)) } else if (prev->type == phVOWEL && (p->synthflags & SFLAG_SEQCONTINUE))
DoSpect2(p->ph, 0, &fmtp, p, 0); DoSpect2(p->ph, 0, &fmtp, p, 0);
else { else {
last_frame = NULL; // only for nasal ?
last_frame = NULL; // only for nasal ?
DoSpect2(p->ph, 0, &fmtp, p, 0); DoSpect2(p->ph, 0, &fmtp, p, 0);
last_frame = NULL; last_frame = NULL;
} }


modulation = 2; modulation = 2;
if (stress <= 1) if (stress <= 1)
modulation = 1; // 16ths
modulation = 1; // 16ths
else if (stress >= 7) else if (stress >= 7)
modulation = 3; modulation = 3;


if (prev->type == phVSTOP || prev->type == phVFRICATIVE) { if (prev->type == phVSTOP || prev->type == phVFRICATIVE) {
DoAmplitude(p->amp, amp_env); DoAmplitude(p->amp, amp_env);
DoPitch(pitch_env, p->pitch1, p->pitch2); // don't use prevocalic rising tone
DoPitch(pitch_env, p->pitch1, p->pitch2); // don't use prevocalic rising tone
DoSpect2(ph, 1, &fmtp, p, modulation); DoSpect2(ph, 1, &fmtp, p, modulation);
} else if (prev->type == phLIQUID || prev->type == phNASAL) { } else if (prev->type == phLIQUID || prev->type == phNASAL) {
DoAmplitude(p->amp, amp_env); DoAmplitude(p->amp, amp_env);
DoSpect2(ph, 1, &fmtp, p, modulation); // continue with pre-vocalic rising tone
DoSpect2(ph, 1, &fmtp, p, modulation); // continue with pre-vocalic rising tone
DoPitch(pitch_env, p->pitch1, p->pitch2); DoPitch(pitch_env, p->pitch1, p->pitch2);
} else if (vowelstart_prev) { } else if (vowelstart_prev) {
// VowelStart from the previous phoneme, but not phLIQUID or phNASAL // VowelStart from the previous phoneme, but not phLIQUID or phNASAL
DoPitch(envelope_data[PITCHrise], p->pitch2 - 15, p->pitch2); DoPitch(envelope_data[PITCHrise], p->pitch2 - 15, p->pitch2);
DoAmplitude(p->amp-1, amp_env); DoAmplitude(p->amp-1, amp_env);
DoSpect2(ph, 1, &fmtp, p, modulation); // continue with pre-vocalic rising tone
DoSpect2(ph, 1, &fmtp, p, modulation); // continue with pre-vocalic rising tone
DoPitch(pitch_env, p->pitch1, p->pitch2); DoPitch(pitch_env, p->pitch1, p->pitch2);
} else { } else {
if (!(p->synthflags & SFLAG_SEQCONTINUE)) { if (!(p->synthflags & SFLAG_SEQCONTINUE)) {
InterpretPhoneme(NULL, 0, next, &phdata_next, NULL); InterpretPhoneme(NULL, 0, next, &phdata_next, NULL);


fmtp.use_vowelin = 1; fmtp.use_vowelin = 1;
fmtp.transition0 = phdata_next.vowel_transition[2]; // always do vowel_transition, even if ph_VWLEND ?? consider [N]
fmtp.transition0 = phdata_next.vowel_transition[2]; // always do vowel_transition, even if ph_VWLEND ?? consider [N]
fmtp.transition1 = phdata_next.vowel_transition[3]; fmtp.transition1 = phdata_next.vowel_transition[3];


if ((fmtp.fmt2_addr = phdata_next.sound_addr[pd_VWLEND]) != 0) {
if ((fmtp.fmt2_addr = phdata_next.sound_addr[pd_VWLEND]) != 0)
fmtp.fmt2_lenadj = phdata_next.sound_param[pd_VWLEND]; fmtp.fmt2_lenadj = phdata_next.sound_param[pd_VWLEND];
}
} }


DoSpect2(ph, 2, &fmtp, p, modulation); DoSpect2(ph, 2, &fmtp, p, modulation);
} }
EndPitch(1); EndPitch(1);
if (*n_ph > 0) { if (*n_ph > 0) {
DoMarker(espeakEVENT_END, count_characters, 0, count_sentences); // end of clause
DoMarker(espeakEVENT_END, count_characters, 0, count_sentences); // end of clause
*n_ph = 0; *n_ph = 0;
} }


return 0; // finished the phoneme list
return 0; // finished the phoneme list
} }


static int timer_on = 0; static int timer_on = 0;


int SpeakNextClause(FILE *f_in, const void *text_in, int control) int SpeakNextClause(FILE *f_in, const void *text_in, int control)
{ {
// Speak text from file (f_in) or memory (text_in)
// control 0: start
// either f_in or text_in is set, the other must be NULL
// Speak text from file (f_in) or memory (text_in)
// control 0: start
// either f_in or text_in is set, the other must be NULL


// The other calls have f_in and text_in = NULL
// control 1: speak next text
// 2: stop
// 3: pause (toggle)
// 4: is file being read (0=no, 1=yes)
// 5: interrupt and flush current text.
// The other calls have f_in and text_in = NULL
// control 1: speak next text
// 2: stop
// 3: pause (toggle)
// 4: is file being read (0=no, 1=yes)
// 5: interrupt and flush current text.


int clause_tone; int clause_tone;
char *voice_change; char *voice_change;
WavegenOpenSound(); WavegenOpenSound();
timer_on = 1; timer_on = 1;
paused = 0; paused = 0;
Generate(phoneme_list, &n_phoneme_list, 0); // re-start from beginning of clause
Generate(phoneme_list, &n_phoneme_list, 0); // re-start from beginning of clause
} }
return 0; return 0;
} }
phoneme_callback(phon_out); phoneme_callback(phon_out);
} }



if (skipping_text) { if (skipping_text) {
n_phoneme_list = 0; n_phoneme_list = 0;
return 1; return 1;

+ 86
- 108
src/libespeak-ng/synthesize.h View File

{ {
#endif #endif


#define espeakINITIALIZE_PHONEME_IPA 0x0002 // move this to speak_lib.h, after eSpeak version 1.46.02
#define espeakINITIALIZE_PHONEME_IPA 0x0002 // move this to speak_lib.h, after eSpeak version 1.46.02


#define N_PHONEME_LIST 1000 // enough for source[N_TR_SOURCE] full of text, else it will truncate


#define N_PHONEME_LIST 1000 // enough for source[N_TR_SOURCE] full of text, else it will truncate

#define MAX_HARMONIC 400 // 400 * 50Hz = 20 kHz, more than enough
#define N_SEQ_FRAMES 25 // max frames in a spectrum sequence (real max is ablut 8)
#define STEPSIZE 64 // 2.9mS at 22 kHz sample rate
#define MAX_HARMONIC 400 // 400 * 50Hz = 20 kHz, more than enough
#define N_SEQ_FRAMES 25 // max frames in a spectrum sequence (real max is ablut 8)
#define STEPSIZE 64 // 2.9mS at 22 kHz sample rate


// flags set for frames within a spectrum sequence // flags set for frames within a spectrum sequence
#define FRFLAG_KLATT 0x01 // this frame includes extra data for Klatt synthesizer
#define FRFLAG_VOWEL_CENTRE 0x02 // centre point of vowel
#define FRFLAG_LEN_MOD 0x04 // reduce effect of length adjustment
#define FRFLAG_BREAK_LF 0x08 // but keep f3 upwards
#define FRFLAG_BREAK 0x10 // don't merge with next frame
#define FRFLAG_BREAK_2 0x18 // FRFLAG_BREAK_LF or FRFLAG_BREAK
#define FRFLAG_FORMANT_RATE 0x20 // Flag5 allow increased rate of change of formant freq
#define FRFLAG_MODULATE 0x40 // Flag6 modulate amplitude of some cycles to give trill
#define FRFLAG_DEFER_WAV 0x80 // Flag7 defer mixing WAV until the next frame
#define FRFLAG_LEN_MOD2 0x4000 // reduce effect of length adjustment, used for the start of a vowel
#define FRFLAG_COPIED 0x8000 // This frame has been copied into temporary rw memory
#define SFLAG_SEQCONTINUE 0x01 // a liquid or nasal after a vowel, but not followed by a vowel
#define SFLAG_EMBEDDED 0x02 // there are embedded commands before this phoneme
#define SFLAG_SYLLABLE 0x04 // vowel or syllabic consonant
#define SFLAG_LENGTHEN 0x08 // lengthen symbol : included after this phoneme
#define SFLAG_DICTIONARY 0x10 // the pronunciation of this word was listed in the xx_list dictionary
#define SFLAG_SWITCHED_LANG 0x20 // this word uses phonemes from a different language
#define SFLAG_PROMOTE_STRESS 0x40 // this unstressed word can be promoted to stressed
#define SFLAG_PREV_PAUSE 0x1000 // consider previous phoneme as pause
#define SFLAG_NEXT_PAUSE 0x2000 // consider next phoneme as pause
#define FRFLAG_KLATT 0x01 // this frame includes extra data for Klatt synthesizer
#define FRFLAG_VOWEL_CENTRE 0x02 // centre point of vowel
#define FRFLAG_LEN_MOD 0x04 // reduce effect of length adjustment
#define FRFLAG_BREAK_LF 0x08 // but keep f3 upwards
#define FRFLAG_BREAK 0x10 // don't merge with next frame
#define FRFLAG_BREAK_2 0x18 // FRFLAG_BREAK_LF or FRFLAG_BREAK
#define FRFLAG_FORMANT_RATE 0x20 // Flag5 allow increased rate of change of formant freq
#define FRFLAG_MODULATE 0x40 // Flag6 modulate amplitude of some cycles to give trill
#define FRFLAG_DEFER_WAV 0x80 // Flag7 defer mixing WAV until the next frame
#define FRFLAG_LEN_MOD2 0x4000 // reduce effect of length adjustment, used for the start of a vowel
#define FRFLAG_COPIED 0x8000 // This frame has been copied into temporary rw memory
#define SFLAG_SEQCONTINUE 0x01 // a liquid or nasal after a vowel, but not followed by a vowel
#define SFLAG_EMBEDDED 0x02 // there are embedded commands before this phoneme
#define SFLAG_SYLLABLE 0x04 // vowel or syllabic consonant
#define SFLAG_LENGTHEN 0x08 // lengthen symbol : included after this phoneme
#define SFLAG_DICTIONARY 0x10 // the pronunciation of this word was listed in the xx_list dictionary
#define SFLAG_SWITCHED_LANG 0x20 // this word uses phonemes from a different language
#define SFLAG_PROMOTE_STRESS 0x40 // this unstressed word can be promoted to stressed
#define SFLAG_PREV_PAUSE 0x1000 // consider previous phoneme as pause
#define SFLAG_NEXT_PAUSE 0x2000 // consider next phoneme as pause


// embedded command numbers // embedded command numbers
#define EMBED_P 1 // pitch
#define EMBED_S 2 // speed (used in setlengths)
#define EMBED_A 3 // amplitude/volume
#define EMBED_R 4 // pitch range/expression
#define EMBED_H 5 // echo/reverberation
#define EMBED_T 6 // different tone for announcing punctuation (not used)
#define EMBED_I 7 // sound icon
#define EMBED_S2 8 // speed (used in synthesize)
#define EMBED_Y 9 // say-as commands
#define EMBED_M 10 // mark name
#define EMBED_U 11 // audio uri
#define EMBED_B 12 // break
#define EMBED_F 13 // emphasis
#define EMBED_C 14 // capital letter indication
#define EMBED_P 1 // pitch
#define EMBED_S 2 // speed (used in setlengths)
#define EMBED_A 3 // amplitude/volume
#define EMBED_R 4 // pitch range/expression
#define EMBED_H 5 // echo/reverberation
#define EMBED_T 6 // different tone for announcing punctuation (not used)
#define EMBED_I 7 // sound icon
#define EMBED_S2 8 // speed (used in synthesize)
#define EMBED_Y 9 // say-as commands
#define EMBED_M 10 // mark name
#define EMBED_U 11 // audio uri
#define EMBED_B 12 // break
#define EMBED_F 13 // emphasis
#define EMBED_C 14 // capital letter indication


#define N_EMBEDDED_VALUES 15 #define N_EMBEDDED_VALUES 15
extern int embedded_value[N_EMBEDDED_VALUES]; extern int embedded_value[N_EMBEDDED_VALUES];
extern int embedded_default[N_EMBEDDED_VALUES]; extern int embedded_default[N_EMBEDDED_VALUES];



#define N_PEAKS 9 #define N_PEAKS 9
#define N_PEAKS2 9 // plus Notch and Fill (not yet implemented)
#define N_PEAKS2 9 // plus Notch and Fill (not yet implemented)
#define N_MARKERS 8 #define N_MARKERS 8


#define N_KLATTP 10 // this affects the phoneme data file format
#define N_KLATTP2 14 // used in vowel files, with extra parameters for future extensions
#define N_KLATTP 10 // this affects the phoneme data file format
#define N_KLATTP2 14 // used in vowel files, with extra parameters for future extensions


#define KLATT_AV 0 #define KLATT_AV 0
#define KLATT_FNZ 1 // nasal zero freq
#define KLATT_FNZ 1 // nasal zero freq
#define KLATT_Tilt 2 #define KLATT_Tilt 2
#define KLATT_Aspr 3 #define KLATT_Aspr 3
#define KLATT_Skew 4 #define KLATT_Skew 4
#define KLATT_FricBP 8 #define KLATT_FricBP 8
#define KLATT_Turb 9 #define KLATT_Turb 9




typedef struct { // 64 bytes
typedef struct { // 64 bytes
short frflags; short frflags;
short ffreq[7]; short ffreq[7];
unsigned char length; unsigned char length;
unsigned char rms; unsigned char rms;
unsigned char fheight[8]; unsigned char fheight[8];
unsigned char fwidth[6]; // width/4 f0-5
unsigned char fright[3]; // width/4 f0-2
unsigned char bw[4]; // Klatt bandwidth BNZ /2, f1,f2,f3
unsigned char klattp[5]; // AV, FNZ, Tilt, Aspr, Skew
unsigned char klattp2[5]; // continuation of klattp[], Avp, Fric, FricBP, Turb
unsigned char klatt_ap[7]; // Klatt parallel amplitude
unsigned char klatt_bp[7]; // Klatt parallel bandwidth /2
unsigned char spare; // pad to multiple of 4 bytes
} frame_t; // with extra Klatt parameters for parallel resonators


typedef struct { // 44 bytes
unsigned char fwidth[6]; // width/4 f0-5
unsigned char fright[3]; // width/4 f0-2
unsigned char bw[4]; // Klatt bandwidth BNZ /2, f1,f2,f3
unsigned char klattp[5]; // AV, FNZ, Tilt, Aspr, Skew
unsigned char klattp2[5]; // continuation of klattp[], Avp, Fric, FricBP, Turb
unsigned char klatt_ap[7]; // Klatt parallel amplitude
unsigned char klatt_bp[7]; // Klatt parallel bandwidth /2
unsigned char spare; // pad to multiple of 4 bytes
} frame_t; // with extra Klatt parameters for parallel resonators

typedef struct { // 44 bytes
short frflags; short frflags;
short ffreq[7]; short ffreq[7];
unsigned char length; unsigned char length;
unsigned char rms; unsigned char rms;
unsigned char fheight[8]; unsigned char fheight[8];
unsigned char fwidth[6]; // width/4 f0-5
unsigned char fright[3]; // width/4 f0-2
unsigned char bw[4]; // Klatt bandwidth BNZ /2, f1,f2,f3
unsigned char klattp[5]; // AV, FNZ, Tilt, Aspr, Skew
} frame_t2; // without the extra Klatt parameters


unsigned char fwidth[6]; // width/4 f0-5
unsigned char fright[3]; // width/4 f0-2
unsigned char bw[4]; // Klatt bandwidth BNZ /2, f1,f2,f3
unsigned char klattp[5]; // AV, FNZ, Tilt, Aspr, Skew
} frame_t2; // without the extra Klatt parameters


// formant data used by wavegen // formant data used by wavegen
typedef struct { typedef struct {
DOUBLEX height1; DOUBLEX height1;
DOUBLEX left1; DOUBLEX left1;
DOUBLEX right1; DOUBLEX right1;
DOUBLEX freq_inc; // increment by this every 64 samples
DOUBLEX freq_inc; // increment by this every 64 samples
DOUBLEX height_inc; DOUBLEX height_inc;
DOUBLEX left_inc; DOUBLEX left_inc;
DOUBLEX right_inc; DOUBLEX right_inc;
} wavegen_peaks_t;
} wavegen_peaks_t;


typedef struct { typedef struct {
unsigned char *pitch_env; unsigned char *pitch_env;
int pitch_range; // Hz*256 range of envelope int pitch_range; // Hz*256 range of envelope


unsigned char *mix_wavefile; // wave file to be added to synthesis unsigned char *mix_wavefile; // wave file to be added to synthesis
int n_mix_wavefile; // length in bytes
int mix_wave_scale; // 0=2 byte samples
int n_mix_wavefile; // length in bytes
int mix_wave_scale; // 0=2 byte samples
int mix_wave_amp; int mix_wave_amp;
int mix_wavefile_ix; int mix_wavefile_ix;
int mix_wavefile_max; // length of available WAV data (in bytes) int mix_wavefile_max; // length of available WAV data (in bytes)
int amplitude_fmt; // percentage amplitude adjustment for formant synthesis int amplitude_fmt; // percentage amplitude adjustment for formant synthesis
} WGEN_DATA; } WGEN_DATA;



typedef struct { typedef struct {
double a; double a;
double b; double b;
double c; double c;
double x1; double x1;
double x2; double x2;
} RESONATOR;

} RESONATOR;


typedef struct { typedef struct {
short length_total; // not used short length_total; // not used
unsigned char n_frames; unsigned char n_frames;
unsigned char sqflags; unsigned char sqflags;
frame_t2 frame[N_SEQ_FRAMES]; // max. frames in a spectrum sequence
} SPECT_SEQ; // sequence of espeak formant frames
frame_t2 frame[N_SEQ_FRAMES]; // max. frames in a spectrum sequence
} SPECT_SEQ; // sequence of espeak formant frames


typedef struct { typedef struct {
short length_total; // not used short length_total; // not used
unsigned char n_frames; unsigned char n_frames;
unsigned char sqflags; unsigned char sqflags;
frame_t frame[N_SEQ_FRAMES]; // max. frames in a spectrum sequence
} SPECT_SEQK; // sequence of klatt formants frames

frame_t frame[N_SEQ_FRAMES]; // max. frames in a spectrum sequence
} SPECT_SEQK; // sequence of klatt formants frames


typedef struct { typedef struct {
short length; short length;


// a clause translated into phoneme codes (first stage) // a clause translated into phoneme codes (first stage)
typedef struct { typedef struct {
unsigned short synthflags; // NOTE Put shorts on 32bit boundaries, because of RISC OS compiler bug?
unsigned short synthflags; // NOTE Put shorts on 32bit boundaries, because of RISC OS compiler bug?
unsigned char phcode; unsigned char phcode;
unsigned char stresslevel; unsigned char stresslevel;
unsigned short sourceix; // ix into the original source text string, only set at the start of a word unsigned short sourceix; // ix into the original source text string, only set at the start of a word
unsigned char wordstress; // the highest level stress in this word
unsigned char wordstress; // the highest level stress in this word
unsigned char tone_ph; // tone phoneme to use with this vowel unsigned char tone_ph; // tone phoneme to use with this vowel
} PHONEME_LIST2; } PHONEME_LIST2;



typedef struct { typedef struct {
// The first section is a copy of PHONEME_LIST2
// The first section is a copy of PHONEME_LIST2
unsigned short synthflags; unsigned short synthflags;
unsigned char phcode; unsigned char phcode;
unsigned char stresslevel; unsigned char stresslevel;
unsigned short sourceix; // ix into the original source text string, only set at the start of a word unsigned short sourceix; // ix into the original source text string, only set at the start of a word
unsigned char wordstress; // the highest level stress in this word
unsigned char wordstress; // the highest level stress in this word
unsigned char tone_ph; // tone phoneme to use with this vowel unsigned char tone_ph; // tone phoneme to use with this vowel


PHONEME_TAB *ph; PHONEME_TAB *ph;
int sound_param; int sound_param;
} PHONEME_LIST; } PHONEME_LIST;



#define pd_FMT 0 #define pd_FMT 0
#define pd_WAV 1 #define pd_WAV 1
#define pd_VWLSTART 2 #define pd_VWLSTART 2
char ipa_string[18]; char ipa_string[18];
} PHONEME_DATA; } PHONEME_DATA;



typedef struct { typedef struct {
int fmt_control; int fmt_control;
int use_vowelin; int use_vowelin;


#define i_ADD_LENGTH 0x0c #define i_ADD_LENGTH 0x0c



// conditions and jumps // conditions and jumps
#define i_CONDITION 0x2000 #define i_CONDITION 0x2000
#define i_OR 0x1000 // added to i_CONDITION #define i_OR 0x1000 // added to i_CONDITION
#define i_isSeqFlag1 0x8f #define i_isSeqFlag1 0x8f
#define i_IsTranslationGiven 0x90 #define i_IsTranslationGiven 0x90



// place of articulation // place of articulation
#define i_isVel 0x28 #define i_isVel 0x28


// phflags // phflags
#define i_isSibilant 0x45 // bit 5 in phflags
#define i_isPalatal 0x49 // bit 9 in phflags
#define i_isLong 0x55 // bit 21 in phflags
#define i_isRhotic 0x57 // bit 23 in phflags
#define i_isSibilant 0x45 // bit 5 in phflags
#define i_isPalatal 0x49 // bit 9 in phflags
#define i_isLong 0x55 // bit 21 in phflags
#define i_isRhotic 0x57 // bit 23 in phflags
#define i_isFlag1 0x5c #define i_isFlag1 0x5c
#define i_isFlag2 0x5d #define i_isFlag2 0x5d
#define i_isFlag3 0x5e #define i_isFlag3 0x5e


#define i_StressLevel 0x800 #define i_StressLevel 0x800




typedef struct { typedef struct {
int name; int name;
int length; int length;
unsigned int next_phoneme; unsigned int next_phoneme;
int mbr_name; int mbr_name;
int mbr_name2; int mbr_name2;
int percent; // percentage length of first component
int percent; // percentage length of first component
int control; int control;
} MBROLA_TAB; } MBROLA_TAB;


int fast_settings[8]; int fast_settings[8];
} SPEED_FACTORS; } SPEED_FACTORS;



typedef struct { typedef struct {
char name[12]; char name[12];
unsigned char flags[4]; unsigned char flags[4];
unsigned char head_max_steps; unsigned char head_max_steps;
unsigned char n_head_extend; unsigned char n_head_extend;


signed char unstr_start[3]; // for: onset, head, last
signed char unstr_start[3]; // for: onset, head, last
signed char unstr_end[3]; signed char unstr_end[3];


unsigned char nucleus0_env; // pitch envelope, tonic syllable is at end, no tail
unsigned char nucleus0_env; // pitch envelope, tonic syllable is at end, no tail
unsigned char nucleus0_max; unsigned char nucleus0_max;
unsigned char nucleus0_min; unsigned char nucleus0_min;


unsigned char nucleus1_env; // when followed by a tail
unsigned char nucleus1_env; // when followed by a tail
unsigned char nucleus1_max; unsigned char nucleus1_max;
unsigned char nucleus1_min; unsigned char nucleus1_min;
unsigned char tail_start; unsigned char tail_start;
unsigned char split_tune; unsigned char split_tune;


unsigned char spare[8]; unsigned char spare[8];
int spare2; // the struct length should be a multiple of 4 bytes
int spare2; // the struct length should be a multiple of 4 bytes
} TUNE; } TUNE;


extern int n_tunes; extern int n_tunes;
#define WCMD_FMT_AMPLITUDE 14 #define WCMD_FMT_AMPLITUDE 14
#define WCMD_SONIC_SPEED 15 #define WCMD_SONIC_SPEED 15




#define N_WCMDQ 170 #define N_WCMDQ 170
#define MIN_WCMDQ 25 // need this many free entries before adding new phoneme #define MIN_WCMDQ 25 // need this many free entries before adding new phoneme


int WavegenFill(int fill_zeros); int WavegenFill(int fill_zeros);
void MarkerEvent(int type, unsigned int char_position, int value, int value2, unsigned char *out_ptr); void MarkerEvent(int type, unsigned int char_position, int value, int value2, unsigned char *out_ptr);



extern unsigned char *wavefile_data; extern unsigned char *wavefile_data;
extern int samplerate; extern int samplerate;
extern int samplerate_native; extern int samplerate_native;
int Reverse4Bytes(int word); int Reverse4Bytes(int word);
int CompileDictionary(const char *dsource, const char *dict_name, FILE *log, char *err_name, int flags); int CompileDictionary(const char *dsource, const char *dict_name, FILE *log, char *err_name, int flags);



#define ENV_LEN 128 // length of pitch envelopes #define ENV_LEN 128 // length of pitch envelopes
#define PITCHfall 0 // standard pitch envelopes
#define PITCHrise 2
#define PITCHfall 0 // standard pitch envelopes
#define PITCHrise 2
#define N_ENVELOPE_DATA 20 #define N_ENVELOPE_DATA 20
extern unsigned char *envelope_data[N_ENVELOPE_DATA]; extern unsigned char *envelope_data[N_ENVELOPE_DATA];



+ 364
- 359
src/libespeak-ng/tr_languages.c
File diff suppressed because it is too large
View File


+ 202
- 201
src/libespeak-ng/translate.c
File diff suppressed because it is too large
View File


+ 156
- 178
src/libespeak-ng/translate.h View File

{ {
#endif #endif


#define L(c1, c2) (c1<<8)+c2 // combine two characters into an integer for translator name
#define L(c1, c2) (c1<<8)+c2 // combine two characters into an integer for translator name


#define CTRL_EMBEDDED 0x01 // control character at the start of an embedded command
#define REPLACED_E 'E' // 'e' replaced by silent e
#define CTRL_EMBEDDED 0x01 // control character at the start of an embedded command
#define REPLACED_E 'E' // 'e' replaced by silent e


#define N_WORD_PHONEMES 200 // max phonemes in a word
#define N_WORD_BYTES 160 // max bytes for the UTF8 characters in a word
#define N_CLAUSE_WORDS 300 // max words in a clause
#define N_TR_SOURCE 800 // the source text of a single clause (UTF8 bytes)
#define N_WORD_PHONEMES 200 // max phonemes in a word
#define N_WORD_BYTES 160 // max bytes for the UTF8 characters in a word
#define N_CLAUSE_WORDS 300 // max words in a clause
#define N_TR_SOURCE 800 // the source text of a single clause (UTF8 bytes)



#define N_RULE_GROUP2 120 // max num of two-letter rule chains
#define N_RULE_GROUP2 120 // max num of two-letter rule chains
#define N_HASH_DICT 1024 #define N_HASH_DICT 1024
#define N_CHARSETS 20 #define N_CHARSETS 20
#define N_LETTER_GROUPS 95 // maximum is 127-32

#define N_LETTER_GROUPS 95 // maximum is 127-32


/* dictionary flags, word 1 */
// dictionary flags, word 1
// bits 0-3 stressed syllable, bit 6=unstressed // bits 0-3 stressed syllable, bit 6=unstressed
#define FLAG_SKIPWORDS 0x80 #define FLAG_SKIPWORDS 0x80
#define FLAG_PREPAUSE 0x100 #define FLAG_PREPAUSE 0x100


#define FLAG_STRESS_END 0x200 // full stress if at end of clause
#define FLAG_STRESS_END2 0x400 // full stress if at end of clause, or only followed by unstressed
#define FLAG_UNSTRESS_END 0x800 // reduce stress at end of clause
#define FLAG_SPELLWORD 0x1000 // re-translate the word as individual letters, separated by spaces
#define FLAG_ACCENT_BEFORE 0x1000 // say this accent name before the letter name
#define FLAG_ABBREV 0x2000 // spell as letters, even with a vowel, OR use specified pronunciation rather than split into letters
#define FLAG_DOUBLING 0x4000 // doubles the following consonant
#define BITNUM_FLAG_ALT 14 // bit number of FLAG_ALT_TRANS - 1
#define FLAG_ALT_TRANS 0x8000 // language specific
#define FLAG_ALT2_TRANS 0x10000 // language specific
#define FLAG_ALT3_TRANS 0x20000 // language specific
#define FLAG_ALT4_TRANS 0x40000 // language specific
#define FLAG_ALT5_TRANS 0x80000 // language specific
#define FLAG_ALT6_TRANS 0x100000 // language specific
#define FLAG_ALT7_TRANS 0x200000 // language specific
#define FLAG_COMBINE 0x800000 // combine with the next word
#define FLAG_ALLOW_DOT 0x01000000 // ignore '.' after word (abbreviation)
#define FLAG_NEEDS_DOT 0x02000000 // only if the word is followed by a dot
#define FLAG_STRESS_END 0x200 // full stress if at end of clause
#define FLAG_STRESS_END2 0x400 // full stress if at end of clause, or only followed by unstressed
#define FLAG_UNSTRESS_END 0x800 // reduce stress at end of clause
#define FLAG_SPELLWORD 0x1000 // re-translate the word as individual letters, separated by spaces
#define FLAG_ACCENT_BEFORE 0x1000 // say this accent name before the letter name
#define FLAG_ABBREV 0x2000 // spell as letters, even with a vowel, OR use specified pronunciation rather than split into letters
#define FLAG_DOUBLING 0x4000 // doubles the following consonant
#define BITNUM_FLAG_ALT 14 // bit number of FLAG_ALT_TRANS - 1
#define FLAG_ALT_TRANS 0x8000 // language specific
#define FLAG_ALT2_TRANS 0x10000 // language specific
#define FLAG_ALT3_TRANS 0x20000 // language specific
#define FLAG_ALT4_TRANS 0x40000 // language specific
#define FLAG_ALT5_TRANS 0x80000 // language specific
#define FLAG_ALT6_TRANS 0x100000 // language specific
#define FLAG_ALT7_TRANS 0x200000 // language specific
#define FLAG_COMBINE 0x800000 // combine with the next word
#define FLAG_ALLOW_DOT 0x01000000 // ignore '.' after word (abbreviation)
#define FLAG_NEEDS_DOT 0x02000000 // only if the word is followed by a dot
#define FLAG_WAS_UNPRONOUNCABLE 0x04000000 // the unpronounceable routine was used #define FLAG_WAS_UNPRONOUNCABLE 0x04000000 // the unpronounceable routine was used
#define FLAG_MAX3 0x08000000 // limit to 3 repeats
#define FLAG_PAUSE1 0x10000000 // shorter prepause
#define FLAG_TEXTMODE 0x20000000 // word translates to replacement text, not phonemes
#define FLAG_MAX3 0x08000000 // limit to 3 repeats
#define FLAG_PAUSE1 0x10000000 // shorter prepause
#define FLAG_TEXTMODE 0x20000000 // word translates to replacement text, not phonemes
#define BITNUM_FLAG_TEXTMODE 29 #define BITNUM_FLAG_TEXTMODE 29


#define FLAG_FOUND_ATTRIBUTES 0x40000000 // word was found in the dictionary list (has attributes)
#define FLAG_FOUND 0x80000000 // pronunciation was found in the dictionary list
#define FLAG_FOUND_ATTRIBUTES 0x40000000 // word was found in the dictionary list (has attributes)
#define FLAG_FOUND 0x80000000 // pronunciation was found in the dictionary list


// dictionary flags, word 2 // dictionary flags, word 2
#define FLAG_VERBF 0x1 /* verb follows */
#define FLAG_VERBSF 0x2 /* verb follows, may have -s suffix */
#define FLAG_NOUNF 0x4 /* noun follows */
#define FLAG_PASTF 0x8 /* past tense follows */
#define FLAG_VERB 0x10 /* pronunciation for verb */
#define FLAG_NOUN 0x20 /* pronunciation for noun */
#define FLAG_PAST 0x40 /* pronunciation for past tense */
#define FLAG_VERB_EXT 0x100 /* extend the 'verb follows' */
#define FLAG_CAPITAL 0x200 /* pronunciation if initial letter is upper case */
#define FLAG_ALLCAPS 0x400 // only if the word is all capitals
#define FLAG_ACCENT 0x800 // character name is base-character name + accent name
#define FLAG_HYPHENATED 0x1000 // multiple-words, but needs hyphen between parts 1 and 2
#define FLAG_SENTENCE 0x2000 // only if the clause is a sentence
#define FLAG_VERBF 0x1 // verb follows
#define FLAG_VERBSF 0x2 // verb follows, may have -s suffix
#define FLAG_NOUNF 0x4 // noun follows
#define FLAG_PASTF 0x8 // past tense follows
#define FLAG_VERB 0x10 // pronunciation for verb
#define FLAG_NOUN 0x20 // pronunciation for noun
#define FLAG_PAST 0x40 // pronunciation for past tense
#define FLAG_VERB_EXT 0x100 // extend the 'verb follows'
#define FLAG_CAPITAL 0x200 // pronunciation if initial letter is upper case
#define FLAG_ALLCAPS 0x400 // only if the word is all capitals
#define FLAG_ACCENT 0x800 // character name is base-character name + accent name
#define FLAG_HYPHENATED 0x1000 // multiple-words, but needs hyphen between parts 1 and 2
#define FLAG_SENTENCE 0x2000 // only if the clause is a sentence
#define FLAG_ONLY 0x4000 #define FLAG_ONLY 0x4000
#define FLAG_ONLY_S 0x8000 #define FLAG_ONLY_S 0x8000
#define FLAG_STEM 0x10000 // must have a suffix
#define FLAG_ATEND 0x20000 // use this pronunciation if at end of clause
#define FLAG_ATSTART 0x40000 // use this pronunciation if at start of clause
#define FLAG_NATIVE 0x80000 // not if we've switched translators
#define FLAG_LOOKUP_SYMBOL 0x40000000 // to indicate called from Lookup()

#define BITNUM_FLAG_ALLCAPS 0x2a
#define BITNUM_FLAG_HYPHENATED 0x2c
#define BITNUM_FLAG_ONLY 0x2e
#define BITNUM_FLAG_ONLY_S 0x2f
#define FLAG_STEM 0x10000 // must have a suffix
#define FLAG_ATEND 0x20000 // use this pronunciation if at end of clause
#define FLAG_ATSTART 0x40000 // use this pronunciation if at start of clause
#define FLAG_NATIVE 0x80000 // not if we've switched translators
#define FLAG_LOOKUP_SYMBOL 0x40000000 // to indicate called from Lookup()


#define BITNUM_FLAG_ALLCAPS 0x2a
#define BITNUM_FLAG_HYPHENATED 0x2c
#define BITNUM_FLAG_ONLY 0x2e
#define BITNUM_FLAG_ONLY_S 0x2f


// wordflags, flags in source word // wordflags, flags in source word
#define FLAG_ALL_UPPER 0x1 /* no lower case letters in the word */
#define FLAG_FIRST_UPPER 0x2 /* first letter is upper case */
#define FLAG_UPPERS 0x3 // FLAG_ALL_UPPER | FLAG_FIRST_UPPER
#define FLAG_HAS_PLURAL 0x4 /* upper-case word with s or 's lower-case ending */
#define FLAG_PHONEMES 0x8 /* word is phonemes */
#define FLAG_LAST_WORD 0x10 /* last word in clause */
#define FLAG_EMBEDDED 0x40 /* word is preceded by embedded commands */
#define FLAG_ALL_UPPER 0x1 // no lower case letters in the word
#define FLAG_FIRST_UPPER 0x2 // first letter is upper case
#define FLAG_UPPERS 0x3 // FLAG_ALL_UPPER | FLAG_FIRST_UPPER
#define FLAG_HAS_PLURAL 0x4 // upper-case word with s or 's lower-case ending
#define FLAG_PHONEMES 0x8 // word is phonemes
#define FLAG_LAST_WORD 0x10 // last word in clause
#define FLAG_EMBEDDED 0x40 // word is preceded by embedded commands
#define FLAG_HYPHEN 0x80 #define FLAG_HYPHEN 0x80
#define FLAG_NOSPACE 0x100 // word is not seperated from previous word by a space
#define FLAG_FIRST_WORD 0x200 // first word in clause
#define FLAG_FOCUS 0x400 // the focus word of a clause
#define FLAG_NOSPACE 0x100 // word is not seperated from previous word by a space
#define FLAG_FIRST_WORD 0x200 // first word in clause
#define FLAG_FOCUS 0x400 // the focus word of a clause
#define FLAG_EMPHASIZED 0x800 #define FLAG_EMPHASIZED 0x800
#define FLAG_EMPHASIZED2 0xc00 // FLAG_FOCUS | FLAG_EMPHASIZED
#define FLAG_EMPHASIZED2 0xc00 // FLAG_FOCUS | FLAG_EMPHASIZED
#define FLAG_DONT_SWITCH_TRANSLATOR 0x1000 #define FLAG_DONT_SWITCH_TRANSLATOR 0x1000
#define FLAG_SUFFIX_REMOVED 0x2000 #define FLAG_SUFFIX_REMOVED 0x2000
#define FLAG_HYPHEN_AFTER 0x4000 #define FLAG_HYPHEN_AFTER 0x4000
#define FLAG_TRANSLATOR2 0x400000 // retranslating using a different language #define FLAG_TRANSLATOR2 0x400000 // retranslating using a different language
#define FLAG_PREFIX_REMOVED 0x800000 // a prefix has been removed from this word #define FLAG_PREFIX_REMOVED 0x800000 // a prefix has been removed from this word


#define FLAG_SUFFIX_VOWEL 0x08000000 // remember an initial vowel from the suffix
#define FLAG_NO_TRACE 0x10000000 // passed to TranslateRules() to suppress dictionary lookup printout
#define FLAG_SUFFIX_VOWEL 0x08000000 // remember an initial vowel from the suffix
#define FLAG_NO_TRACE 0x10000000 // passed to TranslateRules() to suppress dictionary lookup printout
#define FLAG_NO_PREFIX 0x20000000 #define FLAG_NO_PREFIX 0x20000000
#define FLAG_UNPRON_TEST 0x80000000 // do unpronounability test on the beginning of the word

#define FLAG_UNPRON_TEST 0x80000000 // do unpronounability test on the beginning of the word


// prefix/suffix flags (bits 8 to 14, bits 16 to 22) don't use 0x8000, 0x800000 // prefix/suffix flags (bits 8 to 14, bits 16 to 22) don't use 0x8000, 0x800000
#define SUFX_E 0x0100 // e may have been added #define SUFX_E 0x0100 // e may have been added


#define SUFX_UNPRON 0x8000 // used to return $unpron flag from *_rules #define SUFX_UNPRON 0x8000 // used to return $unpron flag from *_rules



#define FLAG_ALLOW_TEXTMODE 0x02 // allow dictionary to translate to text rather than phonemes #define FLAG_ALLOW_TEXTMODE 0x02 // allow dictionary to translate to text rather than phonemes
#define FLAG_SUFX 0x04 #define FLAG_SUFX 0x04
#define FLAG_SUFX_S 0x08 #define FLAG_SUFX_S 0x08
#define FLAG_SUFX_E_ADDED 0x10 #define FLAG_SUFX_E_ADDED 0x10



// codes in dictionary rules // codes in dictionary rules
#define RULE_PRE 1
#define RULE_POST 2
#define RULE_PHONEMES 3
#define RULE_PH_COMMON 4 // At start of rule. Its phoneme string is used by subsequent rules
#define RULE_CONDITION 5 // followed by condition number (byte)
#define RULE_PRE 1
#define RULE_POST 2
#define RULE_PHONEMES 3
#define RULE_PH_COMMON 4 // At start of rule. Its phoneme string is used by subsequent rules
#define RULE_CONDITION 5 // followed by condition number (byte)
#define RULE_GROUP_START 6 #define RULE_GROUP_START 6
#define RULE_GROUP_END 7
#define RULE_PRE_ATSTART 8 // as RULE_PRE but also match with 'start of word'
#define RULE_LINENUM 9 // next 2 bytes give a line number, for debugging purposes
#define RULE_SPACE 32 // ascii space
#define RULE_SYLLABLE 21 // @
#define RULE_STRESSED 10 // &
#define RULE_DOUBLE 11 // %
#define RULE_INC_SCORE 12 // +
#define RULE_DEL_FWD 13 // #
#define RULE_ENDING 14 // S
#define RULE_DIGIT 15 // D digit
#define RULE_NONALPHA 16 // Z non-alpha
#define RULE_LETTERGP 17 // A B C H F G Y letter group number
#define RULE_LETTERGP2 18 // L + letter group number
#define RULE_CAPITAL 19 // ! word starts with a capital letter
#define RULE_REPLACEMENTS 20 // section for character replacements
#define RULE_SKIPCHARS 23 // J
#define RULE_NO_SUFFIX 24 // N
#define RULE_NOTVOWEL 25 // K
#define RULE_IFVERB 26 // V
#define RULE_DOLLAR 28 // $ commands
#define RULE_NOVOWELS 29 // X no vowels up to word boundary
#define RULE_SPELLING 31 // W while spelling letter-by-letter
#define RULE_LAST_RULE 31
#define RULE_GROUP_END 7
#define RULE_PRE_ATSTART 8 // as RULE_PRE but also match with 'start of word'
#define RULE_LINENUM 9 // next 2 bytes give a line number, for debugging purposes
#define RULE_SPACE 32 // ascii space
#define RULE_SYLLABLE 21 // @
#define RULE_STRESSED 10 // &
#define RULE_DOUBLE 11 // %
#define RULE_INC_SCORE 12 // +
#define RULE_DEL_FWD 13 // #
#define RULE_ENDING 14 // S
#define RULE_DIGIT 15 // D digit
#define RULE_NONALPHA 16 // Z non-alpha
#define RULE_LETTERGP 17 // A B C H F G Y letter group number
#define RULE_LETTERGP2 18 // L + letter group number
#define RULE_CAPITAL 19 // ! word starts with a capital letter
#define RULE_REPLACEMENTS 20 // section for character replacements
#define RULE_SKIPCHARS 23 // J
#define RULE_NO_SUFFIX 24 // N
#define RULE_NOTVOWEL 25 // K
#define RULE_IFVERB 26 // V
#define RULE_DOLLAR 28 // $ commands
#define RULE_NOVOWELS 29 // X no vowels up to word boundary
#define RULE_SPELLING 31 // W while spelling letter-by-letter
#define RULE_LAST_RULE 31


#define DOLLAR_UNPR 0x01 #define DOLLAR_UNPR 0x01
#define DOLLAR_NOPREFIX 0x02 #define DOLLAR_NOPREFIX 0x02
#define DOLLAR_LIST 0x03 #define DOLLAR_LIST 0x03



#define LETTERGP_A 0
#define LETTERGP_B 1
#define LETTERGP_C 2
#define LETTERGP_H 3
#define LETTERGP_F 4
#define LETTERGP_G 5
#define LETTERGP_Y 6
#define LETTERGP_VOWEL2 7

#define LETTERGP_A 0
#define LETTERGP_B 1
#define LETTERGP_C 2
#define LETTERGP_H 3
#define LETTERGP_F 4
#define LETTERGP_G 5
#define LETTERGP_Y 6
#define LETTERGP_VOWEL2 7


// Punctuation types returned by ReadClause() // Punctuation types returned by ReadClause()
// bits 0-11 pause x 10mS // bits 0-11 pause x 10mS
// bit 22= dot after the last word // bit 22= dot after the last word
// bit 23= pause is x 320mS (not x 10mS) // bit 23= pause is x 320mS (not x 10mS)


#define CLAUSE_BIT_SENTENCE 0x80000
#define CLAUSE_BIT_CLAUSE 0x40000
#define CLAUSE_BIT_VOICE 0x20000
#define CLAUSE_BITS_INTONATION 0x7000
#define PUNCT_IN_WORD 0x100000
#define PUNCT_SAY_NAME 0x200000
#define CLAUSE_DOT 0x400000
#define CLAUSE_PAUSE_LONG 0x800000
#define CLAUSE_BIT_SENTENCE 0x80000
#define CLAUSE_BIT_CLAUSE 0x40000
#define CLAUSE_BIT_VOICE 0x20000
#define CLAUSE_BITS_INTONATION 0x7000
#define PUNCT_IN_WORD 0x100000
#define PUNCT_SAY_NAME 0x200000
#define CLAUSE_DOT 0x400000
#define CLAUSE_PAUSE_LONG 0x800000


#define CLAUSE_NONE ( 0 + 0x04000) #define CLAUSE_NONE ( 0 + 0x04000)
#define CLAUSE_PARAGRAPH (70 + 0x80000) #define CLAUSE_PARAGRAPH (70 + 0x80000)
#define CLAUSE_COLON (30 + 0x40000) #define CLAUSE_COLON (30 + 0x40000)
#define CLAUSE_SEMICOLON (30 + 0x41000) #define CLAUSE_SEMICOLON (30 + 0x41000)


#define SAYAS_CHARS 0x12
#define SAYAS_GLYPHS 0x13
#define SAYAS_CHARS 0x12
#define SAYAS_GLYPHS 0x13
#define SAYAS_SINGLE_CHARS 0x14 #define SAYAS_SINGLE_CHARS 0x14
#define SAYAS_KEY 0x24
#define SAYAS_DIGITS 0x40 // + number of digits
#define SAYAS_DIGITS1 0xc1
#define SAYAS_KEY 0x24
#define SAYAS_DIGITS 0x40 // + number of digits
#define SAYAS_DIGITS1 0xc1


#define CHAR_EMPHASIS 0x0530 // this is an unused character code
#define CHAR_COMMA_BREAK 0x0557 // unused character code
#define CHAR_EMPHASIS 0x0530 // this is an unused character code
#define CHAR_COMMA_BREAK 0x0557 // unused character code


// Rule: // Rule:
// [4] [match] [1 pre] [2 post] [3 phonemes] 0 // [4] [match] [1 pre] [2 post] [3 phonemes] 0
char *del_fwd; char *del_fwd;
} MatchRecord; } MatchRecord;



// used to mark words with the source[] buffer // used to mark words with the source[] buffer
typedef struct { typedef struct {
unsigned int flags; unsigned int flags;
unsigned char length; unsigned char length;
} WORD_TAB; } WORD_TAB;



typedef struct { typedef struct {
int type; int type;
int parameter[N_SPEECH_PARAM]; int parameter[N_SPEECH_PARAM];
extern PARAM_STACK param_stack[]; extern PARAM_STACK param_stack[];
extern const int param_defaults[N_SPEECH_PARAM]; extern const int param_defaults[N_SPEECH_PARAM];



typedef struct { typedef struct {
const char *name; const char *name;
int offset; int offset;
extern ALPHABET alphabets[]; extern ALPHABET alphabets[];
extern ALPHABET *current_alphabet; extern ALPHABET *current_alphabet;
// alphabet flags // alphabet flags
#define AL_DONT_NAME 0x01 // don't speak the alphabet name
#define AL_NOT_LETTERS 0x02 // don't use the language for speaking letters
#define AL_WORDS 0x04 // use the language to speak words
#define AL_NOT_CODE 0x08 // don't speak the character code
#define AL_NO_SYMBOL 0x10 // don't repeat "symbol" or "character"


#define N_LOPTS 21
#define LOPT_DIERESES 1
#define AL_DONT_NAME 0x01 // don't speak the alphabet name
#define AL_NOT_LETTERS 0x02 // don't use the language for speaking letters
#define AL_WORDS 0x04 // use the language to speak words
#define AL_NOT_CODE 0x08 // don't speak the character code
#define AL_NO_SYMBOL 0x10 // don't repeat "symbol" or "character"

#define N_LOPTS 21
#define LOPT_DIERESES 1
// 1=remove [:] from unstressed syllables, 2= remove from unstressed or non-penultimate syllables // 1=remove [:] from unstressed syllables, 2= remove from unstressed or non-penultimate syllables
// bit 4=0, if stress < 4, bit 4=1, if not the highest stress in the word // bit 4=0, if stress < 4, bit 4=1, if not the highest stress in the word
#define LOPT_IT_LENGTHEN 2
#define LOPT_IT_LENGTHEN 2


// 1=german // 1=german
#define LOPT_PREFIXES 3
#define LOPT_PREFIXES 3


// non-zero, change voiced/unoiced to match last consonant in a cluster // non-zero, change voiced/unoiced to match last consonant in a cluster
// bit 0=use regressive voicing // bit 0=use regressive voicing
// bit 3=LANG=pl, propagate over liquids and nasals // bit 3=LANG=pl, propagate over liquids and nasals
// bit 4=LANG=cz,sk don't progagate to [v] // bit 4=LANG=cz,sk don't progagate to [v]
// bit 8=devoice word-final consonants // bit 8=devoice word-final consonants
#define LOPT_REGRESSIVE_VOICING 4
#define LOPT_REGRESSIVE_VOICING 4


// 0=default, 1=no check, other allow this character as an extra initial letter (default is 's') // 0=default, 1=no check, other allow this character as an extra initial letter (default is 's')
#define LOPT_UNPRONOUNCABLE 5
#define LOPT_UNPRONOUNCABLE 5


// select length_mods tables, (length_mod_tab) + (length_mod_tab0 * 100) // select length_mods tables, (length_mod_tab) + (length_mod_tab0 * 100)
#define LOPT_LENGTH_MODS 6
#define LOPT_LENGTH_MODS 6


// increase this to prevent sonorants being shortened before shortened (eg. unstressed) vowels // increase this to prevent sonorants being shortened before shortened (eg. unstressed) vowels
#define LOPT_SONORANT_MIN 7
#define LOPT_SONORANT_MIN 7


// bit 0: don't break vowels at word boundary // bit 0: don't break vowels at word boundary
#define LOPT_WORD_MERGE 8
#define LOPT_WORD_MERGE 8


// max. amplitude for vowel at the end of a clause // max. amplitude for vowel at the end of a clause
#define LOPT_MAXAMP_EOC 9
#define LOPT_MAXAMP_EOC 9


// bit 0=reduce even if phonemes are specified in the **_list file // bit 0=reduce even if phonemes are specified in the **_list file
// bit 1=don't reduce the strongest vowel in a word which is marked 'unstressed' // bit 1=don't reduce the strongest vowel in a word which is marked 'unstressed'
#define LOPT_REDUCE 10
#define LOPT_REDUCE 10


// LANG=cs,sk combine some prepositions with the following word, if the combination has N or fewer syllables // LANG=cs,sk combine some prepositions with the following word, if the combination has N or fewer syllables
// bits 0-3 N syllables // bits 0-3 N syllables


// 1 = allow capitals inside a word // 1 = allow capitals inside a word
// 2 = stressed syllable is indicated by capitals // 2 = stressed syllable is indicated by capitals
#define LOPT_CAPS_IN_WORD 13
#define LOPT_CAPS_IN_WORD 13


// bit 0=Italian "syntactic doubling" of consoants in the word after a word marked with $double attribute // bit 0=Italian "syntactic doubling" of consoants in the word after a word marked with $double attribute
// bit 1=also after a word which ends with a stressed vowel // bit 1=also after a word which ends with a stressed vowel
#define LOPT_IT_DOUBLING 14
#define LOPT_IT_DOUBLING 14


// Call ApplySpecialAttributes() if $alt or $alt2 is set for a word // Call ApplySpecialAttributes() if $alt or $alt2 is set for a word
// bit 1: stressed syllable: $alt change [e],[o] to [E],[O], $alt2 change [E],[O] to [e],[o] // bit 1: stressed syllable: $alt change [e],[o] to [E],[O], $alt2 change [E],[O] to [e],[o]
#define LOPT_ALT 15
#define LOPT_ALT 15


// pause for bracket (default=4), pause when annoucing bracket names (default=2) // pause for bracket (default=4), pause when annoucing bracket names (default=2)
#define LOPT_BRACKET_PAUSE 16 #define LOPT_BRACKET_PAUSE 16
#define LOPT_LONG_VOWEL_THRESHOLD 18 #define LOPT_LONG_VOWEL_THRESHOLD 18


// bit 0: Don't allow suffices if there is no previous syllable // bit 0: Don't allow suffices if there is no previous syllable
#define LOPT_SUFFIX 19
#define LOPT_SUFFIX 19


// bit 0 Apostrophe at start of word is part of the word // bit 0 Apostrophe at start of word is part of the word
// bit 1 Apostrophe at end of word is part of the word // bit 1 Apostrophe at end of word is part of the word
#define LOPT_APOSTROPHE 20

#define LOPT_APOSTROPHE 20


// stress_rule // stress_rule
#define STRESSPOSN_1L 0 // 1st syllable
#define STRESSPOSN_2L 1 // 2nd syllable
#define STRESSPOSN_2R 2 // penultimate
#define STRESSPOSN_1R 3 // final syllable
#define STRESSPOSN_3R 4 // antipenultimate

#define STRESSPOSN_1L 0 // 1st syllable
#define STRESSPOSN_2L 1 // 2nd syllable
#define STRESSPOSN_2R 2 // penultimate
#define STRESSPOSN_1R 3 // final syllable
#define STRESSPOSN_3R 4 // antipenultimate


typedef struct { typedef struct {
// bits0-2 separate words with (1=pause_vshort, 2=pause_short, 3=pause, 4=pause_long 5=[?] phonemme) // bits0-2 separate words with (1=pause_vshort, 2=pause_short, 3=pause, 4=pause_long 5=[?] phonemme)


// bit15= Give stress to the first unstressed syllable // bit15= Give stress to the first unstressed syllable



int stress_flags; int stress_flags;
int unstressed_wd1; // stress for $u word of 1 syllable int unstressed_wd1; // stress for $u word of 1 syllable
int unstressed_wd2; // stress for $u word of >1 syllable int unstressed_wd2; // stress for $u word of >1 syllable
#define NUM2_PERCENT_BEFORE 0x10000 #define NUM2_PERCENT_BEFORE 0x10000
#define NUM2_OMIT_1_HUNDRED_ONLY 0x20000 #define NUM2_OMIT_1_HUNDRED_ONLY 0x20000
#define NUM2_ORDINAL_AND_THOUSANDS 0x40000 #define NUM2_ORDINAL_AND_THOUSANDS 0x40000
#define NUM2_ORDINAL_DROP_VOWEL 0x80000 // currently only for tens and units
#define NUM2_ORDINAL_DROP_VOWEL 0x80000 // currently only for tens and units
#define NUM2_ZERO_TENS 0x100000 #define NUM2_ZERO_TENS 0x100000
// bits 1-4 use variant form of numbers before thousands,millions,etc. // bits 1-4 use variant form of numbers before thousands,millions,etc.
// bits 6-8 use different forms of thousand, million, etc (M MA MB) // bits 6-8 use different forms of thousand, million, etc (M MA MB)
int dict_dialect; // bitmap, use a dialect for foreign words int dict_dialect; // bitmap, use a dialect for foreign words
} LANGUAGE_OPTIONS; } LANGUAGE_OPTIONS;



// a parameter of ChangePhonemes() // a parameter of ChangePhonemes()
typedef struct { typedef struct {
int flags; int flags;
unsigned char vowel_stressed; // syllable number of the highest stressed vowel unsigned char vowel_stressed; // syllable number of the highest stressed vowel
} CHANGEPH; } CHANGEPH;




typedef struct { typedef struct {

LANGUAGE_OPTIONS langopts; LANGUAGE_OPTIONS langopts;
int translator_name; int translator_name;
int transpose_max; int transpose_max;
const wchar_t *letter_groups[8]; const wchar_t *letter_groups[8];


/* index1=option, index2 by 0=. 1=, 2=?, 3=! 4=none */ /* index1=option, index2 by 0=. 1=, 2=?, 3=! 4=none */
#define INTONATION_TYPES 8
#define PUNCT_INTONATIONS 6
#define INTONATION_TYPES 8
#define PUNCT_INTONATIONS 6
unsigned char punct_to_tone[INTONATION_TYPES][PUNCT_INTONATIONS]; unsigned char punct_to_tone[INTONATION_TYPES][PUNCT_INTONATIONS];


char *data_dictrules; // language_1 translation rules file char *data_dictrules; // language_1 translation rules file
int clause_terminator; int clause_terminator;
} Translator; } Translator;



extern int option_tone2; extern int option_tone2;
#define OPTION_EMPHASIZE_ALLCAPS 0x100 #define OPTION_EMPHASIZE_ALLCAPS 0x100
#define OPTION_EMPHASIZE_PENULTIMATE 0x200 #define OPTION_EMPHASIZE_PENULTIMATE 0x200
extern char *namedata; extern char *namedata;
extern int pre_pause; extern int pre_pause;




#define N_MARKER_LENGTH 50 // max.length of a mark name #define N_MARKER_LENGTH 50 // max.length of a mark name
extern char skip_marker[N_MARKER_LENGTH]; extern char skip_marker[N_MARKER_LENGTH];


void InterpretPhoneme2(int phcode, PHONEME_DATA *phdata); void InterpretPhoneme2(int phcode, PHONEME_DATA *phdata);
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);


extern FILE *f_trans; // for logging
extern FILE *f_trans; // for logging
extern FILE *f_logespeak; extern FILE *f_logespeak;
extern int logging_type; // from config file
extern int logging_type; // from config file


#ifdef __cplusplus #ifdef __cplusplus
} }

+ 6
- 8
src/libespeak-ng/voice.h View File

{ {
#endif #endif




typedef struct { typedef struct {
char v_name[40]; char v_name[40];
char language_name[20]; char language_name[20];


int phoneme_tab_ix; // phoneme table number
int pitch_base; // Hz<<12
int pitch_range; // standard = 0x1000
int phoneme_tab_ix; // phoneme table number
int pitch_base; // Hz<<12
int pitch_range; // standard = 0x1000


int speedf1; int speedf1;
int speedf2; int speedf2;
int n_harmonic_peaks; // highest formant which is formed from adding harmonics int n_harmonic_peaks; // highest formant which is formed from adding harmonics
int peak_shape; // alternative shape for formant peaks (0=standard 1=squarer) int peak_shape; // alternative shape for formant peaks (0=standard 1=squarer)
int voicing; // 100% = 64, level of formant-synthesized sound int voicing; // 100% = 64, level of formant-synthesized sound
int formant_factor; // adjust nominal formant frequencies by this because of the voice's pitch (256ths)
int formant_factor; // adjust nominal formant frequencies by this because of the voice's pitch (256ths)
int consonant_amp; // amplitude of unvoiced consonants int consonant_amp; // amplitude of unvoiced consonants
int consonant_ampv; // amplitude of the noise component of voiced consonants int consonant_ampv; // amplitude of the noise component of voiced consonants
int samplerate; int samplerate;
short freq[N_PEAKS]; // 100% = 256 short freq[N_PEAKS]; // 100% = 256
short height[N_PEAKS]; // 100% = 256 short height[N_PEAKS]; // 100% = 256
short width[N_PEAKS]; // 100% = 256 short width[N_PEAKS]; // 100% = 256
short freqadd[N_PEAKS]; // Hz
short freqadd[N_PEAKS]; // Hz


// copies without temporary adjustments from embedded commands // copies without temporary adjustments from embedded commands
short freq2[N_PEAKS]; // 100% = 256 short freq2[N_PEAKS]; // 100% = 256
short width2[N_PEAKS]; // 100% = 256 short width2[N_PEAKS]; // 100% = 256


int breath[N_PEAKS]; // amount of breath for each formant. breath[0] indicates whether any are set. int breath[N_PEAKS]; // amount of breath for each formant. breath[0] indicates whether any are set.
int breathw[N_PEAKS]; // width of each breath formant
int breathw[N_PEAKS]; // width of each breath formant


// This table provides the opportunity for tone control. // This table provides the opportunity for tone control.
// Adjustment of harmonic amplitudes, steps of 8Hz // Adjustment of harmonic amplitudes, steps of 8Hz

+ 147
- 147
src/libespeak-ng/voices.c View File

int tone_points[12] = { 600, 170, 1200, 135, 2000, 110, 3000, 110, -1, 0 }; int tone_points[12] = { 600, 170, 1200, 135, 2000, 110, 3000, 110, -1, 0 };


// limit the rate of change for each formant number // limit the rate of change for each formant number
static int formant_rate_22050[9] = { 240, 170, 170, 170, 170, 170, 170, 170, 170 }; // values for 22kHz sample rate
int formant_rate[9]; // values adjusted for actual sample rate
static int formant_rate_22050[9] = { 240, 170, 170, 170, 170, 170, 170, 170, 170 }; // values for 22kHz sample rate
int formant_rate[9]; // values adjusted for actual sample rate


#define DEFAULT_LANGUAGE_PRIORITY 5 #define DEFAULT_LANGUAGE_PRIORITY 5
#define N_VOICES_LIST 250 #define N_VOICES_LIST 250
V_PHONEMES, V_PHONEMES,
V_DICTIONARY, V_DICTIONARY,


// these affect voice quality, are independent of language
// these affect voice quality, are independent of language
V_FORMANT, V_FORMANT,
V_PITCH, V_PITCH,
V_ECHO, V_ECHO,
V_BREATH, V_BREATH,
V_BREATHW, V_BREATHW,


// these override defaults set by the translator
// these override defaults set by the translator
V_WORDGAP, V_WORDGAP,
V_INTONATION, V_INTONATION,
V_TUNES, V_TUNES,
V_ALPHABET2, V_ALPHABET2,
V_DICTDIALECT, V_DICTDIALECT,


// these need a phoneme table to have been specified
// these need a phoneme table to have been specified
V_REPLACE, V_REPLACE,
V_CONSONANTS V_CONSONANTS
}; };


static MNEM_TAB options_tab[] = { static MNEM_TAB options_tab[] = {
{ "reduce_t", LOPT_REDUCE_T },
{ "bracket", LOPT_BRACKET_PAUSE },
{ "reduce_t", LOPT_REDUCE_T },
{ "bracket", LOPT_BRACKET_PAUSE },
{ NULL, -1 } { NULL, -1 }
}; };


static MNEM_TAB keyword_tab[] = { static MNEM_TAB keyword_tab[] = {
{ "name", V_NAME },
{ "language", V_LANGUAGE },
{ "gender", V_GENDER },
{ "formant", V_FORMANT },
{ "pitch", V_PITCH },
{ "phonemes", V_PHONEMES },
{ "translator", V_TRANSLATOR },
{ "dictionary", V_DICTIONARY },
{ "name", V_NAME },
{ "language", V_LANGUAGE },
{ "gender", V_GENDER },
{ "formant", V_FORMANT },
{ "pitch", V_PITCH },
{ "phonemes", V_PHONEMES },
{ "translator", V_TRANSLATOR },
{ "dictionary", V_DICTIONARY },
{ "stressLength", V_STRESSLENGTH }, { "stressLength", V_STRESSLENGTH },
{ "stressAmp", V_STRESSAMP },
{ "stressAdd", V_STRESSADD },
{ "intonation", V_INTONATION },
{ "tunes", V_TUNES },
{ "dictrules", V_DICTRULES },
{ "stressrule", V_STRESSRULE },
{ "stressopt", V_STRESSOPT },
{ "charset", V_CHARSET },
{ "replace", V_REPLACE },
{ "words", V_WORDGAP },
{ "echo", V_ECHO },
{ "flutter", V_FLUTTER },
{ "roughness", V_ROUGHNESS },
{ "clarity", V_CLARITY },
{ "tone", V_TONE },
{ "voicing", V_VOICING },
{ "breath", V_BREATH },
{ "breathw", V_BREATHW },
{ "numbers", V_NUMBERS },
{ "option", V_OPTION },
{ "mbrola", V_MBROLA },
{ "consonants", V_CONSONANTS },
{ "klatt", V_KLATT },
{ "fast_test2", V_FAST },
{ "speed", V_SPEED },
{ "dict_min", V_DICTMIN },
{ "alphabet2", V_ALPHABET2 },
{ "dictdialect", V_DICTDIALECT },
{ "stressAmp", V_STRESSAMP },
{ "stressAdd", V_STRESSADD },
{ "intonation", V_INTONATION },
{ "tunes", V_TUNES },
{ "dictrules", V_DICTRULES },
{ "stressrule", V_STRESSRULE },
{ "stressopt", V_STRESSOPT },
{ "charset", V_CHARSET },
{ "replace", V_REPLACE },
{ "words", V_WORDGAP },
{ "echo", V_ECHO },
{ "flutter", V_FLUTTER },
{ "roughness", V_ROUGHNESS },
{ "clarity", V_CLARITY },
{ "tone", V_TONE },
{ "voicing", V_VOICING },
{ "breath", V_BREATH },
{ "breathw", V_BREATHW },
{ "numbers", V_NUMBERS },
{ "option", V_OPTION },
{ "mbrola", V_MBROLA },
{ "consonants", V_CONSONANTS },
{ "klatt", V_KLATT },
{ "fast_test2", V_FAST },
{ "speed", V_SPEED },
{ "dict_min", V_DICTMIN },
{ "alphabet2", V_ALPHABET2 },
{ "dictdialect", V_DICTDIALECT },


// these just set a value in langopts.param[] // these just set a value in langopts.param[]
{ "l_dieresis", 0x100+LOPT_DIERESES },
{ "l_prefix", 0x100+LOPT_PREFIXES },
{ "l_regressive_v", 0x100+LOPT_REGRESSIVE_VOICING },
{ "l_dieresis", 0x100+LOPT_DIERESES },
{ "l_prefix", 0x100+LOPT_PREFIXES },
{ "l_regressive_v", 0x100+LOPT_REGRESSIVE_VOICING },
{ "l_unpronouncable", 0x100+LOPT_UNPRONOUNCABLE }, { "l_unpronouncable", 0x100+LOPT_UNPRONOUNCABLE },
{ "l_sonorant_min", 0x100+LOPT_SONORANT_MIN },
{ "l_length_mods", 0x100+LOPT_LENGTH_MODS },
{ "apostrophe", 0x100+LOPT_APOSTROPHE },
{ NULL, 0 }
{ "l_sonorant_min", 0x100+LOPT_SONORANT_MIN },
{ "l_length_mods", 0x100+LOPT_LENGTH_MODS },
{ "apostrophe", 0x100+LOPT_APOSTROPHE },

{ NULL, 0 }
}; };


static MNEM_TAB dict_dialects[] = { static MNEM_TAB dict_dialects[] = {
{ "en-us", DICTDIALECT_EN_US },
{ "es-la", DICTDIALECT_ES_LA },
{ NULL, 0 }
{ "en-us", DICTDIALECT_EN_US },
{ "es-la", DICTDIALECT_ES_LA },

{ NULL, 0 }
}; };


#define N_VOICE_VARIANTS 12 #define N_VOICE_VARIANTS 12


static char *fgets_strip(char *buf, int size, FILE *f_in) static char *fgets_strip(char *buf, int size, FILE *f_in)
{ {
// strip trailing spaces, and truncate lines at // comment
// strip trailing spaces, and truncate lines at // comment
int len; int len;
char *p; char *p;


if (pt > 0) if (pt > 0)
tone_pts[pt+1] = tone_pts[pt-1]; tone_pts[pt+1] = tone_pts[pt-1];
} }
freq2 = tone_pts[pt] / 8; // 8Hz steps
freq2 = tone_pts[pt] / 8; // 8Hz steps
height2 = tone_pts[pt+1]; height2 = tone_pts[pt+1];
if ((freq2 - freq1) > 0) { if ((freq2 - freq1) > 0) {
rate = (double)(height2-height1)/(freq2-freq1); rate = (double)(height2-height1)/(freq2-freq1);


void ReadTonePoints(char *string, int *tone_pts) void ReadTonePoints(char *string, int *tone_pts)
{ {
// tone_pts[] is int[12]
// tone_pts[] is int[12]
int ix; int ix;


for (ix = 0; ix < 12; ix++) for (ix = 0; ix < 12; ix++)


static espeak_VOICE *ReadVoiceFile(FILE *f_in, const char *fname, const char *leafname) static espeak_VOICE *ReadVoiceFile(FILE *f_in, const char *fname, const char *leafname)
{ {
// Read a Voice file, allocate a VOICE_DATA and set data from the
// file's language, gender, name lines
// Read a Voice file, allocate a VOICE_DATA and set data from the
// file's language, gender, name lines


char linebuf[120]; char linebuf[120];
char vname[80]; char vname[80];
char vgender[80]; char vgender[80];
char vlanguage[80]; char vlanguage[80];
char languages[300]; // allow space for several alternate language names and priorities
char languages[300]; // allow space for several alternate language names and priorities


unsigned int len; unsigned int len;
int langix = 0; int langix = 0;
espeak_VOICE *voice_data; espeak_VOICE *voice_data;
int priority; int priority;
int age; int age;
int n_variants = 4; // default, number of variants of this voice before using another voice
int n_variants = 4; // default, number of variants of this voice before using another voice
int gender; int gender;


#ifdef PLATFORM_WINDOWS #ifdef PLATFORM_WINDOWS
gender = LookupMnem(genders, vgender); gender = LookupMnem(genders, vgender);


if (n_languages == 0) if (n_languages == 0)
return NULL; // no language lines in the voice file
return NULL; // no language lines in the voice file


p = (char *)calloc(sizeof(espeak_VOICE) + langix + strlen(fname) + strlen(vname) + 3, 1); p = (char *)calloc(sizeof(espeak_VOICE) + langix + strlen(fname) + strlen(vname) + 3, 1);
voice_data = (espeak_VOICE *)p; voice_data = (espeak_VOICE *)p;


void VoiceReset(int tone_only) void VoiceReset(int tone_only)
{ {
// Set voice to the default values
// Set voice to the default values


int pk; int pk;
static unsigned char default_heights[N_PEAKS] = { 130, 128, 120, 116, 100, 100, 128, 128, 128 }; // changed for v.1.47
static unsigned char default_heights[N_PEAKS] = { 130, 128, 120, 116, 100, 100, 128, 128, 128 }; // changed for v.1.47
static unsigned char default_widths[N_PEAKS] = { 140, 128, 128, 160, 171, 171, 128, 128, 128 }; static unsigned char default_widths[N_PEAKS] = { 140, 128, 128, 160, 171, 171, 128, 128, 128 };


static int breath_widths[N_PEAKS] = { 0, 200, 200, 400, 400, 400, 600, 600, 600 }; static int breath_widths[N_PEAKS] = { 0, 200, 200, 400, 400, 400, 600, 600, 600 };
voice->n_harmonic_peaks = 5; voice->n_harmonic_peaks = 5;
voice->peak_shape = 0; voice->peak_shape = 0;
voice->voicing = 64; voice->voicing = 64;
voice->consonant_amp = 90; // change from 100 to 90 for v.1.47
voice->consonant_amp = 90; // change from 100 to 90 for v.1.47
voice->consonant_ampv = 100; voice->consonant_ampv = 100;
voice->samplerate = samplerate_native; voice->samplerate = samplerate_native;
memset(voice->klattv, 0, sizeof(voice->klattv)); memset(voice->klattv, 0, sizeof(voice->klattv));
voice->height[pk] = default_heights[pk]*2; voice->height[pk] = default_heights[pk]*2;
voice->width[pk] = default_widths[pk]*2; voice->width[pk] = default_widths[pk]*2;
voice->breath[pk] = 0; voice->breath[pk] = 0;
voice->breathw[pk] = breath_widths[pk]; // default breath formant woidths
voice->breathw[pk] = breath_widths[pk]; // default breath formant woidths
voice->freqadd[pk] = 0; voice->freqadd[pk] = 0;


// adjust formant smoothing depending on sample rate // adjust formant smoothing depending on sample rate
return; return;


if ((phon = LookupPhonemeString(phon_string1)) == 0) if ((phon = LookupPhonemeString(phon_string1)) == 0)
return; // not recognised
return; // not recognised


replace_phonemes[n_replace_phonemes].old_ph = phon; replace_phonemes[n_replace_phonemes].old_ph = phon;
replace_phonemes[n_replace_phonemes].new_ph = LookupPhonemeString(phon_string2); replace_phonemes[n_replace_phonemes].new_ph = LookupPhonemeString(phon_string2);


static int Read8Numbers(char *data_in, int *data) static int Read8Numbers(char *data_in, int *data)
{ {
// Read 8 integer numbers
// Read 8 integer numbers
memset(data, 0, 8+sizeof(int)); memset(data, 0, 8+sizeof(int));
return sscanf(data_in, "%d %d %d %d %d %d %d %d", return sscanf(data_in, "%d %d %d %d %d %d %d %d",
&data[0], &data[1], &data[2], &data[3], &data[4], &data[5], &data[6], &data[7]); &data[0], &data[1], &data[2], &data[3], &data[4], &data[5], &data[6], &data[7]);


static unsigned int StringToWord2(const char *string) static unsigned int StringToWord2(const char *string)
{ {
// Convert a language name string to a word such as L('e','n')
// Convert a language name string to a word such as L('e','n')
int ix; int ix;
int c; int c;
unsigned int value = 0; unsigned int value = 0;


voice_t *LoadVoice(const char *vname, int control) voice_t *LoadVoice(const char *vname, int control)
{ {
// control, bit 0 1= no_default
// bit 1 1 = change tone only, not language
// bit 2 1 = don't report error on LoadDictionary
// bit 4 1 = vname = full path
// control, bit 0 1= no_default
// bit 1 1 = change tone only, not language
// bit 2 1 = don't report error on LoadDictionary
// bit 4 1 = vname = full path


FILE *f_voice = NULL; FILE *f_voice = NULL;
char *p; char *p;
int pitch1; int pitch1;
int pitch2; int pitch2;


static char voice_identifier[40]; // file name for current_voice_selected
static char voice_name[40]; // voice name for current_voice_selected
static char voice_languages[100]; // list of languages and priorities for current_voice_selected
static char voice_identifier[40]; // file name for current_voice_selected
static char voice_name[40]; // voice name for current_voice_selected
static char voice_languages[100]; // list of languages and priorities for current_voice_selected


// which directory to look for a named voice. List of voice names, must end in a space. // which directory to look for a named voice. List of voice names, must end in a space.
static const char *voices_asia = static const char *voices_asia =
strcpy(voicename, "default"); strcpy(voicename, "default");


sprintf(path_voices, "%s%cvoices%c", path_home, PATHSEP, PATHSEP); sprintf(path_voices, "%s%cvoices%c", path_home, PATHSEP, PATHSEP);
sprintf(buf, "%s%s", path_voices, voicename); // first, look in the main voices directory
sprintf(buf, "%s%s", path_voices, voicename); // first, look in the main voices directory


if (GetFileLength(buf) <= 0) { if (GetFileLength(buf) <= 0) {
// then look in the appropriate subdirectory // then look in the appropriate subdirectory
if ((voicename[0] == 'm') && (voicename[1] == 'b')) if ((voicename[0] == 'm') && (voicename[1] == 'b'))
voice_dir = "mb"; // mbrola voices
voice_dir = "mb"; // mbrola voices
else { else {
sprintf(name2, "%s ", voicename); sprintf(name2, "%s ", voicename);
if (strstr(voices_europe, voicename) != NULL) if (strstr(voices_europe, voicename) != NULL)


f_voice = fopen(buf, "r"); f_voice = fopen(buf, "r");


language_type = "en"; // default
language_type = "en"; // default
if (f_voice == NULL) { if (f_voice == NULL) {
if (control & 3) if (control & 3)
return NULL; // can't open file
return NULL; // can't open file


if (SelectPhonemeTableName(voicename) >= 0) if (SelectPhonemeTableName(voicename) >= 0)
language_type = voicename; language_type = voicename;
VoiceReset(tone_only); VoiceReset(tone_only);


if (!tone_only) if (!tone_only)
SelectPhonemeTableName(phonemes_name); // set up phoneme_tab
SelectPhonemeTableName(phonemes_name); // set up phoneme_tab


while ((f_voice != NULL) && (fgets_strip(buf, sizeof(buf), f_voice) != NULL)) { while ((f_voice != NULL) && (fgets_strip(buf, sizeof(buf), f_voice) != NULL)) {
// isolate the attribute name // isolate the attribute name
new_translator = SelectTranslator(translator_name); new_translator = SelectTranslator(translator_name);
langopts = &new_translator->langopts; langopts = &new_translator->langopts;
break; break;
case V_DICTIONARY: // dictionary
case V_DICTIONARY: // dictionary
sscanf(p, "%s", new_dictionary); sscanf(p, "%s", new_dictionary);
break; break;
case V_PHONEMES: // phoneme table
case V_PHONEMES: // phoneme table
sscanf(p, "%s", phonemes_name); sscanf(p, "%s", phonemes_name);
break; break;
case V_FORMANT: case V_FORMANT:
voice->pitch_base = (pitch1 - 9) << 12; voice->pitch_base = (pitch1 - 9) << 12;
voice->pitch_range = (pitch2 - pitch1) * 108; voice->pitch_range = (pitch2 - pitch1) * 108;
factor = (double)(pitch1 - 82)/82; factor = (double)(pitch1 - 82)/82;
voice->formant_factor = (int)((1+factor/4) * 256); // nominal formant shift for a different voice pitch
voice->formant_factor = (int)((1+factor/4) * 256); // nominal formant shift for a different voice pitch
} }
break; break;
case V_STRESSLENGTH: // stressLength
case V_STRESSLENGTH: // stressLength
stress_lengths_set = Read8Numbers(p, stress_lengths); stress_lengths_set = Read8Numbers(p, stress_lengths);
break; break;
case V_STRESSAMP: // stressAmp
case V_STRESSAMP: // stressAmp
stress_amps_set = Read8Numbers(p, stress_amps); stress_amps_set = Read8Numbers(p, stress_amps);
break; break;
case V_STRESSADD: // stressAdd
case V_STRESSADD: // stressAdd
stress_add_set = Read8Numbers(p, stress_add); stress_add_set = Read8Numbers(p, stress_add);
break; break;
case V_INTONATION: // intonation
case V_INTONATION: // intonation
sscanf(p, "%d %d", &option_tone_flags, &option_tone2); sscanf(p, "%d %d", &option_tone_flags, &option_tone2);
if ((option_tone_flags & 0xff) != 0) if ((option_tone_flags & 0xff) != 0)
langopts->intonation_group = option_tone_flags & 0xff; langopts->intonation_group = option_tone_flags & 0xff;
langopts->tunes[ix] = value; langopts->tunes[ix] = value;
} }
break; break;
case V_DICTRULES: // conditional dictionary rules and list entries
case V_DICTRULES: // conditional dictionary rules and list entries
case V_NUMBERS: case V_NUMBERS:
case V_STRESSOPT: case V_STRESSOPT:
// expect a list of numbers // expect a list of numbers
} }
PhonemeReplacement(key, p); PhonemeReplacement(key, p);
break; break;
case V_WORDGAP: // words
case V_WORDGAP: // words
sscanf(p, "%d %d", &langopts->word_gap, &langopts->vowel_pause); sscanf(p, "%d %d", &langopts->word_gap, &langopts->vowel_pause);
break; break;
case V_STRESSRULE: case V_STRESSRULE:
voice->echo_amp = 0; voice->echo_amp = 0;
sscanf(p, "%d %d", &voice->echo_delay, &voice->echo_amp); sscanf(p, "%d %d", &voice->echo_delay, &voice->echo_amp);
break; break;
case V_FLUTTER: // flutter
case V_FLUTTER: // flutter
if (sscanf(p, "%d", &value) == 1) if (sscanf(p, "%d", &value) == 1)
voice->flutter = value * 32; voice->flutter = value * 32;
break; break;
case V_ROUGHNESS: // roughness
case V_ROUGHNESS: // roughness
if (sscanf(p, "%d", &value) == 1) if (sscanf(p, "%d", &value) == 1)
voice->roughness = value; voice->roughness = value;
break; break;
case V_CLARITY: // formantshape
case V_CLARITY: // formantshape
if (sscanf(p, "%d", &value) == 1) { if (sscanf(p, "%d", &value) == 1) {
if (value > 4) { if (value > 4) {
voice->peak_shape = 1; // squarer formant peaks
voice->peak_shape = 1; // squarer formant peaks
value = 4; value = 4;
} }
voice->n_harmonic_peaks = 1+value; voice->n_harmonic_peaks = 1+value;


name2[0] = 0; name2[0] = 0;
sscanf(p, "%s %s %d", name1, name2, &srate); sscanf(p, "%s %s %d", name1, name2, &srate);
if (LoadMbrolaTable(name1, name2, &srate) != EE_OK) {
if (LoadMbrolaTable(name1, name2, &srate) != EE_OK)
fprintf(stderr, "mbrola voice not found\n"); fprintf(stderr, "mbrola voice not found\n");
}
voice->samplerate = srate; voice->samplerate = srate;
} }
break; break;
case V_KLATT: case V_KLATT:
voice->klattv[0] = 1; // default source: IMPULSIVE
voice->klattv[0] = 1; // default source: IMPULSIVE
Read8Numbers(p, voice->klattv); Read8Numbers(p, voice->klattv);
voice->klattv[KLATT_Kopen] -= 40; voice->klattv[KLATT_Kopen] -= 40;
break; break;
new_translator = SelectTranslator(translator_name); new_translator = SelectTranslator(translator_name);
} }


SetSpeed(3); // for speed_percent
SetSpeed(3); // for speed_percent


for (ix = 0; ix < N_PEAKS; ix++) { for (ix = 0; ix < N_PEAKS; ix++) {
voice->freq2[ix] = voice->freq[ix]; voice->freq2[ix] = voice->freq[ix];
LoadDictionary(new_translator, new_dictionary, control & 4); LoadDictionary(new_translator, new_dictionary, control & 4);
if (dictionary_name[0] == 0) { if (dictionary_name[0] == 0) {
DeleteTranslator(new_translator); DeleteTranslator(new_translator);
return NULL; // no dictionary loaded
return NULL; // no dictionary loaded
} }


new_translator->dict_condition = conditional_rules; new_translator->dict_condition = conditional_rules;


langopts = &new_translator->langopts; langopts = &new_translator->langopts;



if ((value = langopts->param[LOPT_LENGTH_MODS]) != 0) if ((value = langopts->param[LOPT_LENGTH_MODS]) != 0)
SetLengthMods(new_translator, value); SetLengthMods(new_translator, value);


if (!tone_only) if (!tone_only)
translator = new_translator; translator = new_translator;



// relative lengths of different stress syllables // relative lengths of different stress syllables
for (ix = 0; ix < stress_lengths_set; ix++) for (ix = 0; ix < stress_lengths_set; ix++)
translator->stress_lengths[ix] = stress_lengths[ix]; translator->stress_lengths[ix] = stress_lengths[ix];


static char *ExtractVoiceVariantName(char *vname, int variant_num, int add_dir) static char *ExtractVoiceVariantName(char *vname, int variant_num, int add_dir)
{ {
// Remove any voice variant suffix (name or number) from a voice name
// Returns the voice variant name
// Remove any voice variant suffix (name or number) from a voice name
// Returns the voice variant name


char *p; char *p;
static char variant_name[40]; static char variant_name[40];
if ((p = strchr(vname, '+')) != NULL) { if ((p = strchr(vname, '+')) != NULL) {
// The voice name has a +variant suffix // The voice name has a +variant suffix
variant_num = 0; variant_num = 0;
*p++ = 0; // delete the suffix from the voice name
*p++ = 0; // delete the suffix from the voice name
if (IsDigit09(*p)) if (IsDigit09(*p))
variant_num = atoi(p); // variant number
variant_num = atoi(p); // variant number
else { else {
// voice variant name, not number // voice variant name, not number
sprintf(variant_name, "%s%s", variant_prefix, p); sprintf(variant_name, "%s%s", variant_prefix, p);


if (variant_num > 0) { if (variant_num > 0) {
if (variant_num < 10) if (variant_num < 10)
sprintf(variant_name, "%sm%d", variant_prefix, variant_num); // male
sprintf(variant_name, "%sm%d", variant_prefix, variant_num); // male
else else
sprintf(variant_name, "%sf%d", variant_prefix, variant_num-10); // female
sprintf(variant_name, "%sf%d", variant_prefix, variant_num-10); // female
} }


return variant_name; return variant_name;


voice_t *LoadVoiceVariant(const char *vname, int variant_num) voice_t *LoadVoiceVariant(const char *vname, int variant_num)
{ {
// Load a voice file.
// Also apply a voice variant if specified by "variant", or by "+number" or "+name" in the "vname"
// Load a voice file.
// Also apply a voice variant if specified by "variant", or by "+number" or "+name" in the "vname"


voice_t *v; voice_t *v;
char *variant_name; char *variant_name;
espeak_VOICE *v1 = *(espeak_VOICE **)p1; espeak_VOICE *v1 = *(espeak_VOICE **)p1;
espeak_VOICE *v2 = *(espeak_VOICE **)p2; espeak_VOICE *v2 = *(espeak_VOICE **)p2;


if ((ix = strcmp(&v1->languages[1], &v2->languages[1])) != 0) // primary language name
if ((ix = strcmp(&v1->languages[1], &v2->languages[1])) != 0) // primary language name
return ix; return ix;
if ((ix = v1->languages[0] - v2->languages[0]) != 0) // priority number
if ((ix = v1->languages[0] - v2->languages[0]) != 0) // priority number
return ix; return ix;
return strcmp(v1->name, v2->name); return strcmp(v1->name, v2->name);
} }
int required_age; int required_age;
int diff; int diff;


p = voice->languages; // list of languages+dialects for which this voice is suitable
p = voice->languages; // list of languages+dialects for which this voice is suitable


if (spec_n_parts < 0) { if (spec_n_parts < 0) {
// match on the subdirectory // match on the subdirectory
break; break;
} }
p += (ix+1); p += (ix+1);
matching_parts += matching; // number of parts which match
matching_parts += matching; // number of parts which match


if (matching_parts == 0) if (matching_parts == 0)
continue; // no matching parts for this language
continue; // no matching parts for this language


x = 5; x = 5;
// reduce the score if not all parts of the required language match // reduce the score if not all parts of the required language match
} }


if ((voice_spec->age <= 12) && (voice->gender == 2) && (voice->age > 12)) if ((voice_spec->age <= 12) && (voice->gender == 2) && (voice->age > 12))
score += 5; // give some preference for non-child female voice if a child is requested
score += 5; // give some preference for non-child female voice if a child is requested


if (voice->age != 0) { if (voice->age != 0) {
if (voice_spec->age == 0) if (voice_spec->age == 0)
ratio = (required_age*100)/voice->age; ratio = (required_age*100)/voice->age;
if (ratio < 100) if (ratio < 100)
ratio = 10000/ratio; ratio = 10000/ratio;
ratio = (ratio - 100)/10; // 0=exact match, 10=out by factor of 2
ratio = (ratio - 100)/10; // 0=exact match, 10=out by factor of 2
x = 5 - ratio; x = 5 - ratio;
if (x > 0) x = 0; if (x > 0) x = 0;


score = score + x; score = score + x;


if (voice_spec->age > 0) if (voice_spec->age > 0)
score += 10; // required age specified, favour voices with a specified age (near it)
score += 10; // required age specified, favour voices with a specified age (near it)
} }
if (score < 1) if (score < 1)
score = 1; score = 1;


static int SetVoiceScores(espeak_VOICE *voice_select, espeak_VOICE **voices, int control) static int SetVoiceScores(espeak_VOICE *voice_select, espeak_VOICE **voices, int control)
{ {
// control: bit0=1 include mbrola voices
// control: bit0=1 include mbrola voices
int ix; int ix;
int score; int score;
int nv; // number of candidates
int nv; // number of candidates
int n_parts = 0; int n_parts = 0;
int lang_len = 0; int lang_len = 0;
espeak_VOICE *vp; espeak_VOICE *vp;


if ((n_parts == 1) && (control & 1)) { if ((n_parts == 1) && (control & 1)) {
if (strcmp(language, "mbrola") == 0) { if (strcmp(language, "mbrola") == 0) {
language[2] = 0; // truncate to "mb"
language[2] = 0; // truncate to "mb"
lang_len = 2; lang_len = 2;
} }


vp->score = score; vp->score = score;
} }
} }
voices[nv] = NULL; // list terminator
voices[nv] = NULL; // list terminator


if (nv == 0) if (nv == 0)
return 0; return 0;
int match_fname = -1; int match_fname = -1;
int match_fname2 = -1; int match_fname2 = -1;
int match_name = -1; int match_name = -1;
const char *id; // this is the filename within espeak-data/voices
const char *id; // this is the filename within espeak-data/voices
char *variant_name; char *variant_name;
int last_part_len; int last_part_len;
char last_part[41]; char last_part[41];


if (voices == NULL) { if (voices == NULL) {
if (n_voices_list == 0) if (n_voices_list == 0)
espeak_ListVoices(NULL); // create the voices list
espeak_ListVoices(NULL); // create the voices list
voices = voices_list; voices = voices_list;
} }




for (ix = 0; voices[ix] != NULL; ix++) { for (ix = 0; voices[ix] != NULL; ix++) {
if (strcmp(name, voices[ix]->name) == 0) { if (strcmp(name, voices[ix]->name) == 0) {
match_name = ix; // found matching voice name
match_name = ix; // found matching voice name
break; break;
} else { } else {
id = voices[ix]->identifier; id = voices[ix]->identifier;
if (strcmp(name, id) == 0) if (strcmp(name, id) == 0)
match_fname = ix; // matching identifier, use this if no matching name
match_fname = ix; // matching identifier, use this if no matching name
else if (strcmp(last_part, &id[strlen(id)-last_part_len]) == 0) else if (strcmp(last_part, &id[strlen(id)-last_part_len]) == 0)
match_fname2 = ix; match_fname2 = ix;
} }
} }


if (match_name < 0) { if (match_name < 0) {
match_name = match_fname; // no matching name, try matching filename
match_name = match_fname; // no matching name, try matching filename
if (match_name < 0) if (match_name < 0)
match_name = match_fname2; // try matching just the last part of the filename
match_name = match_fname2; // try matching just the last part of the filename
} }


if (match_name < 0) if (match_name < 0)


char const *SelectVoice(espeak_VOICE *voice_select, int *found) char const *SelectVoice(espeak_VOICE *voice_select, int *found)
{ {
// Returns a path within espeak-voices, with a possible +variant suffix
// variant is an output-only parameter
int nv; // number of candidates
// Returns a path within espeak-voices, with a possible +variant suffix
// variant is an output-only parameter

int nv; // number of candidates
int ix, ix2; int ix, ix2;
int j; int j;
int n_variants; int n_variants;
memcpy(&voice_select2, voice_select, sizeof(voice_select2)); memcpy(&voice_select2, voice_select, sizeof(voice_select2));


if (n_voices_list == 0) if (n_voices_list == 0)
espeak_ListVoices(NULL); // create the voices list
espeak_ListVoices(NULL); // create the voices list


if ((voice_select2.languages == NULL) || (voice_select2.languages[0] == 0)) { if ((voice_select2.languages == NULL) || (voice_select2.languages[0] == 0)) {
// no language is specified. Get language from the named voice // no language is specified. Get language from the named voice
else if (voice_select2.gender == 1) else if (voice_select2.gender == 1)
gender = 1; gender = 1;


#define AGE_OLD 60
#define AGE_OLD 60
if (voice_select2.age < AGE_OLD) if (voice_select2.age < AGE_OLD)
aged = 0; aged = 0;


p = p_start = variant_lists[gender]; p = p_start = variant_lists[gender];
if (aged == 0) if (aged == 0)
p++; // the first voice in the variants list is older
p++; // the first voice in the variants list is older


// add variants for the top voices // add variants for the top voices
n_variants = 0; n_variants = 0;
continue; continue;
} }


vp2 = &voice_variants[n_variants++]; // allocate space for voice variant
memcpy(vp2, vp, sizeof(espeak_VOICE)); // copy from the original voice
vp2 = &voice_variants[n_variants++]; // allocate space for voice variant
memcpy(vp2, vp, sizeof(espeak_VOICE)); // copy from the original voice
vp2->variant = variant_number; vp2->variant = variant_number;
voices2[ix2++] = vp2; voices2[ix2++] = vp2;
p++; p++;
} }
// add any more variants to the end of the list // add any more variants to the end of the list
while ((vp != NULL) && ((variant_number = *p++) != 0) && (n_variants < N_VOICE_VARIANTS)) { while ((vp != NULL) && ((variant_number = *p++) != 0) && (n_variants < N_VOICE_VARIANTS)) {
vp2 = &voice_variants[n_variants++]; // allocate space for voice variant
memcpy(vp2, vp, sizeof(espeak_VOICE)); // copy from the original voice
vp2 = &voice_variants[n_variants++]; // allocate space for voice variant
memcpy(vp2, vp, sizeof(espeak_VOICE)); // copy from the original voice
vp2->variant = variant_number; vp2->variant = variant_number;
voices2[ix2++] = vp2; voices2[ix2++] = vp2;
} }
regs.r[6] = 0; regs.r[6] = 0;


while (regs.r[3] > 0) { while (regs.r[3] > 0) {
error = _kernel_swi(0x0c+0x20000, &regs, &regs); /* OS_GBPB 10, read directory entries */
error = _kernel_swi(0x0c+0x20000, &regs, &regs); // OS_GBPB 10, read directory entries
if ((error != NULL) || (regs.r[3] == 0)) if ((error != NULL) || (regs.r[3] == 0))
break; break;
type = (int *)(&buf[16]); type = (int *)(&buf[16]);
WIN32_FIND_DATAA FindFileData; WIN32_FIND_DATAA FindFileData;
HANDLE hFind = INVALID_HANDLE_VALUE; HANDLE hFind = INVALID_HANDLE_VALUE;


#undef UNICODE // we need FindFirstFileA() which takes an 8-bit c-string
#undef UNICODE // we need FindFirstFileA() which takes an 8-bit c-string
sprintf(fname, "%s\\*", path); sprintf(fname, "%s\\*", path);
hFind = FindFirstFileA(fname, &FindFileData); hFind = FindFirstFileA(fname, &FindFileData);
if (hFind == INVALID_HANDLE_VALUE) if (hFind == INVALID_HANDLE_VALUE)


do { do {
if (n_voices_list >= (N_VOICES_LIST-2)) if (n_voices_list >= (N_VOICES_LIST-2))
break; // voices list is full
break; // voices list is full


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);
DIR *dir; DIR *dir;
struct dirent *ent; struct dirent *ent;


if ((dir = opendir((char *)path)) == NULL) // note: (char *) is needed for WINCE
if ((dir = opendir((char *)path)) == NULL) // note: (char *) is needed for WINCE
return; return;


while ((ent = readdir(dir)) != NULL) { while ((ent = readdir(dir)) != NULL) {
if (n_voices_list >= (N_VOICES_LIST-2)) if (n_voices_list >= (N_VOICES_LIST-2))
break; // voices list is full
break; // voices list is full


if (ent->d_name[0] == '.') if (ent->d_name[0] == '.')
continue; continue;
} }


memset(&voice_selector, 0, sizeof(voice_selector)); memset(&voice_selector, 0, sizeof(voice_selector));
voice_selector.name = (char *)name; // include variant name in voice stack ??
voice_selector.name = (char *)name; // include variant name in voice stack ??


// first check for a voice with this filename // first check for a voice with this filename
// This may avoid the need to call espeak_ListVoices(). // This may avoid the need to call espeak_ListVoices().
} }


if (n_voices_list == 0) if (n_voices_list == 0)
espeak_ListVoices(NULL); // create the voices list
espeak_ListVoices(NULL); // create the voices list


if ((v = SelectVoiceByName(voices_list, buf)) != NULL) { if ((v = SelectVoiceByName(voices_list, buf)) != NULL) {
if (LoadVoice(v->identifier, 0) != NULL) { if (LoadVoice(v->identifier, 0) != NULL) {
return EE_OK; return EE_OK;
} }
} }
return EE_INTERNAL_ERROR; // voice name not found
return EE_INTERNAL_ERROR; // voice name not found
} }


espeak_ERROR SetVoiceByProperties(espeak_VOICE *voice_selector) espeak_ERROR SetVoiceByProperties(espeak_VOICE *voice_selector)
sprintf(path_voices, "%s%cvoices", path_home, PATHSEP); sprintf(path_voices, "%s%cvoices", path_home, PATHSEP);
len_path_voices = strlen(path_voices)+1; len_path_voices = strlen(path_voices)+1;
GetVoices(path_voices); GetVoices(path_voices);
voices_list[n_voices_list] = NULL; // voices list terminator
voices_list[n_voices_list] = NULL; // voices list terminator
} }
return (const espeak_VOICE **)voices_list; return (const espeak_VOICE **)voices_list;
#else #else
len_path_voices = strlen(path_voices)+1; len_path_voices = strlen(path_voices)+1;


GetVoices(path_voices); GetVoices(path_voices);
voices_list[n_voices_list] = NULL; // voices list terminator
voices_list[n_voices_list] = NULL; // voices list terminator
voices = (espeak_VOICE **)realloc(voices, sizeof(espeak_VOICE *)*(n_voices_list+1)); voices = (espeak_VOICE **)realloc(voices, sizeof(espeak_VOICE *)*(n_voices_list+1));


// sort the voices list // sort the voices list

+ 92
- 66
src/libespeak-ng/wave.c View File

long tv_sec; long tv_sec;
long tv_nsec; long tv_nsec;
}; };
#endif /* HAVE_STRUCT_TIMESPEC */
#endif


enum { ONE_BILLION = 1000000000 }; enum { ONE_BILLION = 1000000000 };


int wave_pulse_get_remaining_time(uint32_t sample, uint32_t *time); int wave_pulse_get_remaining_time(uint32_t sample, uint32_t *time);


// wrappers // wrappers
int wave_init(int srate) {
int wave_init(int srate)
{
pulse_running = is_pulse_running(); pulse_running = is_pulse_running();


if (pulse_running) if (pulse_running)
return wave_port_init(srate); return wave_port_init(srate);
} }


void *wave_open(const char *the_api) {
void *wave_open(const char *the_api)
{
if (pulse_running) if (pulse_running)
return wave_pulse_open(the_api); return wave_pulse_open(the_api);
else else
return wave_port_open(the_api); return wave_port_open(the_api);
} }


size_t wave_write(void *theHandler, char *theMono16BitsWaveBuffer, size_t theSize) {
size_t wave_write(void *theHandler, char *theMono16BitsWaveBuffer, size_t theSize)
{
if (pulse_running) if (pulse_running)
return wave_pulse_write(theHandler, theMono16BitsWaveBuffer, theSize); return wave_pulse_write(theHandler, theMono16BitsWaveBuffer, theSize);
else else
return wave_port_write(theHandler, theMono16BitsWaveBuffer, theSize); return wave_port_write(theHandler, theMono16BitsWaveBuffer, theSize);
} }


int wave_close(void *theHandler) {
int wave_close(void *theHandler)
{
if (pulse_running) if (pulse_running)
return wave_pulse_close(theHandler); return wave_pulse_close(theHandler);
else else
return wave_port_close(theHandler); return wave_port_close(theHandler);
} }


int wave_is_busy(void *theHandler) {
int wave_is_busy(void *theHandler)
{
if (pulse_running) if (pulse_running)
return wave_pulse_is_busy(theHandler); return wave_pulse_is_busy(theHandler);
else else
return wave_port_is_busy(theHandler); return wave_port_is_busy(theHandler);
} }


void wave_terminate() {
void wave_terminate()
{
if (pulse_running) if (pulse_running)
wave_pulse_terminate(); wave_pulse_terminate();
else else
wave_port_terminate(); wave_port_terminate();
} }


uint32_t wave_get_read_position(void *theHandler) {
uint32_t wave_get_read_position(void *theHandler)
{
if (pulse_running) if (pulse_running)
return wave_pulse_get_read_position(theHandler); return wave_pulse_get_read_position(theHandler);
else else
return wave_port_get_read_position(theHandler); return wave_port_get_read_position(theHandler);
} }


uint32_t wave_get_write_position(void *theHandler) {
uint32_t wave_get_write_position(void *theHandler)
{
if (pulse_running) if (pulse_running)
return wave_pulse_get_write_position(theHandler); return wave_pulse_get_write_position(theHandler);
else else
return wave_port_get_write_position(theHandler); return wave_port_get_write_position(theHandler);
} }


void wave_flush(void *theHandler) {
void wave_flush(void *theHandler)
{
if (pulse_running) if (pulse_running)
wave_pulse_flush(theHandler); wave_pulse_flush(theHandler);
else else
wave_port_flush(theHandler); wave_port_flush(theHandler);
} }


void wave_set_callback_is_output_enabled(t_wave_callback *cb) {
void wave_set_callback_is_output_enabled(t_wave_callback *cb)
{
if (pulse_running) if (pulse_running)
wave_pulse_set_callback_is_output_enabled(cb); wave_pulse_set_callback_is_output_enabled(cb);
else else
wave_port_set_callback_is_output_enabled(cb); wave_port_set_callback_is_output_enabled(cb);
} }


void *wave_test_get_write_buffer() {
void *wave_test_get_write_buffer()
{
if (pulse_running) if (pulse_running)
return wave_pulse_test_get_write_buffer(); return wave_pulse_test_get_write_buffer();
else else
#define wave_test_get_write_buffer wave_port_test_get_write_buffer #define wave_test_get_write_buffer wave_port_test_get_write_buffer
#define wave_get_remaining_time wave_port_get_remaining_time #define wave_get_remaining_time wave_port_get_remaining_time


#endif // USE_PULSEAUDIO
#endif


static t_wave_callback *my_callback_is_output_enabled = NULL; static t_wave_callback *my_callback_is_output_enabled = NULL;


mInCallbackFinishedState = true; mInCallbackFinishedState = true;
size_t aUsedMem = 0; size_t aUsedMem = 0;
aUsedMem = (size_t)(aWrite - myRead); aUsedMem = (size_t)(aWrite - myRead);
if (aUsedMem) {
if (aUsedMem)
memcpy(outputBuffer, myRead, aUsedMem); memcpy(outputBuffer, myRead, aUsedMem);
}
char *p = (char *)outputBuffer + aUsedMem; char *p = (char *)outputBuffer + aUsedMem;
memset(p, 0, n - aUsedMem); memset(p, 0, n - aUsedMem);
myRead = aWrite; myRead = aWrite;
PaDeviceID playbackDevice = Pa_GetDefaultOutputDeviceID(); PaDeviceID playbackDevice = Pa_GetDefaultOutputDeviceID();


PaError err = Pa_OpenStream(&pa_stream, PaError err = Pa_OpenStream(&pa_stream,
/* capture parameters */
// capture parameters
paNoDevice, paNoDevice,
0, 0,
paInt16, paInt16,
NULL, NULL,
/* playback parameters */
// playback parameters
playbackDevice, playbackDevice,
out_channels, out_channels,
paInt16, paInt16,
NULL, NULL,
/* general parameters */
// general parameters
wave_samplerate, FRAMES_PER_BUFFER, 0, wave_samplerate, FRAMES_PER_BUFFER, 0,
paNoFlag, paNoFlag,
pa_callback, (void *)userdata); pa_callback, (void *)userdata);
// failed to open with mono, try stereo // failed to open with mono, try stereo
out_channels = 2; out_channels = 2;
PaError err = Pa_OpenStream(&pa_stream, PaError err = Pa_OpenStream(&pa_stream,
/* capture parameters */
// capture parameters
paNoDevice, paNoDevice,
0, 0,
paInt16, paInt16,
NULL, NULL,
/* playback parameters */
// playback parameters
playbackDevice, playbackDevice,
out_channels, out_channels,
paInt16, paInt16,
NULL, NULL,
/* general parameters */
// general parameters
wave_samplerate, FRAMES_PER_BUFFER, 0, wave_samplerate, FRAMES_PER_BUFFER, 0,
paNoFlag, paNoFlag,
pa_callback, (void *)userdata); pa_callback, (void *)userdata);
#else #else
myOutputParameters.channelCount = out_channels; myOutputParameters.channelCount = out_channels;
unsigned long framesPerBuffer = paFramesPerBufferUnspecified; unsigned long framesPerBuffer = paFramesPerBufferUnspecified;
err = Pa_OpenStream(
&pa_stream,
NULL, /* no input */
&myOutputParameters,
wave_samplerate,
framesPerBuffer,
paNoFlag,
pa_callback,
(void *)userdata);
err = Pa_OpenStream(&pa_stream,
NULL, // no input
&myOutputParameters,
wave_samplerate,
framesPerBuffer,
paNoFlag,
pa_callback,
(void *)userdata);
if ((err != paNoError) if ((err != paNoError)
&& (err != paInvalidChannelCount)) { // err==paUnanticipatedHostError
&& (err != paInvalidChannelCount)) {
fprintf(stderr, "wave_open_sound > Pa_OpenStream : err=%d (%s)\n", err, Pa_GetErrorText(err)); fprintf(stderr, "wave_open_sound > Pa_OpenStream : err=%d (%s)\n", err, Pa_GetErrorText(err));
framesPerBuffer = FRAMES_PER_BUFFER; framesPerBuffer = FRAMES_PER_BUFFER;
err = Pa_OpenStream(
&pa_stream,
NULL, /* no input */
&myOutputParameters,
wave_samplerate,
framesPerBuffer,
paNoFlag,
// paClipOff | paDitherOff,
pa_callback,
(void *)userdata);
err = Pa_OpenStream(&pa_stream,
NULL, // no input
&myOutputParameters,
wave_samplerate,
framesPerBuffer,
paNoFlag,
pa_callback,
(void *)userdata);
} }
if (err == paInvalidChannelCount) { if (err == paInvalidChannelCount) {
SHOW_TIME("wave_open_sound > try stereo"); SHOW_TIME("wave_open_sound > try stereo");
// failed to open with mono, try stereo // failed to open with mono, try stereo
out_channels = 2; out_channels = 2;
myOutputParameters.channelCount = out_channels; myOutputParameters.channelCount = out_channels;
err = Pa_OpenStream(
&pa_stream,
NULL, /* no input */
&myOutputParameters,
wave_samplerate,
framesPerBuffer,
paNoFlag,
// paClipOff | paDitherOff,
pa_callback,
(void *)userdata);
err = Pa_OpenStream(&pa_stream,
NULL, // no input
&myOutputParameters,
wave_samplerate,
framesPerBuffer,
paNoFlag,
pa_callback,
(void *)userdata);
} }
mInCallbackFinishedState = false; mInCallbackFinishedState = false;
#endif #endif
myWrite = myBuffer; myWrite = myBuffer;
myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer+aFreeMem/2, theSize - aFreeMem/2); myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer+aFreeMem/2, theSize - aFreeMem/2);
} else { // 1 channel (mono) } else { // 1 channel (mono)
// copy with wrap around at the end of ringbuffer
// copy with wrap around at the end of ringbuffer
copyBuffer(myWrite, theMono16BitsWaveBuffer, aFreeMem); copyBuffer(myWrite, theMono16BitsWaveBuffer, aFreeMem);
myWrite = myBuffer; myWrite = myBuffer;
myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer+aFreeMem, theSize - aFreeMem); myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer+aFreeMem, theSize - aFreeMem);
// DMM: This doesn't seem to be true; it seems to be necessary to // DMM: This doesn't seem to be true; it seems to be necessary to
// call StopStream if the callback brought us here, and AbortStream // call StopStream if the callback brought us here, and AbortStream
// if the user brought us here. // if the user brought us here.
//


#if (USE_PORTAUDIO == 19) #if (USE_PORTAUDIO == 19)
if (pa_stream) { if (pa_stream) {


#else #else


int wave_init(int srate) {
int wave_init(int srate)
{
return 1; return 1;
} }
void *wave_open(const char *the_api) {

void *wave_open(const char *the_api)
{
return (void *)1; return (void *)1;
} }
size_t wave_write(void *theHandler, char *theMono16BitsWaveBuffer, size_t theSize) {

size_t wave_write(void *theHandler, char *theMono16BitsWaveBuffer, size_t theSize)
{
return theSize; return theSize;
} }
int wave_close(void *theHandler) {

int wave_close(void *theHandler)
{
return 0; return 0;
} }
int wave_is_busy(void *theHandler) {

int wave_is_busy(void *theHandler)
{
return 0; return 0;
} }
void wave_terminate() {

void wave_terminate()
{
} }
uint32_t wave_get_read_position(void *theHandler) {

uint32_t wave_get_read_position(void *theHandler)
{
return 0; return 0;
} }
uint32_t wave_get_write_position(void *theHandler) {

uint32_t wave_get_write_position(void *theHandler)
{
return 0; return 0;
} }
void wave_flush(void *theHandler) {

void wave_flush(void *theHandler)
{
} }

typedef int (t_wave_callback)(void); typedef int (t_wave_callback)(void);
void wave_set_callback_is_output_enabled(t_wave_callback *cb) {

void wave_set_callback_is_output_enabled(t_wave_callback *cb)
{
} }
extern void *wave_test_get_write_buffer() {

extern void *wave_test_get_write_buffer()
{
return NULL; return NULL;
} }


return 0; return 0;
} }


#endif // of USE_PORTAUDIO
#endif


void clock_gettime2(struct timespec *ts) void clock_gettime2(struct timespec *ts)
{ {
ts->tv_nsec = (long int)t_ns; ts->tv_nsec = (long int)t_ns;
} }


#endif // USE_ASYNC
#endif

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

typedef int (t_wave_callback)(void); typedef int (t_wave_callback)(void);
extern void wave_set_callback_is_output_enabled(t_wave_callback *cb); extern void wave_set_callback_is_output_enabled(t_wave_callback *cb);



// general functions // general functions
extern void clock_gettime2(struct timespec *ts); extern void clock_gettime2(struct timespec *ts);
extern void add_time_in_ms(struct timespec *ts, int time_in_ms); extern void add_time_in_ms(struct timespec *ts, int time_in_ms);

+ 67
- 33
src/libespeak-ng/wave_pulse.c View File

} else } else
return 0; return 0;
} }
#endif // USE_PORTAUDIO

#endif


static pthread_mutex_t pulse_mutex; static pthread_mutex_t pulse_mutex;


if (!connected) { SHOW("CHECK_CONNECTED_NO_RETVAL: !pulse_connected\n", ""); return; } \ if (!connected) { SHOW("CHECK_CONNECTED_NO_RETVAL: !pulse_connected\n", ""); return; } \
} while (0); } while (0);


static void subscribe_cb(struct pa_context *c, enum pa_subscription_event_type t, uint32_t index, void *userdata) {
static void subscribe_cb(struct pa_context *c, enum pa_subscription_event_type t, uint32_t index, void *userdata)
{
ENTER(__FUNCTION__); ENTER(__FUNCTION__);


assert(c); assert(c);
return; return;
} }


static void context_state_cb(pa_context *c, void *userdata) {
static void context_state_cb(pa_context *c, void *userdata)
{
ENTER(__FUNCTION__); ENTER(__FUNCTION__);
assert(c); assert(c);


} }
} }


static void stream_state_cb(pa_stream *s, void *userdata) {
static void stream_state_cb(pa_stream *s, void *userdata)
{
ENTER(__FUNCTION__); ENTER(__FUNCTION__);
assert(s); assert(s);


} }
} }


static void stream_success_cb(pa_stream *s, int success, void *userdata) {
static void stream_success_cb(pa_stream *s, int success, void *userdata)
{
ENTER(__FUNCTION__); ENTER(__FUNCTION__);
assert(s); assert(s);


pa_threaded_mainloop_signal(mainloop, 0); pa_threaded_mainloop_signal(mainloop, 0);
} }


static void context_success_cb(pa_context *c, int success, void *userdata) {
static void context_success_cb(pa_context *c, int success, void *userdata)
{
ENTER(__FUNCTION__); ENTER(__FUNCTION__);
assert(c); assert(c);


pa_threaded_mainloop_signal(mainloop, 0); pa_threaded_mainloop_signal(mainloop, 0);
} }


static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
static void stream_request_cb(pa_stream *s, size_t length, void *userdata)
{
ENTER(__FUNCTION__); ENTER(__FUNCTION__);
assert(s); assert(s);


pa_threaded_mainloop_signal(mainloop, 0); pa_threaded_mainloop_signal(mainloop, 0);
} }


static void stream_latency_update_cb(pa_stream *s, void *userdata) {
static void stream_latency_update_cb(pa_stream *s, void *userdata)
{
assert(s); assert(s);


pa_threaded_mainloop_signal(mainloop, 0); pa_threaded_mainloop_signal(mainloop, 0);
} }


static int pulse_free(void) {
static int pulse_free(void)
{
ENTER(__FUNCTION__); ENTER(__FUNCTION__);
size_t l = 0; size_t l = 0;
pa_operation *o = NULL; pa_operation *o = NULL;
return (int)l; return (int)l;
} }


static int pulse_playing(const pa_timing_info *the_timing_info) {
static int pulse_playing(const pa_timing_info *the_timing_info)
{
ENTER(__FUNCTION__); ENTER(__FUNCTION__);
int r = 0; int r = 0;
const pa_timing_info *i; const pa_timing_info *i;
return r; return r;
} }


static void pulse_write(void *ptr, int length) {
static void pulse_write(void *ptr, int length)
{
ENTER(__FUNCTION__); ENTER(__FUNCTION__);


SHOW("pulse_write > length=%d\n", length); SHOW("pulse_write > length=%d\n", length);
pa_threaded_mainloop_unlock(mainloop); pa_threaded_mainloop_unlock(mainloop);
} }


static int drain(void) {
static int drain(void)
{
pa_operation *o = NULL; pa_operation *o = NULL;
int success = 0; int success = 0;
int ret = PULSE_ERROR; int ret = PULSE_ERROR;
return ret; return ret;
} }


static void pulse_close(void) {
static void pulse_close(void)
{
ENTER(__FUNCTION__); ENTER(__FUNCTION__);


drain(); drain();
goto unlock_and_fail; goto unlock_and_fail;
} }


/* Wait until the context is ready */
// Wait until the context is ready
SHOW_TIME("pa_threaded_mainloop_wait"); SHOW_TIME("pa_threaded_mainloop_wait");
pa_threaded_mainloop_wait(mainloop); pa_threaded_mainloop_wait(mainloop);


goto unlock_and_fail; goto unlock_and_fail;
} }


/* Wait until the stream is ready */
// Wait until the stream is ready
SHOW_TIME("pa_threaded_mainloop_wait"); SHOW_TIME("pa_threaded_mainloop_wait");
pa_threaded_mainloop_wait(mainloop); pa_threaded_mainloop_wait(mainloop);


goto unlock_and_fail; goto unlock_and_fail;
} }


/* Now subscribe to events */
// Now subscribe to events
SHOW_TIME("pa_context_subscribe"); SHOW_TIME("pa_context_subscribe");
if (!(o = pa_context_subscribe(context, PA_SUBSCRIPTION_MASK_SINK_INPUT, context_success_cb, &success))) { if (!(o = pa_context_subscribe(context, PA_SUBSCRIPTION_MASK_SINK_INPUT, context_success_cb, &success))) {
SHOW("pa_context_subscribe() failed: %s", pa_strerror(pa_context_errno(context))); SHOW("pa_context_subscribe() failed: %s", pa_strerror(pa_context_errno(context)));
// TBD: take in account time suplied by portaudio V18 API // TBD: take in account time suplied by portaudio V18 API
a_time = sample - a_timing_info.read_index; a_time = sample - a_timing_info.read_index;
a_time = 0.5 + (a_time * 1000.0) / wave_samplerate; a_time = 0.5 + (a_time * 1000.0) / wave_samplerate;
} else {
} else
a_time = 0; a_time = 0;
}


SHOW("wave_get_remaining_time > sample=%d, time=%d\n", sample, (uint32_t)a_time); SHOW("wave_get_remaining_time > sample=%d, time=%d\n", sample, (uint32_t)a_time);




#else #else


int wave_init(return 1; ) {
int wave_init(int srate)
{
return 1;
} }
void *wave_open(const char *the_api) {

void *wave_open(const char *the_api)
{
return (void *)1; return (void *)1;
} }
size_t wave_write(void *theHandler, char *theMono16BitsWaveBuffer, size_t theSize) {

size_t wave_write(void *theHandler, char *theMono16BitsWaveBuffer, size_t theSize)
{
return theSize; return theSize;
} }
int wave_close(void *theHandler) {

int wave_close(void *theHandler)
{
return 0; return 0;
} }
int wave_is_busy(void *theHandler) {

int wave_is_busy(void *theHandler)
{
return 0; return 0;
} }
void wave_terminate() {

void wave_terminate()
{
} }
uint32_t wave_get_read_position(void *theHandler) {

uint32_t wave_get_read_position(void *theHandler)
{
return 0; return 0;
} }
uint32_t wave_get_write_position(void *theHandler) {

uint32_t wave_get_write_position(void *theHandler)
{
return 0; return 0;
} }
void wave_flush(void *theHandler) {

void wave_flush(void *theHandler)
{
} }

typedef int (t_wave_callback)(void); typedef int (t_wave_callback)(void);
void wave_set_callback_is_output_enabled(t_wave_callback *cb) {

void wave_set_callback_is_output_enabled(t_wave_callback *cb)
{
} }
extern void *wave_test_get_write_buffer() {

extern void *wave_test_get_write_buffer()
{
return NULL; return NULL;
} }


return 0; return 0;
} }


#endif // of USE_PULSEAUDIO
#endif


#ifndef USE_PORTAUDIO #ifndef USE_PORTAUDIO


} }
ts->tv_nsec = (long int)t_ns; ts->tv_nsec = (long int)t_ns;
} }
#endif // ifndef USE_PORTAUDIO


#endif // USE_ASYNC
#endif
#endif

+ 39
- 16
src/libespeak-ng/wave_sada.c View File

// sun_audio_fd: modified to hold the file descriptor of the opened // sun_audio_fd: modified to hold the file descriptor of the opened
// audio device. // audio device.
// //
int wave_init(int srate) {
int wave_init(int srate)
{
ENTER("wave_init"); ENTER("wave_init");


audio_info_t ainfo; audio_info_t ainfo;
// //
actual_index = sample - total_samples_skipped; actual_index = sample - total_samples_skipped;
if ((sample < total_samples_skipped) || if ((sample < total_samples_skipped) ||
(actual_index <= ainfo.play.samples)) {
(actual_index <= ainfo.play.samples))
*time = 0; *time = 0;
} else {
else {
a_time = ((actual_index - ainfo.play.samples) * 1000) / wave_samplerate; a_time = ((actual_index - ainfo.play.samples) * 1000) / wave_samplerate;
*time = (uint32_t)a_time; *time = (uint32_t)a_time;
} }


#else #else


init wave_init() {
int wave_init(int srate)
{
return 1; return 1;
} }
void *wave_open(const char *the_api) {

void *wave_open(const char *the_api)
{
return (void *)1; return (void *)1;
} }
size_t wave_write(void *theHandler, char *theMono16BitsWaveBuffer, size_t theSize) {

size_t wave_write(void *theHandler, char *theMono16BitsWaveBuffer, size_t theSize)
{
return theSize; return theSize;
} }
int wave_close(void *theHandler) {

int wave_close(void *theHandler)
{
return 0; return 0;
} }
int wave_is_busy(void *theHandler) {

int wave_is_busy(void *theHandler)
{
return 0; return 0;
} }
void wave_terminate() {

void wave_terminate()
{
} }
uint32_t wave_get_read_position(void *theHandler) {

uint32_t wave_get_read_position(void *theHandler)
{
return 0; return 0;
} }
uint32_t wave_get_write_position(void *theHandler) {

uint32_t wave_get_write_position(void *theHandler)
{
return 0; return 0;
} }
void wave_flush(void *theHandler) {

void wave_flush(void *theHandler)
{
} }

typedef int (t_wave_callback)(void); typedef int (t_wave_callback)(void);
void wave_set_callback_is_output_enabled(t_wave_callback *cb) {

void wave_set_callback_is_output_enabled(t_wave_callback *cb)
{
} }
extern void *wave_test_get_write_buffer() {

extern void *wave_test_get_write_buffer()
{
return NULL; return NULL;
} }


return 0; return 0;
} }


#endif // of USE_PORTAUDIO
#endif


void clock_gettime2(struct timespec *ts) void clock_gettime2(struct timespec *ts)
{ {
ts->tv_nsec = (long int)t_ns; ts->tv_nsec = (long int)t_ns;
} }


#endif // USE_ASYNC
#endif

+ 126
- 127
src/libespeak-ng/wavegen.c View File



FILE *f_log = NULL; FILE *f_log = NULL;
int option_waveout = 0; int option_waveout = 0;
static int option_harmonic1 = 10; // 10
static int option_harmonic1 = 10;
static int flutter_amp = 64; static int flutter_amp = 64;


static int general_amplitude = 60; static int general_amplitude = 60;
static int consonant_amp = 26; // 24
static int consonant_amp = 26;


int embedded_value[N_EMBEDDED_VALUES]; int embedded_value[N_EMBEDDED_VALUES];


static int PHASE_INC_FACTOR; static int PHASE_INC_FACTOR;
int samplerate = 0; // this is set by Wavegeninit()
int samplerate = 0; // this is set by Wavegeninit()
int samplerate_native = 0; int samplerate_native = 0;
extern int option_device_number; extern int option_device_number;
extern int option_quiet; extern int option_quiet;
int echo_tail; int echo_tail;
int echo_amp = 0; int echo_amp = 0;
short echo_buf[N_ECHO_BUF]; short echo_buf[N_ECHO_BUF];
static int echo_length = 0; // period (in sample\) to ensure completion of echo at the end of speech, set in WavegenSetEcho()
static int echo_length = 0; // period (in sample\) to ensure completion of echo at the end of speech, set in WavegenSetEcho()


static int voicing; static int voicing;
static RESONATOR rbreath[N_PEAKS]; static RESONATOR rbreath[N_PEAKS];
static int harm_sqrt_n = 0; static int harm_sqrt_n = 0;


#define N_LOWHARM 30 #define N_LOWHARM 30
static int harm_inc[N_LOWHARM]; // only for these harmonics do we interpolate amplitude between steps
static int harm_inc[N_LOWHARM]; // only for these harmonics do we interpolate amplitude between steps
static int *harmspect; static int *harmspect;
static int hswitch = 0; static int hswitch = 0;
static int hspect[2][MAX_HARMONIC]; // 2 copies, we interpolate between then
static int hspect[2][MAX_HARMONIC]; // 2 copies, we interpolate between then
static int max_hval = 0; static int max_hval = 0;


static int nsamples = 0; // number to do
static int nsamples = 0; // number to do
static int modulation_type = 0; static int modulation_type = 0;
static int glottal_flag = 0; static int glottal_flag = 0;
static int glottal_reduce = 0; static int glottal_reduce = 0;
static int amp_inc; static int amp_inc;
static unsigned char *amplitude_env = NULL; static unsigned char *amplitude_env = NULL;


static int samplecount = 0; // number done
static int samplecount_start = 0; // count at start of this segment
static int end_wave = 0; // continue to end of wave cycle
static int samplecount = 0; // number done
static int samplecount_start = 0; // count at start of this segment
static int end_wave = 0; // continue to end of wave cycle
static int wavephase; static int wavephase;
static int phaseinc; static int phaseinc;
static int cycle_samples; // number of samples in a cycle at current pitch
static int cycle_samples; // number of samples in a cycle at current pitch
static int cbytes; static int cbytes;
static int hf_factor; static int hf_factor;


int wcmdq_tail = 0; int wcmdq_tail = 0;


// pitch,speed, // pitch,speed,
int embedded_default[N_EMBEDDED_VALUES] = { 0, 50, 175, 100, 50, 0, 0, 0, 175, 0, 0, 0, 0, 0, 0 };
static int embedded_max[N_EMBEDDED_VALUES] = { 0, 0x7fff, 750, 300, 99, 99, 99, 0, 750, 0, 0, 0, 0, 4, 0 };
int embedded_default[N_EMBEDDED_VALUES] = { 0, 50, 175, 100, 50, 0, 0, 0, 175, 0, 0, 0, 0, 0, 0 };
static int embedded_max[N_EMBEDDED_VALUES] = { 0, 0x7fff, 750, 300, 99, 99, 99, 0, 750, 0, 0, 0, 0, 4, 0 };


#define N_CALLBACK_IX N_WAV_BUF-2 // adjust this delay to match display with the currently spoken word
#define N_CALLBACK_IX N_WAV_BUF-2 // adjust this delay to match display with the currently spoken word
int current_source_index = 0; int current_source_index = 0;


extern FILE *f_wave; extern FILE *f_wave;
// the presets are for 22050 Hz sample rate. // the presets are for 22050 Hz sample rate.
// A different rate will need to recalculate the presets in WavegenInit() // A different rate will need to recalculate the presets in WavegenInit()
static unsigned char wavemult[N_WAVEMULT] = { static unsigned char wavemult[N_WAVEMULT] = {
0, 0, 0, 2, 3, 5, 8, 11, 14, 18, 22, 27, 32, 37, 43, 49,
55, 62, 69, 76, 83, 90, 98, 105, 113, 121, 128, 136, 144, 152, 159, 166,
0, 0, 0, 2, 3, 5, 8, 11, 14, 18, 22, 27, 32, 37, 43, 49,
55, 62, 69, 76, 83, 90, 98, 105, 113, 121, 128, 136, 144, 152, 159, 166,
174, 181, 188, 194, 201, 207, 213, 218, 224, 228, 233, 237, 240, 244, 246, 249, 174, 181, 188, 194, 201, 207, 213, 218, 224, 228, 233, 237, 240, 244, 246, 249,
251, 252, 253, 253, 253, 253, 252, 251, 249, 246, 244, 240, 237, 233, 228, 224, 251, 252, 253, 253, 253, 253, 252, 251, 249, 246, 244, 240, 237, 233, 228, 224,
218, 213, 207, 201, 194, 188, 181, 174, 166, 159, 152, 144, 136, 128, 121, 113, 218, 213, 207, 201, 194, 188, 181, 174, 166, 159, 152, 144, 136, 128, 121, 113,
105, 98, 90, 83, 76, 69, 62, 55, 49, 43, 37, 32, 27, 22, 18, 14,
11, 8, 5, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
105, 98, 90, 83, 76, 69, 62, 55, 49, 43, 37, 32, 27, 22, 18, 14,
11, 8, 5, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}; };


// set from y = pow(2,x) * 128, x=-1 to 1 // set from y = pow(2,x) * 128, x=-1 to 1
unsigned char pitch_adjust_tab[MAX_PITCH_VALUE+1] = { unsigned char pitch_adjust_tab[MAX_PITCH_VALUE+1] = {
64, 65, 66, 67, 68, 69, 70, 71,
72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 86, 87, 88,
89, 91, 92, 93, 94, 96, 97, 98,
64, 65, 66, 67, 68, 69, 70, 71,
72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 86, 87, 88,
89, 91, 92, 93, 94, 96, 97, 98,
100, 101, 103, 104, 105, 107, 108, 110, 100, 101, 103, 104, 105, 107, 108, 110,
111, 113, 115, 116, 118, 119, 121, 123, 111, 113, 115, 116, 118, 119, 121, 123,
124, 126, 128, 130, 132, 133, 135, 137, 124, 126, 128, 130, 132, 133, 135, 137,
245, 244, 242, 241, 239, 238, 236, 234, 233, 231, 229, 227, 225, 223, 220, 218, 245, 244, 242, 241, 239, 238, 236, 234, 233, 231, 229, 227, 225, 223, 220, 218,
216, 213, 211, 209, 207, 205, 203, 201, 199, 197, 195, 193, 191, 189, 187, 185, 216, 213, 211, 209, 207, 205, 203, 201, 199, 197, 195, 193, 191, 189, 187, 185,
183, 180, 178, 176, 173, 171, 169, 166, 164, 161, 159, 156, 154, 151, 148, 146, 183, 180, 178, 176, 173, 171, 169, 166, 164, 161, 159, 156, 154, 151, 148, 146,
143, 140, 138, 135, 132, 129, 126, 123, 120, 118, 115, 112, 108, 105, 102, 99,
96, 95, 93, 91, 90, 88, 86, 85, 83, 82, 80, 79, 77, 76, 74, 73,
72, 70, 69, 68, 67, 66, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55,
55, 54, 53, 52, 52, 51, 50, 50, 49, 48, 48, 47, 47, 46, 46, 46,
45, 45, 45, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 44, 43,
42, 42, 41, 40, 40, 39, 38, 38, 37, 36, 36, 35, 35, 34, 33, 33,
32, 32, 31, 30, 30, 29, 29, 28, 28, 27, 26, 26, 25, 25, 24, 24,
23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18, 18, 17, 17, 16,
16, 15, 15, 15, 14, 14, 13, 13, 13, 12, 12, 11, 11, 11, 10, 10,
10, 9, 9, 9, 8, 8, 8, 7, 7, 7, 7, 6, 6, 6, 5, 5,
5, 5, 4, 4, 4, 4, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2,
2, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0
143, 140, 138, 135, 132, 129, 126, 123, 120, 118, 115, 112, 108, 105, 102, 99,
96, 95, 93, 91, 90, 88, 86, 85, 83, 82, 80, 79, 77, 76, 74, 73,
72, 70, 69, 68, 67, 66, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55,
55, 54, 53, 52, 52, 51, 50, 50, 49, 48, 48, 47, 47, 46, 46, 46,
45, 45, 45, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 44, 43,
42, 42, 41, 40, 40, 39, 38, 38, 37, 36, 36, 35, 35, 34, 33, 33,
32, 32, 31, 30, 30, 29, 29, 28, 28, 27, 26, 26, 25, 25, 24, 24,
23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18, 18, 17, 17, 16,
16, 15, 15, 15, 14, 14, 13, 13, 13, 12, 12, 11, 11, 11, 10, 10,
10, 9, 9, 9, 8, 8, 8, 7, 7, 7, 7, 6, 6, 6, 5, 5,
5, 5, 4, 4, 4, 4, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2,
2, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0
}; };


static unsigned char pk_shape2[PEAKSHAPEW+1] = { static unsigned char pk_shape2[PEAKSHAPEW+1] = {
243, 243, 242, 241, 239, 237, 235, 233, 231, 229, 227, 225, 223, 221, 218, 216, 243, 243, 242, 241, 239, 237, 235, 233, 231, 229, 227, 225, 223, 221, 218, 216,
213, 211, 208, 205, 203, 200, 197, 194, 191, 187, 184, 181, 178, 174, 171, 167, 213, 211, 208, 205, 203, 200, 197, 194, 191, 187, 184, 181, 178, 174, 171, 167,
163, 160, 156, 152, 148, 144, 140, 136, 132, 127, 123, 119, 114, 110, 105, 100, 163, 160, 156, 152, 148, 144, 140, 136, 132, 127, 123, 119, 114, 110, 105, 100,
96, 94, 91, 88, 86, 83, 81, 78, 76, 74, 71, 69, 66, 64, 62, 60,
57, 55, 53, 51, 49, 47, 44, 42, 40, 38, 36, 34, 32, 30, 29, 27,
25, 23, 21, 19, 18, 16, 14, 12, 11, 9, 7, 6, 4, 3, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0
96, 94, 91, 88, 86, 83, 81, 78, 76, 74, 71, 69, 66, 64, 62, 60,
57, 55, 53, 51, 49, 47, 44, 42, 40, 38, 36, 34, 32, 30, 29, 27,
25, 23, 21, 19, 18, 16, 14, 12, 11, 9, 7, 6, 4, 3, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0
}; };


static unsigned char *pk_shape; static unsigned char *pk_shape;
out_ptr = out_start = outbuffer; out_ptr = out_start = outbuffer;
out_end = out_start + outbuffer_size; out_end = out_start + outbuffer_size;
} }
out_end2 = &outbuffer[pa_size]; // top of data needed for the portaudio buffer
out_end2 = &outbuffer[pa_size]; // top of data needed for the portaudio buffer


#ifdef LIBRARY #ifdef LIBRARY
event_list_ix = 0; event_list_ix = 0;
result = WavegenFill(1); result = WavegenFill(1);


// copy from the outbut buffer into the portaudio buffer // copy from the outbut buffer into the portaudio buffer
if (result && (out_ptr > out_end2)) {
result = 0; // don't end yet, there is more data in the buffer than can fit in portaudio
}
if (result && (out_ptr > out_end2))
result = 0; // don't end yet, there is more data in the buffer than can fit in portaudio


while (out_ptr < out_end2) while (out_ptr < out_end2)
*out_ptr++ = 0; // fill with zeros up to the size of the portaudio buffer
*out_ptr++ = 0; // fill with zeros up to the size of the portaudio buffer


memcpy(outputBuffer, outbuffer, pa_size); memcpy(outputBuffer, outbuffer, pa_size);


// move the remaining contents of the start of the output buffer // move the remaining contents of the start of the output buffer
for (p = out_end2; p < out_end; p++) {
for (p = out_end2; p < out_end; p++)
p[-pa_size] = p[0]; p[-pa_size] = p[0];
}
out_ptr -= pa_size; out_ptr -= pa_size;


#ifdef LIBRARY #ifdef LIBRARY
event_list[event_list_ix].user_data = 0; event_list[event_list_ix].user_data = 0;


if (synth_callback(NULL, 0, event_list) == 1) { if (synth_callback(NULL, 0, event_list) == 1) {
SpeakNextClause(NULL, NULL, 2); // stop speaking
SpeakNextClause(NULL, NULL, 2); // stop speaking
result = 1; result = 1;
} }
} }
#endif #endif


#ifdef ARCH_BIG #ifdef ARCH_BIG
{
// swap the order of bytes in each sound sample in the portaudio buffer
int c;
unsigned char *buf_end;
out_buf = (unsigned char *)outputBuffer;
buf_end = out_buf + framesPerBuffer*2;
while (out_buf < buf_end) {
c = out_buf[0];
out_buf[0] = out_buf[1];
out_buf[1] = c;
out_buf += 2;
}
// swap the order of bytes in each sound sample in the portaudio buffer
int c;
unsigned char *buf_end;
out_buf = (unsigned char *)outputBuffer;
buf_end = out_buf + framesPerBuffer*2;
while (out_buf < buf_end) {
c = out_buf[0];
out_buf[0] = out_buf[1];
out_buf[1] = c;
out_buf += 2;
} }
#endif #endif


return 1; return 1;
} }
} else } else
WavegenOpenSound(); // still items in the queue, shouldn't be closed
WavegenOpenSound(); // still items in the queue, shouldn't be closed
} }
return 0; return 0;
} }
} }
return 0; return 0;
} }

#else #else

int WavegenOpenSound() int WavegenOpenSound()
{ {
return 0; return 0;
} }

int WavegenCloseSound() int WavegenCloseSound()
{ {
return 0; return 0;
} }

int WavegenInitSound() int WavegenInitSound()
{ {
return 0; return 0;
} }

#endif #endif


void WavegenInit(int rate, int wavemult_fact) void WavegenInit(int rate, int wavemult_fact)
double x; double x;


if (wavemult_fact == 0) if (wavemult_fact == 0)
wavemult_fact = 60; // default
wavemult_fact = 60; // default


wvoice = NULL; wvoice = NULL;
samplerate = samplerate_native = rate; samplerate = samplerate_native = rate;
PHASE_INC_FACTOR = 0x8000000 / samplerate; // assumes pitch is Hz*32
PHASE_INC_FACTOR = 0x8000000 / samplerate; // assumes pitch is Hz*32
Flutter_inc = (64 * samplerate)/rate; Flutter_inc = (64 * samplerate)/rate;
samplecount = 0; samplecount = 0;
nsamples = 0; nsamples = 0;
} }
} }


pk_shape = pk_shape2; // pk_shape2
pk_shape = pk_shape2;


#ifdef INCLUDE_KLATT #ifdef INCLUDE_KLATT
KlattInit(); KlattInit();
amp = 0; amp = 0;


echo_head = (delay * samplerate)/1000; echo_head = (delay * samplerate)/1000;
echo_length = echo_head; // ensure completion of echo at the end of speech. Use 1 delay period?
echo_length = echo_head; // ensure completion of echo at the end of speech. Use 1 delay period?
if (amp == 0) if (amp == 0)
echo_length = 0; echo_length = 0;
if (amp > 20) if (amp > 20)
echo_length = echo_head * 2; // perhaps allow 2 echo periods if the echo is loud.
echo_length = echo_head * 2; // perhaps allow 2 echo periods if the echo is loud.


// echo_amp units are 1/256ths of the amplitude of the original sound. // echo_amp units are 1/256ths of the amplitude of the original sound.
echo_amp = amp; echo_amp = amp;


int PeaksToHarmspect(wavegen_peaks_t *peaks, int pitch, int *htab, int control) int PeaksToHarmspect(wavegen_peaks_t *peaks, int pitch, int *htab, int control)
{ {
// Calculate the amplitude of each harmonics from the formants
// Only for formants 0 to 5
// Calculate the amplitude of each harmonics from the formants
// Only for formants 0 to 5


// control 0=initial call, 1=every 64 cycles
// control 0=initial call, 1=every 64 cycles


// pitch and freqs are Hz<<16 // pitch and freqs are Hz<<16


int f; int f;
wavegen_peaks_t *p; wavegen_peaks_t *p;
int fp; // centre freq of peak
int fhi; // high freq of peak
int h; // harmonic number
int fp; // centre freq of peak
int fhi; // high freq of peak
int h; // harmonic number
int pk; int pk;
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 ix;
int h1; int h1;
hmax = MAX_HARMONIC-1; hmax = MAX_HARMONIC-1;


// restrict highest harmonic to half the samplerate // restrict highest harmonic to half the samplerate
hmax_samplerate = (((samplerate * 19)/40) << 16)/pitch; // only 95% of Nyquist freq
hmax_samplerate = (((samplerate * 19)/40) << 16)/pitch; // only 95% of Nyquist freq


if (hmax > hmax_samplerate) if (hmax > hmax_samplerate)
hmax = hmax_samplerate; hmax = hmax_samplerate;
int h2; int h2;
// increase bass // increase bass
y = peaks[1].height * 10; // addition as a multiple of 1/256s y = peaks[1].height * 10; // addition as a multiple of 1/256s
h2 = (1000<<16)/pitch; // decrease until 1000Hz
h2 = (1000<<16)/pitch; // decrease until 1000Hz
if (h2 > 0) { if (h2 > 0) {
x = y/h2; x = y/h2;
h = 1; h = 1;
htab[h] = (x * x) >> 8; htab[h] = (x * x) >> 8;


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


// adjust the amplitude of the first harmonic, affects tonal quality // adjust the amplitude of the first harmonic, affects tonal quality
h1 = htab[1] * option_harmonic1; h1 = htab[1] * option_harmonic1;
htab[1] = h1/8; htab[1] = h1/8;



// calc intermediate increments of LF harmonics // calc intermediate increments of LF harmonics
if (control & 1) { if (control & 1) {
for (h = 1; h < N_LOWHARM; h++) for (h = 1; h < N_LOWHARM; h++)
harm_inc[h] = (htab[h] - harmspect[h]) >> 3; harm_inc[h] = (htab[h] - harmspect[h]) >> 3;
} }


return hmax; // highest harmonic number
return hmax; // highest harmonic number
} }


static void AdvanceParameters() static void AdvanceParameters()
{ {
// Called every 64 samples to increment the formant freq, height, and widths
// Called every 64 samples to increment the formant freq, height, and widths


int x; int x;
int ix; int ix;
Flutter_ix += Flutter_inc; Flutter_ix += Flutter_inc;
wdata.pitch += x; wdata.pitch += x;
if (wdata.pitch < 102400) if (wdata.pitch < 102400)
wdata.pitch = 102400; // min pitch, 25 Hz (25 << 12)
wdata.pitch = 102400; // min pitch, 25 Hz (25 << 12)


if (samplecount == samplecount_start) if (samplecount == samplecount_start)
return; return;


static void setresonator(RESONATOR *rp, int freq, int bwidth, int init) static void setresonator(RESONATOR *rp, int freq, int bwidth, int init)
{ {
// freq Frequency of resonator in Hz
// bwidth Bandwidth of resonator in Hz
// init Initialize internal data
// freq Frequency of resonator in Hz
// bwidth Bandwidth of resonator in Hz
// init Initialize internal data


double x; double x;
double arg; double arg;
static int agc = 256; static int agc = 256;
static int h_switch_sign = 0; static int h_switch_sign = 0;
static int cycle_count = 0; static int cycle_count = 0;
static int amplitude2 = 0; // adjusted for pitch
static int amplitude2 = 0; // adjusted for pitch


// continue until the output buffer is full, or // continue until the output buffer is full, or
// the required number of samples have been produced // the required number of samples have been produced


// pitch is Hz<<12 // pitch is Hz<<12
phaseinc = (wdata.pitch>>7) * PHASE_INC_FACTOR; phaseinc = (wdata.pitch>>7) * PHASE_INC_FACTOR;
cycle_samples = samplerate/(wdata.pitch >> 12); // sr/(pitch*2)
cycle_samples = samplerate/(wdata.pitch >> 12); // sr/(pitch*2)
hf_factor = wdata.pitch >> 11; hf_factor = wdata.pitch >> 11;


maxh = maxh2; maxh = maxh2;
// 8 bit data, shift by the specified scale factor // 8 bit data, shift by the specified scale factor
value = (signed char)data[ix++] * scale; value = (signed char)data[ix++] * scale;
} }
value *= (consonant_amp * general_amplitude); // reduce strength of consonant
value *= (consonant_amp * general_amplitude); // reduce strength of consonant
value = value >> 10; value = value >> 10;
value = (value * amp)/32; value = (value * amp)/32;


switch (command) switch (command)
{ {
case EMBED_T: case EMBED_T:
WavegenSetEcho(); // and drop through to case P
WavegenSetEcho(); // and drop through to case P
case EMBED_P: case EMBED_P:
SetPitchFormants(); SetPitchFormants();
break; break;
case EMBED_A: // amplitude
case EMBED_A: // amplitude
general_amplitude = GetAmplitude(); general_amplitude = GetAmplitude();
break; break;
case EMBED_F: // emphasis
case EMBED_F: // emphasis
general_amplitude = GetAmplitude(); general_amplitude = GetAmplitude();
break; break;
case EMBED_H: case EMBED_H:


consonant_amp = (v->consonant_amp * 26) /100; consonant_amp = (v->consonant_amp * 26) /100;
if (samplerate <= 11000) { if (samplerate <= 11000) {
consonant_amp = consonant_amp*2; // emphasize consonants at low sample rates
consonant_amp = consonant_amp*2; // emphasize consonants at low sample rates
option_harmonic1 = 6; option_harmonic1 = 6;
} }
WavegenSetEcho(); WavegenSetEcho();
amp_inc = (256 * ENV_LEN * STEPSIZE)/length; amp_inc = (256 * ENV_LEN * STEPSIZE)/length;


wdata.amplitude = (value * general_amplitude)/16; wdata.amplitude = (value * general_amplitude)/16;
wdata.amplitude_v = (wdata.amplitude * wvoice->consonant_ampv * 15)/100; // for wave mixed with voiced sounds
wdata.amplitude_v = (wdata.amplitude * wvoice->consonant_ampv * 15)/100; // for wave mixed with voiced sounds


amplitude_env = amp_env; amplitude_env = amp_env;
} }
int pitch_value; int pitch_value;


if (pitch1 > pitch2) { if (pitch1 > pitch2) {
x = pitch1; // swap values
x = pitch1; // swap values
pitch1 = pitch2; pitch1 = pitch2;
pitch2 = x; pitch2 = x;
} }


if ((pitch_value = embedded_value[EMBED_P]) > MAX_PITCH_VALUE) if ((pitch_value = embedded_value[EMBED_P]) > MAX_PITCH_VALUE)
pitch_value = MAX_PITCH_VALUE; pitch_value = MAX_PITCH_VALUE;
pitch_value -= embedded_value[EMBED_T]; // adjust tone for announcing punctuation
pitch_value -= embedded_value[EMBED_T]; // adjust tone for announcing punctuation
if (pitch_value < 0) if (pitch_value < 0)
pitch_value = 0; pitch_value = 0;




void SetPitch(int length, unsigned char *env, int pitch1, int pitch2) void SetPitch(int length, unsigned char *env, int pitch1, int pitch2)
{ {
// length in samples
// length in samples


if ((wdata.pitch_env = env) == NULL) if ((wdata.pitch_env = env) == NULL)
wdata.pitch_env = env_fall; // default
wdata.pitch_env = env_fall; // default


wdata.pitch_ix = 0; wdata.pitch_ix = 0;
if (length == 0) if (length == 0)


SetPitch2(wvoice, pitch1, pitch2, &wdata.pitch_base, &wdata.pitch_range); SetPitch2(wvoice, pitch1, pitch2, &wdata.pitch_base, &wdata.pitch_range);
// set initial pitch // set initial pitch
wdata.pitch = ((wdata.pitch_env[0] * wdata.pitch_range) >>8) + wdata.pitch_base; // Hz << 12
wdata.pitch = ((wdata.pitch_env[0] * wdata.pitch_range) >>8) + wdata.pitch_base; // Hz << 12


flutter_amp = wvoice->flutter; flutter_amp = wvoice->flutter;
} }
int length4; int length4;
int qix; int qix;
int cmd; int cmd;
static int glottal_reduce_tab1[4] = { 0x30, 0x30, 0x40, 0x50 }; // vowel before [?], amp * 1/256
static int glottal_reduce_tab2[4] = { 0x90, 0xa0, 0xb0, 0xc0 }; // vowel after [?], amp * 1/256
static int glottal_reduce_tab1[4] = { 0x30, 0x30, 0x40, 0x50 }; // vowel before [?], amp * 1/256
static int glottal_reduce_tab2[4] = { 0x90, 0xa0, 0xb0, 0xc0 }; // vowel after [?], amp * 1/256


harm_sqrt_n = 0; harm_sqrt_n = 0;
end_wave = 1; end_wave = 1;


glottal_flag = 0; glottal_flag = 0;
if (modn & 0x400) { if (modn & 0x400) {
glottal_flag = 3; // before a glottal stop
glottal_flag = 3; // before a glottal stop
glottal_reduce = glottal_reduce_tab1[(modn >> 8) & 3]; glottal_reduce = glottal_reduce_tab1[(modn >> 8) & 3];
} }
if (modn & 0x800) { if (modn & 0x800) {
glottal_flag = 4; // after a glottal stop
glottal_flag = 4; // after a glottal stop
glottal_reduce = glottal_reduce_tab2[(modn >> 8) & 3]; glottal_reduce = glottal_reduce_tab2[(modn >> 8) & 3];
} }




cmd = wcmdq[qix][0]; 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;
} }
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
} }


// round the length to a multiple of the stepsize // round the length to a multiple of the stepsize
peaks[ix].freq1 = (fr1->ffreq[ix] * v->freq[ix] + v->freqadd[ix]*256) << 8; peaks[ix].freq1 = (fr1->ffreq[ix] * v->freq[ix] + v->freqadd[ix]*256) << 8;
peaks[ix].freq = (int)peaks[ix].freq1; peaks[ix].freq = (int)peaks[ix].freq1;
next = (fr2->ffreq[ix] * v->freq[ix] + v->freqadd[ix]*256) << 8; next = (fr2->ffreq[ix] * v->freq[ix] + v->freqadd[ix]*256) << 8;
peaks[ix].freq_inc = ((next - peaks[ix].freq1) * (STEPSIZE/4)) / length4; // lower headroom for fixed point math
peaks[ix].freq_inc = ((next - peaks[ix].freq1) * (STEPSIZE/4)) / length4; // lower headroom for fixed point math
} }


peaks[ix].height1 = (fr1->fheight[ix] * v->height[ix]) << 6; peaks[ix].height1 = (fr1->fheight[ix] * v->height[ix]) << 6;
peaks[ix].right = (int)peaks[ix].right1; peaks[ix].right = (int)peaks[ix].right1;
next = (fr2->fright[ix] * v->width[ix]) << 10; next = (fr2->fright[ix] * v->width[ix]) << 10;
peaks[ix].right_inc = ((next - peaks[ix].right1) * STEPSIZE) / length2; peaks[ix].right_inc = ((next - peaks[ix].right1) * STEPSIZE) / length2;
} else {
} else
peaks[ix].right = peaks[ix].left; peaks[ix].right = peaks[ix].left;
}
} }
} }
} }


void Write4Bytes(FILE *f, int value) void Write4Bytes(FILE *f, int value)
{ {
// Write 4 bytes to a file, least significant first
// Write 4 bytes to a file, least significant first
int ix; int ix;


for (ix = 0; ix < 4; ix++) { for (ix = 0; ix < 4; ix++) {


int WavegenFill2(int fill_zeros) int WavegenFill2(int fill_zeros)
{ {
// Pick up next wavegen commands from the queue
// return: 0 output buffer has been filled
// return: 1 input command queue is now empty
// Pick up next wavegen commands from the queue
// return: 0 output buffer has been filled
// return: 1 input command queue is now empty


intptr_t *q; intptr_t *q;
int length; int length;
// continue to play silence until echo is completed // continue to play silence until echo is completed
resume = PlaySilence(echo_complete, resume); resume = PlaySilence(echo_complete, resume);
if (resume == 1) if (resume == 1)
return 0; // not yet finished
return 0; // not yet finished
} }


if (fill_zeros) { if (fill_zeros) {
while (out_ptr < out_end) while (out_ptr < out_end)
*out_ptr++ = 0; *out_ptr++ = 0;
} }
return 1; // queue empty, close sound channel
return 1; // queue empty, close sound channel
} }


result = 0; result = 0;
wdata.mix_wavefile_offset = 0; wdata.mix_wavefile_offset = 0;
wdata.mix_wavefile = (unsigned char *)q[2]; wdata.mix_wavefile = (unsigned char *)q[2];
break; break;
case WCMD_SPECT2: // as WCMD_SPECT but stop any concurrent wave file
wdata.n_mix_wavefile = 0; // ... and drop through to WCMD_SPECT case
case WCMD_SPECT2: // as WCMD_SPECT but stop any concurrent wave file
wdata.n_mix_wavefile = 0; // ... and drop through to WCMD_SPECT case
case WCMD_SPECT: case WCMD_SPECT:
echo_complete = echo_length; echo_complete = echo_length;
result = Wavegen2(length & 0xffff, q[1] >> 16, resume, (frame_t *)q[2], (frame_t *)q[3]); result = Wavegen2(length & 0xffff, q[1] >> 16, resume, (frame_t *)q[2], (frame_t *)q[3]);
break; break;
#ifdef INCLUDE_KLATT #ifdef INCLUDE_KLATT
case WCMD_KLATT2: // as WCMD_SPECT but stop any concurrent wave file
wdata.n_mix_wavefile = 0; // ... and drop through to WCMD_SPECT case
case WCMD_KLATT2: // as WCMD_SPECT but stop any concurrent wave file
wdata.n_mix_wavefile = 0; // ... and drop through to WCMD_SPECT case
case WCMD_KLATT: case WCMD_KLATT:
echo_complete = echo_length; echo_complete = echo_length;
result = Wavegen_Klatt2(length & 0xffff, q[1] >> 16, resume, (frame_t *)q[2], (frame_t *)q[3]); result = Wavegen_Klatt2(length & 0xffff, q[1] >> 16, resume, (frame_t *)q[2], (frame_t *)q[3]);
break; break;
case WCMD_FMT_AMPLITUDE: case WCMD_FMT_AMPLITUDE:
if ((wdata.amplitude_fmt = q[1]) == 0) if ((wdata.amplitude_fmt = q[1]) == 0)
wdata.amplitude_fmt = 100; // percentage, but value=0 means 100%
wdata.amplitude_fmt = 100; // percentage, but value=0 means 100%
break; break;
#if HAVE_SONIC_H #if HAVE_SONIC_H
case WCMD_SONIC_SPEED: case WCMD_SONIC_SPEED:
} }


#if HAVE_SONIC_H #if HAVE_SONIC_H
/* Speed up the audio samples with libsonic. */
// Speed up the audio samples with libsonic.
static int SpeedUp(short *outbuf, int length_in, int length_out, int end_of_text) static int SpeedUp(short *outbuf, int length_in, int length_out, int end_of_text)
{ {
if (length_in > 0) { if (length_in > 0) {
} }
#endif #endif


/* Call WavegenFill2, and then speed up the output samples. */
// Call WavegenFill2, and then speed up the output samples.
int WavegenFill(int fill_zeros) int WavegenFill(int fill_zeros)
{ {
int finished; int finished;
out_ptr = p_start + length; out_ptr = p_start + length;


if (length >= max_length) if (length >= max_length)
finished = 0; // there may be more data to flush
finished = 0; // there may be more data to flush
} }
#endif #endif
return finished; return finished;

+ 39
- 40
src/speak-ng.c View File

#include "translate.h" #include "translate.h"


extern void Write4Bytes(FILE *f, int value); extern void Write4Bytes(FILE *f, int value);
char path_home[N_PATH_HOME]; // this is the espeak-data directory
char path_home[N_PATH_HOME]; // this is the espeak-data directory


char filetype[5]; char filetype[5];
char wavefile[200]; char wavefile[200];
if (path[0] != 0) { if (path[0] != 0) {
if (strcmp(path, "stdout") == 0) { if (strcmp(path, "stdout") == 0) {
#ifdef PLATFORM_WINDOWS #ifdef PLATFORM_WINDOWS
// prevent Windows adding 0x0d before 0x0a bytes
// prevent Windows adding 0x0d before 0x0a bytes
_setmode(_fileno(stdout), _O_BINARY); _setmode(_fileno(stdout), _O_BINARY);
#endif #endif
f_wave = stdout; f_wave = stdout;
if (((env = getenv("ESPEAK_DATA_PATH")) != NULL) && ((strlen(env)+12) < sizeof(path_home))) { if (((env = getenv("ESPEAK_DATA_PATH")) != NULL) && ((strlen(env)+12) < sizeof(path_home))) {
sprintf(path_home, "%s\\espeak-data", env); sprintf(path_home, "%s\\espeak-data", env);
if (GetFileLength(path_home) == -2) if (GetFileLength(path_home) == -2)
return; // an espeak-data directory exists in the directory specified by environment variable
return; // an espeak-data directory exists in the directory specified by environment variable
} }


strcpy(path_home, argv0); strcpy(path_home, argv0);
if ((p = strrchr(path_home, '\\')) != NULL) { if ((p = strrchr(path_home, '\\')) != NULL) {
strcpy(&p[1], "espeak-data"); strcpy(&p[1], "espeak-data");
if (GetFileLength(path_home) == -2) if (GetFileLength(path_home) == -2)
return; // an espeak-data directory exists in the same directory as the espeak program
return; // an espeak-data directory exists in the same directory as the espeak program
} }


// otherwise, look in the Windows Registry // otherwise, look in the Windows Registry
if ((env = getenv("ESPEAK_DATA_PATH")) != NULL) { if ((env = getenv("ESPEAK_DATA_PATH")) != NULL) {
snprintf(path_home, sizeof(path_home), "%s/espeak-data", env); snprintf(path_home, sizeof(path_home), "%s/espeak-data", env);
if (GetFileLength(path_home) == -2) if (GetFileLength(path_home) == -2)
return; // an espeak-data directory exists
return; // an espeak-data directory exists
} }


snprintf(path_home, sizeof(path_home), "%s/espeak-data", getenv("HOME")); snprintf(path_home, sizeof(path_home), "%s/espeak-data", getenv("HOME"));
{ {
int param; int param;
int result; int result;
int srate = 22050; // default sample rate
int srate = 22050; // default sample rate


// It seems that the wctype functions don't work until the locale has been set // It seems that the wctype functions don't work until the locale has been set
// to something other than the default "C". Then, not only Latin1 but also the // to something other than the default "C". Then, not only Latin1 but also the


FILE *f_text = NULL; FILE *f_text = NULL;
const char *p_text = NULL; const char *p_text = NULL;
char *data_path = NULL; // use default path for espeak-data
char *data_path = NULL; // use default path for espeak-data


int option_index = 0; int option_index = 0;
int c; int c;
int speed = 175; int speed = 175;
int ix; int ix;
char *optarg2; char *optarg2;
int amp = 100; // default
int amp = 100; // default
int wordgap = 0; int wordgap = 0;
int flag_stdin = 0; int flag_stdin = 0;
int flag_compile = 0; int flag_compile = 0;
option_wordgap = 0; option_wordgap = 0;
option_endpause = 1; option_endpause = 1;
option_phoneme_input = 1; option_phoneme_input = 1;
option_multibyte = espeakCHARS_AUTO; // auto
option_multibyte = espeakCHARS_AUTO;
f_trans = stdout; f_trans = stdout;


#ifdef NEED_GETOPT #ifdef NEED_GETOPT


if (c == '-') { if (c == '-') {
if (p[0] == 0) if (p[0] == 0)
break; // -- means don't interpret further - as commands
break; // -- means don't interpret further - as commands


opt_string = ""; opt_string = "";
for (ix = 0;; ix++) { for (ix = 0;; ix++) {
} }
#else #else
while (true) { while (true) {
c = getopt_long(argc, argv, "a:b:f:g:hk:l:p:qs:v:w:xXmz", // NOTE: also change arg_opts to indicate which commands have a numeric value
c = getopt_long(argc, argv, "a:b:f:g:hk:l:p:qs:v:w:xXmz", // NOTE: also change arg_opts to indicate which commands have a numeric value
long_options, &option_index); long_options, &option_index);


/* Detect the end of the options. */
// Detect the end of the options.
if (c == -1) if (c == -1)
break; break;
optarg2 = optarg; optarg2 = optarg;
case 'z': case 'z':
option_endpause = 0; option_endpause = 0;
break; break;
case 0x100: // --stdin
case 0x100: // --stdin
flag_stdin = 1; flag_stdin = 1;
break; break;
case 0x105: // --stdout
case 0x105: // --stdout
option_waveout = 1; option_waveout = 1;
strcpy(wavefile, "stdout"); strcpy(wavefile, "stdout");
break; break;
case 0x101: // --compile-debug
case 0x102: // --compile
case 0x101: // --compile-debug
case 0x102: // --compile
if (optarg2 != NULL) if (optarg2 != NULL)
strncpy0(voicename, optarg2, sizeof(voicename)); strncpy0(voicename, optarg2, sizeof(voicename));
flag_compile = c; flag_compile = c;
break; break;
case 0x103: // --punct
case 0x103: // --punct
option_punctuation = 1; option_punctuation = 1;
if (optarg2 != NULL) { if (optarg2 != NULL) {
ix = 0; ix = 0;
option_punctuation = 2; option_punctuation = 2;
} }
break; break;
case 0x104: // --voices
case 0x104: // --voices
init_path(argv[0], data_path); init_path(argv[0], data_path);
DisplayVoices(stdout, optarg2); DisplayVoices(stdout, optarg2);
exit(0); exit(0);
case 0x106: // -- split
case 0x106: // -- split
if (optarg2 == NULL) if (optarg2 == NULL)
samples_split = 30; // default 30 minutes
samples_split = 30; // default 30 minutes
else else
samples_split = atoi(optarg2); samples_split = atoi(optarg2);
break; break;
case 0x107: // --path
case 0x107: // --path
data_path = optarg2; data_path = optarg2;
break; break;
case 0x108: // --phonout
case 0x108: // --phonout
if ((f_trans = fopen(optarg2, "w")) == NULL) { if ((f_trans = fopen(optarg2, "w")) == NULL) {
fprintf(stderr, "Can't write to: %s\n", optarg2); fprintf(stderr, "Can't write to: %s\n", optarg2);
f_trans = stderr; f_trans = stderr;
} }
break; break;
case 0x109: // --pho
case 0x109: // --pho
phoneme_options |= espeakPHONEMES_MBROLA; phoneme_options |= espeakPHONEMES_MBROLA;
break; break;
case 0x10a: // --ipa
case 0x10a: // --ipa
phoneme_options |= espeakPHONEMES_IPA; phoneme_options |= espeakPHONEMES_IPA;
if (optarg2 != NULL) { if (optarg2 != NULL) {
// deprecated and obsolete // deprecated and obsolete
phoneme_options |= espeakPHONEMES_TIE; phoneme_options |= espeakPHONEMES_TIE;
break; break;
case 3: case 3:
phonemes_separator = 0x200d; // ZWJ
phonemes_separator = 0x200d; // ZWJ
phoneme_options |= espeakPHONEMES_TIE; phoneme_options |= espeakPHONEMES_TIE;
break; break;
} }
} }
break; break;
case 0x10b: // --version
case 0x10b: // --version
init_path(argv[0], data_path); init_path(argv[0], data_path);
printf("speak text-to-speech: %s Data at: %s\n", version_string, path_home); printf("speak text-to-speech: %s Data at: %s\n", version_string, path_home);
exit(0); exit(0);
case 0x10c: // --sep
case 0x10c: // --sep
phoneme_options |= espeakPHONEMES_SHOW; phoneme_options |= espeakPHONEMES_SHOW;
if (optarg2 == 0) if (optarg2 == 0)
phonemes_separator = ' '; phonemes_separator = ' ';
else else
utf8_in(&phonemes_separator, optarg2); utf8_in(&phonemes_separator, optarg2);
if (phonemes_separator == 'z') if (phonemes_separator == 'z')
phonemes_separator = 0x200c; // ZWNJ
phonemes_separator = 0x200c; // ZWNJ
break; break;
case 0x10d: // --tie
case 0x10d: // --tie
phoneme_options |= (espeakPHONEMES_SHOW | espeakPHONEMES_TIE); phoneme_options |= (espeakPHONEMES_SHOW | espeakPHONEMES_TIE);
if (optarg2 == 0) if (optarg2 == 0)
phonemes_separator = 0x0361; // default: combining-double-inverted-breve
phonemes_separator = 0x0361; // default: combining-double-inverted-breve
else else
utf8_in(&phonemes_separator, optarg2); utf8_in(&phonemes_separator, optarg2);
if (phonemes_separator == 'z') if (phonemes_separator == 'z')
phonemes_separator = 0x200d; // ZWJ
phonemes_separator = 0x200d; // ZWJ
break; break;
default: default:
exit(0); exit(0);
#ifdef PLATFORM_DOS #ifdef PLATFORM_DOS
char path_dsource[sizeof(path_home)+20]; char path_dsource[sizeof(path_home)+20];
strcpy(path_dsource, path_home); strcpy(path_dsource, path_home);
path_dsource[strlen(path_home)-11] = 0; // remove "espeak-data" from the end
path_dsource[strlen(path_home)-11] = 0; // remove "espeak-data" from the end
strcat(path_dsource, "dictsource\\"); strcat(path_dsource, "dictsource\\");
CompileDictionary(path_dsource, dictionary_name, NULL, NULL, flag_compile & 0x1); CompileDictionary(path_dsource, dictionary_name, NULL, NULL, flag_compile & 0x1);
#else #else
#ifdef PLATFORM_WINDOWS #ifdef PLATFORM_WINDOWS
char path_dsource[sizeof(path_home)+20]; char path_dsource[sizeof(path_home)+20];
strcpy(path_dsource, path_home); strcpy(path_dsource, path_home);
path_dsource[strlen(path_home)-11] = 0; // remove "espeak-data" from the end
path_dsource[strlen(path_home)-11] = 0; // remove "espeak-data" from the end
strcat(path_dsource, "dictsource\\"); strcat(path_dsource, "dictsource\\");
CompileDictionary(path_dsource, dictionary_name, NULL, NULL, flag_compile & 0x1); CompileDictionary(path_dsource, dictionary_name, NULL, NULL, flag_compile & 0x1);
#else #else


option_phonemes = phoneme_options | (phonemes_separator << 8); option_phonemes = phoneme_options | (phonemes_separator << 8);


if (pitch_adjustment != 50) {
if (pitch_adjustment != 50)
SetParameter(espeakPITCH, pitch_adjustment, 0); SetParameter(espeakPITCH, pitch_adjustment, 0);
}
DoVoiceChange(voice); DoVoiceChange(voice);


if (filename[0] == 0) { if (filename[0] == 0) {
} else { } else {
f_text = stdin; f_text = stdin;
if (flag_stdin == 0) if (flag_stdin == 0)
option_linelength = -1; // single input lines on stdin
option_linelength = -1; // single input lines on stdin
} }
} else } else
f_text = fopen(filename, "r"); f_text = fopen(filename, "r");
for (;;) { for (;;) {
if (WavegenFile() != 0) { if (WavegenFile() != 0) {
if (ix == 0) if (ix == 0)
break; // finished, wavegen command queue is empty
break; // finished, wavegen command queue is empty
} }


if (Generate(phoneme_list, &n_phoneme_list, 1) == 0) if (Generate(phoneme_list, &n_phoneme_list, 1) == 0)
// NOTE: if nanosleep() isn't recognised on your system, try replacing // NOTE: if nanosleep() isn't recognised on your system, try replacing
// this by sleep(1); // this by sleep(1);
#ifdef PLATFORM_WINDOWS #ifdef PLATFORM_WINDOWS
Sleep(300); // 0.3s
Sleep(300); // 0.3s
#else #else
#ifdef USE_NANOSLEEP #ifdef USE_NANOSLEEP
struct timespec period; struct timespec period;
struct timespec remaining; struct timespec remaining;
period.tv_sec = 0; period.tv_sec = 0;
period.tv_nsec = 300000000; // 0.3 sec
period.tv_nsec = 300000000; // 0.3 sec
nanosleep(&period, &remaining); nanosleep(&period, &remaining);
#else #else
sleep(1); sleep(1);
} }


if ((f_trans != stdout) && (f_trans != stderr)) if ((f_trans != stdout) && (f_trans != stderr))
fclose(f_trans); // needed for WinCe
fclose(f_trans);
return 0; return 0;
} }

Loading…
Cancel
Save