#include "speak_lib.h" | #include "speak_lib.h" | ||||
#include "espeak_ng.h" | #include "espeak_ng.h" | ||||
extern void strncpy0(char *to,const char *from, int size); | |||||
extern void strncpy0(char *to, const char *from, int size); | |||||
extern int utf8_in(int *c, const char *buf); | extern int utf8_in(int *c, const char *buf); | ||||
extern int GetFileLength(const char *filename); | extern int GetFileLength(const char *filename); | ||||
const espeak_VOICE **voices; | const espeak_VOICE **voices; | ||||
espeak_VOICE voice_select; | espeak_VOICE voice_select; | ||||
static char genders[4] = {'-','M','F','-'}; | |||||
static char genders[4] = { '-', 'M', 'F', '-' }; | |||||
if((language != NULL) && (language[0] != 0)) | |||||
{ | |||||
if ((language != NULL) && (language[0] != 0)) { | |||||
// display only voices for the specified language, in order of priority | // display only voices for the specified language, in order of priority | ||||
voice_select.languages = language; | voice_select.languages = language; | ||||
voice_select.age = 0; | voice_select.age = 0; | ||||
voice_select.gender = 0; | voice_select.gender = 0; | ||||
voice_select.name = NULL; | voice_select.name = NULL; | ||||
voices = espeak_ListVoices(&voice_select); | voices = espeak_ListVoices(&voice_select); | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
voices = espeak_ListVoices(NULL); | voices = espeak_ListVoices(NULL); | ||||
} | } | ||||
fprintf(f_out,"Pty Language Age/Gender VoiceName File Other Languages\n"); | |||||
fprintf(f_out, "Pty Language Age/Gender VoiceName File Other Languages\n"); | |||||
for(ix=0; (v = voices[ix]) != NULL; ix++) | |||||
{ | |||||
for (ix = 0; (v = voices[ix]) != NULL; ix++) { | |||||
count = 0; | count = 0; | ||||
p = v->languages; | p = v->languages; | ||||
while(*p != 0) | |||||
{ | |||||
while (*p != 0) { | |||||
len = strlen(p+1); | len = strlen(p+1); | ||||
lang_name = p+1; | lang_name = p+1; | ||||
if(v->age == 0) | |||||
strcpy(age_buf," "); | |||||
if (v->age == 0) | |||||
strcpy(age_buf, " "); | |||||
else | else | ||||
sprintf(age_buf,"%3d",v->age); | |||||
sprintf(age_buf, "%3d", v->age); | |||||
if(count==0) | |||||
{ | |||||
for(j=0; j < sizeof(buf); j++) | |||||
{ | |||||
if (count == 0) { | |||||
for (j = 0; j < sizeof(buf); j++) { | |||||
// replace spaces in the name | // replace spaces in the name | ||||
if((c = v->name[j]) == ' ') | |||||
if ((c = v->name[j]) == ' ') | |||||
c = '_'; | c = '_'; | ||||
if((buf[j] = c) == 0) | |||||
if ((buf[j] = c) == 0) | |||||
break; | break; | ||||
} | } | ||||
fprintf(f_out,"%2d %-12s%s%c %-20s %-13s ", | |||||
p[0],lang_name,age_buf,genders[v->gender],buf,v->identifier); | |||||
} | |||||
else | |||||
{ | |||||
fprintf(f_out,"(%s %d)",lang_name,p[0]); | |||||
fprintf(f_out, "%2d %-12s%s%c %-20s %-13s ", | |||||
p[0], lang_name, age_buf, genders[v->gender], buf, v->identifier); | |||||
} else { | |||||
fprintf(f_out, "(%s %d)", lang_name, p[0]); | |||||
} | } | ||||
count++; | count++; | ||||
p += len+2; | p += len+2; | ||||
} | } | ||||
fputc('\n',f_out); | |||||
fputc('\n', f_out); | |||||
} | } | ||||
} | } | ||||
// 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++) | |||||
{ | |||||
fputc(value & 0xff,f); | |||||
for (ix = 0; ix < 4; ix++) { | |||||
fputc(value & 0xff, f); | |||||
value = value >> 8; | value = value >> 8; | ||||
} | } | ||||
} | } | ||||
int OpenWavFile(char *path, int rate) | int OpenWavFile(char *path, int rate) | ||||
{ | { | ||||
static unsigned char wave_hdr[44] = { | static unsigned char wave_hdr[44] = { | ||||
'R','I','F','F',0x24,0xf0,0xff,0x7f,'W','A','V','E','f','m','t',' ', | |||||
0x10,0,0,0,1,0,1,0, 9,0x3d,0,0,0x12,0x7a,0,0, | |||||
2,0,0x10,0,'d','a','t','a', 0x00,0xf0,0xff,0x7f | |||||
'R', 'I', 'F', 'F', 0x24, 0xf0, 0xff, 0x7f, 'W', 'A', 'V', 'E', 'f', 'm', 't', ' ', | |||||
0x10, 0, 0, 0, 1, 0, 1, 0, 9, 0x3d, 0, 0, 0x12, 0x7a, 0, 0, | |||||
2, 0, 0x10, 0, 'd', 'a', 't', 'a', 0x00, 0xf0, 0xff, 0x7f | |||||
}; | }; | ||||
if(path == NULL) | |||||
return(2); | |||||
if (path == NULL) | |||||
return (2); | |||||
while(isspace(*path)) path++; | |||||
while (isspace(*path)) path++; | |||||
f_wavfile = NULL; | f_wavfile = NULL; | ||||
if(path[0] != 0) | |||||
{ | |||||
if(strcmp(path,"stdout")==0) | |||||
if (path[0] != 0) { | |||||
if (strcmp(path, "stdout") == 0) | |||||
f_wavfile = stdout; | f_wavfile = stdout; | ||||
else | else | ||||
f_wavfile = fopen(path,"wb"); | |||||
f_wavfile = fopen(path, "wb"); | |||||
} | } | ||||
if(f_wavfile == NULL) | |||||
{ | |||||
fprintf(stderr,"Can't write to: '%s'\n",path); | |||||
return(1); | |||||
if (f_wavfile == NULL) { | |||||
fprintf(stderr, "Can't write to: '%s'\n", path); | |||||
return (1); | |||||
} | } | ||||
fwrite(wave_hdr,1,24,f_wavfile); | |||||
Write4Bytes(f_wavfile,rate); | |||||
Write4Bytes(f_wavfile,rate * 2); | |||||
fwrite(&wave_hdr[32],1,12,f_wavfile); | |||||
return(0); | |||||
fwrite(wave_hdr, 1, 24, f_wavfile); | |||||
Write4Bytes(f_wavfile, rate); | |||||
Write4Bytes(f_wavfile, rate * 2); | |||||
fwrite(&wave_hdr[32], 1, 12, f_wavfile); | |||||
return (0); | |||||
} | } | ||||
{ | { | ||||
unsigned int pos; | unsigned int pos; | ||||
if((f_wavfile==NULL) || (f_wavfile == stdout)) | |||||
if ((f_wavfile == NULL) || (f_wavfile == stdout)) | |||||
return; | return; | ||||
fflush(f_wavfile); | fflush(f_wavfile); | ||||
pos = ftell(f_wavfile); | pos = ftell(f_wavfile); | ||||
fseek(f_wavfile,4,SEEK_SET); | |||||
Write4Bytes(f_wavfile,pos - 8); | |||||
fseek(f_wavfile, 4, SEEK_SET); | |||||
Write4Bytes(f_wavfile, pos - 8); | |||||
fseek(f_wavfile,40,SEEK_SET); | |||||
Write4Bytes(f_wavfile,pos - 44); | |||||
fseek(f_wavfile, 40, SEEK_SET); | |||||
Write4Bytes(f_wavfile, pos - 44); | |||||
fclose(f_wavfile); | fclose(f_wavfile); | ||||
f_wavfile = NULL; | f_wavfile = NULL; | ||||
{ | { | ||||
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(); | ||||
return(0); | |||||
return (0); | |||||
} | } | ||||
while(events->type != 0) | |||||
{ | |||||
if(events->type == espeakEVENT_SAMPLERATE) | |||||
{ | |||||
while (events->type != 0) { | |||||
if (events->type == espeakEVENT_SAMPLERATE) { | |||||
samplerate = events->id.number; | samplerate = events->id.number; | ||||
samples_split = samples_split_seconds * samplerate; | samples_split = samples_split_seconds * samplerate; | ||||
} | |||||
else | |||||
if(events->type == espeakEVENT_SENTENCE) | |||||
{ | |||||
} else if (events->type == espeakEVENT_SENTENCE) { | |||||
// start a new WAV file when the limit is reached, at this sentence boundary | // start a new WAV file when the limit is reached, at this sentence boundary | ||||
if((samples_split > 0) && (samples_total > samples_split)) | |||||
{ | |||||
if ((samples_split > 0) && (samples_total > samples_split)) { | |||||
CloseWavFile(); | CloseWavFile(); | ||||
samples_total = 0; | samples_total = 0; | ||||
wavefile_count++; | wavefile_count++; | ||||
events++; | events++; | ||||
} | } | ||||
if(f_wavfile == NULL) | |||||
{ | |||||
if(samples_split > 0) | |||||
{ | |||||
sprintf(fname,"%s_%.2d%s",wavefile,wavefile_count+1,filetype); | |||||
if(OpenWavFile(fname, samplerate) != 0) | |||||
return(1); | |||||
} | |||||
else | |||||
{ | |||||
if(OpenWavFile(wavefile, samplerate) != 0) | |||||
return(1); | |||||
if (f_wavfile == NULL) { | |||||
if (samples_split > 0) { | |||||
sprintf(fname, "%s_%.2d%s", wavefile, wavefile_count+1, filetype); | |||||
if (OpenWavFile(fname, samplerate) != 0) | |||||
return (1); | |||||
} else { | |||||
if (OpenWavFile(wavefile, samplerate) != 0) | |||||
return (1); | |||||
} | } | ||||
} | } | ||||
if(numsamples > 0) | |||||
{ | |||||
if (numsamples > 0) { | |||||
samples_total += numsamples; | samples_total += numsamples; | ||||
fwrite(wav,numsamples*2,1,f_wavfile); | |||||
fwrite(wav, numsamples*2, 1, f_wavfile); | |||||
} | } | ||||
return(0); | |||||
return (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 | ||||
#define optional_argument 2 | #define optional_argument 2 | ||||
#endif | #endif | ||||
int main (int argc, char **argv) | |||||
int main(int argc, char **argv) | |||||
{ | { | ||||
static struct option long_options[] = | |||||
{ | |||||
{"help", no_argument, 0, 'h'}, | |||||
{"stdin", no_argument, 0, 0x100}, | |||||
{"compile-debug", optional_argument, 0, 0x101}, | |||||
{"compile", optional_argument, 0, 0x102}, | |||||
{"punct", optional_argument, 0, 0x103}, | |||||
{"voices", optional_argument, 0, 0x104}, | |||||
{"stdout", no_argument, 0, 0x105}, | |||||
{"split", optional_argument, 0, 0x106}, | |||||
{"path", required_argument, 0, 0x107}, | |||||
{"phonout", required_argument, 0, 0x108}, | |||||
{"pho", no_argument, 0, 0x109}, | |||||
{"ipa", optional_argument, 0, 0x10a}, | |||||
{"version", no_argument, 0, 0x10b}, | |||||
{"sep", optional_argument, 0, 0x10c}, | |||||
{"tie", optional_argument, 0, 0x10d}, | |||||
{"compile-mbrola", optional_argument, 0, 0x10e}, | |||||
{"compile-intonations", no_argument, 0, 0x10f}, | |||||
{"compile-phonemes", no_argument, 0, 0x110}, | |||||
{0, 0, 0, 0} | |||||
static struct option long_options[] = { | |||||
{ "help", no_argument, 0, 'h' }, | |||||
{ "stdin", no_argument, 0, 0x100 }, | |||||
{ "compile-debug", optional_argument, 0, 0x101 }, | |||||
{ "compile", optional_argument, 0, 0x102 }, | |||||
{ "punct", optional_argument, 0, 0x103 }, | |||||
{ "voices", optional_argument, 0, 0x104 }, | |||||
{ "stdout", no_argument, 0, 0x105 }, | |||||
{ "split", optional_argument, 0, 0x106 }, | |||||
{ "path", required_argument, 0, 0x107 }, | |||||
{ "phonout", required_argument, 0, 0x108 }, | |||||
{ "pho", no_argument, 0, 0x109 }, | |||||
{ "ipa", optional_argument, 0, 0x10a }, | |||||
{ "version", no_argument, 0, 0x10b }, | |||||
{ "sep", optional_argument, 0, 0x10c }, | |||||
{ "tie", optional_argument, 0, 0x10d }, | |||||
{ "compile-mbrola", optional_argument, 0, 0x10e }, | |||||
{ "compile-intonations", no_argument, 0, 0x10f }, | |||||
{ "compile-phonemes", no_argument, 0, 0x110 }, | |||||
{ 0, 0, 0, 0 } | |||||
}; | }; | ||||
static const char* err_load = "Failed to read "; | |||||
static const char *err_load = "Failed to read "; | |||||
FILE *f_text=NULL; | |||||
char *p_text=NULL; | |||||
FILE *f_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 | ||||
#ifdef NEED_GETOPT | #ifdef NEED_GETOPT | ||||
optind = 1; | optind = 1; | ||||
opt_string = ""; | opt_string = ""; | ||||
while(optind < argc) | |||||
{ | |||||
while (optind < argc) { | |||||
int len; | int len; | ||||
char *p; | char *p; | ||||
if((c = *opt_string) == 0) | |||||
{ | |||||
if ((c = *opt_string) == 0) { | |||||
opt_string = argv[optind]; | opt_string = argv[optind]; | ||||
if(opt_string[0] != '-') | |||||
if (opt_string[0] != '-') | |||||
break; | break; | ||||
optind++; | optind++; | ||||
opt_string++; | opt_string++; | ||||
p = optarg2 = opt_string; | p = optarg2 = opt_string; | ||||
if(c == '-') | |||||
{ | |||||
if(p[0] == 0) | |||||
if (c == '-') { | |||||
if (p[0] == 0) | |||||
break; // -- means don't interpret further - as commands | break; // -- means don't interpret further - as commands | ||||
opt_string=""; | |||||
for(ix=0;; ix++) | |||||
{ | |||||
if(long_options[ix].name == 0) | |||||
opt_string = ""; | |||||
for (ix = 0;; ix++) { | |||||
if (long_options[ix].name == 0) | |||||
break; | break; | ||||
len = strlen(long_options[ix].name); | len = strlen(long_options[ix].name); | ||||
if(memcmp(long_options[ix].name,p,len)==0) | |||||
{ | |||||
if (memcmp(long_options[ix].name, p, len) == 0) { | |||||
c = long_options[ix].val; | c = long_options[ix].val; | ||||
optarg2 = NULL; | optarg2 = NULL; | ||||
if((long_options[ix].has_arg != 0) && (p[len]=='=')) | |||||
{ | |||||
if ((long_options[ix].has_arg != 0) && (p[len] == '=')) { | |||||
optarg2 = &p[len+1]; | optarg2 = &p[len+1]; | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
} | |||||
else | |||||
if(strchr(arg_opts,c) != NULL) | |||||
{ | |||||
opt_string=""; | |||||
if(optarg2[0]==0) | |||||
{ | |||||
} else if (strchr(arg_opts, c) != NULL) { | |||||
opt_string = ""; | |||||
if (optarg2[0] == 0) { | |||||
// the option's value is in the next argument | // the option's value is in the next argument | ||||
optarg2 = argv[optind++]; | optarg2 = argv[optind++]; | ||||
} | } | ||||
} | } | ||||
#else | #else | ||||
while(true) | |||||
{ | |||||
c = getopt_long (argc, argv, "a:b:f:g:hk:l:mp:qs:v:w:xXz", | |||||
long_options, &option_index); | |||||
while (true) { | |||||
c = getopt_long(argc, argv, "a:b:f:g:hk:l:mp:qs:v:w:xXz", | |||||
long_options, &option_index); | |||||
/* Detect the end of the options. */ | /* Detect the end of the options. */ | ||||
if (c == -1) | if (c == -1) | ||||
{ | { | ||||
case 'b': | case 'b': | ||||
// input character encoding, 8bit, 16bit, UTF8 | // input character encoding, 8bit, 16bit, UTF8 | ||||
if((sscanf(optarg2,"%d",&value) == 1) && (value <= 4)) | |||||
if ((sscanf(optarg2, "%d", &value) == 1) && (value <= 4)) | |||||
synth_flags |= value; | synth_flags |= value; | ||||
else | else | ||||
synth_flags |= espeakCHARS_8BIT; | synth_flags |= espeakCHARS_8BIT; | ||||
break; | break; | ||||
case 'f': | case 'f': | ||||
strncpy0(filename,optarg2,sizeof(filename)); | |||||
strncpy0(filename, optarg2, sizeof(filename)); | |||||
break; | break; | ||||
case 'l': | case 'l': | ||||
break; | break; | ||||
case 'v': | case 'v': | ||||
strncpy0(voicename,optarg2,sizeof(voicename)); | |||||
strncpy0(voicename, optarg2, sizeof(voicename)); | |||||
break; | break; | ||||
case 'w': | case 'w': | ||||
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 | ||||
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 0x101: // --compile-debug | ||||
case 0x102: // --compile | 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; | ||||
while((ix < N_PUNCTLIST) && ((option_punctlist[ix] = optarg2[ix]) != 0)) ix++; | |||||
while ((ix < N_PUNCTLIST) && ((option_punctlist[ix] = optarg2[ix]) != 0)) ix++; | |||||
option_punctlist[N_PUNCTLIST-1] = 0; | option_punctlist[N_PUNCTLIST-1] = 0; | ||||
option_punctuation = 2; | option_punctuation = 2; | ||||
} | } | ||||
break; | break; | ||||
case 0x104: // --voices | case 0x104: // --voices | ||||
espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS,0,data_path,0); | |||||
DisplayVoices(stdout,optarg2); | |||||
espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS, 0, data_path, 0); | |||||
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 0x108: // --phonout | case 0x108: // --phonout | ||||
if((f_phonemes_out = fopen(optarg2,"w")) == NULL) | |||||
{ | |||||
fprintf(stderr,"Can't write to: %s\n",optarg2); | |||||
if ((f_phonemes_out = fopen(optarg2, "w")) == NULL) { | |||||
fprintf(stderr, "Can't write to: %s\n", optarg2); | |||||
} | } | ||||
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 | ||||
switch(atoi(optarg2)) | |||||
switch (atoi(optarg2)) | |||||
{ | { | ||||
case 1: | case 1: | ||||
phonemes_separator = '_'; | phonemes_separator = '_'; | ||||
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(option_waveout || quiet) | |||||
{ | |||||
if (option_waveout || quiet) { | |||||
// writing to a file (or no output), we can use synchronous mode | // writing to a file (or no output), we can use synchronous mode | ||||
samplerate = espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS,0,data_path,0); | |||||
samplerate = espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS, 0, data_path, 0); | |||||
samples_split = samplerate * samples_split_seconds; | samples_split = samplerate * samples_split_seconds; | ||||
espeak_SetSynthCallback(SynthCallback); | espeak_SetSynthCallback(SynthCallback); | ||||
if(samples_split) | |||||
{ | |||||
if (samples_split) { | |||||
char *extn; | char *extn; | ||||
extn = strrchr(wavefile,'.'); | |||||
if((extn != NULL) && ((wavefile + strlen(wavefile) - extn) <= 4)) | |||||
{ | |||||
strcpy(filetype,extn); | |||||
extn = strrchr(wavefile, '.'); | |||||
if ((extn != NULL) && ((wavefile + strlen(wavefile) - extn) <= 4)) { | |||||
strcpy(filetype, extn); | |||||
*extn = 0; | *extn = 0; | ||||
} | } | ||||
} | } | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
// play the sound output | // play the sound output | ||||
samplerate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK,0,data_path,0); | |||||
samplerate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, data_path, 0); | |||||
} | } | ||||
if(voicename[0] == 0) | |||||
strcpy(voicename,"default"); | |||||
if (voicename[0] == 0) | |||||
strcpy(voicename, "default"); | |||||
if(espeak_SetVoiceByName(voicename) != EE_OK) | |||||
{ | |||||
memset(&voice_select,0,sizeof(voice_select)); | |||||
if (espeak_SetVoiceByName(voicename) != EE_OK) { | |||||
memset(&voice_select, 0, sizeof(voice_select)); | |||||
voice_select.languages = voicename; | voice_select.languages = voicename; | ||||
if(espeak_SetVoiceByProperties(&voice_select) != EE_OK) | |||||
{ | |||||
fprintf(stderr,"%svoice '%s'\n",err_load,voicename); | |||||
if (espeak_SetVoiceByProperties(&voice_select) != EE_OK) { | |||||
fprintf(stderr, "%svoice '%s'\n", err_load, voicename); | |||||
exit(2); | exit(2); | ||||
} | } | ||||
} | } | ||||
if(flag_compile) | |||||
{ | |||||
if (flag_compile) { | |||||
// This must be done after the voice is set | // This must be done after the voice is set | ||||
espeak_CompileDictionary("", stderr, flag_compile & 0x1); | espeak_CompileDictionary("", stderr, flag_compile & 0x1); | ||||
exit(0); | exit(0); | ||||
} | } | ||||
// set any non-default values of parameters. This must be done after espeak_Initialize() | // set any non-default values of parameters. This must be done after espeak_Initialize() | ||||
if(speed > 0) | |||||
espeak_SetParameter(espeakRATE,speed,0); | |||||
if(volume >= 0) | |||||
espeak_SetParameter(espeakVOLUME,volume,0); | |||||
if(pitch >= 0) | |||||
espeak_SetParameter(espeakPITCH,pitch,0); | |||||
if(option_capitals >= 0) | |||||
espeak_SetParameter(espeakCAPITALS,option_capitals,0); | |||||
if(option_punctuation >= 0) | |||||
espeak_SetParameter(espeakPUNCTUATION,option_punctuation,0); | |||||
if(wordgap >= 0) | |||||
espeak_SetParameter(espeakWORDGAP,wordgap,0); | |||||
if(option_linelength > 0) | |||||
espeak_SetParameter(espeakLINELENGTH,option_linelength,0); | |||||
if(option_punctuation == 2) | |||||
if (speed > 0) | |||||
espeak_SetParameter(espeakRATE, speed, 0); | |||||
if (volume >= 0) | |||||
espeak_SetParameter(espeakVOLUME, volume, 0); | |||||
if (pitch >= 0) | |||||
espeak_SetParameter(espeakPITCH, pitch, 0); | |||||
if (option_capitals >= 0) | |||||
espeak_SetParameter(espeakCAPITALS, option_capitals, 0); | |||||
if (option_punctuation >= 0) | |||||
espeak_SetParameter(espeakPUNCTUATION, option_punctuation, 0); | |||||
if (wordgap >= 0) | |||||
espeak_SetParameter(espeakWORDGAP, wordgap, 0); | |||||
if (option_linelength > 0) | |||||
espeak_SetParameter(espeakLINELENGTH, option_linelength, 0); | |||||
if (option_punctuation == 2) | |||||
espeak_SetPunctuationList(option_punctlist); | espeak_SetPunctuationList(option_punctlist); | ||||
espeak_SetPhonemeTrace(phoneme_options | (phonemes_separator << 8), f_phonemes_out); | espeak_SetPhonemeTrace(phoneme_options | (phonemes_separator << 8), f_phonemes_out); | ||||
if(filename[0]==0) | |||||
{ | |||||
if((optind < argc) && (flag_stdin == 0)) | |||||
{ | |||||
if (filename[0] == 0) { | |||||
if ((optind < argc) && (flag_stdin == 0)) { | |||||
// there's a non-option parameter, and no -f or --stdin | // there's a non-option parameter, and no -f or --stdin | ||||
// use it as text | // use it as text | ||||
p_text = argv[optind]; | p_text = argv[optind]; | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
f_text = stdin; | f_text = stdin; | ||||
if(flag_stdin == 0) | |||||
{ | |||||
if (flag_stdin == 0) { | |||||
flag_stdin = 2; | flag_stdin = 2; | ||||
} | } | ||||
} | } | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
filesize = GetFileLength(filename); | filesize = GetFileLength(filename); | ||||
f_text = fopen(filename,"r"); | |||||
f_text = fopen(filename, "r"); | |||||
} | } | ||||
if((f_text == NULL) && (p_text == NULL)) | |||||
{ | |||||
fprintf(stderr,"%sfile '%s'\n",err_load,filename); | |||||
if ((f_text == NULL) && (p_text == NULL)) { | |||||
fprintf(stderr, "%sfile '%s'\n", err_load, filename); | |||||
exit(1); | exit(1); | ||||
} | } | ||||
if(p_text != NULL) | |||||
{ | |||||
if (p_text != NULL) { | |||||
int size; | int size; | ||||
size = strlen(p_text); | size = strlen(p_text); | ||||
espeak_Synth(p_text,size+1,0,POS_CHARACTER,0,synth_flags,NULL,NULL); | |||||
} | |||||
else | |||||
if(flag_stdin) | |||||
{ | |||||
espeak_Synth(p_text, size+1, 0, POS_CHARACTER, 0, synth_flags, NULL, NULL); | |||||
} else if (flag_stdin) { | |||||
int max = 1000; | int max = 1000; | ||||
p_text = (char *)malloc(max); | p_text = (char *)malloc(max); | ||||
if(flag_stdin == 2) | |||||
{ | |||||
if (flag_stdin == 2) { | |||||
// line by line input on stdin | // line by line input on stdin | ||||
while(fgets(p_text,max,stdin) != NULL) | |||||
{ | |||||
while (fgets(p_text, max, stdin) != NULL) { | |||||
p_text[max-1] = 0; | p_text[max-1] = 0; | ||||
espeak_Synth(p_text,max,0,POS_CHARACTER,0,synth_flags,NULL,NULL); | |||||
espeak_Synth(p_text, max, 0, POS_CHARACTER, 0, synth_flags, NULL, NULL); | |||||
} | } | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
// bulk input on stdin | // bulk input on stdin | ||||
ix = 0; | ix = 0; | ||||
while(!feof(stdin)) | |||||
{ | |||||
while (!feof(stdin)) { | |||||
p_text[ix++] = fgetc(stdin); | p_text[ix++] = fgetc(stdin); | ||||
if(ix >= (max-1)) | |||||
{ | |||||
if (ix >= (max-1)) { | |||||
max += 1000; | max += 1000; | ||||
p_text = (char *)realloc(p_text,max); | |||||
p_text = (char *)realloc(p_text, max); | |||||
} | } | ||||
} | } | ||||
if(ix > 0) | |||||
{ | |||||
if (ix > 0) { | |||||
p_text[ix-1] = 0; | p_text[ix-1] = 0; | ||||
espeak_Synth(p_text,ix+1,0,POS_CHARACTER,0,synth_flags,NULL,NULL); | |||||
espeak_Synth(p_text, ix+1, 0, POS_CHARACTER, 0, synth_flags, NULL, NULL); | |||||
} | } | ||||
} | } | ||||
} | |||||
else | |||||
if(f_text != NULL) | |||||
{ | |||||
if((p_text = (char *)malloc(filesize+1)) == NULL) | |||||
{ | |||||
fprintf(stderr,"Failed to allocate memory %d bytes",filesize); | |||||
} else if (f_text != NULL) { | |||||
if ((p_text = (char *)malloc(filesize+1)) == NULL) { | |||||
fprintf(stderr, "Failed to allocate memory %d bytes", filesize); | |||||
exit(3); | exit(3); | ||||
} | } | ||||
fread(p_text,1,filesize,f_text); | |||||
p_text[filesize]=0; | |||||
espeak_Synth(p_text,filesize+1,0,POS_CHARACTER,0,synth_flags,NULL,NULL); | |||||
fread(p_text, 1, filesize, f_text); | |||||
p_text[filesize] = 0; | |||||
espeak_Synth(p_text, filesize+1, 0, POS_CHARACTER, 0, synth_flags, NULL, NULL); | |||||
fclose(f_text); | fclose(f_text); | ||||
} | } | ||||
if(espeak_Synchronize() != EE_OK) | |||||
{ | |||||
if (espeak_Synchronize() != EE_OK) { | |||||
fprintf(stderr, "espeak_Synchronize() failed, maybe error when opening output device\n"); | fprintf(stderr, "espeak_Synchronize() failed, maybe error when opening output device\n"); | ||||
exit(4); | exit(4); | ||||
} | } | ||||
if(f_phonemes_out != stdout) | |||||
if (f_phonemes_out != stdout) | |||||
fclose(f_phonemes_out); // needed for WinCE | fclose(f_phonemes_out); // needed for WinCE | ||||
return(0); | |||||
return (0); | |||||
} | } |
unsigned char c; | unsigned char c; | ||||
unsigned int word; | unsigned int word; | ||||
if(string==NULL) | |||||
return(0); | |||||
if (string == NULL) | |||||
return (0); | |||||
word = 0; | word = 0; | ||||
for(ix=0; ix<4; ix++) | |||||
{ | |||||
if(string[ix]==0) break; | |||||
for (ix = 0; ix < 4; ix++) { | |||||
if (string[ix] == 0) break; | |||||
c = string[ix]; | c = string[ix]; | ||||
word |= (c << (ix*8)); | word |= (c << (ix*8)); | ||||
} | } | ||||
return(word); | |||||
return (word); | |||||
} | } | ||||
#pragma GCC visibility push(default) | #pragma GCC visibility push(default) | ||||
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); | |||||
if((f_in = fopen(buf,"r")) == NULL) | |||||
{ | |||||
strcpy(buf, filepath); | |||||
if ((f_in = fopen(buf, "r")) == NULL) { | |||||
fprintf(log, "Can't read: %s\n", filepath); | fprintf(log, "Can't read: %s\n", filepath); | ||||
return ENE_READ_ERROR; | return ENE_READ_ERROR; | ||||
} | } | ||||
while(fgets(buf,sizeof(phoneme),f_in) != NULL) | |||||
{ | |||||
while (fgets(buf, sizeof(phoneme), f_in) != NULL) { | |||||
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]); | ||||
continue; | continue; | ||||
} | } | ||||
n = sscanf(buf,"%d %s %s %d %s %s",&control,phoneme,phoneme2,&percent,name1,name2); | |||||
if(n >= 5) | |||||
{ | |||||
n = sscanf(buf, "%d %s %s %d %s %s", &control, phoneme, phoneme2, &percent, name1, name2); | |||||
if (n >= 5) { | |||||
data[count].name = StringToWord(phoneme); | data[count].name = StringToWord(phoneme); | ||||
if(strcmp(phoneme2,"NULL")==0) | |||||
if (strcmp(phoneme2, "NULL") == 0) | |||||
data[count].next_phoneme = 0; | data[count].next_phoneme = 0; | ||||
else | |||||
if(strcmp(phoneme2,"VWL")==0) | |||||
else if (strcmp(phoneme2, "VWL") == 0) | |||||
data[count].next_phoneme = 2; | data[count].next_phoneme = 2; | ||||
else | else | ||||
data[count].next_phoneme = StringToWord(phoneme2); | data[count].next_phoneme = StringToWord(phoneme2); | ||||
data[count].mbr_name2 = 0; | data[count].mbr_name2 = 0; | ||||
data[count].percent = percent; | data[count].percent = percent; | ||||
data[count].control = control; | data[count].control = control; | ||||
if(strcmp(name1,"NULL")!=0) | |||||
if (strcmp(name1, "NULL") != 0) | |||||
data[count].mbr_name = StringToWord(name1); | data[count].mbr_name = StringToWord(name1); | ||||
if(n == 6) | |||||
if (n == 6) | |||||
data[count].mbr_name2 = StringToWord(name2); | data[count].mbr_name2 = StringToWord(name2); | ||||
count++; | count++; | ||||
} | } | ||||
fclose(f_in); | fclose(f_in); | ||||
strcpy(mbrola_voice,basename(filepath)); | |||||
sprintf(buf,"%s/mbrola_ph/%s_phtrans",path_home,mbrola_voice); | |||||
if((f_out = fopen(buf,"wb")) == NULL) | |||||
{ | |||||
strcpy(mbrola_voice, basename(filepath)); | |||||
sprintf(buf, "%s/mbrola_ph/%s_phtrans", path_home, mbrola_voice); | |||||
if ((f_out = fopen(buf, "wb")) == NULL) { | |||||
fprintf(log, "Can't write to: %s\n", buf); | fprintf(log, "Can't write to: %s\n", buf); | ||||
return ENE_WRITE_ERROR; | return ENE_WRITE_ERROR; | ||||
} | } | ||||
Write4Bytes(f_out, mbrola_ctrl); | Write4Bytes(f_out, mbrola_ctrl); | ||||
pw_end = (int *)(&data[count+1]); | pw_end = (int *)(&data[count+1]); | ||||
for(pw = (int *)data; pw < pw_end; pw++) | |||||
{ | |||||
for (pw = (int *)data; pw < pw_end; pw++) { | |||||
Write4Bytes(f_out, *pw); | Write4Bytes(f_out, *pw); | ||||
} | } | ||||
fclose(f_out); | fclose(f_out); |
#include <sys/time.h> | #include <sys/time.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
static FILE* fd_log=NULL; | |||||
static const char* FILENAME="/tmp/espeak.log"; | |||||
static FILE *fd_log = NULL; | |||||
static const char *FILENAME = "/tmp/espeak.log"; | |||||
void debug_init() | void debug_init() | ||||
{ | { | ||||
if((fd_log = fopen(FILENAME,"a")) != NULL) | |||||
if ((fd_log = fopen(FILENAME, "a")) != NULL) | |||||
setvbuf(fd_log, NULL, _IONBF, 0); | setvbuf(fd_log, NULL, _IONBF, 0); | ||||
} | } | ||||
void debug_enter(const char* text) | |||||
void debug_enter(const char *text) | |||||
{ | { | ||||
struct timeval tv; | struct timeval tv; | ||||
gettimeofday(&tv, NULL); | gettimeofday(&tv, NULL); | ||||
if (!fd_log) | |||||
{ | |||||
if (!fd_log) { | |||||
debug_init(); | debug_init(); | ||||
} | } | ||||
if (fd_log) | |||||
{ | |||||
fprintf(fd_log, "%03d.%03dms > ENTER %s\n",(int)(tv.tv_sec%1000), (int)(tv.tv_usec/1000), text); | |||||
if (fd_log) { | |||||
fprintf(fd_log, "%03d.%03dms > ENTER %s\n", (int)(tv.tv_sec%1000), (int)(tv.tv_usec/1000), text); | |||||
} | } | ||||
} | } | ||||
{ | { | ||||
va_list args; | va_list args; | ||||
va_start(args, format); | va_start(args, format); | ||||
if (!fd_log) | |||||
{ | |||||
if (!fd_log) { | |||||
debug_init(); | debug_init(); | ||||
} | } | ||||
if (fd_log) | |||||
{ | |||||
if (fd_log) { | |||||
vfprintf(fd_log, format, args); | vfprintf(fd_log, format, args); | ||||
} | } | ||||
va_end(args); | va_end(args); | ||||
} | } | ||||
void debug_time(const char* text) | |||||
void debug_time(const char *text) | |||||
{ | { | ||||
struct timeval tv; | struct timeval tv; | ||||
gettimeofday(&tv, NULL); | gettimeofday(&tv, NULL); | ||||
if (!fd_log) | |||||
{ | |||||
if (!fd_log) { | |||||
debug_init(); | debug_init(); | ||||
} | } | ||||
if (fd_log) | |||||
{ | |||||
fprintf(fd_log, "%03d.%03dms > %s\n",(int)(tv.tv_sec%1000), (int)(tv.tv_usec/1000), text); | |||||
if (fd_log) { | |||||
fprintf(fd_log, "%03d.%03dms > %s\n", (int)(tv.tv_sec%1000), (int)(tv.tv_usec/1000), text); | |||||
} | } | ||||
} | } | ||||
#ifdef DEBUG_ENABLED | #ifdef DEBUG_ENABLED | ||||
#define ENTER(text) debug_enter(text) | #define ENTER(text) debug_enter(text) | ||||
#define SHOW(format,...) debug_show(format,__VA_ARGS__); | |||||
#define SHOW(format, ...) debug_show(format, __VA_ARGS__); | |||||
#define SHOW_TIME(text) debug_time(text); | #define SHOW_TIME(text) debug_time(text); | ||||
extern void debug_enter(const char* text); | |||||
extern void debug_show(const char* format,...); | |||||
extern void debug_time(const char* text); | |||||
extern void debug_enter(const char *text); | |||||
extern void debug_show(const char *format, ...); | |||||
extern void debug_time(const char *text); | |||||
#else | #else | ||||
#ifdef NO_VARIADIC_MACROS | #ifdef NO_VARIADIC_MACROS | ||||
#define SHOW(format) // VC6 doesn't allow "..." | #define SHOW(format) // VC6 doesn't allow "..." | ||||
#else | #else | ||||
#define SHOW(format,...) | |||||
#define SHOW(format, ...) | |||||
#endif | #endif | ||||
#define SHOW_TIME(text) | #define SHOW_TIME(text) | ||||
#define ENTER(text) | #define ENTER(text) |
#include "debug.h" | #include "debug.h" | ||||
static unsigned int my_current_text_id=0; | |||||
static unsigned int my_current_text_id = 0; | |||||
t_espeak_command* create_espeak_text(const void *text, size_t size, unsigned int position, espeak_POSITION_TYPE position_type, unsigned int end_position, unsigned int flags, void* user_data) | |||||
t_espeak_command *create_espeak_text(const void *text, size_t size, unsigned int position, espeak_POSITION_TYPE position_type, unsigned int end_position, unsigned int flags, void *user_data) | |||||
{ | { | ||||
ENTER("create_espeak_text"); | ENTER("create_espeak_text"); | ||||
int a_error=1; | |||||
void* a_text = NULL; | |||||
t_espeak_text* data = NULL; | |||||
t_espeak_command* a_command = (t_espeak_command*)malloc(sizeof(t_espeak_command)); | |||||
int a_error = 1; | |||||
void *a_text = NULL; | |||||
t_espeak_text *data = NULL; | |||||
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | |||||
if (!text || !size || !a_command) | |||||
{ | |||||
if (!text || !size || !a_command) { | |||||
goto text_error; | goto text_error; | ||||
} | } | ||||
a_text = malloc( size+1 ); | |||||
if (!a_text) | |||||
{ | |||||
a_text = malloc(size+1); | |||||
if (!a_text) { | |||||
goto text_error; | goto text_error; | ||||
} | } | ||||
memcpy(a_text, text, size); | memcpy(a_text, text, size); | ||||
data->end_position = end_position; | data->end_position = end_position; | ||||
data->flags = flags; | data->flags = flags; | ||||
data->user_data = user_data; | data->user_data = user_data; | ||||
a_error=0; | |||||
a_error = 0; | |||||
SHOW("ET_TEXT malloc text=%x, command=%x (uid=%d)\n", a_text, a_command, data->unique_identifier); | SHOW("ET_TEXT malloc text=%x, command=%x (uid=%d)\n", a_text, a_command, data->unique_identifier); | ||||
text_error: | text_error: | ||||
if (a_error) | |||||
{ | |||||
if (a_text) | |||||
{ | |||||
free (a_text); | |||||
if (a_error) { | |||||
if (a_text) { | |||||
free(a_text); | |||||
} | } | ||||
if (a_command) | |||||
{ | |||||
free (a_command); | |||||
if (a_command) { | |||||
free(a_command); | |||||
} | } | ||||
a_command = NULL; | a_command = NULL; | ||||
} | } | ||||
return a_command; | return a_command; | ||||
} | } | ||||
t_espeak_command* create_espeak_terminated_msg(unsigned int unique_identifier, void* user_data) | |||||
t_espeak_command *create_espeak_terminated_msg(unsigned int unique_identifier, void *user_data) | |||||
{ | { | ||||
ENTER("create_espeak_terminated_msg"); | ENTER("create_espeak_terminated_msg"); | ||||
int a_error=1; | |||||
t_espeak_terminated_msg* data = NULL; | |||||
t_espeak_command* a_command = (t_espeak_command*)malloc(sizeof(t_espeak_command)); | |||||
int a_error = 1; | |||||
t_espeak_terminated_msg *data = NULL; | |||||
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | |||||
if (!a_command) | |||||
{ | |||||
if (!a_command) { | |||||
goto msg_error; | goto msg_error; | ||||
} | } | ||||
data = &(a_command->u.my_terminated_msg); | data = &(a_command->u.my_terminated_msg); | ||||
data->unique_identifier = unique_identifier; | data->unique_identifier = unique_identifier; | ||||
data->user_data = user_data; | data->user_data = user_data; | ||||
a_error=0; | |||||
a_error = 0; | |||||
SHOW("ET_TERMINATED_MSG command=%x (uid=%d, user_data=0x%x)\n", a_command, unique_identifier, (int)user_data); | SHOW("ET_TERMINATED_MSG command=%x (uid=%d, user_data=0x%x)\n", a_command, unique_identifier, (int)user_data); | ||||
msg_error: | msg_error: | ||||
if (a_error) | |||||
{ | |||||
if (a_command) | |||||
{ | |||||
free (a_command); | |||||
if (a_error) { | |||||
if (a_command) { | |||||
free(a_command); | |||||
} | } | ||||
a_command = NULL; | a_command = NULL; | ||||
} | } | ||||
} | } | ||||
t_espeak_command* create_espeak_mark(const void *text, size_t size, const char *index_mark, unsigned int end_position, unsigned int flags, void* user_data) | |||||
t_espeak_command *create_espeak_mark(const void *text, size_t size, const char *index_mark, unsigned int end_position, unsigned int flags, void *user_data) | |||||
{ | { | ||||
ENTER("create_espeak_mark"); | ENTER("create_espeak_mark"); | ||||
int a_error=1; | |||||
void* a_text = NULL; | |||||
int a_error = 1; | |||||
void *a_text = NULL; | |||||
char *a_index_mark = NULL; | char *a_index_mark = NULL; | ||||
t_espeak_mark* data = NULL; | |||||
t_espeak_command* a_command = (t_espeak_command*)malloc(sizeof(t_espeak_command)); | |||||
t_espeak_mark *data = NULL; | |||||
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | |||||
if (!text || !size || !index_mark || !a_command) | |||||
{ | |||||
if (!text || !size || !index_mark || !a_command) { | |||||
goto mark_error; | goto mark_error; | ||||
} | } | ||||
a_text = malloc( size ); | |||||
if (!a_text) | |||||
{ | |||||
a_text = malloc(size); | |||||
if (!a_text) { | |||||
goto mark_error; | goto mark_error; | ||||
} | } | ||||
memcpy(a_text, text, size); | memcpy(a_text, text, size); | ||||
a_index_mark = strdup( index_mark); | |||||
a_index_mark = strdup(index_mark); | |||||
a_command->type = ET_MARK; | a_command->type = ET_MARK; | ||||
a_command->state = CS_UNDEFINED; | a_command->state = CS_UNDEFINED; | ||||
data->end_position = end_position; | data->end_position = end_position; | ||||
data->flags = flags; | data->flags = flags; | ||||
data->user_data = user_data; | data->user_data = user_data; | ||||
a_error=0; | |||||
a_error = 0; | |||||
mark_error: | mark_error: | ||||
if (a_error) | |||||
{ | |||||
if (a_text) | |||||
{ | |||||
free (a_text); | |||||
if (a_error) { | |||||
if (a_text) { | |||||
free(a_text); | |||||
} | } | ||||
if (a_command) | |||||
{ | |||||
free (a_command); | |||||
if (a_command) { | |||||
free(a_command); | |||||
} | } | ||||
a_command = NULL; | a_command = NULL; | ||||
if (a_index_mark) | |||||
{ | |||||
free (a_index_mark); | |||||
if (a_index_mark) { | |||||
free(a_index_mark); | |||||
} | } | ||||
} | } | ||||
return a_command; | return a_command; | ||||
} | } | ||||
t_espeak_command* create_espeak_key(const char *key_name, void *user_data) | |||||
t_espeak_command *create_espeak_key(const char *key_name, void *user_data) | |||||
{ | { | ||||
ENTER("create_espeak_key"); | ENTER("create_espeak_key"); | ||||
int a_error=1; | |||||
t_espeak_command* a_command = (t_espeak_command*)malloc(sizeof(t_espeak_command)); | |||||
int a_error = 1; | |||||
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | |||||
if (!key_name || !a_command) | |||||
{ | |||||
if (!key_name || !a_command) { | |||||
goto key_error; | goto key_error; | ||||
} | } | ||||
a_command->state = CS_UNDEFINED; | a_command->state = CS_UNDEFINED; | ||||
a_command->u.my_key.user_data = user_data; | a_command->u.my_key.user_data = user_data; | ||||
a_command->u.my_key.unique_identifier = ++my_current_text_id; | a_command->u.my_key.unique_identifier = ++my_current_text_id; | ||||
a_command->u.my_key.key_name = strdup( key_name); | |||||
a_error=0; | |||||
a_command->u.my_key.key_name = strdup(key_name); | |||||
a_error = 0; | |||||
key_error: | key_error: | ||||
if (a_error) | |||||
{ | |||||
if (a_command) | |||||
{ | |||||
free (a_command); | |||||
if (a_error) { | |||||
if (a_command) { | |||||
free(a_command); | |||||
} | } | ||||
a_command = NULL; | a_command = NULL; | ||||
} | } | ||||
return a_command; | return a_command; | ||||
} | } | ||||
t_espeak_command* create_espeak_char(wchar_t character, void* user_data) | |||||
t_espeak_command *create_espeak_char(wchar_t character, void *user_data) | |||||
{ | { | ||||
ENTER("create_espeak_char"); | ENTER("create_espeak_char"); | ||||
int a_error=1; | |||||
t_espeak_command* a_command = (t_espeak_command*)malloc(sizeof(t_espeak_command)); | |||||
if (!a_command) | |||||
{ | |||||
int a_error = 1; | |||||
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | |||||
if (!a_command) { | |||||
goto char_error; | goto char_error; | ||||
} | } | ||||
a_command->u.my_char.user_data = user_data; | a_command->u.my_char.user_data = user_data; | ||||
a_command->u.my_char.unique_identifier = ++my_current_text_id; | a_command->u.my_char.unique_identifier = ++my_current_text_id; | ||||
a_command->u.my_char.character = character; | a_command->u.my_char.character = character; | ||||
a_error=0; | |||||
a_error = 0; | |||||
char_error: | char_error: | ||||
if (a_error) | |||||
{ | |||||
if (a_command) | |||||
{ | |||||
free (a_command); | |||||
if (a_error) { | |||||
if (a_command) { | |||||
free(a_command); | |||||
} | } | ||||
a_command = NULL; | a_command = NULL; | ||||
} | } | ||||
return a_command; | return a_command; | ||||
} | } | ||||
t_espeak_command* create_espeak_parameter(espeak_PARAMETER parameter, int value, int relative) | |||||
t_espeak_command *create_espeak_parameter(espeak_PARAMETER parameter, int value, int relative) | |||||
{ | { | ||||
ENTER("create_espeak_parameter"); | ENTER("create_espeak_parameter"); | ||||
int a_error=1; | |||||
t_espeak_parameter* data = NULL; | |||||
t_espeak_command* a_command = (t_espeak_command*)malloc(sizeof(t_espeak_command)); | |||||
if (!a_command) | |||||
{ | |||||
int a_error = 1; | |||||
t_espeak_parameter *data = NULL; | |||||
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | |||||
if (!a_command) { | |||||
goto param_error; | goto param_error; | ||||
} | } | ||||
data->parameter = parameter; | data->parameter = parameter; | ||||
data->value = value; | data->value = value; | ||||
data->relative = relative; | data->relative = relative; | ||||
a_error=0; | |||||
a_error = 0; | |||||
param_error: | param_error: | ||||
if (a_error) | |||||
{ | |||||
if (a_command) | |||||
{ | |||||
free (a_command); | |||||
if (a_error) { | |||||
if (a_command) { | |||||
free(a_command); | |||||
} | } | ||||
a_command = NULL; | a_command = NULL; | ||||
} | } | ||||
return a_command; | return a_command; | ||||
} | } | ||||
t_espeak_command* create_espeak_punctuation_list(const wchar_t *punctlist) | |||||
t_espeak_command *create_espeak_punctuation_list(const wchar_t *punctlist) | |||||
{ | { | ||||
ENTER("create_espeak_punctuation_list"); | ENTER("create_espeak_punctuation_list"); | ||||
int a_error=1; | |||||
t_espeak_command* a_command = (t_espeak_command*)malloc(sizeof(t_espeak_command)); | |||||
int a_error = 1; | |||||
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | |||||
if (!punctlist || !a_command) | |||||
{ | |||||
if (!punctlist || !a_command) { | |||||
goto list_error; | goto list_error; | ||||
} | } | ||||
{ | { | ||||
size_t len = (wcslen(punctlist) + 1)*sizeof(wchar_t); | size_t len = (wcslen(punctlist) + 1)*sizeof(wchar_t); | ||||
wchar_t* a_list = (wchar_t*)malloc(len); | |||||
wchar_t *a_list = (wchar_t *)malloc(len); | |||||
memcpy(a_list, punctlist, len); | memcpy(a_list, punctlist, len); | ||||
a_command->u.my_punctuation_list = a_list; | a_command->u.my_punctuation_list = a_list; | ||||
} | } | ||||
a_error=0; | |||||
a_error = 0; | |||||
list_error: | list_error: | ||||
if (a_error) | |||||
{ | |||||
if (a_command) | |||||
{ | |||||
free (a_command); | |||||
if (a_error) { | |||||
if (a_command) { | |||||
free(a_command); | |||||
} | } | ||||
a_command = NULL; | a_command = NULL; | ||||
} | } | ||||
return a_command; | return a_command; | ||||
} | } | ||||
t_espeak_command* create_espeak_voice_name(const char *name) | |||||
t_espeak_command *create_espeak_voice_name(const char *name) | |||||
{ | { | ||||
ENTER("create_espeak_voice_name"); | ENTER("create_espeak_voice_name"); | ||||
int a_error=1; | |||||
t_espeak_command* a_command = (t_espeak_command*)malloc(sizeof(t_espeak_command)); | |||||
int a_error = 1; | |||||
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | |||||
if (!name || !a_command) | |||||
{ | |||||
if (!name || !a_command) { | |||||
goto name_error; | goto name_error; | ||||
} | } | ||||
a_command->type = ET_VOICE_NAME; | a_command->type = ET_VOICE_NAME; | ||||
a_command->state = CS_UNDEFINED; | a_command->state = CS_UNDEFINED; | ||||
a_command->u.my_voice_name = strdup( name); | |||||
a_error=0; | |||||
a_command->u.my_voice_name = strdup(name); | |||||
a_error = 0; | |||||
name_error: | name_error: | ||||
if (a_error) | |||||
{ | |||||
if (a_command) | |||||
{ | |||||
free (a_command); | |||||
if (a_error) { | |||||
if (a_command) { | |||||
free(a_command); | |||||
} | } | ||||
a_command = NULL; | a_command = NULL; | ||||
} | } | ||||
return a_command; | return a_command; | ||||
} | } | ||||
t_espeak_command* create_espeak_voice_spec(espeak_VOICE *voice) | |||||
t_espeak_command *create_espeak_voice_spec(espeak_VOICE *voice) | |||||
{ | { | ||||
ENTER("create_espeak_voice_spec"); | ENTER("create_espeak_voice_spec"); | ||||
int a_error=1; | |||||
t_espeak_command* a_command = (t_espeak_command*)malloc(sizeof(t_espeak_command)); | |||||
int a_error = 1; | |||||
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | |||||
if (!voice || !a_command) | |||||
{ | |||||
if (!voice || !a_command) { | |||||
goto spec_error; | goto spec_error; | ||||
} | } | ||||
a_command->type = ET_VOICE_SPEC; | a_command->type = ET_VOICE_SPEC; | ||||
a_command->state = CS_UNDEFINED; | a_command->state = CS_UNDEFINED; | ||||
{ | { | ||||
espeak_VOICE* data = &(a_command->u.my_voice_spec); | |||||
espeak_VOICE *data = &(a_command->u.my_voice_spec); | |||||
memcpy(data, voice, sizeof(espeak_VOICE)); | memcpy(data, voice, sizeof(espeak_VOICE)); | ||||
if (voice->name) | |||||
{ | |||||
if (voice->name) { | |||||
data->name = strdup(voice->name); | data->name = strdup(voice->name); | ||||
} | } | ||||
if (voice->languages) | |||||
{ | |||||
if (voice->languages) { | |||||
data->languages = strdup(voice->languages); | data->languages = strdup(voice->languages); | ||||
} | } | ||||
if (voice->identifier) | |||||
{ | |||||
if (voice->identifier) { | |||||
data->identifier = strdup(voice->identifier); | data->identifier = strdup(voice->identifier); | ||||
} | } | ||||
a_error=0; | |||||
a_error = 0; | |||||
} | } | ||||
spec_error: | spec_error: | ||||
if (a_error) | |||||
{ | |||||
if (a_command) | |||||
{ | |||||
free (a_command); | |||||
if (a_error) { | |||||
if (a_command) { | |||||
free(a_command); | |||||
} | } | ||||
a_command = NULL; | a_command = NULL; | ||||
} | } | ||||
return a_command; | return a_command; | ||||
} | } | ||||
int delete_espeak_command( t_espeak_command* the_command) | |||||
int delete_espeak_command(t_espeak_command *the_command) | |||||
{ | { | ||||
ENTER("delete_espeak_command"); | ENTER("delete_espeak_command"); | ||||
int a_status = 0; | int a_status = 0; | ||||
if (the_command) | |||||
{ | |||||
switch(the_command->type) | |||||
if (the_command) { | |||||
switch (the_command->type) | |||||
{ | { | ||||
case ET_TEXT: | case ET_TEXT: | ||||
if (the_command->u.my_text.text) | |||||
{ | |||||
if (the_command->u.my_text.text) { | |||||
SHOW("delete_espeak_command > ET_TEXT free text=%x, command=%x, uid=%d\n", the_command->u.my_text.text, the_command, the_command->u.my_text.unique_identifier); | SHOW("delete_espeak_command > ET_TEXT free text=%x, command=%x, uid=%d\n", the_command->u.my_text.text, the_command, the_command->u.my_text.unique_identifier); | ||||
free(the_command->u.my_text.text); | free(the_command->u.my_text.text); | ||||
} | } | ||||
break; | break; | ||||
case ET_MARK: | case ET_MARK: | ||||
if (the_command->u.my_mark.text) | |||||
{ | |||||
if (the_command->u.my_mark.text) { | |||||
free(the_command->u.my_mark.text); | free(the_command->u.my_mark.text); | ||||
} | } | ||||
if (the_command->u.my_mark.index_mark) | |||||
{ | |||||
free((void*)(the_command->u.my_mark.index_mark)); | |||||
if (the_command->u.my_mark.index_mark) { | |||||
free((void *)(the_command->u.my_mark.index_mark)); | |||||
} | } | ||||
break; | break; | ||||
// it must be processed here for informing the calling program | // it must be processed here for informing the calling program | ||||
// that its message is finished. | // that its message is finished. | ||||
// This can be important for cleaning the related user data. | // This can be important for cleaning the related user data. | ||||
t_espeak_terminated_msg* data = &(the_command->u.my_terminated_msg); | |||||
if (the_command->state == CS_PENDING) | |||||
{ | |||||
t_espeak_terminated_msg *data = &(the_command->u.my_terminated_msg); | |||||
if (the_command->state == CS_PENDING) { | |||||
the_command->state = CS_PROCESSED; | the_command->state = CS_PROCESSED; | ||||
SHOW("delete_espeak_command > ET_TERMINATED_MSG callback (command=0x%x, uid=%d) \n", the_command, data->unique_identifier); | SHOW("delete_espeak_command > ET_TERMINATED_MSG callback (command=0x%x, uid=%d) \n", the_command, data->unique_identifier); | ||||
sync_espeak_terminated_msg( data->unique_identifier, data->user_data); | |||||
sync_espeak_terminated_msg(data->unique_identifier, data->user_data); | |||||
} | } | ||||
} | } | ||||
break; | break; | ||||
case ET_KEY: | case ET_KEY: | ||||
if (the_command->u.my_key.key_name) | |||||
{ | |||||
free((void*)(the_command->u.my_key.key_name)); | |||||
if (the_command->u.my_key.key_name) { | |||||
free((void *)(the_command->u.my_key.key_name)); | |||||
} | } | ||||
break; | break; | ||||
break; | break; | ||||
case ET_PUNCTUATION_LIST: | case ET_PUNCTUATION_LIST: | ||||
if (the_command->u.my_punctuation_list) | |||||
{ | |||||
free((void*)(the_command->u.my_punctuation_list)); | |||||
if (the_command->u.my_punctuation_list) { | |||||
free((void *)(the_command->u.my_punctuation_list)); | |||||
} | } | ||||
break; | break; | ||||
case ET_VOICE_NAME: | case ET_VOICE_NAME: | ||||
if (the_command->u.my_voice_name) | |||||
{ | |||||
free((void*)(the_command->u.my_voice_name)); | |||||
if (the_command->u.my_voice_name) { | |||||
free((void *)(the_command->u.my_voice_name)); | |||||
} | } | ||||
break; | break; | ||||
case ET_VOICE_SPEC: | case ET_VOICE_SPEC: | ||||
{ | { | ||||
espeak_VOICE* data = &(the_command->u.my_voice_spec); | |||||
espeak_VOICE *data = &(the_command->u.my_voice_spec); | |||||
if (data->name) | |||||
{ | |||||
if (data->name) { | |||||
free((void *)data->name); | free((void *)data->name); | ||||
} | } | ||||
if (data->languages) | |||||
{ | |||||
if (data->languages) { | |||||
free((void *)data->languages); | free((void *)data->languages); | ||||
} | } | ||||
if (data->identifier) | |||||
{ | |||||
if (data->identifier) { | |||||
free((void *)data->identifier); | free((void *)data->identifier); | ||||
} | } | ||||
} | } | ||||
return a_status; | return a_status; | ||||
} | } | ||||
void process_espeak_command( t_espeak_command* the_command) | |||||
void process_espeak_command(t_espeak_command *the_command) | |||||
{ | { | ||||
ENTER("process_espeak_command"); | ENTER("process_espeak_command"); | ||||
SHOW("command=0x%x\n", the_command); | SHOW("command=0x%x\n", the_command); | ||||
if (the_command == NULL) | |||||
{ | |||||
if (the_command == NULL) { | |||||
return; | return; | ||||
} | } | ||||
the_command->state = CS_PROCESSED; | the_command->state = CS_PROCESSED; | ||||
switch(the_command->type) | |||||
switch (the_command->type) | |||||
{ | { | ||||
case ET_TEXT: | case ET_TEXT: | ||||
{ | { | ||||
t_espeak_text* data = &(the_command->u.my_text); | |||||
sync_espeak_Synth( data->unique_identifier, data->text, data->size, | |||||
data->position, data->position_type, | |||||
data->end_position, data->flags, data->user_data); | |||||
t_espeak_text *data = &(the_command->u.my_text); | |||||
sync_espeak_Synth(data->unique_identifier, data->text, data->size, | |||||
data->position, data->position_type, | |||||
data->end_position, data->flags, data->user_data); | |||||
} | } | ||||
break; | break; | ||||
case ET_MARK: | case ET_MARK: | ||||
{ | { | ||||
t_espeak_mark* data = &(the_command->u.my_mark); | |||||
sync_espeak_Synth_Mark( data->unique_identifier, data->text, data->size, | |||||
data->index_mark, data->end_position, data->flags, | |||||
data->user_data); | |||||
t_espeak_mark *data = &(the_command->u.my_mark); | |||||
sync_espeak_Synth_Mark(data->unique_identifier, data->text, data->size, | |||||
data->index_mark, data->end_position, data->flags, | |||||
data->user_data); | |||||
} | } | ||||
break; | break; | ||||
case ET_TERMINATED_MSG: | case ET_TERMINATED_MSG: | ||||
{ | { | ||||
t_espeak_terminated_msg* data = &(the_command->u.my_terminated_msg); | |||||
sync_espeak_terminated_msg( data->unique_identifier, data->user_data); | |||||
t_espeak_terminated_msg *data = &(the_command->u.my_terminated_msg); | |||||
sync_espeak_terminated_msg(data->unique_identifier, data->user_data); | |||||
} | } | ||||
break; | break; | ||||
case ET_KEY: | case ET_KEY: | ||||
{ | { | ||||
const char* data = the_command->u.my_key.key_name; | |||||
const char *data = the_command->u.my_key.key_name; | |||||
sync_espeak_Key(data); | sync_espeak_Key(data); | ||||
} | } | ||||
break; | break; | ||||
case ET_CHAR: | case ET_CHAR: | ||||
{ | { | ||||
const wchar_t data = the_command->u.my_char.character; | const wchar_t data = the_command->u.my_char.character; | ||||
sync_espeak_Char( data); | |||||
sync_espeak_Char(data); | |||||
} | } | ||||
break; | break; | ||||
case ET_PARAMETER: | case ET_PARAMETER: | ||||
{ | { | ||||
t_espeak_parameter* data = &(the_command->u.my_param); | |||||
SetParameter( data->parameter, data->value, data->relative); | |||||
t_espeak_parameter *data = &(the_command->u.my_param); | |||||
SetParameter(data->parameter, data->value, data->relative); | |||||
} | } | ||||
break; | break; | ||||
case ET_PUNCTUATION_LIST: | case ET_PUNCTUATION_LIST: | ||||
{ | { | ||||
const wchar_t* data = the_command->u.my_punctuation_list; | |||||
sync_espeak_SetPunctuationList( data); | |||||
const wchar_t *data = the_command->u.my_punctuation_list; | |||||
sync_espeak_SetPunctuationList(data); | |||||
} | } | ||||
break; | break; | ||||
case ET_VOICE_NAME: | case ET_VOICE_NAME: | ||||
{ | { | ||||
const char* data = the_command->u.my_voice_name; | |||||
SetVoiceByName( data); | |||||
const char *data = the_command->u.my_voice_name; | |||||
SetVoiceByName(data); | |||||
} | } | ||||
break; | break; | ||||
case ET_VOICE_SPEC: | case ET_VOICE_SPEC: | ||||
{ | { | ||||
espeak_VOICE* data = &(the_command->u.my_voice_spec); | |||||
espeak_VOICE *data = &(the_command->u.my_voice_spec); | |||||
SetVoiceByProperties(data); | SetVoiceByProperties(data); | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
void display_espeak_command( t_espeak_command* the_command) | |||||
void display_espeak_command(t_espeak_command *the_command) | |||||
{ | { | ||||
ENTER("display_espeak_command"); | ENTER("display_espeak_command"); | ||||
#ifdef DEBUG_ENABLED | #ifdef DEBUG_ENABLED | ||||
if (the_command == NULL) | |||||
{ | |||||
SHOW("display_espeak_command > command=%s\n","NULL"); | |||||
if (the_command == NULL) { | |||||
SHOW("display_espeak_command > command=%s\n", "NULL"); | |||||
return; | return; | ||||
} | } | ||||
SHOW("display_espeak_command > state=%d\n",the_command->state); | |||||
SHOW("display_espeak_command > state=%d\n", the_command->state); | |||||
switch(the_command->type) | |||||
switch (the_command->type) | |||||
{ | { | ||||
case ET_TEXT: | case ET_TEXT: | ||||
{ | { | ||||
t_espeak_text* data = &(the_command->u.my_text); | |||||
SHOW("display_espeak_command > (0x%x) uid=%d, TEXT=%s, user_data=0x%x\n", the_command, data->unique_identifier, (char*)data->text, (int)(data->user_data)); | |||||
t_espeak_text *data = &(the_command->u.my_text); | |||||
SHOW("display_espeak_command > (0x%x) uid=%d, TEXT=%s, user_data=0x%x\n", the_command, data->unique_identifier, (char *)data->text, (int)(data->user_data)); | |||||
} | } | ||||
break; | break; | ||||
case ET_MARK: | case ET_MARK: | ||||
{ | { | ||||
t_espeak_mark* data = &(the_command->u.my_mark); | |||||
SHOW("display_espeak_command > (0x%x) uid=%d, MARK=%s, user_data=0x%x\n", the_command, data->unique_identifier, (char*)data->text, (int)(data->user_data)); | |||||
t_espeak_mark *data = &(the_command->u.my_mark); | |||||
SHOW("display_espeak_command > (0x%x) uid=%d, MARK=%s, user_data=0x%x\n", the_command, data->unique_identifier, (char *)data->text, (int)(data->user_data)); | |||||
} | } | ||||
break; | break; | ||||
case ET_KEY: | case ET_KEY: | ||||
{ | { | ||||
const char* data = the_command->u.my_key.key_name; | |||||
const char *data = the_command->u.my_key.key_name; | |||||
SHOW("display_espeak_command > (0x%x) KEY=%c\n", the_command, data); | SHOW("display_espeak_command > (0x%x) KEY=%c\n", the_command, data); | ||||
} | } | ||||
break; | break; | ||||
case ET_TERMINATED_MSG: | case ET_TERMINATED_MSG: | ||||
{ | { | ||||
t_espeak_terminated_msg* data = &(the_command->u.my_terminated_msg); | |||||
t_espeak_terminated_msg *data = &(the_command->u.my_terminated_msg); | |||||
SHOW("display_espeak_command > (0x%x) TERMINATED_MSG uid=%d, user_data=0x%x, state=%d\n", | SHOW("display_espeak_command > (0x%x) TERMINATED_MSG uid=%d, user_data=0x%x, state=%d\n", | ||||
the_command, data->unique_identifier, data->user_data, | the_command, data->unique_identifier, data->user_data, | ||||
case ET_PARAMETER: | case ET_PARAMETER: | ||||
{ | { | ||||
t_espeak_parameter* data = &(the_command->u.my_param); | |||||
t_espeak_parameter *data = &(the_command->u.my_param); | |||||
SHOW("display_espeak_command > (0x%x) PARAMETER=%d, value=%d, relative=%d\n", | SHOW("display_espeak_command > (0x%x) PARAMETER=%d, value=%d, relative=%d\n", | ||||
the_command, data->parameter, data->value, data->relative); | the_command, data->parameter, data->value, data->relative); | ||||
} | } | ||||
case ET_PUNCTUATION_LIST: | case ET_PUNCTUATION_LIST: | ||||
{ | { | ||||
const wchar_t* data = the_command->u.my_punctuation_list; | |||||
sync_espeak_SetPunctuationList( data); | |||||
SHOW("display_espeak_command > (0x%x) PUNCTLIST=%s\n", the_command, (char*)data); | |||||
const wchar_t *data = the_command->u.my_punctuation_list; | |||||
sync_espeak_SetPunctuationList(data); | |||||
SHOW("display_espeak_command > (0x%x) PUNCTLIST=%s\n", the_command, (char *)data); | |||||
} | } | ||||
break; | break; | ||||
case ET_VOICE_NAME: | case ET_VOICE_NAME: | ||||
{ | { | ||||
const char* data = the_command->u.my_voice_name; | |||||
const char *data = the_command->u.my_voice_name; | |||||
SHOW("display_espeak_command > (0x%x) VOICE_NAME=%s\n", the_command, data); | SHOW("display_espeak_command > (0x%x) VOICE_NAME=%s\n", the_command, data); | ||||
} | } | ||||
break; | break; |
{ | { | ||||
#endif | #endif | ||||
typedef enum | |||||
{ | |||||
typedef enum { | |||||
ET_TEXT, | ET_TEXT, | ||||
ET_MARK, | ET_MARK, | ||||
ET_KEY, | ET_KEY, | ||||
ET_TERMINATED_MSG | ET_TERMINATED_MSG | ||||
} t_espeak_type; | } t_espeak_type; | ||||
typedef struct | |||||
{ | |||||
typedef struct { | |||||
unsigned int unique_identifier; | unsigned int unique_identifier; | ||||
void* text; | |||||
void *text; | |||||
size_t size; | size_t size; | ||||
unsigned int position; | unsigned int position; | ||||
espeak_POSITION_TYPE position_type; | espeak_POSITION_TYPE position_type; | ||||
unsigned int end_position; | unsigned int end_position; | ||||
unsigned int flags; | unsigned int flags; | ||||
void* user_data; | |||||
void *user_data; | |||||
} t_espeak_text; | } t_espeak_text; | ||||
typedef struct | |||||
{ | |||||
typedef struct { | |||||
unsigned int unique_identifier; | unsigned int unique_identifier; | ||||
void* text; | |||||
void *text; | |||||
size_t size; | size_t size; | ||||
const char* index_mark; | |||||
const char *index_mark; | |||||
unsigned int end_position; | unsigned int end_position; | ||||
unsigned int flags; | unsigned int flags; | ||||
void* user_data; | |||||
void *user_data; | |||||
} t_espeak_mark; | } t_espeak_mark; | ||||
typedef struct | |||||
{ | |||||
typedef struct { | |||||
unsigned int unique_identifier; | unsigned int unique_identifier; | ||||
void* user_data; | |||||
void *user_data; | |||||
wchar_t character; | wchar_t character; | ||||
} t_espeak_character; | } t_espeak_character; | ||||
typedef struct | |||||
{ | |||||
typedef struct { | |||||
unsigned int unique_identifier; | unsigned int unique_identifier; | ||||
void* user_data; | |||||
const char* key_name; | |||||
void *user_data; | |||||
const char *key_name; | |||||
} t_espeak_key; | } t_espeak_key; | ||||
typedef struct | |||||
{ | |||||
typedef struct { | |||||
unsigned int unique_identifier; | unsigned int unique_identifier; | ||||
void* user_data; | |||||
void *user_data; | |||||
} t_espeak_terminated_msg; | } t_espeak_terminated_msg; | ||||
typedef struct | |||||
{ | |||||
typedef struct { | |||||
espeak_PARAMETER parameter; | espeak_PARAMETER parameter; | ||||
int value; | int value; | ||||
int relative; | int relative; | ||||
} t_espeak_parameter; | } t_espeak_parameter; | ||||
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_PENDING, // stored in the fifo | ||||
CS_PROCESSED // processed | CS_PROCESSED // processed | ||||
} t_command_state; | } t_command_state; | ||||
typedef struct | |||||
{ | |||||
typedef struct { | |||||
t_espeak_type type; | t_espeak_type type; | ||||
t_command_state state; | t_command_state state; | ||||
union command | |||||
{ | |||||
union command { | |||||
t_espeak_text my_text; | t_espeak_text my_text; | ||||
t_espeak_mark my_mark; | t_espeak_mark my_mark; | ||||
t_espeak_key my_key; | t_espeak_key my_key; | ||||
t_espeak_character my_char; | t_espeak_character my_char; | ||||
t_espeak_parameter my_param; | t_espeak_parameter my_param; | ||||
const wchar_t* my_punctuation_list; | |||||
const wchar_t *my_punctuation_list; | |||||
const char *my_voice_name; | const char *my_voice_name; | ||||
espeak_VOICE my_voice_spec; | espeak_VOICE my_voice_spec; | ||||
t_espeak_terminated_msg my_terminated_msg; | t_espeak_terminated_msg my_terminated_msg; | ||||
} t_espeak_command; | } t_espeak_command; | ||||
t_espeak_command* create_espeak_text(const void *text, size_t size, unsigned int position, espeak_POSITION_TYPE position_type, unsigned int end_position, unsigned int flags, void* user_data); | |||||
t_espeak_command *create_espeak_text(const void *text, size_t size, unsigned int position, espeak_POSITION_TYPE position_type, unsigned int end_position, unsigned int flags, void *user_data); | |||||
t_espeak_command* create_espeak_mark(const void *text, size_t size, const char *index_mark, unsigned int end_position, unsigned int flags, void* user_data); | |||||
t_espeak_command *create_espeak_mark(const void *text, size_t size, const char *index_mark, unsigned int end_position, unsigned int flags, void *user_data); | |||||
t_espeak_command* create_espeak_terminated_msg(unsigned int unique_identifier, void* user_data); | |||||
t_espeak_command *create_espeak_terminated_msg(unsigned int unique_identifier, void *user_data); | |||||
t_espeak_command* create_espeak_key(const char *key_name, void *user_data); | |||||
t_espeak_command *create_espeak_key(const char *key_name, void *user_data); | |||||
t_espeak_command* create_espeak_char(wchar_t character, void *user_data); | |||||
t_espeak_command *create_espeak_char(wchar_t character, void *user_data); | |||||
t_espeak_command* create_espeak_parameter(espeak_PARAMETER parameter, int value, int relative); | |||||
t_espeak_command *create_espeak_parameter(espeak_PARAMETER parameter, int value, int relative); | |||||
t_espeak_command* create_espeak_punctuation_list(const wchar_t *punctlist); | |||||
t_espeak_command *create_espeak_punctuation_list(const wchar_t *punctlist); | |||||
t_espeak_command* create_espeak_voice_name(const char *name); | |||||
t_espeak_command *create_espeak_voice_name(const char *name); | |||||
t_espeak_command* create_espeak_voice_spec(espeak_VOICE *voice_spec); | |||||
t_espeak_command *create_espeak_voice_spec(espeak_VOICE *voice_spec); | |||||
void process_espeak_command( t_espeak_command* the_command); | |||||
void process_espeak_command(t_espeak_command *the_command); | |||||
int delete_espeak_command( t_espeak_command* the_command); | |||||
int delete_espeak_command(t_espeak_command *the_command); | |||||
void display_espeak_command(t_espeak_command* the_command); | |||||
void display_espeak_command(t_espeak_command *the_command); | |||||
espeak_ERROR sync_espeak_Synth(unsigned int unique_identifier, const void *text, size_t size, | espeak_ERROR sync_espeak_Synth(unsigned int unique_identifier, const void *text, size_t size, | ||||
unsigned int position, espeak_POSITION_TYPE position_type, | unsigned int position, espeak_POSITION_TYPE position_type, | ||||
unsigned int end_position, unsigned int flags, void* user_data); | |||||
unsigned int end_position, unsigned int flags, void *user_data); | |||||
espeak_ERROR sync_espeak_Synth_Mark(unsigned int unique_identifier, const void *text, size_t size, | espeak_ERROR sync_espeak_Synth_Mark(unsigned int unique_identifier, const void *text, size_t size, | ||||
const char *index_mark, unsigned int end_position, | const char *index_mark, unsigned int end_position, | ||||
unsigned int flags, void* user_data); | |||||
unsigned int flags, void *user_data); | |||||
void sync_espeak_Key(const char *key); | void sync_espeak_Key(const char *key); | ||||
void sync_espeak_Char(wchar_t character); | void sync_espeak_Char(wchar_t character); | ||||
void sync_espeak_SetPunctuationList(const wchar_t *punctlist); | void sync_espeak_SetPunctuationList(const wchar_t *punctlist); | ||||
espeak_ERROR SetVoiceByProperties(espeak_VOICE *voice_selector); | espeak_ERROR SetVoiceByProperties(espeak_VOICE *voice_selector); | ||||
void SetParameter(int parameter, int value, int relative); | void SetParameter(int parameter, int value, int relative); | ||||
int sync_espeak_terminated_msg(unsigned int unique_identifier, void* user_data); | |||||
int sync_espeak_terminated_msg(unsigned int unique_identifier, void *user_data); | |||||
#ifdef __cplusplus | #ifdef __cplusplus | ||||
} | } | ||||
#endif | #endif | ||||
//> | |||||
// > | |||||
#endif | #endif |
static pthread_t my_thread; | static pthread_t my_thread; | ||||
static bool thread_inited; | static bool thread_inited; | ||||
static t_espeak_callback* my_callback = NULL; | |||||
static int my_event_is_running=0; | |||||
static t_espeak_callback *my_callback = NULL; | |||||
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 | |||||
{ | |||||
void* data; | |||||
typedef struct t_node { | |||||
void *data; | |||||
struct t_node *next; | struct t_node *next; | ||||
} node; | } node; | ||||
static node* head=NULL; | |||||
static node* tail=NULL; | |||||
static int node_counter=0; | |||||
static espeak_ERROR push(void* data); | |||||
static void* pop(); | |||||
static node *head = NULL; | |||||
static node *tail = NULL; | |||||
static int node_counter = 0; | |||||
static espeak_ERROR push(void *data); | |||||
static void *pop(); | |||||
static void init(); | static void init(); | ||||
static void* polling_thread(void*); | |||||
static void *polling_thread(void *); | |||||
void event_set_callback(t_espeak_callback* SynthCallback) | |||||
void event_set_callback(t_espeak_callback *SynthCallback) | |||||
{ | { | ||||
my_callback = SynthCallback; | my_callback = SynthCallback; | ||||
} | } | ||||
{ | { | ||||
ENTER("event_init"); | ENTER("event_init"); | ||||
my_event_is_running=0; | |||||
my_event_is_running = 0; | |||||
// security | // security | ||||
pthread_mutex_init( &my_mutex, (const pthread_mutexattr_t *)NULL); | |||||
pthread_mutex_init(&my_mutex, (const pthread_mutexattr_t *)NULL); | |||||
init(); | init(); | ||||
assert(-1 != sem_init(&my_sem_start_is_required, 0, 0)); | assert(-1 != sem_init(&my_sem_start_is_required, 0, 0)); | ||||
pthread_attr_t a_attrib; | pthread_attr_t a_attrib; | ||||
if (pthread_attr_init (&a_attrib) == 0 | |||||
&& pthread_attr_setdetachstate(&a_attrib, PTHREAD_CREATE_JOINABLE) == 0) | |||||
{ | |||||
if (pthread_attr_init(&a_attrib) == 0 | |||||
&& pthread_attr_setdetachstate(&a_attrib, PTHREAD_CREATE_JOINABLE) == 0) { | |||||
thread_inited = (0 == pthread_create(&my_thread, | thread_inited = (0 == pthread_create(&my_thread, | ||||
&a_attrib, | &a_attrib, | ||||
polling_thread, | polling_thread, | ||||
(void*)NULL)); | |||||
(void *)NULL)); | |||||
} | } | ||||
assert(thread_inited); | assert(thread_inited); | ||||
pthread_attr_destroy(&a_attrib); | pthread_attr_destroy(&a_attrib); | ||||
} | } | ||||
static void event_display(espeak_EVENT* event) | |||||
static void event_display(espeak_EVENT *event) | |||||
{ | { | ||||
ENTER("event_display"); | ENTER("event_display"); | ||||
#ifdef DEBUG_ENABLED | #ifdef DEBUG_ENABLED | ||||
if (event==NULL) | |||||
{ | |||||
SHOW("event_display > event=%s\n","NULL"); | |||||
} | |||||
else | |||||
{ | |||||
static const char* label[] = { | |||||
if (event == NULL) { | |||||
SHOW("event_display > event=%s\n", "NULL"); | |||||
} else { | |||||
static const char *label[] = { | |||||
"LIST_TERMINATED", | "LIST_TERMINATED", | ||||
"WORD", | "WORD", | ||||
"SENTENCE", | "SENTENCE", | ||||
"??" | "??" | ||||
}; | }; | ||||
SHOW("event_display > event=0x%x\n",event); | |||||
SHOW("event_display > type=%s\n",label[event->type]); | |||||
SHOW("event_display > uid=%d\n",event->unique_identifier); | |||||
SHOW("event_display > text_position=%d\n",event->text_position); | |||||
SHOW("event_display > length=%d\n",event->length); | |||||
SHOW("event_display > audio_position=%d\n",event->audio_position); | |||||
SHOW("event_display > sample=%d\n",event->sample); | |||||
SHOW("event_display > user_data=0x%x\n",event->user_data); | |||||
SHOW("event_display > event=0x%x\n", event); | |||||
SHOW("event_display > type=%s\n", label[event->type]); | |||||
SHOW("event_display > uid=%d\n", event->unique_identifier); | |||||
SHOW("event_display > text_position=%d\n", event->text_position); | |||||
SHOW("event_display > length=%d\n", event->length); | |||||
SHOW("event_display > audio_position=%d\n", event->audio_position); | |||||
SHOW("event_display > sample=%d\n", event->sample); | |||||
SHOW("event_display > user_data=0x%x\n", event->user_data); | |||||
} | } | ||||
#endif | #endif | ||||
} | } | ||||
static espeak_EVENT* event_copy (espeak_EVENT* event) | |||||
static espeak_EVENT *event_copy(espeak_EVENT *event) | |||||
{ | { | ||||
ENTER("event_copy"); | ENTER("event_copy"); | ||||
if (event==NULL) | |||||
{ | |||||
if (event == NULL) { | |||||
return NULL; | return NULL; | ||||
} | } | ||||
espeak_EVENT* a_event=(espeak_EVENT*)malloc(sizeof(espeak_EVENT)); | |||||
if (a_event) | |||||
{ | |||||
espeak_EVENT *a_event = (espeak_EVENT *)malloc(sizeof(espeak_EVENT)); | |||||
if (a_event) { | |||||
memcpy(a_event, event, sizeof(espeak_EVENT)); | memcpy(a_event, event, sizeof(espeak_EVENT)); | ||||
switch(event->type) | |||||
switch (event->type) | |||||
{ | { | ||||
case espeakEVENT_MARK: | case espeakEVENT_MARK: | ||||
case espeakEVENT_PLAY: | case espeakEVENT_PLAY: | ||||
if (event->id.name) | |||||
{ | |||||
if (event->id.name) { | |||||
a_event->id.name = strdup(event->id.name); | a_event->id.name = strdup(event->id.name); | ||||
} | } | ||||
break; | break; | ||||
// * Last call: event->type = espeakEVENT_MSG_TERMINATED | // * Last call: event->type = espeakEVENT_MSG_TERMINATED | ||||
// | // | ||||
static void event_notify(espeak_EVENT* event) | |||||
static void event_notify(espeak_EVENT *event) | |||||
{ | { | ||||
ENTER("event_notify"); | ENTER("event_notify"); | ||||
static unsigned int a_old_uid = 0; | static unsigned int a_old_uid = 0; | ||||
espeak_EVENT events[2]; | espeak_EVENT events[2]; | ||||
memcpy(&events[0],event,sizeof(espeak_EVENT)); // the event parameter in the callback function should be an array of eventd | |||||
memcpy(&events[1],event,sizeof(espeak_EVENT)); | |||||
memcpy(&events[0], event, sizeof(espeak_EVENT)); // the event parameter in the callback function should be an array of eventd | |||||
memcpy(&events[1], event, sizeof(espeak_EVENT)); | |||||
events[1].type = espeakEVENT_LIST_TERMINATED; // ... terminated by an event type=0 | events[1].type = espeakEVENT_LIST_TERMINATED; // ... terminated by an event type=0 | ||||
if (event && my_callback) | |||||
{ | |||||
if (event && my_callback) { | |||||
event_display(event); | event_display(event); | ||||
switch(event->type) | |||||
switch (event->type) | |||||
{ | { | ||||
case espeakEVENT_SENTENCE: | case espeakEVENT_SENTENCE: | ||||
my_callback(NULL, 0, events); | my_callback(NULL, 0, events); | ||||
case espeakEVENT_END: | case espeakEVENT_END: | ||||
case espeakEVENT_PHONEME: | case espeakEVENT_PHONEME: | ||||
{ | { | ||||
if (a_old_uid != event->unique_identifier) | |||||
{ | |||||
if (a_old_uid != event->unique_identifier) { | |||||
espeak_EVENT_TYPE a_new_type = events[0].type; | espeak_EVENT_TYPE a_new_type = events[0].type; | ||||
events[0].type = espeakEVENT_SENTENCE; | events[0].type = espeakEVENT_SENTENCE; | ||||
my_callback(NULL, 0, events); | my_callback(NULL, 0, events); | ||||
} | } | ||||
} | } | ||||
static int event_delete(espeak_EVENT* event) | |||||
static int event_delete(espeak_EVENT *event) | |||||
{ | { | ||||
ENTER("event_delete"); | ENTER("event_delete"); | ||||
event_display(event); | event_display(event); | ||||
if(event==NULL) | |||||
{ | |||||
if (event == NULL) { | |||||
return 0; | return 0; | ||||
} | } | ||||
switch(event->type) | |||||
switch (event->type) | |||||
{ | { | ||||
case espeakEVENT_MSG_TERMINATED: | case espeakEVENT_MSG_TERMINATED: | ||||
event_notify(event); | event_notify(event); | ||||
case espeakEVENT_MARK: | case espeakEVENT_MARK: | ||||
case espeakEVENT_PLAY: | case espeakEVENT_PLAY: | ||||
if(event->id.name) | |||||
{ | |||||
free((void*)(event->id.name)); | |||||
if (event->id.name) { | |||||
free((void *)(event->id.name)); | |||||
} | } | ||||
break; | break; | ||||
return 1; | return 1; | ||||
} | } | ||||
espeak_ERROR event_declare (espeak_EVENT* event) | |||||
espeak_ERROR event_declare(espeak_EVENT *event) | |||||
{ | { | ||||
ENTER("event_declare"); | ENTER("event_declare"); | ||||
event_display(event); | event_display(event); | ||||
if (!event) | |||||
{ | |||||
if (!event) { | |||||
return EE_INTERNAL_ERROR; | return EE_INTERNAL_ERROR; | ||||
} | } | ||||
int a_status = pthread_mutex_lock(&my_mutex); | int a_status = pthread_mutex_lock(&my_mutex); | ||||
espeak_ERROR a_error = EE_OK; | espeak_ERROR a_error = EE_OK; | ||||
if (!a_status) | |||||
{ | |||||
if (!a_status) { | |||||
SHOW_TIME("event_declare > locked\n"); | SHOW_TIME("event_declare > locked\n"); | ||||
espeak_EVENT* a_event = event_copy(event); | |||||
espeak_EVENT *a_event = event_copy(event); | |||||
a_error = push(a_event); | a_error = push(a_event); | ||||
if (a_error != EE_OK) | |||||
{ | |||||
if (a_error != EE_OK) { | |||||
event_delete(a_event); | event_delete(a_event); | ||||
} | } | ||||
SHOW_TIME("event_declare > unlocking\n"); | SHOW_TIME("event_declare > unlocking\n"); | ||||
SHOW_TIME("event_declare > post my_sem_start_is_required\n"); | SHOW_TIME("event_declare > post my_sem_start_is_required\n"); | ||||
sem_post(&my_sem_start_is_required); | sem_post(&my_sem_start_is_required); | ||||
if (a_status != 0) | |||||
{ | |||||
if (a_status != 0) { | |||||
a_error = EE_INTERNAL_ERROR; | a_error = EE_INTERNAL_ERROR; | ||||
} | } | ||||
return a_error; | return a_error; | ||||
} | } | ||||
espeak_ERROR event_clear_all () | |||||
espeak_ERROR event_clear_all() | |||||
{ | { | ||||
ENTER("event_clear_all"); | ENTER("event_clear_all"); | ||||
int a_event_is_running = 0; | int a_event_is_running = 0; | ||||
SHOW_TIME("event_stop > locked\n"); | SHOW_TIME("event_stop > locked\n"); | ||||
if (a_status != 0) | |||||
{ | |||||
if (a_status != 0) { | |||||
return EE_INTERNAL_ERROR; | return EE_INTERNAL_ERROR; | ||||
} | } | ||||
if (my_event_is_running) | |||||
{ | |||||
if (my_event_is_running) { | |||||
SHOW_TIME("event_stop > post my_sem_stop_is_required\n"); | SHOW_TIME("event_stop > post my_sem_stop_is_required\n"); | ||||
sem_post(&my_sem_stop_is_required); | sem_post(&my_sem_stop_is_required); | ||||
a_event_is_running = 1; | a_event_is_running = 1; | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
init(); // clear pending events | init(); // clear pending events | ||||
} | } | ||||
SHOW_TIME("event_stop > unlocking\n"); | SHOW_TIME("event_stop > unlocking\n"); | ||||
a_status = pthread_mutex_unlock(&my_mutex); | a_status = pthread_mutex_unlock(&my_mutex); | ||||
if (a_status != 0) | |||||
{ | |||||
if (a_status != 0) { | |||||
return EE_INTERNAL_ERROR; | return EE_INTERNAL_ERROR; | ||||
} | } | ||||
if (a_event_is_running) | |||||
{ | |||||
if (a_event_is_running) { | |||||
SHOW_TIME("event_stop > wait for my_sem_stop_is_acknowledged\n"); | SHOW_TIME("event_stop > wait for my_sem_stop_is_acknowledged\n"); | ||||
while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR) | |||||
{ | |||||
while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR) { | |||||
continue; // Restart when interrupted by handler | continue; // Restart when interrupted by handler | ||||
} | } | ||||
SHOW_TIME("event_stop > get my_sem_stop_is_acknowledged\n"); | SHOW_TIME("event_stop > get my_sem_stop_is_acknowledged\n"); | ||||
{ | { | ||||
ENTER("sleep_until_timeout_or_stop_request"); | ENTER("sleep_until_timeout_or_stop_request"); | ||||
int a_stop_is_required=0; | |||||
int a_stop_is_required = 0; | |||||
struct timespec ts; | struct timespec ts; | ||||
struct timeval tv; | struct timeval tv; | ||||
int err=0; | |||||
int err = 0; | |||||
clock_gettime2( &ts); | |||||
clock_gettime2(&ts); | |||||
#ifdef DEBUG_ENABLED | #ifdef DEBUG_ENABLED | ||||
struct timespec to; | struct timespec to; | ||||
to.tv_nsec = ts.tv_nsec; | to.tv_nsec = ts.tv_nsec; | ||||
#endif | #endif | ||||
add_time_in_ms( &ts, time_in_ms); | |||||
add_time_in_ms(&ts, time_in_ms); | |||||
SHOW("polling_thread > sleep_until_timeout_or_stop_request > start sem_timedwait from %d.%09lu to %d.%09lu \n", | SHOW("polling_thread > sleep_until_timeout_or_stop_request > start sem_timedwait from %d.%09lu to %d.%09lu \n", | ||||
to.tv_sec, to.tv_nsec, | to.tv_sec, to.tv_nsec, | ||||
ts.tv_sec, ts.tv_nsec); | ts.tv_sec, ts.tv_nsec); | ||||
while ((err = sem_timedwait(&my_sem_stop_is_required, &ts)) == -1 | while ((err = sem_timedwait(&my_sem_stop_is_required, &ts)) == -1 | ||||
&& errno == EINTR) | |||||
{ | |||||
&& errno == EINTR) { | |||||
continue; // Restart when interrupted by handler | continue; // Restart when interrupted by handler | ||||
} | } | ||||
assert (gettimeofday(&tv, NULL) != -1); | |||||
assert(gettimeofday(&tv, NULL) != -1); | |||||
SHOW("polling_thread > sleep_until_timeout_or_stop_request > stop sem_timedwait %d.%09lu \n", | SHOW("polling_thread > sleep_until_timeout_or_stop_request > stop sem_timedwait %d.%09lu \n", | ||||
tv.tv_sec, tv.tv_usec*1000); | tv.tv_sec, tv.tv_usec*1000); | ||||
if (err == 0) | |||||
{ | |||||
SHOW("polling_thread > sleep_until_timeout_or_stop_request > %s\n","stop required!"); | |||||
a_stop_is_required=1; // stop required | |||||
if (err == 0) { | |||||
SHOW("polling_thread > sleep_until_timeout_or_stop_request > %s\n", "stop required!"); | |||||
a_stop_is_required = 1; // stop required | |||||
} | } | ||||
return a_stop_is_required; | return a_stop_is_required; | ||||
} | } | ||||
// If the stream is opened but the audio samples are not played, | // If the stream is opened but the audio samples are not played, | ||||
// a timeout is started. | // a timeout is started. | ||||
static int get_remaining_time(uint32_t sample, uint32_t* time_in_ms, int* stop_is_required) | |||||
static int get_remaining_time(uint32_t sample, uint32_t *time_in_ms, int *stop_is_required) | |||||
{ | { | ||||
ENTER("get_remaining_time"); | ENTER("get_remaining_time"); | ||||
int err = 0; | int err = 0; | ||||
*stop_is_required = 0; | *stop_is_required = 0; | ||||
int i=0; | |||||
int i = 0; | |||||
for (i=0; i < MAX_ACTIVITY_CHECK && (*stop_is_required == 0); i++) | |||||
{ | |||||
err = wave_get_remaining_time( sample, time_in_ms); | |||||
for (i = 0; i < MAX_ACTIVITY_CHECK && (*stop_is_required == 0); i++) { | |||||
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 || 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 | |||||
break; | break; | ||||
} | } | ||||
// | // | ||||
// wait for the close of stream | // wait for the close of stream | ||||
*stop_is_required = sleep_until_timeout_or_stop_request( ACTIVITY_TIMEOUT); | |||||
*stop_is_required = sleep_until_timeout_or_stop_request(ACTIVITY_TIMEOUT); | |||||
} | } | ||||
return err; | return err; | ||||
} | } | ||||
static void* polling_thread(void*p) | |||||
static void *polling_thread(void *p) | |||||
{ | { | ||||
ENTER("polling_thread"); | ENTER("polling_thread"); | ||||
while(1) | |||||
{ | |||||
int a_stop_is_required=0; | |||||
while (1) { | |||||
int a_stop_is_required = 0; | |||||
SHOW_TIME("polling_thread > locking\n"); | SHOW_TIME("polling_thread > locking\n"); | ||||
int a_status = pthread_mutex_lock(&my_mutex); | int a_status = pthread_mutex_lock(&my_mutex); | ||||
SHOW_TIME("polling_thread > wait for my_sem_start_is_required\n"); | SHOW_TIME("polling_thread > wait for my_sem_start_is_required\n"); | ||||
while ((sem_wait(&my_sem_start_is_required) == -1) && errno == EINTR) | |||||
{ | |||||
while ((sem_wait(&my_sem_start_is_required) == -1) && errno == EINTR) { | |||||
continue; // Restart when interrupted by handler | continue; // Restart when interrupted by handler | ||||
} | } | ||||
pthread_mutex_unlock(&my_mutex); | pthread_mutex_unlock(&my_mutex); | ||||
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)) | |||||
{ | |||||
}; | |||||
} | |||||
else | |||||
{ | |||||
a_stop_is_required=0; | |||||
while (0 == sem_trywait(&my_sem_stop_is_required)) { | |||||
} | |||||
; | |||||
} else { | |||||
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); | ||||
uint32_t time_in_ms = 0; | uint32_t time_in_ms = 0; | ||||
int err = get_remaining_time((uint32_t)event->sample, | int err = get_remaining_time((uint32_t)event->sample, | ||||
&time_in_ms, | &time_in_ms, | ||||
&a_stop_is_required); | &a_stop_is_required); | ||||
if (a_stop_is_required > 0) | |||||
{ | |||||
if (a_stop_is_required > 0) { | |||||
break; | break; | ||||
} | |||||
else if (err != 0) | |||||
{ | |||||
} else if (err != 0) { | |||||
// No available time: the event is deleted. | // No available time: the event is deleted. | ||||
SHOW("polling_thread > %s\n","audio device down"); | |||||
SHOW("polling_thread > %s\n", "audio device down"); | |||||
a_status = pthread_mutex_lock(&my_mutex); | a_status = pthread_mutex_lock(&my_mutex); | ||||
SHOW_TIME("polling_thread > locked\n"); | SHOW_TIME("polling_thread > locked\n"); | ||||
event_delete( (espeak_EVENT*)pop()); | |||||
event_delete((espeak_EVENT *)pop()); | |||||
a_status = pthread_mutex_unlock(&my_mutex); | a_status = pthread_mutex_unlock(&my_mutex); | ||||
SHOW_TIME("polling_thread > unlocked\n"); | SHOW_TIME("polling_thread > unlocked\n"); | ||||
} | |||||
else if (time_in_ms==0) | |||||
{ // the event is already reached. | |||||
if (my_callback) | |||||
{ | |||||
} else if (time_in_ms == 0) { // the event is already reached. | |||||
if (my_callback) { | |||||
event_notify(event); | event_notify(event); | ||||
// the user_data (and the type) are cleaned to be sure | // the user_data (and the type) are cleaned to be sure | ||||
// that MSG_TERMINATED is called twice (at delete time too). | // that MSG_TERMINATED is called twice (at delete time too). | ||||
event->type=espeakEVENT_LIST_TERMINATED; | |||||
event->user_data=NULL; | |||||
event->type = espeakEVENT_LIST_TERMINATED; | |||||
event->user_data = NULL; | |||||
} | } | ||||
a_status = pthread_mutex_lock(&my_mutex); | a_status = pthread_mutex_lock(&my_mutex); | ||||
SHOW_TIME("polling_thread > locked\n"); | SHOW_TIME("polling_thread > locked\n"); | ||||
event_delete( (espeak_EVENT*)pop()); | |||||
event_delete((espeak_EVENT *)pop()); | |||||
a_status = pthread_mutex_unlock(&my_mutex); | a_status = pthread_mutex_unlock(&my_mutex); | ||||
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); | 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)) | |||||
{ | |||||
}; | |||||
} | |||||
else | |||||
{ | |||||
a_stop_is_required=0; | |||||
while (0 == sem_trywait(&my_sem_stop_is_required)) { | |||||
} | |||||
; | |||||
} else { | |||||
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_stop_is_required = sleep_until_timeout_or_stop_request(time_in_ms); | a_stop_is_required = sleep_until_timeout_or_stop_request(time_in_ms); | ||||
} | } | ||||
} | } | ||||
SHOW_TIME("polling_thread > my_event_is_running = 0\n"); | SHOW_TIME("polling_thread > my_event_is_running = 0\n"); | ||||
my_event_is_running = 0; | my_event_is_running = 0; | ||||
if(a_stop_is_required <= 0) | |||||
{ | |||||
if (a_stop_is_required <= 0) { | |||||
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)) | |||||
{ | |||||
}; | |||||
} | |||||
else | |||||
{ | |||||
a_stop_is_required=0; | |||||
while (0 == sem_trywait(&my_sem_stop_is_required)) { | |||||
} | |||||
; | |||||
} else { | |||||
a_stop_is_required = 0; | |||||
} | } | ||||
} | } | ||||
a_status = pthread_mutex_unlock(&my_mutex); | a_status = pthread_mutex_unlock(&my_mutex); | ||||
SHOW_TIME("polling_thread > unlocked\n"); | SHOW_TIME("polling_thread > unlocked\n"); | ||||
if (a_stop_is_required > 0) | |||||
{ | |||||
SHOW("polling_thread > %s\n","stop required!"); | |||||
if (a_stop_is_required > 0) { | |||||
SHOW("polling_thread > %s\n", "stop required!"); | |||||
// no mutex required since the stop command is synchronous | // no mutex required since the stop command is synchronous | ||||
// and waiting for my_sem_stop_is_acknowledged | // and waiting for my_sem_stop_is_acknowledged | ||||
init(); | init(); | ||||
return NULL; | return NULL; | ||||
} | } | ||||
enum {MAX_NODE_COUNTER=1000}; | |||||
enum { MAX_NODE_COUNTER = 1000 }; | |||||
static espeak_ERROR push(void* the_data) | |||||
static espeak_ERROR push(void *the_data) | |||||
{ | { | ||||
ENTER("event > push"); | ENTER("event > push"); | ||||
assert((!head && !tail) || (head && tail)); | assert((!head && !tail) || (head && tail)); | ||||
if (the_data == NULL) | |||||
{ | |||||
if (the_data == NULL) { | |||||
SHOW("event > push > event=0x%x\n", NULL); | SHOW("event > push > event=0x%x\n", NULL); | ||||
return EE_INTERNAL_ERROR; | return EE_INTERNAL_ERROR; | ||||
} | } | ||||
if (node_counter >= MAX_NODE_COUNTER) | |||||
{ | |||||
if (node_counter >= MAX_NODE_COUNTER) { | |||||
SHOW("event > push > %s\n", "EE_BUFFER_FULL"); | SHOW("event > push > %s\n", "EE_BUFFER_FULL"); | ||||
return EE_BUFFER_FULL; | return EE_BUFFER_FULL; | ||||
} | } | ||||
node *n = (node *)malloc(sizeof(node)); | node *n = (node *)malloc(sizeof(node)); | ||||
if (n == NULL) | |||||
{ | |||||
if (n == NULL) { | |||||
return EE_INTERNAL_ERROR; | return EE_INTERNAL_ERROR; | ||||
} | } | ||||
if (head == NULL) | |||||
{ | |||||
if (head == NULL) { | |||||
head = n; | head = n; | ||||
tail = n; | tail = n; | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
tail->next = n; | tail->next = n; | ||||
tail = n; | tail = n; | ||||
} | } | ||||
tail->data = the_data; | tail->data = the_data; | ||||
node_counter++; | node_counter++; | ||||
SHOW("event > push > counter=%d (uid=%d)\n",node_counter,((espeak_EVENT*)the_data)->unique_identifier); | |||||
SHOW("event > push > counter=%d (uid=%d)\n", node_counter, ((espeak_EVENT *)the_data)->unique_identifier); | |||||
return EE_OK; | return EE_OK; | ||||
} | } | ||||
static void* pop() | |||||
static void *pop() | |||||
{ | { | ||||
ENTER("event > pop"); | ENTER("event > pop"); | ||||
void* the_data = NULL; | |||||
void *the_data = NULL; | |||||
assert((!head && !tail) || (head && tail)); | assert((!head && !tail) || (head && tail)); | ||||
if (head != NULL) | |||||
{ | |||||
node* n = head; | |||||
if (head != NULL) { | |||||
node *n = head; | |||||
the_data = n->data; | the_data = n->data; | ||||
head = n->next; | head = n->next; | ||||
free(n); | free(n); | ||||
node_counter--; | node_counter--; | ||||
SHOW("event > pop > event=0x%x (counter=%d, uid=%d)\n",the_data, node_counter,((espeak_EVENT*)the_data)->unique_identifier); | |||||
SHOW("event > pop > event=0x%x (counter=%d, uid=%d)\n", the_data, node_counter, ((espeak_EVENT *)the_data)->unique_identifier); | |||||
} | } | ||||
if(head == NULL) | |||||
{ | |||||
if (head == NULL) { | |||||
tail = NULL; | tail = NULL; | ||||
} | } | ||||
{ | { | ||||
ENTER("event > init"); | ENTER("event > init"); | ||||
while (event_delete( (espeak_EVENT*)pop() )) | |||||
{} | |||||
while (event_delete((espeak_EVENT *)pop())) { | |||||
} | |||||
node_counter = 0; | node_counter = 0; | ||||
} | } | ||||
{ | { | ||||
ENTER("event_terminate"); | ENTER("event_terminate"); | ||||
if (thread_inited) | |||||
{ | |||||
if (thread_inited) { | |||||
pthread_cancel(my_thread); | pthread_cancel(my_thread); | ||||
pthread_join(my_thread,NULL); | |||||
pthread_join(my_thread, NULL); | |||||
pthread_mutex_destroy(&my_mutex); | pthread_mutex_destroy(&my_mutex); | ||||
sem_destroy(&my_sem_start_is_required); | sem_destroy(&my_sem_start_is_required); | ||||
sem_destroy(&my_sem_stop_is_required); | sem_destroy(&my_sem_stop_is_required); |
// the callback will be called when the event actually occurs. | // the callback will be called when the event actually occurs. | ||||
// The callback is detailled in speak_lib.h . | // The callback is detailled in speak_lib.h . | ||||
void event_init(void); | void event_init(void); | ||||
void event_set_callback(t_espeak_callback* cb); | |||||
void event_set_callback(t_espeak_callback *cb); | |||||
// Clear any pending event. | // Clear any pending event. | ||||
// | // | ||||
// Return: EE_OK: operation achieved | // Return: EE_OK: operation achieved | ||||
// EE_INTERNAL_ERROR. | // EE_INTERNAL_ERROR. | ||||
espeak_ERROR event_clear_all (); | |||||
espeak_ERROR event_clear_all(); | |||||
// Declare a future event | // Declare a future event | ||||
// | // | ||||
// EE_BUFFER_FULL: the event can not be buffered; | // EE_BUFFER_FULL: the event can not be buffered; | ||||
// you may try after a while to call the function again. | // you may try after a while to call the function again. | ||||
// EE_INTERNAL_ERROR. | // EE_INTERNAL_ERROR. | ||||
espeak_ERROR event_declare (espeak_EVENT* event); | |||||
espeak_ERROR event_declare(espeak_EVENT *event); | |||||
// Terminate the event component. | // Terminate the event component. | ||||
// Last function to be called. | // Last function to be called. |
static sem_t my_sem_start_is_required; | static sem_t my_sem_start_is_required; | ||||
static sem_t my_sem_stop_is_acknowledged; | static sem_t my_sem_stop_is_acknowledged; | ||||
static void* say_thread(void*); | |||||
static void *say_thread(void *); | |||||
static espeak_ERROR push(t_espeak_command* the_command); | |||||
static t_espeak_command* pop(); | |||||
static espeak_ERROR push(t_espeak_command *the_command); | |||||
static t_espeak_command *pop(); | |||||
static void init(int process_parameters); | static void init(int process_parameters); | ||||
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}; | |||||
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 }; | |||||
void fifo_init() | void fifo_init() | ||||
{ | { | ||||
ENTER("fifo_init"); | ENTER("fifo_init"); | ||||
// security | // security | ||||
pthread_mutex_init( &my_mutex, (const pthread_mutexattr_t *)NULL); | |||||
pthread_mutex_init(&my_mutex, (const pthread_mutexattr_t *)NULL); | |||||
init(0); | init(0); | ||||
assert(-1 != sem_init(&my_sem_start_is_required, 0, 0)); | assert(-1 != sem_init(&my_sem_start_is_required, 0, 0)); | ||||
assert(-1 != sem_init(&my_sem_stop_is_acknowledged, 0, 0)); | assert(-1 != sem_init(&my_sem_stop_is_acknowledged, 0, 0)); | ||||
pthread_attr_t a_attrib; | pthread_attr_t a_attrib; | ||||
if (pthread_attr_init (&a_attrib) | |||||
if (pthread_attr_init(&a_attrib) | |||||
|| pthread_attr_setdetachstate(&a_attrib, PTHREAD_CREATE_JOINABLE) | || pthread_attr_setdetachstate(&a_attrib, PTHREAD_CREATE_JOINABLE) | ||||
|| pthread_create( &my_thread, | |||||
&a_attrib, | |||||
say_thread, | |||||
(void*)NULL)) | |||||
{ | |||||
|| pthread_create(&my_thread, | |||||
&a_attrib, | |||||
say_thread, | |||||
(void *)NULL)) { | |||||
assert(0); | assert(0); | ||||
} | } | ||||
// leave once the thread is actually started | // leave once the thread is actually started | ||||
SHOW_TIME("fifo > wait for my_sem_stop_is_acknowledged\n"); | SHOW_TIME("fifo > wait for my_sem_stop_is_acknowledged\n"); | ||||
while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR) | |||||
{ | |||||
while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR) { | |||||
continue; // Restart when interrupted by handler | continue; // Restart when interrupted by handler | ||||
} | } | ||||
SHOW_TIME("fifo > get my_sem_stop_is_acknowledged\n"); | SHOW_TIME("fifo > get my_sem_stop_is_acknowledged\n"); | ||||
} | } | ||||
espeak_ERROR fifo_add_command (t_espeak_command* the_command) | |||||
espeak_ERROR fifo_add_command(t_espeak_command *the_command) | |||||
{ | { | ||||
ENTER("fifo_add_command"); | ENTER("fifo_add_command"); | ||||
int a_status = pthread_mutex_lock(&my_mutex); | int a_status = pthread_mutex_lock(&my_mutex); | ||||
espeak_ERROR a_error = EE_OK; | espeak_ERROR a_error = EE_OK; | ||||
if (!a_status) | |||||
{ | |||||
if (!a_status) { | |||||
SHOW_TIME("fifo_add_command > locked\n"); | SHOW_TIME("fifo_add_command > locked\n"); | ||||
a_error = push(the_command); | a_error = push(the_command); | ||||
SHOW_TIME("fifo_add_command > unlocking\n"); | SHOW_TIME("fifo_add_command > unlocking\n"); | ||||
a_status = pthread_mutex_unlock(&my_mutex); | a_status = pthread_mutex_unlock(&my_mutex); | ||||
} | } | ||||
if (!a_status && !my_command_is_running && (a_error == EE_OK)) | |||||
{ | |||||
if (!a_status && !my_command_is_running && (a_error == EE_OK)) { | |||||
// quit when command is actually started | // quit when command is actually started | ||||
// (for possible forthcoming 'end of command' checks) | // (for possible forthcoming 'end of command' checks) | ||||
SHOW_TIME("fifo_add_command > post my_sem_start_is_required\n"); | SHOW_TIME("fifo_add_command > post my_sem_start_is_required\n"); | ||||
sem_post(&my_sem_start_is_required); | sem_post(&my_sem_start_is_required); | ||||
int val=1; | |||||
while (val > 0) | |||||
{ | |||||
int val = 1; | |||||
while (val > 0) { | |||||
usleep(50000); // TBD: event? | usleep(50000); // TBD: event? | ||||
sem_getvalue(&my_sem_start_is_required, &val); | sem_getvalue(&my_sem_start_is_required, &val); | ||||
} | } | ||||
} | } | ||||
if (a_status != 0) | |||||
{ | |||||
if (a_status != 0) { | |||||
a_error = EE_INTERNAL_ERROR; | a_error = EE_INTERNAL_ERROR; | ||||
} | } | ||||
return a_error; | return a_error; | ||||
} | } | ||||
espeak_ERROR fifo_add_commands (t_espeak_command* command1, t_espeak_command* command2) | |||||
espeak_ERROR fifo_add_commands(t_espeak_command *command1, t_espeak_command *command2) | |||||
{ | { | ||||
ENTER("fifo_add_command"); | ENTER("fifo_add_command"); | ||||
int a_status = pthread_mutex_lock(&my_mutex); | int a_status = pthread_mutex_lock(&my_mutex); | ||||
espeak_ERROR a_error = EE_OK; | espeak_ERROR a_error = EE_OK; | ||||
if (!a_status) | |||||
{ | |||||
if (!a_status) { | |||||
SHOW_TIME("fifo_add_commands > locked\n"); | SHOW_TIME("fifo_add_commands > locked\n"); | ||||
if (node_counter+1 >= MAX_NODE_COUNTER) | |||||
{ | |||||
if (node_counter+1 >= MAX_NODE_COUNTER) { | |||||
SHOW("push > %s\n", "EE_BUFFER_FULL"); | SHOW("push > %s\n", "EE_BUFFER_FULL"); | ||||
a_error = EE_BUFFER_FULL; | a_error = EE_BUFFER_FULL; | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
push(command1); | push(command1); | ||||
push(command2); | push(command2); | ||||
} | } | ||||
a_status = pthread_mutex_unlock(&my_mutex); | a_status = pthread_mutex_unlock(&my_mutex); | ||||
} | } | ||||
if (!a_status && !my_command_is_running && (a_error == EE_OK)) | |||||
{ | |||||
if (!a_status && !my_command_is_running && (a_error == EE_OK)) { | |||||
// quit when one command is actually started | // quit when one command is actually started | ||||
// (for possible forthcoming 'end of command' checks) | // (for possible forthcoming 'end of command' checks) | ||||
SHOW_TIME("fifo_add_command > post my_sem_start_is_required\n"); | SHOW_TIME("fifo_add_command > post my_sem_start_is_required\n"); | ||||
sem_post(&my_sem_start_is_required); | sem_post(&my_sem_start_is_required); | ||||
int val=1; | |||||
while (val > 0) | |||||
{ | |||||
int val = 1; | |||||
while (val > 0) { | |||||
usleep(50000); // TBD: event? | usleep(50000); // TBD: event? | ||||
sem_getvalue(&my_sem_start_is_required, &val); | sem_getvalue(&my_sem_start_is_required, &val); | ||||
} | } | ||||
} | } | ||||
if (a_status != 0) | |||||
{ | |||||
if (a_status != 0) { | |||||
a_error = EE_INTERNAL_ERROR; | a_error = EE_INTERNAL_ERROR; | ||||
} | } | ||||
return a_error; | return a_error; | ||||
} | } | ||||
espeak_ERROR fifo_stop () | |||||
espeak_ERROR fifo_stop() | |||||
{ | { | ||||
ENTER("fifo_stop"); | ENTER("fifo_stop"); | ||||
int a_command_is_running = 0; | int a_command_is_running = 0; | ||||
int a_status = pthread_mutex_lock(&my_mutex); | int a_status = pthread_mutex_lock(&my_mutex); | ||||
SHOW_TIME("fifo_stop > locked\n"); | SHOW_TIME("fifo_stop > locked\n"); | ||||
if (a_status != 0) | |||||
{ | |||||
if (a_status != 0) { | |||||
return EE_INTERNAL_ERROR; | return EE_INTERNAL_ERROR; | ||||
} | } | ||||
if (my_command_is_running) | |||||
{ | |||||
if (my_command_is_running) { | |||||
a_command_is_running = 1; | a_command_is_running = 1; | ||||
my_stop_is_required = 1; | my_stop_is_required = 1; | ||||
SHOW_TIME("fifo_stop > my_stop_is_required = 1\n"); | SHOW_TIME("fifo_stop > my_stop_is_required = 1\n"); | ||||
} | } | ||||
SHOW_TIME("fifo_stop > unlocking\n"); | SHOW_TIME("fifo_stop > unlocking\n"); | ||||
a_status = pthread_mutex_unlock(&my_mutex); | a_status = pthread_mutex_unlock(&my_mutex); | ||||
if (a_status != 0) | |||||
{ | |||||
if (a_status != 0) { | |||||
return EE_INTERNAL_ERROR; | return EE_INTERNAL_ERROR; | ||||
} | } | ||||
if (a_command_is_running) | |||||
{ | |||||
if (a_command_is_running) { | |||||
SHOW_TIME("fifo_stop > wait for my_sem_stop_is_acknowledged\n"); | SHOW_TIME("fifo_stop > wait for my_sem_stop_is_acknowledged\n"); | ||||
while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR) | |||||
{ | |||||
while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR) { | |||||
continue; // Restart when interrupted by handler | continue; // Restart when interrupted by handler | ||||
} | } | ||||
SHOW_TIME("fifo_stop > get my_sem_stop_is_acknowledged\n"); | SHOW_TIME("fifo_stop > get my_sem_stop_is_acknowledged\n"); | ||||
return EE_OK; | return EE_OK; | ||||
} | } | ||||
int fifo_is_busy () | |||||
int fifo_is_busy() | |||||
{ | { | ||||
SHOW("fifo_is_busy > aResult = %d\n",my_command_is_running); | |||||
SHOW("fifo_is_busy > aResult = %d\n", my_command_is_running); | |||||
return my_command_is_running; | return my_command_is_running; | ||||
} | } | ||||
static int sleep_until_start_request_or_inactivity() | static int sleep_until_start_request_or_inactivity() | ||||
{ | { | ||||
SHOW_TIME("fifo > sleep_until_start_request_or_inactivity > ENTER"); | SHOW_TIME("fifo > sleep_until_start_request_or_inactivity > ENTER"); | ||||
int a_start_is_required=0; | |||||
int a_start_is_required = 0; | |||||
// Wait for the start request (my_sem_start_is_required). | // Wait for the start request (my_sem_start_is_required). | ||||
// Besides this, if the audio stream is still busy, | // Besides this, if the audio stream is still busy, | ||||
// The end of the stream is confirmed by several checks | // The end of the stream is confirmed by several checks | ||||
// for filtering underflow. | // for filtering underflow. | ||||
// | // | ||||
int i=0; | |||||
while((i<= MAX_INACTIVITY_CHECK) && !a_start_is_required) | |||||
{ | |||||
if (wave_is_busy( NULL) ) | |||||
{ | |||||
int i = 0; | |||||
while ((i <= MAX_INACTIVITY_CHECK) && !a_start_is_required) { | |||||
if (wave_is_busy(NULL)) { | |||||
i = 0; | i = 0; | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
i++; | i++; | ||||
} | } | ||||
int err=0; | |||||
int err = 0; | |||||
struct timespec ts; | struct timespec ts; | ||||
struct timeval tv; | struct timeval tv; | ||||
clock_gettime2( &ts); | |||||
clock_gettime2(&ts); | |||||
#ifdef DEBUG_ENABLED | #ifdef DEBUG_ENABLED | ||||
struct timespec to; | struct timespec to; | ||||
to.tv_nsec = ts.tv_nsec; | to.tv_nsec = ts.tv_nsec; | ||||
#endif | #endif | ||||
add_time_in_ms( &ts, INACTIVITY_TIMEOUT); | |||||
add_time_in_ms(&ts, INACTIVITY_TIMEOUT); | |||||
SHOW("fifo > sleep_until_start_request_or_inactivity > start sem_timedwait (start_is_required) from %d.%09lu to %d.%09lu \n", | SHOW("fifo > sleep_until_start_request_or_inactivity > start sem_timedwait (start_is_required) from %d.%09lu to %d.%09lu \n", | ||||
to.tv_sec, to.tv_nsec, | to.tv_sec, to.tv_nsec, | ||||
ts.tv_sec, ts.tv_nsec); | ts.tv_sec, ts.tv_nsec); | ||||
while ((err = sem_timedwait(&my_sem_start_is_required, &ts)) == -1 | while ((err = sem_timedwait(&my_sem_start_is_required, &ts)) == -1 | ||||
&& errno == EINTR) | |||||
{ | |||||
&& errno == EINTR) { | |||||
continue; | continue; | ||||
} | } | ||||
assert (gettimeofday(&tv, NULL) != -1); | |||||
assert(gettimeofday(&tv, NULL) != -1); | |||||
SHOW("fifo > sleep_until_start_request_or_inactivity > stop sem_timedwait (start_is_required, err=%d) %d.%09lu \n", err, | SHOW("fifo > sleep_until_start_request_or_inactivity > stop sem_timedwait (start_is_required, err=%d) %d.%09lu \n", err, | ||||
tv.tv_sec, tv.tv_usec*1000); | tv.tv_sec, tv.tv_usec*1000); | ||||
if (err==0) | |||||
{ | |||||
if (err == 0) { | |||||
a_start_is_required = 1; | a_start_is_required = 1; | ||||
} | } | ||||
} | } | ||||
// my_stop_is_required = 1; | // my_stop_is_required = 1; | ||||
int a_status = pthread_mutex_lock(&my_mutex); | int a_status = pthread_mutex_lock(&my_mutex); | ||||
assert (!a_status); | |||||
assert(!a_status); | |||||
int a_stop_is_required = my_stop_is_required; | int a_stop_is_required = my_stop_is_required; | ||||
if (!a_stop_is_required) | |||||
{ | |||||
if (!a_stop_is_required) { | |||||
my_command_is_running = 1; | my_command_is_running = 1; | ||||
} | } | ||||
a_status = pthread_mutex_unlock(&my_mutex); | a_status = pthread_mutex_unlock(&my_mutex); | ||||
if (!a_stop_is_required) | |||||
{ | |||||
if (!a_stop_is_required) { | |||||
wave_close(NULL); | wave_close(NULL); | ||||
int a_status = pthread_mutex_lock(&my_mutex); | int a_status = pthread_mutex_lock(&my_mutex); | ||||
assert (!a_status); | |||||
assert(!a_status); | |||||
my_command_is_running = 0; | my_command_is_running = 0; | ||||
a_stop_is_required = my_stop_is_required; | a_stop_is_required = my_stop_is_required; | ||||
a_status = pthread_mutex_unlock(&my_mutex); | a_status = pthread_mutex_unlock(&my_mutex); | ||||
if (a_stop_is_required) | |||||
{ | |||||
if (a_stop_is_required) { | |||||
// acknowledge the stop request | // acknowledge the stop request | ||||
SHOW_TIME("fifo > close_stream > post my_sem_stop_is_acknowledged\n"); | SHOW_TIME("fifo > close_stream > post my_sem_stop_is_acknowledged\n"); | ||||
int a_status = sem_post(&my_sem_stop_is_acknowledged); | int a_status = sem_post(&my_sem_stop_is_acknowledged); | ||||
assert( a_status != -1); | |||||
assert(a_status != -1); | |||||
} | } | ||||
} | } | ||||
SHOW_TIME("fifo > close_stream > LEAVE\n"); | SHOW_TIME("fifo > close_stream > LEAVE\n"); | ||||
} | } | ||||
static void* say_thread(void*p) | |||||
static void *say_thread(void *p) | |||||
{ | { | ||||
ENTER("say_thread"); | ENTER("say_thread"); | ||||
// announce that thread is started | // announce that thread is started | ||||
sem_post(&my_sem_stop_is_acknowledged); | sem_post(&my_sem_stop_is_acknowledged); | ||||
int look_for_inactivity=0; | |||||
int look_for_inactivity = 0; | |||||
while(1) | |||||
{ | |||||
while (1) { | |||||
SHOW_TIME("say_thread > wait for my_sem_start_is_required\n"); | SHOW_TIME("say_thread > wait for my_sem_start_is_required\n"); | ||||
int a_start_is_required = 0; | int a_start_is_required = 0; | ||||
if (look_for_inactivity) | |||||
{ | |||||
if (look_for_inactivity) { | |||||
a_start_is_required = sleep_until_start_request_or_inactivity(); | a_start_is_required = sleep_until_start_request_or_inactivity(); | ||||
if (!a_start_is_required) | |||||
{ | |||||
if (!a_start_is_required) { | |||||
close_stream(); | close_stream(); | ||||
} | } | ||||
} | } | ||||
look_for_inactivity = 1; | look_for_inactivity = 1; | ||||
if (!a_start_is_required) | |||||
{ | |||||
while ((sem_wait(&my_sem_start_is_required) == -1) && errno == EINTR) | |||||
{ | |||||
if (!a_start_is_required) { | |||||
while ((sem_wait(&my_sem_start_is_required) == -1) && errno == EINTR) { | |||||
continue; // Restart when interrupted by handler | continue; // Restart when interrupted by handler | ||||
} | } | ||||
} | } | ||||
SHOW_TIME("say_thread > my_command_is_running = 1\n"); | SHOW_TIME("say_thread > my_command_is_running = 1\n"); | ||||
my_command_is_running = 1; | my_command_is_running = 1; | ||||
while( my_command_is_running) | |||||
{ | |||||
while (my_command_is_running) { | |||||
SHOW_TIME("say_thread > locking\n"); | SHOW_TIME("say_thread > locking\n"); | ||||
int a_status = pthread_mutex_lock(&my_mutex); | int a_status = pthread_mutex_lock(&my_mutex); | ||||
assert (!a_status); | |||||
t_espeak_command* a_command = (t_espeak_command*)pop(); | |||||
assert(!a_status); | |||||
t_espeak_command *a_command = (t_espeak_command *)pop(); | |||||
if (a_command == NULL) | |||||
{ | |||||
if (a_command == NULL) { | |||||
SHOW_TIME("say_thread > text empty (talking=0) \n"); | SHOW_TIME("say_thread > text empty (talking=0) \n"); | ||||
a_status = pthread_mutex_unlock(&my_mutex); | a_status = pthread_mutex_unlock(&my_mutex); | ||||
SHOW_TIME("say_thread > unlocked\n"); | SHOW_TIME("say_thread > unlocked\n"); | ||||
SHOW_TIME("say_thread > my_command_is_running = 0\n"); | SHOW_TIME("say_thread > my_command_is_running = 0\n"); | ||||
my_command_is_running = 0; | my_command_is_running = 0; | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
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"); | ||||
my_command_is_running = 0; | my_command_is_running = 0; | ||||
} | } | ||||
SHOW_TIME("say_thread > unlocking\n"); | SHOW_TIME("say_thread > unlocking\n"); | ||||
a_status = pthread_mutex_unlock(&my_mutex); | a_status = pthread_mutex_unlock(&my_mutex); | ||||
if (my_command_is_running) | |||||
{ | |||||
if (my_command_is_running) { | |||||
process_espeak_command(a_command); | process_espeak_command(a_command); | ||||
} | } | ||||
delete_espeak_command(a_command); | delete_espeak_command(a_command); | ||||
} | } | ||||
} | } | ||||
if (my_stop_is_required) | |||||
{ | |||||
if (my_stop_is_required) { | |||||
// no mutex required since the stop command is synchronous | // no mutex required since the stop command is synchronous | ||||
// and waiting for my_sem_stop_is_acknowledged | // and waiting for my_sem_stop_is_acknowledged | ||||
init(1); | init(1); | ||||
// 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"); | ||||
int a_status = sem_post(&my_sem_stop_is_acknowledged); | int a_status = sem_post(&my_sem_stop_is_acknowledged); | ||||
assert( a_status != -1); | |||||
assert(a_status != -1); | |||||
} | } | ||||
// and wait for the next start | // and wait for the next start | ||||
SHOW_TIME("say_thread > wait for my_sem_start_is_required\n"); | SHOW_TIME("say_thread > wait for my_sem_start_is_required\n"); | ||||
int fifo_is_command_enabled() | int fifo_is_command_enabled() | ||||
{ | { | ||||
SHOW("ENTER fifo_is_command_enabled=%d\n",(int)(0 == my_stop_is_required)); | |||||
SHOW("ENTER fifo_is_command_enabled=%d\n", (int)(0 == my_stop_is_required)); | |||||
return (0 == my_stop_is_required); | return (0 == my_stop_is_required); | ||||
} | } | ||||
typedef struct t_node | |||||
{ | |||||
t_espeak_command* data; | |||||
typedef struct t_node { | |||||
t_espeak_command *data; | |||||
struct t_node *next; | struct t_node *next; | ||||
} node; | } node; | ||||
static node* head=NULL; | |||||
static node* tail=NULL; | |||||
static node *head = NULL; | |||||
static node *tail = NULL; | |||||
static espeak_ERROR push(t_espeak_command* the_command) | |||||
static espeak_ERROR push(t_espeak_command *the_command) | |||||
{ | { | ||||
ENTER("fifo > push"); | ENTER("fifo > push"); | ||||
assert((!head && !tail) || (head && tail)); | assert((!head && !tail) || (head && tail)); | ||||
if (the_command == NULL) | |||||
{ | |||||
if (the_command == NULL) { | |||||
SHOW("push > command=0x%x\n", NULL); | SHOW("push > command=0x%x\n", NULL); | ||||
return EE_INTERNAL_ERROR; | return EE_INTERNAL_ERROR; | ||||
} | } | ||||
if (node_counter >= MAX_NODE_COUNTER) | |||||
{ | |||||
if (node_counter >= MAX_NODE_COUNTER) { | |||||
SHOW("push > %s\n", "EE_BUFFER_FULL"); | SHOW("push > %s\n", "EE_BUFFER_FULL"); | ||||
return EE_BUFFER_FULL; | return EE_BUFFER_FULL; | ||||
} | } | ||||
node *n = (node *)malloc(sizeof(node)); | node *n = (node *)malloc(sizeof(node)); | ||||
if (n == NULL) | |||||
{ | |||||
if (n == NULL) { | |||||
return EE_INTERNAL_ERROR; | return EE_INTERNAL_ERROR; | ||||
} | } | ||||
if (head == NULL) | |||||
{ | |||||
if (head == NULL) { | |||||
head = n; | head = n; | ||||
tail = n; | tail = n; | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
tail->next = n; | tail->next = n; | ||||
tail = n; | tail = n; | ||||
} | } | ||||
tail->data = the_command; | tail->data = the_command; | ||||
node_counter++; | node_counter++; | ||||
SHOW("push > counter=%d\n",node_counter); | |||||
SHOW("push > counter=%d\n", node_counter); | |||||
the_command->state = CS_PENDING; | the_command->state = CS_PENDING; | ||||
display_espeak_command(the_command); | display_espeak_command(the_command); | ||||
return EE_OK; | return EE_OK; | ||||
} | } | ||||
static t_espeak_command* pop() | |||||
static t_espeak_command *pop() | |||||
{ | { | ||||
ENTER("fifo > pop"); | ENTER("fifo > pop"); | ||||
t_espeak_command* the_command = NULL; | |||||
t_espeak_command *the_command = NULL; | |||||
assert((!head && !tail) || (head && tail)); | assert((!head && !tail) || (head && tail)); | ||||
if (head != NULL) | |||||
{ | |||||
node* n = head; | |||||
if (head != NULL) { | |||||
node *n = head; | |||||
the_command = n->data; | the_command = n->data; | ||||
head = n->next; | head = n->next; | ||||
free(n); | free(n); | ||||
node_counter--; | node_counter--; | ||||
SHOW("pop > command=0x%x (counter=%d)\n",the_command, node_counter); | |||||
SHOW("pop > command=0x%x (counter=%d)\n", the_command, node_counter); | |||||
} | } | ||||
if(head == NULL) | |||||
{ | |||||
if (head == NULL) { | |||||
tail = NULL; | tail = NULL; | ||||
} | } | ||||
ENTER("fifo > init"); | ENTER("fifo > init"); | ||||
c = pop(); | c = pop(); | ||||
while (c != NULL) { | while (c != NULL) { | ||||
if (process_parameters && (c->type == ET_PARAMETER || c->type == ET_VOICE_NAME || c->type == ET_VOICE_SPEC)) | |||||
{ | |||||
if (process_parameters && (c->type == ET_PARAMETER || c->type == ET_VOICE_NAME || c->type == ET_VOICE_SPEC)) { | |||||
process_espeak_command(c); | process_espeak_command(c); | ||||
} | } | ||||
delete_espeak_command(c); | delete_espeak_command(c); | ||||
ENTER("fifo_terminate"); | ENTER("fifo_terminate"); | ||||
pthread_cancel(my_thread); | pthread_cancel(my_thread); | ||||
pthread_join(my_thread,NULL); | |||||
pthread_join(my_thread, NULL); | |||||
pthread_mutex_destroy(&my_mutex); | pthread_mutex_destroy(&my_mutex); | ||||
sem_destroy(&my_sem_start_is_required); | sem_destroy(&my_sem_start_is_required); | ||||
sem_destroy(&my_sem_stop_is_acknowledged); | sem_destroy(&my_sem_stop_is_acknowledged); |
// EE_BUFFER_FULL: the command can not be buffered; | // EE_BUFFER_FULL: the command can not be buffered; | ||||
// you may try after a while to call the function again. | // you may try after a while to call the function again. | ||||
// EE_INTERNAL_ERROR. | // EE_INTERNAL_ERROR. | ||||
espeak_ERROR fifo_add_command (t_espeak_command* c); | |||||
espeak_ERROR fifo_add_command(t_espeak_command *c); | |||||
// Add two espeak commands in a single transaction. | // Add two espeak commands in a single transaction. | ||||
// | // | ||||
// EE_BUFFER_FULL: at least one command can not be buffered; | // EE_BUFFER_FULL: at least one command can not be buffered; | ||||
// you may try after a while to call the function again. | // you may try after a while to call the function again. | ||||
// EE_INTERNAL_ERROR. | // EE_INTERNAL_ERROR. | ||||
espeak_ERROR fifo_add_commands (t_espeak_command* c1, t_espeak_command* c2); | |||||
espeak_ERROR fifo_add_commands(t_espeak_command *c1, t_espeak_command *c2); | |||||
// The current running command must be stopped and the awaiting commands are cleared. | // The current running command must be stopped and the awaiting commands are cleared. | ||||
// Return: EE_OK: operation achieved | // Return: EE_OK: operation achieved | ||||
// EE_INTERNAL_ERROR. | // EE_INTERNAL_ERROR. | ||||
espeak_ERROR fifo_stop (); | |||||
espeak_ERROR fifo_stop(); | |||||
// Is there a running command? | // Is there a running command? | ||||
// Returns 1 if yes; 0 otherwise. | // Returns 1 if yes; 0 otherwise. | ||||
int fifo_is_busy (); | |||||
int fifo_is_busy(); | |||||
// Terminate the fifo component. | // Terminate the fifo component. | ||||
// Last function to be called. | // Last function to be called. |
/* Resonator Structure */ | /* Resonator Structure */ | ||||
typedef struct | |||||
{ | |||||
typedef struct { | |||||
double a; | double a; | ||||
double b; | double b; | ||||
double c; | double c; | ||||
/* Structure for Klatt Globals */ | /* Structure for Klatt Globals */ | ||||
typedef struct | |||||
{ | |||||
typedef struct { | |||||
flag synthesis_model; /* cascade-parallel or all-parallel */ | flag synthesis_model; /* cascade-parallel or all-parallel */ | ||||
flag outsl; /* Output waveform selector */ | flag outsl; /* Output waveform selector */ | ||||
long samrate; /* Number of output samples per second */ | long samrate; /* Number of output samples per second */ | ||||
#define F_NP 9 // nasal pole formant | #define F_NP 9 // nasal pole formant | ||||
typedef struct | |||||
{ | |||||
typedef struct { | |||||
int F0hz10; /* Voicing fund freq in Hz */ | int F0hz10; /* Voicing fund freq in Hz */ | ||||
int AVdb; /* Amp of voicing in dB, 0 to 70 */ | int AVdb; /* Amp of voicing in dB, 0 to 70 */ | ||||
int Fhz[10]; // formant Hz, F_NZ to F6 to F_NP | int Fhz[10]; // formant Hz, F_NZ to F6 to F_NP | ||||
int AVpdb; /* Amp of voicing, par in dB, 0 to 70 */ | int AVpdb; /* Amp of voicing, par in dB, 0 to 70 */ | ||||
int Gain0; /* Overall gain, 60 dB is unity, 0 to 60 */ | int Gain0; /* Overall gain, 60 dB is unity, 0 to 60 */ | ||||
int AVdb_tmp; //copy of AVdb, which is changed within parwave() | |||||
int AVdb_tmp; // copy of AVdb, which is changed within parwave() | |||||
int Fhz_next[10]; // Fhz for the next chunk, so we can do interpolation of resonator (a,b,c) parameters | int Fhz_next[10]; // Fhz for the next chunk, so we can do interpolation of resonator (a,b,c) parameters | ||||
int Bhz_next[10]; | int Bhz_next[10]; | ||||
} klatt_frame_t, *klatt_frame_ptr; | } klatt_frame_t, *klatt_frame_ptr; |
char *buf_ptr, *lf; | char *buf_ptr, *lf; | ||||
buf_ptr = buffer; | buf_ptr = buffer; | ||||
for (;; ) { | |||||
for (;;) { | |||||
result = read(mbr_error_fd, buf_ptr, | result = read(mbr_error_fd, buf_ptr, | ||||
sizeof(buffer) - (buf_ptr - buffer) - 1); | sizeof(buffer) - (buf_ptr - buffer) - 1); | ||||
if (result == -1) { | if (result == -1) { | ||||
} | } | ||||
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); | |||||
// 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) { |
#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 PhonemeCode2(c1,c2) PhonemeCode((c2<<8)+c1) | |||||
#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 PhonemeCode2(c1, c2) PhonemeCode((c2<<8)+c1) | |||||
int LookupPhonemeString(const char *string); | int LookupPhonemeString(const char *string); | ||||
int PhonemeCode(unsigned int mnem); | int PhonemeCode(unsigned int mnem); | ||||
#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; | ||||
int n_plist_out = 0; | int n_plist_out = 0; | ||||
int word_end; | int word_end; | ||||
PHONEME_LIST2 *plist2; | PHONEME_LIST2 *plist2; | ||||
PHONEME_TAB *next=NULL; | |||||
PHONEME_TAB *next = NULL; | |||||
for(ix=0; (ix < n_ph_list2) && (n_plist_out < N_PHONEME_LIST); ix++) | |||||
{ | |||||
for (ix = 0; (ix < n_ph_list2) && (n_plist_out < N_PHONEME_LIST); ix++) { | |||||
plist2 = &ph_list2[ix]; | plist2 = &ph_list2[ix]; | ||||
// don't do any substitution if the language has been temporarily changed | // don't do any substitution if the language has been temporarily changed | ||||
if(!(plist2->synthflags & SFLAG_SWITCHED_LANG)) | |||||
{ | |||||
if(ix < (n_ph_list2 -1)) | |||||
if (!(plist2->synthflags & SFLAG_SWITCHED_LANG)) { | |||||
if (ix < (n_ph_list2 -1)) | |||||
next = phoneme_tab[ph_list2[ix+1].phcode]; | next = phoneme_tab[ph_list2[ix+1].phcode]; | ||||
word_end = 0; | word_end = 0; | ||||
if((plist2+1)->sourceix || ((next != 0) && (next->type == phPAUSE))) | |||||
if ((plist2+1)->sourceix || ((next != 0) && (next->type == phPAUSE))) | |||||
word_end = 1; // this phoneme is the end of a word | word_end = 1; // this phoneme is the end of a word | ||||
// check whether a Voice has specified that we should replace this phoneme | // check whether a Voice has specified that we should replace this phoneme | ||||
for(k=0; k<n_replace_phonemes; k++) | |||||
{ | |||||
if(plist2->phcode == replace_phonemes[k].old_ph) | |||||
{ | |||||
for (k = 0; k < n_replace_phonemes; k++) { | |||||
if (plist2->phcode == replace_phonemes[k].old_ph) { | |||||
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 | ||||
memcpy(&plist_out[n_plist_out],plist2,sizeof(PHONEME_LIST2)); | |||||
memcpy(&plist_out[n_plist_out], plist2, sizeof(PHONEME_LIST2)); | |||||
plist_out[n_plist_out].ph = phoneme_tab[plist2->phcode]; | plist_out[n_plist_out].ph = phoneme_tab[plist2->phcode]; | ||||
plist_out[n_plist_out].type = plist_out[n_plist_out].ph->type; | plist_out[n_plist_out].type = plist_out[n_plist_out].ph->type; | ||||
n_plist_out++; | n_plist_out++; | ||||
} | } | ||||
return(n_plist_out); | |||||
return (n_plist_out); | |||||
} | } | ||||
void MakePhonemeList(Translator *tr, int post_pause, int start_sentence) | void MakePhonemeList(Translator *tr, int post_pause, int start_sentence) | ||||
{ | { | ||||
int ix=0; | |||||
int ix = 0; | |||||
int j; | int j; | ||||
int insert_ph = 0; | int insert_ph = 0; | ||||
PHONEME_LIST *phlist; | PHONEME_LIST *phlist; | ||||
// is the last word of the clause unstressed ? | // is the last word of the clause unstressed ? | ||||
max_stress = 0; | max_stress = 0; | ||||
for(j = n_ph_list2-3; j>=0; j--) | |||||
{ | |||||
for (j = n_ph_list2-3; j >= 0; j--) { | |||||
// start with the last phoneme (before the terminating pauses) and move backwards | // start with the last phoneme (before the terminating pauses) and move backwards | ||||
if((plist2[j].stresslevel & 0x7f) > max_stress) | |||||
if ((plist2[j].stresslevel & 0x7f) > max_stress) | |||||
max_stress = plist2[j].stresslevel & 0x7f; | max_stress = plist2[j].stresslevel & 0x7f; | ||||
if(plist2[j].sourceix != 0) | |||||
if (plist2[j].sourceix != 0) | |||||
break; | break; | ||||
} | } | ||||
if(max_stress < 4) | |||||
{ | |||||
if (max_stress < 4) { | |||||
// 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) | |||||
{ | |||||
if(plist2[j].synthflags & SFLAG_PROMOTE_STRESS) // dictionary flags indicated that this stress can be promoted | |||||
{ | |||||
while (--j >= 0) { | |||||
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) { | |||||
// found a stressed syllable, so stop looking | // found a stressed syllable, so stop looking | ||||
break; | break; | ||||
} | } | ||||
// look for switch of phoneme tables | // look for switch of phoneme tables | ||||
delete_count = 0; | delete_count = 0; | ||||
current_phoneme_tab = tr->phoneme_tab_ix; | current_phoneme_tab = tr->phoneme_tab_ix; | ||||
for(j = 0; j < n_ph_list2; j++) | |||||
{ | |||||
if(current_phoneme_tab != tr->phoneme_tab_ix) | |||||
{ | |||||
for (j = 0; j < n_ph_list2; j++) { | |||||
if (current_phoneme_tab != tr->phoneme_tab_ix) { | |||||
plist2[j].synthflags |= SFLAG_SWITCHED_LANG; | plist2[j].synthflags |= SFLAG_SWITCHED_LANG; | ||||
} | } | ||||
if(delete_count > 0) | |||||
{ | |||||
if (delete_count > 0) { | |||||
memcpy(&plist2[j-delete_count], &plist2[j], sizeof(plist2[0])); | memcpy(&plist2[j-delete_count], &plist2[j], sizeof(plist2[0])); | ||||
} | } | ||||
if(plist2[j].phcode == phonSWITCH) | |||||
{ | |||||
if((!(plist2[j].synthflags & SFLAG_EMBEDDED)) && ( | |||||
(plist2[j].tone_ph == current_phoneme_tab) || | |||||
(plist2[j+1].phcode == phonSWITCH) || | |||||
((plist2[j+1].phcode == phonPAUSE) && (plist2[j+2].phcode == phonSWITCH)) | |||||
)) | |||||
{ | |||||
if (plist2[j].phcode == phonSWITCH) { | |||||
if ((!(plist2[j].synthflags & SFLAG_EMBEDDED)) && ( | |||||
(plist2[j].tone_ph == current_phoneme_tab) || | |||||
(plist2[j+1].phcode == phonSWITCH) || | |||||
((plist2[j+1].phcode == phonPAUSE) && (plist2[j+2].phcode == phonSWITCH)) | |||||
)) { | |||||
// delete this phonSWITCH if it's switching to the current phoneme table, or | // delete this phonSWITCH if it's switching to the current phoneme table, or | ||||
// delete this phonSWITCH if its followed by another phonSWITCH | // delete this phonSWITCH if its followed by another phonSWITCH | ||||
delete_count++; | delete_count++; | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
current_phoneme_tab = plist2[j].tone_ph; | current_phoneme_tab = plist2[j].tone_ph; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
n_ph_list2 -= delete_count; | n_ph_list2 -= delete_count; | ||||
if((regression = tr->langopts.param[LOPT_REGRESSIVE_VOICING]) != 0) | |||||
{ | |||||
if ((regression = tr->langopts.param[LOPT_REGRESSIVE_VOICING]) != 0) { | |||||
// set consonant clusters to all voiced or all unvoiced | // set consonant clusters to all voiced or all unvoiced | ||||
// Regressive | // Regressive | ||||
int type; | int type; | ||||
int stop_propagation = 0; | int stop_propagation = 0; | ||||
voicing = 0; | voicing = 0; | ||||
for(j=n_ph_list2-1; j>=0; j--) | |||||
{ | |||||
for (j = n_ph_list2-1; j >= 0; j--) { | |||||
ph = phoneme_tab[plist2[j].phcode]; | ph = phoneme_tab[plist2[j].phcode]; | ||||
if(ph == NULL) | |||||
if (ph == NULL) | |||||
continue; | continue; | ||||
if(plist2[j].synthflags & SFLAG_SWITCHED_LANG) | |||||
{ | |||||
if (plist2[j].synthflags & SFLAG_SWITCHED_LANG) { | |||||
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; | ||||
} | } | ||||
type = ph->type; | type = ph->type; | ||||
if(regression & 0x2) | |||||
{ | |||||
if (regression & 0x2) { | |||||
// [v] amd [v;] don't cause regression, or [R^] | // [v] amd [v;] don't cause regression, or [R^] | ||||
if(((ph->mnemonic & 0xff) == 'v') || ((ph->mnemonic & 0xff)== 'R')) | |||||
{ | |||||
if (((ph->mnemonic & 0xff) == 'v') || ((ph->mnemonic & 0xff) == 'R')) { | |||||
stop_propagation = 1; | stop_propagation = 1; | ||||
if(regression & 0x10) | |||||
if (regression & 0x10) | |||||
voicing = 0; | voicing = 0; | ||||
} | } | ||||
} | } | ||||
if((type==phSTOP) || type==(phFRICATIVE)) | |||||
{ | |||||
if((voicing==0) && (regression & 0xf)) | |||||
{ | |||||
if ((type == phSTOP) || type == (phFRICATIVE)) { | |||||
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)) | |||||
{ | |||||
if((voicing==0) && (regression & 0xf)) | |||||
{ | |||||
} else if ((type == phVSTOP) || type == (phVFRICATIVE)) { | |||||
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 | |||||
{ | |||||
if(regression & 0x8) | |||||
{ | |||||
} else { | |||||
if (regression & 0x8) { | |||||
// LANG=Polish, propagate through liquids and nasals | // LANG=Polish, propagate through liquids and nasals | ||||
if((type == phPAUSE) || (type == phVOWEL)) | |||||
if ((type == phPAUSE) || (type == phVOWEL)) | |||||
voicing = 0; | voicing = 0; | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
voicing = 0; | voicing = 0; | ||||
} | } | ||||
} | } | ||||
if(stop_propagation) | |||||
{ | |||||
if (stop_propagation) { | |||||
voicing = 0; | voicing = 0; | ||||
stop_propagation = 0; | stop_propagation = 0; | ||||
} | } | ||||
if(plist2[j].sourceix) | |||||
{ | |||||
if(regression & 0x04) | |||||
{ | |||||
if (plist2[j].sourceix) { | |||||
if (regression & 0x04) { | |||||
// stop propagation at a word boundary | // stop propagation at a word boundary | ||||
voicing = 0; | voicing = 0; | ||||
} | } | ||||
if(regression & 0x100) | |||||
{ | |||||
if (regression & 0x100) { | |||||
// devoice word-final consonants, unless propagating voiced | // devoice word-final consonants, unless propagating voiced | ||||
if(voicing == 0) | |||||
{ | |||||
if (voicing == 0) { | |||||
voicing = 1; | voicing = 1; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
n_ph_list3 = SubstitutePhonemes(tr,ph_list3) - 2; | |||||
n_ph_list3 = SubstitutePhonemes(tr, ph_list3) - 2; | |||||
for(j=0; (j < n_ph_list3) && (ix < N_PHONEME_LIST-3); ) | |||||
{ | |||||
if(ph_list3[j].sourceix) | |||||
{ | |||||
for (j = 0; (j < n_ph_list3) && (ix < N_PHONEME_LIST-3);) { | |||||
if (ph_list3[j].sourceix) { | |||||
// start of a word | // start of a word | ||||
int k; | int k; | ||||
int nextw; | int nextw; | ||||
word_stress = 0; | word_stress = 0; | ||||
// find the highest stress level in this word | // find the highest stress level in this word | ||||
for(nextw=j; nextw < n_ph_list3; ) | |||||
{ | |||||
if(ph_list3[nextw].stresslevel > word_stress) | |||||
for (nextw = j; nextw < n_ph_list3;) { | |||||
if (ph_list3[nextw].stresslevel > word_stress) | |||||
word_stress = ph_list3[nextw].stresslevel; | word_stress = ph_list3[nextw].stresslevel; | ||||
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; | ||||
} | } | ||||
j = nextw; | j = nextw; | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
j++; | j++; | ||||
} | } | ||||
} | } | ||||
ph_list3[0].ph = ph; | ph_list3[0].ph = ph; | ||||
word_start = 1; | word_start = 1; | ||||
for(j=0; insert_ph || ((j < n_ph_list3) && (ix < N_PHONEME_LIST-3)); j++) | |||||
{ | |||||
for (j = 0; insert_ph || ((j < n_ph_list3) && (ix < N_PHONEME_LIST-3)); j++) { | |||||
plist3 = &ph_list3[j]; | plist3 = &ph_list3[j]; | ||||
inserted = 0; | inserted = 0; | ||||
deleted = 0; | deleted = 0; | ||||
if(insert_ph != 0) | |||||
{ | |||||
if (insert_ph != 0) { | |||||
// we have a (linking) phoneme which we need to insert here | // we have a (linking) phoneme which we need to insert here | ||||
next = phoneme_tab[plist3->phcode]; // this phoneme, i.e. after the insert | next = phoneme_tab[plist3->phcode]; // this phoneme, i.e. after the insert | ||||
// That's OK because we don't look backwards from plist3 *** but CountVowelPosition() and isAfterStress does !!! | // That's OK because we don't look backwards from plist3 *** but CountVowelPosition() and isAfterStress does !!! | ||||
j--; | j--; | ||||
plist3 = plist3_inserted = &ph_list3[j]; | plist3 = plist3_inserted = &ph_list3[j]; | ||||
if(j > 0) | |||||
{ | |||||
if (j > 0) { | |||||
// move all previous phonemes in the word back one place | // move all previous phonemes in the word back one place | ||||
int k; | int k; | ||||
if(word_start > 0) | |||||
{ | |||||
if (word_start > 0) { | |||||
k = word_start; | k = word_start; | ||||
word_start--; | word_start--; | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
k = 2; // No more space, don't loose the start of word mark at ph_list2[word_start] | k = 2; // No more space, don't loose the start of word mark at ph_list2[word_start] | ||||
} | } | ||||
for(; k<=j; k++) | |||||
for (; k <= j; k++) | |||||
memcpy(&ph_list3[k-1], &ph_list3[k], sizeof(*plist3)); | memcpy(&ph_list3[k-1], &ph_list3[k], sizeof(*plist3)); | ||||
} | } | ||||
memset(&plist3[0], 0, sizeof(*plist3)); | memset(&plist3[0], 0, sizeof(*plist3)); | ||||
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) | |||||
word_start = j; | word_start = j; | ||||
ph = phoneme_tab[plist3->phcode]; | ph = phoneme_tab[plist3->phcode]; | ||||
plist3[0].ph = ph; | plist3[0].ph = ph; | ||||
if(plist3->phcode == phonSWITCH) | |||||
{ | |||||
if (plist3->phcode == phonSWITCH) { | |||||
// change phoneme table | // change phoneme table | ||||
SelectPhonemeTable(plist3->tone_ph); | SelectPhonemeTable(plist3->tone_ph); | ||||
} | } | ||||
plist3[1].ph = next; | plist3[1].ph = next; | ||||
} | } | ||||
if(ph == NULL) continue; | |||||
if (ph == NULL) continue; | |||||
InterpretPhoneme(tr, 0x100, plist3, &phdata, &worddata); | InterpretPhoneme(tr, 0x100, plist3, &phdata, &worddata); | ||||
if((alternative = phdata.pd_param[pd_CHANGE_NEXTPHONEME]) > 0) | |||||
{ | |||||
if ((alternative = phdata.pd_param[pd_CHANGE_NEXTPHONEME]) > 0) { | |||||
ph_list3[j+1].ph = phoneme_tab[alternative]; | ph_list3[j+1].ph = phoneme_tab[alternative]; | ||||
ph_list3[j+1].phcode = alternative; | ph_list3[j+1].phcode = alternative; | ||||
ph_list3[j+1].type = phoneme_tab[alternative]->type; | ph_list3[j+1].type = phoneme_tab[alternative]->type; | ||||
next = phoneme_tab[alternative]; | next = phoneme_tab[alternative]; | ||||
} | } | ||||
if(((alternative = phdata.pd_param[pd_INSERTPHONEME]) > 0) && (inserted == 0)) | |||||
{ | |||||
if (((alternative = phdata.pd_param[pd_INSERTPHONEME]) > 0) && (inserted == 0)) { | |||||
// PROBLEM: if we insert a phoneme before a vowel then we loose the stress. | // PROBLEM: if we insert a phoneme before a vowel then we loose the stress. | ||||
PHONEME_TAB *ph2; | PHONEME_TAB *ph2; | ||||
ph2 = ph; | ph2 = ph; | ||||
plist3->ph = ph; | plist3->ph = ph; | ||||
plist3->phcode = alternative; | plist3->phcode = alternative; | ||||
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; | ||||
// re-interpret the changed phoneme | // re-interpret the changed phoneme | ||||
InterpretPhoneme(tr, 0x100, plist3, &phdata, &worddata); | InterpretPhoneme(tr, 0x100, plist3, &phdata, &worddata); | ||||
} | } | ||||
if((alternative = phdata.pd_param[pd_CHANGEPHONEME]) > 0) | |||||
{ | |||||
if ((alternative = phdata.pd_param[pd_CHANGEPHONEME]) > 0) { | |||||
PHONEME_TAB *ph2; | PHONEME_TAB *ph2; | ||||
ph2 = ph; | ph2 = ph; | ||||
ph = phoneme_tab[alternative]; | ph = phoneme_tab[alternative]; | ||||
plist3->ph = ph; | plist3->ph = ph; | ||||
plist3->phcode = alternative; | plist3->phcode = alternative; | ||||
if(alternative == 1) | |||||
{ | |||||
if (alternative == 1) { | |||||
deleted = 1; // NULL phoneme, discard | deleted = 1; // NULL phoneme, discard | ||||
} | |||||
else | |||||
{ | |||||
if(ph->type == phVOWEL) | |||||
{ | |||||
} else { | |||||
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; | ||||
// re-interpret the changed phoneme | // re-interpret the changed phoneme | ||||
} | } | ||||
} | } | ||||
if((ph->type == phVOWEL) && (deleted == 0)) | |||||
{ | |||||
if ((ph->type == phVOWEL) && (deleted == 0)) { | |||||
PHONEME_LIST *p; | PHONEME_LIST *p; | ||||
// Check for consecutive unstressed syllables, even across word boundaries. | // Check for consecutive unstressed syllables, even across word boundaries. | ||||
// Do this after changing phonemes according to stress level. | // Do this after changing phonemes according to stress level. | ||||
if(plist3->stresslevel <= 1) | |||||
{ | |||||
if (plist3->stresslevel <= 1) { | |||||
// an unstressed vowel | // an unstressed vowel | ||||
unstress_count++; | unstress_count++; | ||||
if(tr->langopts.stress_flags & 0x08) | |||||
{ | |||||
if (tr->langopts.stress_flags & 0x08) { | |||||
// change sequences of consecutive unstressed vowels in unstressed words to diminished stress (TEST) | // change sequences of consecutive unstressed vowels in unstressed words to diminished stress (TEST) | ||||
for(p=plist3+1; p->type != phPAUSE; p++) | |||||
{ | |||||
if(p->type == phVOWEL) | |||||
{ | |||||
if(p->stresslevel <= 1) | |||||
{ | |||||
if(plist3->wordstress < 4) | |||||
for (p = plist3+1; p->type != phPAUSE; p++) { | |||||
if (p->type == phVOWEL) { | |||||
if (p->stresslevel <= 1) { | |||||
if (plist3->wordstress < 4) | |||||
plist3->stresslevel = 0; | plist3->stresslevel = 0; | ||||
if(p->wordstress < 4) | |||||
if (p->wordstress < 4) | |||||
p->stresslevel = 0; | p->stresslevel = 0; | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
} | |||||
else | |||||
{ | |||||
if((unstress_count > 1) && ((unstress_count & 1)==0)) | |||||
{ | |||||
} else { | |||||
if ((unstress_count > 1) && ((unstress_count & 1) == 0)) { | |||||
// in a sequence of unstressed syllables, reduce alternate syllables to 'diminished' | // in a sequence of unstressed syllables, reduce alternate syllables to 'diminished' | ||||
// 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 | |||||
} | |||||
else | |||||
{ | |||||
unstress_count = 1; // try again for next syllable | |||||
} 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) | |||||
{ | |||||
static char types_double[] = {phFRICATIVE,phVFRICATIVE,phNASAL,phLIQUID,0}; | |||||
if((j > 0) && (strchr(types_double,next->type))) | |||||
{ | |||||
if ((plist3+1)->synthflags & SFLAG_LENGTHEN) { | |||||
static char types_double[] = { phFRICATIVE, phVFRICATIVE, phNASAL, phLIQUID, 0 }; | |||||
if ((j > 0) && (strchr(types_double, next->type))) { | |||||
// lengthen this consonant by doubling it | // lengthen this consonant by doubling it | ||||
// BUT, can't insert a phoneme at position plist3[0] because it crashes PrevPh() | // BUT, can't insert a phoneme at position plist3[0] because it crashes PrevPh() | ||||
insert_ph = next->code; | insert_ph = next->code; | ||||
} | } | ||||
} | } | ||||
if((plist3+1)->sourceix != 0) | |||||
{ | |||||
if ((plist3+1)->sourceix != 0) { | |||||
int x; | int x; | ||||
if(tr->langopts.vowel_pause && (ph->type != phPAUSE)) | |||||
{ | |||||
if (tr->langopts.vowel_pause && (ph->type != phPAUSE)) { | |||||
if((ph->type != phVOWEL) && (tr->langopts.vowel_pause & 0x200)) | |||||
{ | |||||
if ((ph->type != phVOWEL) && (tr->langopts.vowel_pause & 0x200)) { | |||||
// add a pause after a word which ends in a consonant | // add a pause after a word which ends in a consonant | ||||
insert_ph = phonPAUSE_NOLINK; | insert_ph = phonPAUSE_NOLINK; | ||||
} | } | ||||
if(next->type == phVOWEL) | |||||
{ | |||||
if((x = tr->langopts.vowel_pause & 0x0c) != 0) | |||||
{ | |||||
if (next->type == phVOWEL) { | |||||
if ((x = tr->langopts.vowel_pause & 0x0c) != 0) { | |||||
// break before a word which starts with a vowel | // break before a word which starts with a vowel | ||||
if(x == 0xc) | |||||
if (x == 0xc) | |||||
insert_ph = phonPAUSE_NOLINK; | insert_ph = phonPAUSE_NOLINK; | ||||
else | else | ||||
insert_ph = phonPAUSE_VSHORT; | insert_ph = phonPAUSE_VSHORT; | ||||
} | } | ||||
if((ph->type == phVOWEL) && ((x = tr->langopts.vowel_pause & 0x03) != 0)) | |||||
{ | |||||
if ((ph->type == phVOWEL) && ((x = tr->langopts.vowel_pause & 0x03) != 0)) { | |||||
// adjacent vowels over a word boundary | // adjacent vowels over a word boundary | ||||
if(x == 2) | |||||
if (x == 2) | |||||
insert_ph = phonPAUSE_SHORT; | insert_ph = phonPAUSE_SHORT; | ||||
else | else | ||||
insert_ph = phonPAUSE_VSHORT; | insert_ph = phonPAUSE_VSHORT; | ||||
} | } | ||||
if(((plist3+1)->stresslevel >= 4) && (tr->langopts.vowel_pause & 0x100)) | |||||
{ | |||||
if (((plist3+1)->stresslevel >= 4) && (tr->langopts.vowel_pause & 0x100)) { | |||||
// pause before a words which starts with a stressed vowel | // pause before a words which starts with a stressed vowel | ||||
insert_ph = phonPAUSE_SHORT; | insert_ph = phonPAUSE_SHORT; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if((plist3 != plist3_inserted) && (ix > 0)) | |||||
{ | |||||
if((x = (tr->langopts.word_gap & 0x7)) != 0) | |||||
{ | |||||
if((x > 1) || ((insert_ph != phonPAUSE_SHORT) && (insert_ph != phonPAUSE_NOLINK))) | |||||
{ | |||||
if ((plist3 != plist3_inserted) && (ix > 0)) { | |||||
if ((x = (tr->langopts.word_gap & 0x7)) != 0) { | |||||
if ((x > 1) || ((insert_ph != phonPAUSE_SHORT) && (insert_ph != phonPAUSE_NOLINK))) { | |||||
// don't reduce the pause | // don't reduce the pause | ||||
insert_ph = pause_phonemes[x]; | insert_ph = pause_phonemes[x]; | ||||
} | } | ||||
} | } | ||||
if(option_wordgap > 0) | |||||
{ | |||||
if (option_wordgap > 0) { | |||||
insert_ph = phonPAUSE_LONG; | insert_ph = phonPAUSE_LONG; | ||||
} | } | ||||
} | } | ||||
next2 = phoneme_tab[plist3[2].phcode]; | next2 = phoneme_tab[plist3[2].phcode]; | ||||
plist3[2].ph = next2; | plist3[2].ph = next2; | ||||
if((insert_ph == 0) && (phdata.pd_param[pd_APPENDPHONEME] != 0)) | |||||
{ | |||||
if ((insert_ph == 0) && (phdata.pd_param[pd_APPENDPHONEME] != 0)) { | |||||
insert_ph = phdata.pd_param[pd_APPENDPHONEME]; | insert_ph = phdata.pd_param[pd_APPENDPHONEME]; | ||||
} | } | ||||
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].sourceix = 0; | phlist[ix].sourceix = 0; | ||||
phlist[ix].phcode = ph->code; | phlist[ix].phcode = ph->code; | ||||
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].newword = 0; | phlist[ix].newword = 0; | ||||
} | } | ||||
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].phcode = phonPAUSE; | phlist[ix].phcode = phonPAUSE; | ||||
phlist[ix].type = phPAUSE; | phlist[ix].type = phPAUSE; | ||||
phlist[ix].length = 0; | phlist[ix].length = 0; | ||||
phlist[ix].sourceix=0; | |||||
phlist[ix].sourceix = 0; | |||||
phlist[ix].synthflags = 0; | phlist[ix].synthflags = 0; | ||||
phlist[ix++].ph = phoneme_tab[phonPAUSE_SHORT]; | phlist[ix++].ph = phoneme_tab[phonPAUSE_SHORT]; | ||||
// 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 | |||||
21,20,20,19,19,18,17,16,15,15, // 360 | |||||
15,15,15,15,15 | |||||
22, 22, 22, 22, 22, 22, 22, 21, 21, 21, // 350 | |||||
21, 20, 20, 19, 19, 18, 17, 16, 15, 15, // 360 | |||||
15, 15, 15, 15, 15 | |||||
}; // 370 | }; // 370 | ||||
// wav_factor adjustments for speeds 350 to 450 | // wav_factor adjustments for speeds 350 to 450 | ||||
speed.min_pause = 5; | speed.min_pause = 5; | ||||
wpm = embedded_value[EMBED_S]; | wpm = embedded_value[EMBED_S]; | ||||
if(control == 2) | |||||
if (control == 2) | |||||
wpm = embedded_value[EMBED_S2]; | wpm = embedded_value[EMBED_S2]; | ||||
wpm_value = wpm; | wpm_value = wpm; | ||||
if(voice->speed_percent > 0) | |||||
{ | |||||
if (voice->speed_percent > 0) { | |||||
wpm = (wpm * voice->speed_percent)/100; | wpm = (wpm * voice->speed_percent)/100; | ||||
} | } | ||||
if(control & 2) | |||||
{ | |||||
if (control & 2) { | |||||
DoSonicSpeed(1 * 1024); | DoSonicSpeed(1 * 1024); | ||||
} | } | ||||
if((wpm_value >= 450) || ((wpm_value > speed.fast_settings[0]) && (wpm > 350))) | |||||
{ | |||||
if ((wpm_value >= 450) || ((wpm_value > speed.fast_settings[0]) && (wpm > 350))) { | |||||
wpm2 = wpm; | wpm2 = wpm; | ||||
wpm = 175; | wpm = 175; | ||||
// set special eSpeak speed parameters for Sonic use | // set special eSpeak speed parameters for Sonic use | ||||
// The eSpeak output will be speeded up by at least x2 | // The eSpeak output will be speeded up by at least x2 | ||||
x = 73; | x = 73; | ||||
if(control & 1) | |||||
{ | |||||
if (control & 1) { | |||||
speed1 = (x * voice->speedf1)/256; | speed1 = (x * voice->speedf1)/256; | ||||
speed2 = (x * voice->speedf2)/256; | speed2 = (x * voice->speedf2)/256; | ||||
speed3 = (x * voice->speedf3)/256; | speed3 = (x * voice->speedf3)/256; | ||||
} | } | ||||
if(control & 2) | |||||
{ | |||||
if (control & 2) { | |||||
sonic = ((double)wpm2)/wpm; | sonic = ((double)wpm2)/wpm; | ||||
DoSonicSpeed((int)(sonic * 1024)); | DoSonicSpeed((int)(sonic * 1024)); | ||||
speed.pause_factor = 85; | speed.pause_factor = 85; | ||||
return; | return; | ||||
} | } | ||||
if(wpm > 450) | |||||
if (wpm > 450) | |||||
wpm = 450; | wpm = 450; | ||||
if(wpm > 360) | |||||
{ | |||||
if (wpm > 360) { | |||||
speed.loud_consonants = (wpm - 360) / 8; | speed.loud_consonants = (wpm - 360) / 8; | ||||
} | } | ||||
wpm2 = wpm; | wpm2 = wpm; | ||||
if(wpm > 359) wpm2 = 359; | |||||
if(wpm < 80) wpm2 = 80; | |||||
if (wpm > 359) wpm2 = 359; | |||||
if (wpm < 80) wpm2 = 80; | |||||
x = speed_lookup[wpm2-80]; | x = speed_lookup[wpm2-80]; | ||||
if(wpm >= 380) | |||||
if (wpm >= 380) | |||||
x = 7; | x = 7; | ||||
if(wpm >= 400) | |||||
if (wpm >= 400) | |||||
x = 6; | x = 6; | ||||
if(control & 1) | |||||
{ | |||||
if (control & 1) { | |||||
// set speed factors for different syllable positions within a word | // set speed factors for different syllable positions within a word | ||||
// these are used in CalcLengths() | // these are used in CalcLengths() | ||||
speed1 = (x * voice->speedf1)/256; | speed1 = (x * voice->speedf1)/256; | ||||
speed2 = (x * voice->speedf2)/256; | speed2 = (x * voice->speedf2)/256; | ||||
speed3 = (x * voice->speedf3)/256; | speed3 = (x * voice->speedf3)/256; | ||||
if(x <= 7) | |||||
{ | |||||
if (x <= 7) { | |||||
speed1 = x; | speed1 = x; | ||||
speed2 = speed3 = x - 1; | speed2 = speed3 = x - 1; | ||||
} | } | ||||
} | } | ||||
if(control & 2) | |||||
{ | |||||
if (control & 2) { | |||||
// these are used in synthesis file | // these are used in synthesis file | ||||
if(wpm > 350) | |||||
{ | |||||
if (wpm > 350) { | |||||
speed.lenmod_factor = 85 - (wpm - 350) / 3; | speed.lenmod_factor = 85 - (wpm - 350) / 3; | ||||
speed.lenmod2_factor = 60 - (wpm - 350) / 8; | speed.lenmod2_factor = 60 - (wpm - 350) / 8; | ||||
} | |||||
else | |||||
if(wpm > 250) | |||||
{ | |||||
} else if (wpm > 250) { | |||||
speed.lenmod_factor = 110 - (wpm - 250)/4; | speed.lenmod_factor = 110 - (wpm - 250)/4; | ||||
speed.lenmod2_factor = 110 - (wpm - 250)/2; | speed.lenmod2_factor = 110 - (wpm - 250)/2; | ||||
} | } | ||||
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]; | ||||
} | } | ||||
if(wpm >= 390) | |||||
{ | |||||
if (wpm >= 390) { | |||||
speed.min_sample_len = 450 - (wpm - 400)/2; | speed.min_sample_len = 450 - (wpm - 400)/2; | ||||
if(wpm > 440) | |||||
if (wpm > 440) | |||||
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) { | |||||
speed.pause_factor = 12; | speed.pause_factor = 12; | ||||
} | |||||
else | |||||
if(wpm > 400) | |||||
{ | |||||
} else if (wpm > 400) { | |||||
speed.pause_factor = 13; | speed.pause_factor = 13; | ||||
} | |||||
else | |||||
if(wpm > 374) | |||||
{ | |||||
} else if (wpm > 374) { | |||||
speed.pause_factor = 14; | speed.pause_factor = 14; | ||||
} | |||||
else | |||||
if(wpm > 350) | |||||
{ | |||||
} else if (wpm > 350) { | |||||
speed.pause_factor = pause_factor_350[wpm - 350]; | speed.pause_factor = pause_factor_350[wpm - 350]; | ||||
} | } | ||||
if(speed.clause_pause_factor == 0) | |||||
{ | |||||
if (speed.clause_pause_factor == 0) { | |||||
// restrict the reduction of pauses between clauses | // restrict the reduction of pauses between clauses | ||||
if((speed.clause_pause_factor = speed.pause_factor) < 16) | |||||
if ((speed.clause_pause_factor = speed.pause_factor) < 16) | |||||
speed.clause_pause_factor = 16; | speed.clause_pause_factor = 16; | ||||
} | } | ||||
} | } | ||||
speed.lenmod2_factor = 100; | speed.lenmod2_factor = 100; | ||||
wpm = embedded_value[EMBED_S]; | wpm = embedded_value[EMBED_S]; | ||||
if(control == 2) | |||||
if (control == 2) | |||||
wpm = embedded_value[EMBED_S2]; | wpm = embedded_value[EMBED_S2]; | ||||
if(voice->speed_percent > 0) | |||||
{ | |||||
if (voice->speed_percent > 0) { | |||||
wpm = (wpm * voice->speed_percent)/100; | wpm = (wpm * voice->speed_percent)/100; | ||||
} | } | ||||
if(wpm > 450) | |||||
if (wpm > 450) | |||||
wpm = 450; | wpm = 450; | ||||
if(wpm > 360) | |||||
{ | |||||
if (wpm > 360) { | |||||
speed.loud_consonants = (wpm - 360) / 8; | speed.loud_consonants = (wpm - 360) / 8; | ||||
} | } | ||||
wpm2 = wpm; | wpm2 = wpm; | ||||
if(wpm > 359) wpm2 = 359; | |||||
if(wpm < 80) wpm2 = 80; | |||||
if (wpm > 359) wpm2 = 359; | |||||
if (wpm < 80) wpm2 = 80; | |||||
x = speed_lookup[wpm2-80]; | x = speed_lookup[wpm2-80]; | ||||
if(wpm >= 380) | |||||
if (wpm >= 380) | |||||
x = 7; | x = 7; | ||||
if(wpm >= 400) | |||||
if (wpm >= 400) | |||||
x = 6; | x = 6; | ||||
if(control & 1) | |||||
{ | |||||
if (control & 1) { | |||||
// set speed factors for different syllable positions within a word | // set speed factors for different syllable positions within a word | ||||
// these are used in CalcLengths() | // these are used in CalcLengths() | ||||
speed1 = (x * voice->speedf1)/256; | speed1 = (x * voice->speedf1)/256; | ||||
speed2 = (x * voice->speedf2)/256; | speed2 = (x * voice->speedf2)/256; | ||||
speed3 = (x * voice->speedf3)/256; | speed3 = (x * voice->speedf3)/256; | ||||
if(x <= 7) | |||||
{ | |||||
if (x <= 7) { | |||||
speed1 = x; | speed1 = x; | ||||
speed2 = speed3 = x - 1; | speed2 = speed3 = x - 1; | ||||
} | } | ||||
} | } | ||||
if(control & 2) | |||||
{ | |||||
if (control & 2) { | |||||
// these are used in synthesis file | // these are used in synthesis file | ||||
if(wpm > 350) | |||||
{ | |||||
if (wpm > 350) { | |||||
speed.lenmod_factor = 85 - (wpm - 350) / 3; | speed.lenmod_factor = 85 - (wpm - 350) / 3; | ||||
speed.lenmod2_factor = 60 - (wpm - 350) / 8; | speed.lenmod2_factor = 60 - (wpm - 350) / 8; | ||||
} | |||||
else | |||||
if(wpm > 250) | |||||
{ | |||||
} else if (wpm > 250) { | |||||
speed.lenmod_factor = 110 - (wpm - 250)/4; | speed.lenmod_factor = 110 - (wpm - 250)/4; | ||||
speed.lenmod2_factor = 110 - (wpm - 250)/2; | speed.lenmod2_factor = 110 - (wpm - 250)/2; | ||||
} | } | ||||
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]; | ||||
} | } | ||||
if(wpm >= 390) | |||||
{ | |||||
if (wpm >= 390) { | |||||
speed.min_sample_len = 450 - (wpm - 400)/2; | speed.min_sample_len = 450 - (wpm - 400)/2; | ||||
if(wpm > 440) | |||||
if (wpm > 440) | |||||
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) { | |||||
speed.pause_factor = 12; | speed.pause_factor = 12; | ||||
} | |||||
else | |||||
if(wpm > 400) | |||||
{ | |||||
} else if (wpm > 400) { | |||||
speed.pause_factor = 13; | speed.pause_factor = 13; | ||||
} | |||||
else | |||||
if(wpm > 374) | |||||
{ | |||||
} else if (wpm > 374) { | |||||
speed.pause_factor = 14; | speed.pause_factor = 14; | ||||
} | |||||
else | |||||
if(wpm > 350) | |||||
{ | |||||
} else if (wpm > 350) { | |||||
speed.pause_factor = pause_factor_350[wpm - 350]; | speed.pause_factor = pause_factor_350[wpm - 350]; | ||||
} | } | ||||
if(speed.clause_pause_factor == 0) | |||||
{ | |||||
if (speed.clause_pause_factor == 0) { | |||||
// restrict the reduction of pauses between clauses | // restrict the reduction of pauses between clauses | ||||
if((speed.clause_pause_factor = speed.pause_factor) < 16) | |||||
if ((speed.clause_pause_factor = speed.pause_factor) < 16) | |||||
speed.clause_pause_factor = 16; | speed.clause_pause_factor = 16; | ||||
} | } | ||||
} | } | ||||
int new_value = value; | int new_value = value; | ||||
int default_value; | int default_value; | ||||
if(relative) | |||||
{ | |||||
if(parameter < 5) | |||||
{ | |||||
if (relative) { | |||||
if (parameter < 5) { | |||||
default_value = param_defaults[parameter]; | default_value = param_defaults[parameter]; | ||||
new_value = default_value + (default_value * value)/100; | new_value = default_value + (default_value * value)/100; | ||||
} | } | ||||
param_stack[0].parameter[parameter] = new_value; | param_stack[0].parameter[parameter] = new_value; | ||||
saved_parameters[parameter] = new_value; | saved_parameters[parameter] = new_value; | ||||
switch(parameter) | |||||
switch (parameter) | |||||
{ | { | ||||
case espeakRATE: | case espeakRATE: | ||||
embedded_value[EMBED_S] = new_value; | embedded_value[EMBED_S] = new_value; | ||||
break; | break; | ||||
case espeakPITCH: | case espeakPITCH: | ||||
if(new_value > 99) new_value = 99; | |||||
if(new_value < 0) new_value = 0; | |||||
if (new_value > 99) new_value = 99; | |||||
if (new_value < 0) new_value = 0; | |||||
embedded_value[EMBED_P] = new_value; | embedded_value[EMBED_P] = new_value; | ||||
break; | break; | ||||
case espeakRANGE: | case espeakRANGE: | ||||
if(new_value > 99) new_value = 99; | |||||
if (new_value > 99) new_value = 99; | |||||
embedded_value[EMBED_R] = new_value; | embedded_value[EMBED_R] = new_value; | ||||
break; | break; | ||||
break; | break; | ||||
case espeakINTONATION: | case espeakINTONATION: | ||||
if((new_value & 0xff) != 0) | |||||
if ((new_value & 0xff) != 0) | |||||
translator->langopts.intonation_group = new_value & 0xff; | translator->langopts.intonation_group = new_value & 0xff; | ||||
option_tone_flags = new_value; | option_tone_flags = new_value; | ||||
break; | break; | ||||
do { | do { | ||||
word = embedded_list[(*embix)++]; | word = embedded_list[(*embix)++]; | ||||
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); | |||||
} | } | ||||
int stress; | int stress; | ||||
int type; | int type; | ||||
static int more_syllables=0; | |||||
int pre_sonorant=0; | |||||
int pre_voiced=0; | |||||
static int more_syllables = 0; | |||||
int pre_sonorant = 0; | |||||
int pre_voiced = 0; | |||||
int last_pitch = 0; | int last_pitch = 0; | ||||
int pitch_start; | int pitch_start; | ||||
int length_mod; | int length_mod; | ||||
int pitch1; | int pitch1; | ||||
int emphasized; | int emphasized; | ||||
int tone_mod; | int tone_mod; | ||||
unsigned char *pitch_env=NULL; | |||||
unsigned char *pitch_env = NULL; | |||||
PHONEME_DATA phdata_tone; | PHONEME_DATA phdata_tone; | ||||
for(ix=1; ix<n_phoneme_list; ix++) | |||||
{ | |||||
for (ix = 1; ix < n_phoneme_list; ix++) { | |||||
prev = &phoneme_list[ix-1]; | prev = &phoneme_list[ix-1]; | ||||
p = &phoneme_list[ix]; | p = &phoneme_list[ix]; | ||||
stress = p->stresslevel & 0x7; | stress = p->stresslevel & 0x7; | ||||
next = &phoneme_list[ix+1]; | next = &phoneme_list[ix+1]; | ||||
if(p->synthflags & SFLAG_EMBEDDED) | |||||
{ | |||||
if (p->synthflags & SFLAG_EMBEDDED) { | |||||
DoEmbedded2(&embedded_ix); | DoEmbedded2(&embedded_ix); | ||||
} | } | ||||
type = p->type; | type = p->type; | ||||
if(p->synthflags & SFLAG_SYLLABLE) | |||||
if (p->synthflags & SFLAG_SYLLABLE) | |||||
type = phVOWEL; | type = phVOWEL; | ||||
switch(type) | |||||
switch (type) | |||||
{ | { | ||||
case phPAUSE: | case phPAUSE: | ||||
last_pitch = 0; | last_pitch = 0; | ||||
case phSTOP: | case phSTOP: | ||||
last_pitch = 0; | last_pitch = 0; | ||||
if(prev->type == phFRICATIVE) | |||||
if (prev->type == phFRICATIVE) | |||||
p->prepause = 25; | p->prepause = 25; | ||||
else | |||||
if((more_syllables > 0) || (stress < 4)) | |||||
else if ((more_syllables > 0) || (stress < 4)) | |||||
p->prepause = 48; | p->prepause = 48; | ||||
else | else | ||||
p->prepause = 60; | p->prepause = 60; | ||||
if(prev->type == phSTOP) | |||||
if (prev->type == phSTOP) | |||||
p->prepause = 60; | p->prepause = 60; | ||||
if((tr->langopts.word_gap & 0x10) && (p->newword)) | |||||
if ((tr->langopts.word_gap & 0x10) && (p->newword)) | |||||
p->prepause = 60; | p->prepause = 60; | ||||
if(p->ph->phflags & phLENGTHENSTOP) | |||||
if (p->ph->phflags & phLENGTHENSTOP) | |||||
p->prepause += 30; | p->prepause += 30; | ||||
if(p->synthflags & SFLAG_LENGTHEN) | |||||
if (p->synthflags & SFLAG_LENGTHEN) | |||||
p->prepause += tr->langopts.long_stop; | p->prepause += tr->langopts.long_stop; | ||||
break; | break; | ||||
case phVFRICATIVE: | case phVFRICATIVE: | ||||
case phFRICATIVE: | case phFRICATIVE: | ||||
if(p->newword) | |||||
{ | |||||
if((prev->type == phVOWEL) && (p->ph->phflags & phNOPAUSE)) | |||||
{ | |||||
} | |||||
else | |||||
{ | |||||
if (p->newword) { | |||||
if ((prev->type == phVOWEL) && (p->ph->phflags & phNOPAUSE)) { | |||||
} else { | |||||
p->prepause = 15; | p->prepause = 15; | ||||
} | } | ||||
} | } | ||||
if(next->type==phPAUSE && prev->type==phNASAL && !(p->ph->phflags&phFORTIS)) | |||||
if (next->type == phPAUSE && prev->type == phNASAL && !(p->ph->phflags&phFORTIS)) | |||||
p->prepause = 25; | p->prepause = 25; | ||||
if(prev->ph->phflags & phBRKAFTER) | |||||
if (prev->ph->phflags & phBRKAFTER) | |||||
p->prepause = 30; | p->prepause = 30; | ||||
if((tr->langopts.word_gap & 0x10) && (p->newword)) | |||||
if ((tr->langopts.word_gap & 0x10) && (p->newword)) | |||||
p->prepause = 30; | p->prepause = 30; | ||||
if((p->ph->phflags & phSIBILANT) && next->type==phSTOP && !next->newword) | |||||
{ | |||||
if(prev->type == phVOWEL) | |||||
if ((p->ph->phflags & phSIBILANT) && next->type == phSTOP && !next->newword) { | |||||
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->length = 256; | p->length = 256; | ||||
if(type == phVFRICATIVE) | |||||
{ | |||||
if(next->type==phVOWEL) | |||||
{ | |||||
if (type == phVFRICATIVE) { | |||||
if (next->type == phVOWEL) { | |||||
pre_voiced = 1; | pre_voiced = 1; | ||||
} | } | ||||
if((prev->type==phVOWEL) || (prev->type == phLIQUID)) | |||||
{ | |||||
if ((prev->type == phVOWEL) || (prev->type == phLIQUID)) { | |||||
p->length = (255 + prev->length)/2; | p->length = (255 + prev->length)/2; | ||||
} | } | ||||
} | } | ||||
break; | break; | ||||
case phVSTOP: | case phVSTOP: | ||||
if(prev->type==phVFRICATIVE || prev->type==phFRICATIVE || (prev->ph->phflags & phSIBILANT) || (prev->type == phLIQUID)) | |||||
if (prev->type == phVFRICATIVE || prev->type == phFRICATIVE || (prev->ph->phflags & phSIBILANT) || (prev->type == phLIQUID)) | |||||
p->prepause = 30; | p->prepause = 30; | ||||
if(next->type==phVOWEL || next->type==phLIQUID) | |||||
{ | |||||
if((next->type==phVOWEL) || !next->newword) | |||||
if (next->type == phVOWEL || next->type == phLIQUID) { | |||||
if ((next->type == phVOWEL) || !next->newword) | |||||
pre_voiced = 1; | pre_voiced = 1; | ||||
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) | |||||
p->prepause -= prev->length; | p->prepause -= prev->length; | ||||
else | else | ||||
p->prepause = 0; | p->prepause = 0; | ||||
} | |||||
else | |||||
if(p->newword==0) | |||||
{ | |||||
if(prev->type==phLIQUID) | |||||
} else if (p->newword == 0) { | |||||
if (prev->type == phLIQUID) | |||||
p->prepause = 20; | p->prepause = 20; | ||||
if(prev->type==phNASAL) | |||||
if (prev->type == phNASAL) | |||||
p->prepause = 12; | p->prepause = 12; | ||||
if(prev->type==phSTOP && !(prev->ph->phflags & phFORTIS)) | |||||
if (prev->type == phSTOP && !(prev->ph->phflags & phFORTIS)) | |||||
p->prepause = 0; | p->prepause = 0; | ||||
} | } | ||||
} | } | ||||
if((tr->langopts.word_gap & 0x10) && (p->newword) && (p->prepause < 20)) | |||||
if ((tr->langopts.word_gap & 0x10) && (p->newword) && (p->prepause < 20)) | |||||
p->prepause = 20; | p->prepause = 20; | ||||
break; | break; | ||||
p->length = 256; // TEMPORARY | p->length = 256; // TEMPORARY | ||||
min_drop = 0; | min_drop = 0; | ||||
if(p->newword) | |||||
{ | |||||
if(prev->type==phLIQUID) | |||||
if (p->newword) { | |||||
if (prev->type == phLIQUID) | |||||
p->prepause = 25; | p->prepause = 25; | ||||
if(prev->type==phVOWEL) | |||||
{ | |||||
if(!(p->ph->phflags & phNOPAUSE)) | |||||
if (prev->type == phVOWEL) { | |||||
if (!(p->ph->phflags & phNOPAUSE)) | |||||
p->prepause = 12; | p->prepause = 12; | ||||
} | } | ||||
} | } | ||||
if(next->type==phVOWEL) | |||||
{ | |||||
if (next->type == phVOWEL) { | |||||
pre_sonorant = 1; | pre_sonorant = 1; | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
p->pitch2 = last_pitch; | p->pitch2 = last_pitch; | ||||
if((prev->type==phVOWEL) || (prev->type == phLIQUID)) | |||||
{ | |||||
if ((prev->type == phVOWEL) || (prev->type == phLIQUID)) { | |||||
p->length = prev->length; | p->length = prev->length; | ||||
if(p->type == phLIQUID) | |||||
{ | |||||
if (p->type == phLIQUID) { | |||||
p->length = speed1; | p->length = speed1; | ||||
} | } | ||||
if(next->type == phVSTOP) | |||||
{ | |||||
if (next->type == phVSTOP) { | |||||
p->length = (p->length * 160)/100; | p->length = (p->length * 160)/100; | ||||
} | } | ||||
if(next->type == phVFRICATIVE) | |||||
{ | |||||
if (next->type == phVFRICATIVE) { | |||||
p->length = (p->length * 120)/100; | p->length = (p->length * 120)/100; | ||||
} | } | ||||
} | |||||
else | |||||
{ | |||||
for(ix2=ix; ix2<n_phoneme_list; ix2++) | |||||
{ | |||||
if(phoneme_list[ix2].type == phVOWEL) | |||||
{ | |||||
} else { | |||||
for (ix2 = ix; ix2 < n_phoneme_list; ix2++) { | |||||
if (phoneme_list[ix2].type == phVOWEL) { | |||||
p->pitch2 = phoneme_list[ix2].pitch2; | p->pitch2 = phoneme_list[ix2].pitch2; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
p->pitch1 = p->pitch2-16; | p->pitch1 = p->pitch2-16; | ||||
if(p->pitch2 < 16) | |||||
{ | |||||
if (p->pitch2 < 16) { | |||||
p->pitch1 = 0; | p->pitch1 = 0; | ||||
} | } | ||||
p->env = PITCHfall; | p->env = PITCHfall; | ||||
next2 = &phoneme_list[ix+2]; | next2 = &phoneme_list[ix+2]; | ||||
next3 = &phoneme_list[ix+3]; | next3 = &phoneme_list[ix+3]; | ||||
if(stress > 7) stress = 7; | |||||
if (stress > 7) stress = 7; | |||||
if(stress <= 1) | |||||
{ | |||||
if (stress <= 1) { | |||||
stress = stress ^ 1; // swap diminished and unstressed (until we swap stress_amps,stress_lengths in tr_languages) | stress = stress ^ 1; // swap diminished and unstressed (until we swap stress_amps,stress_lengths in tr_languages) | ||||
} | } | ||||
if(pre_sonorant) | |||||
if (pre_sonorant) | |||||
p->amp = tr->stress_amps[stress]-1; | p->amp = tr->stress_amps[stress]-1; | ||||
else | else | ||||
p->amp = tr->stress_amps[stress]; | p->amp = tr->stress_amps[stress]; | ||||
if(emphasized) | |||||
if (emphasized) | |||||
p->amp = 25; | p->amp = 25; | ||||
if(ix >= (n_phoneme_list-3)) | |||||
{ | |||||
if (ix >= (n_phoneme_list-3)) { | |||||
// last phoneme of a clause, limit its amplitude | // last phoneme of a clause, limit its amplitude | ||||
if(p->amp > tr->langopts.param[LOPT_MAXAMP_EOC]) | |||||
if (p->amp > tr->langopts.param[LOPT_MAXAMP_EOC]) | |||||
p->amp = tr->langopts.param[LOPT_MAXAMP_EOC]; | p->amp = tr->langopts.param[LOPT_MAXAMP_EOC]; | ||||
} | } | ||||
// is the last syllable of a word ? | // is the last syllable of a word ? | ||||
more_syllables=0; | |||||
more_syllables = 0; | |||||
end_of_clause = 0; | end_of_clause = 0; | ||||
for(p2 = p+1; p2->newword== 0; p2++) | |||||
{ | |||||
if((p2->type == phVOWEL) && !(p2->ph->phflags & phNONSYLLABIC)) | |||||
for (p2 = p+1; p2->newword == 0; p2++) { | |||||
if ((p2->type == phVOWEL) && !(p2->ph->phflags & phNONSYLLABIC)) | |||||
more_syllables++; | more_syllables++; | ||||
if(p2->ph->code == phonPAUSE_CLAUSE) | |||||
if (p2->ph->code == phonPAUSE_CLAUSE) | |||||
end_of_clause = 2; | end_of_clause = 2; | ||||
} | } | ||||
if(p2->ph->code == phonPAUSE_CLAUSE) | |||||
if (p2->ph->code == phonPAUSE_CLAUSE) | |||||
end_of_clause = 2; | end_of_clause = 2; | ||||
if((p2->newword & 2) && (more_syllables==0)) | |||||
{ | |||||
if ((p2->newword & 2) && (more_syllables == 0)) { | |||||
end_of_clause = 2; | end_of_clause = 2; | ||||
} | } | ||||
// calc length modifier | // calc length modifier | ||||
if((next->ph->code == phonPAUSE_VSHORT) && (next2->type == phPAUSE)) | |||||
{ | |||||
if ((next->ph->code == phonPAUSE_VSHORT) && (next2->type == phPAUSE)) { | |||||
// if PAUSE_VSHORT is followed by a pause, then use that | // if PAUSE_VSHORT is followed by a pause, then use that | ||||
next = next2; | next = next2; | ||||
next2 = next3; | next2 = next3; | ||||
} | } | ||||
next2type = next2->ph->length_mod; | next2type = next2->ph->length_mod; | ||||
if(more_syllables==0) | |||||
{ | |||||
if(next->newword || next2->newword) | |||||
{ | |||||
if (more_syllables == 0) { | |||||
if (next->newword || next2->newword) { | |||||
// don't use 2nd phoneme over a word boundary, unless it's a pause | // don't use 2nd phoneme over a word boundary, unless it's a pause | ||||
if(next2type != 1) | |||||
if (next2type != 1) | |||||
next2type = 0; | next2type = 0; | ||||
} | } | ||||
len = tr->langopts.length_mods0[next2type *10+ next->ph->length_mod]; | len = tr->langopts.length_mods0[next2type *10+ next->ph->length_mod]; | ||||
if((next->newword) && (tr->langopts.word_gap & 0x20)) | |||||
{ | |||||
if ((next->newword) && (tr->langopts.word_gap & 0x20)) { | |||||
// consider as a pause + first phoneme of the next word | // consider as a pause + first phoneme of the next word | ||||
length_mod = (len + tr->langopts.length_mods0[next->ph->length_mod *10+ 1])/2; | length_mod = (len + tr->langopts.length_mods0[next->ph->length_mod *10+ 1])/2; | ||||
} | |||||
else | |||||
} else | |||||
length_mod = len; | length_mod = len; | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
length_mod = tr->langopts.length_mods[next2type *10+ next->ph->length_mod]; | length_mod = tr->langopts.length_mods[next2type *10+ next->ph->length_mod]; | ||||
if((next->type == phNASAL) && (next2->type == phSTOP || next2->type == phVSTOP) && (next3->ph->phflags & phFORTIS)) | |||||
if ((next->type == phNASAL) && (next2->type == phSTOP || next2->type == phVSTOP) && (next3->ph->phflags & phFORTIS)) | |||||
length_mod -= 15; | length_mod -= 15; | ||||
} | } | ||||
if(more_syllables==0) | |||||
if (more_syllables == 0) | |||||
length_mod *= speed1; | length_mod *= speed1; | ||||
else | |||||
if(more_syllables==1) | |||||
else if (more_syllables == 1) | |||||
length_mod *= speed2; | length_mod *= speed2; | ||||
else | else | ||||
length_mod *= speed3; | length_mod *= speed3; | ||||
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 | ||||
length_mod += tr->langopts.lengthen_tonic; | length_mod += tr->langopts.lengthen_tonic; | ||||
if(emphasized) | |||||
if (emphasized) | |||||
length_mod += (tr->langopts.lengthen_tonic/2); | length_mod += (tr->langopts.lengthen_tonic/2); | ||||
} | |||||
else | |||||
if(emphasized) | |||||
{ | |||||
} else if (emphasized) { | |||||
length_mod += tr->langopts.lengthen_tonic; | length_mod += tr->langopts.lengthen_tonic; | ||||
} | } | ||||
if((len = tr->stress_lengths[stress]) == 0) | |||||
if ((len = tr->stress_lengths[stress]) == 0) | |||||
len = tr->stress_lengths[6]; | len = tr->stress_lengths[6]; | ||||
length_mod = length_mod * len; | length_mod = length_mod * len; | ||||
if(p->tone_ph != 0) | |||||
{ | |||||
if((tone_mod = phoneme_tab[p->tone_ph]->std_length) > 0) | |||||
{ | |||||
if (p->tone_ph != 0) { | |||||
if ((tone_mod = phoneme_tab[p->tone_ph]->std_length) > 0) { | |||||
// a tone phoneme specifies a percentage change to the length | // a tone phoneme specifies a percentage change to the length | ||||
length_mod = (length_mod * tone_mod) / 100; | length_mod = (length_mod * tone_mod) / 100; | ||||
} | } | ||||
} | } | ||||
if((end_of_clause == 2) && !(tr->langopts.stress_flags & S_NO_EOC_LENGTHEN)) | |||||
{ | |||||
if ((end_of_clause == 2) && !(tr->langopts.stress_flags & S_NO_EOC_LENGTHEN)) { | |||||
// 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) | |||||
len=200; // don't lengthen short vowels more than long vowels at end-of-clause | |||||
if (tr->langopts.stress_flags & S_EO_CLAUSE1) | |||||
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; | ||||
} | } | ||||
if(length_mod > tr->langopts.max_lengthmod*speed1) | |||||
{ | |||||
//limit the vowel length adjustment for some languages | |||||
if (length_mod > tr->langopts.max_lengthmod*speed1) { | |||||
// limit the vowel length adjustment for some languages | |||||
length_mod = (tr->langopts.max_lengthmod*speed1); | length_mod = (tr->langopts.max_lengthmod*speed1); | ||||
} | } | ||||
length_mod = length_mod / 128; | length_mod = length_mod / 128; | ||||
if(p->type != phVOWEL) | |||||
{ | |||||
if (p->type != phVOWEL) { | |||||
length_mod = 256; // syllabic consonant | length_mod = 256; // syllabic consonant | ||||
min_drop = 16; | min_drop = 16; | ||||
} | } | ||||
p->length = length_mod; | p->length = length_mod; | ||||
if(p->env >= (N_ENVELOPE_DATA-1)) | |||||
{ | |||||
fprintf(stderr,"espeak: Bad intonation data\n"); | |||||
if (p->env >= (N_ENVELOPE_DATA-1)) { | |||||
fprintf(stderr, "espeak: Bad intonation data\n"); | |||||
p->env = 0; | p->env = 0; | ||||
} | } | ||||
// 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); | ||||
pitch_env = GetEnvelope(phdata_tone.pitch_env); | pitch_env = GetEnvelope(phdata_tone.pitch_env); | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
pitch_env = envelope_data[env2]; | pitch_env = envelope_data[env2]; | ||||
} | } | ||||
pitch_start = p->pitch1 + ((p->pitch2-p->pitch1)*pitch_env[0])/256; | pitch_start = p->pitch1 + ((p->pitch2-p->pitch1)*pitch_env[0])/256; | ||||
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; | ||||
prev->pitch1 = last_pitch; | prev->pitch1 = last_pitch; | ||||
prev->pitch2 = pitch_start; | prev->pitch2 = pitch_start; | ||||
if(last_pitch < pitch_start) | |||||
{ | |||||
if (last_pitch < pitch_start) { | |||||
prev->env = PITCHrise; | prev->env = PITCHrise; | ||||
p->env = env2; | p->env = env2; | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
prev->env = PITCHfall; | prev->env = PITCHfall; | ||||
} | } | ||||
prev->length = length_mod; | prev->length = length_mod; | ||||
prev->amp = p->amp; | prev->amp = p->amp; | ||||
if((prev->type != phLIQUID) && (prev->amp > 18)) | |||||
if ((prev->type != phLIQUID) && (prev->amp > 18)) | |||||
prev->amp = 18; | prev->amp = 18; | ||||
} | } | ||||
// vowel & post-vocalic part | // vowel & post-vocalic part | ||||
next->synthflags &= ~SFLAG_SEQCONTINUE; | next->synthflags &= ~SFLAG_SEQCONTINUE; | ||||
if(next->type == phNASAL && next2->type != phVOWEL) | |||||
if (next->type == phNASAL && next2->type != phVOWEL) | |||||
next->synthflags |= SFLAG_SEQCONTINUE; | next->synthflags |= SFLAG_SEQCONTINUE; | ||||
if(next->type == phLIQUID) | |||||
{ | |||||
if (next->type == phLIQUID) { | |||||
next->synthflags |= SFLAG_SEQCONTINUE; | next->synthflags |= SFLAG_SEQCONTINUE; | ||||
if(next2->type == phVOWEL) | |||||
{ | |||||
if (next2->type == phVOWEL) { | |||||
next->synthflags &= ~SFLAG_SEQCONTINUE; | next->synthflags &= ~SFLAG_SEQCONTINUE; | ||||
} | } | ||||
if(next2->type != phVOWEL) | |||||
{ | |||||
if(next->ph->mnemonic == ('/'*256+'r')) | |||||
{ | |||||
if (next2->type != phVOWEL) { | |||||
if (next->ph->mnemonic == ('/'*256+'r')) { | |||||
next->synthflags &= ~SFLAG_SEQCONTINUE; | next->synthflags &= ~SFLAG_SEQCONTINUE; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if((min_drop > 0) && ((p->pitch2 - p->pitch1) < min_drop)) | |||||
{ | |||||
if ((min_drop > 0) && ((p->pitch2 - p->pitch1) < min_drop)) { | |||||
pitch1 = p->pitch2 - min_drop; | pitch1 = p->pitch2 - min_drop; | ||||
if(pitch1 < 0) | |||||
if (pitch1 < 0) | |||||
pitch1 = 0; | pitch1 = 0; | ||||
p->pitch1 = pitch1; | p->pitch1 = pitch1; | ||||
} | } |
#define PEAKSHAPEW 256 | #define PEAKSHAPEW 256 | ||||
static int default_freq[N_PEAKS] = | static int default_freq[N_PEAKS] = | ||||
{200,500,1200,3000,3500,4000,6900,7800,9000}; | |||||
{ 200, 500, 1200, 3000, 3500, 4000, 6900, 7800, 9000 }; | |||||
static int default_width[N_PEAKS] = | static int default_width[N_PEAKS] = | ||||
{750,500,550,550,600,700,700,700,700}; | |||||
{ 750, 500, 550, 550, 600, 700, 700, 700, 700 }; | |||||
static int default_klt_bw[N_PEAKS] = | static int default_klt_bw[N_PEAKS] = | ||||
{89,90,140,260,260,260,500,500,500}; | |||||
{ 89, 90, 140, 260, 260, 260, 500, 500, 500 }; | |||||
static double read_double(FILE *stream) | static double read_double(FILE *stream) | ||||
{ | { | ||||
unsigned char bytes[10]; | unsigned char bytes[10]; | ||||
fread(bytes,sizeof(char),10,stream); | |||||
fread(bytes, sizeof(char), 10, stream); | |||||
return ConvertFromIeeeExtended(bytes); | return ConvertFromIeeeExtended(bytes); | ||||
} | } | ||||
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; | |||||
float den,dif,dift,ho,hp,w; | |||||
int i, m, ns = 1; | |||||
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]); | |||||
for(i=1; i<=n; i++) { | |||||
if((dift=fabs(x-xa[i])) < dif) { | |||||
ns=i; | |||||
dif=dift; | |||||
for (i = 1; i <= n; i++) { | |||||
if ((dift = fabs(x-xa[i])) < dif) { | |||||
ns = i; | |||||
dif = dift; | |||||
} | } | ||||
c[i]=ya[i]; | |||||
d[i]=ya[i]; | |||||
c[i] = ya[i]; | |||||
d[i] = ya[i]; | |||||
} | } | ||||
y=ya[ns--]; | |||||
for(m=1; m<n; m++) { | |||||
for(i=1; i<=n-m; i++) { | |||||
ho=xa[i]-x; | |||||
hp=xa[i+m]-x; | |||||
w=c[i+1]-d[i]; | |||||
if((den=ho-hp) == 0.0) | |||||
{ | |||||
return(ya[2]); // two input xa are identical | |||||
y = ya[ns--]; | |||||
for (m = 1; m < n; m++) { | |||||
for (i = 1; i <= n-m; i++) { | |||||
ho = xa[i]-x; | |||||
hp = xa[i+m]-x; | |||||
w = c[i+1]-d[i]; | |||||
if ((den = ho-hp) == 0.0) { | |||||
return (ya[2]); // two input xa are identical | |||||
} | } | ||||
den=w/den; | |||||
d[i]=hp*den; | |||||
c[i]=ho*den; | |||||
den = w/den; | |||||
d[i] = hp*den; | |||||
c[i] = ho*den; | |||||
} | } | ||||
y += ((2*ns < (n-m) ? c[ns+1] : d[ns--])); | y += ((2*ns < (n-m) ? c[ns+1] : d[ns--])); | ||||
} | } | ||||
return(y); | |||||
return (y); | |||||
} | } | ||||
frame->amp_adjust = 100; | frame->amp_adjust = 100; | ||||
frame->length_adjust = 0; | frame->length_adjust = 0; | ||||
for(ix=0; ix<N_PEAKS; ix++) | |||||
{ | |||||
for (ix = 0; ix < N_PEAKS; ix++) { | |||||
frame->formants[ix].freq = 0; | frame->formants[ix].freq = 0; | ||||
frame->peaks[ix].pkfreq = default_freq[ix]; | frame->peaks[ix].pkfreq = default_freq[ix]; | ||||
frame->peaks[ix].pkheight = 0; | frame->peaks[ix].pkheight = 0; | ||||
static void SpectFrameDestroy(SpectFrame *frame) | static void SpectFrameDestroy(SpectFrame *frame) | ||||
{ | { | ||||
if(frame->spect != NULL) | |||||
if (frame->spect != NULL) | |||||
free(frame->spect); | free(frame->spect); | ||||
free(frame); | free(frame); | ||||
} | } | ||||
frame->pitch = read_double(stream); | frame->pitch = read_double(stream); | ||||
frame->length = read_double(stream); | frame->length = read_double(stream); | ||||
frame->dx = read_double(stream); | frame->dx = read_double(stream); | ||||
fread(&frame->nx,sizeof(short),1,stream); | |||||
fread(&frame->markers,sizeof(short),1,stream); | |||||
fread(&frame->amp_adjust,sizeof(short),1,stream); | |||||
if(file_format_type == 2) | |||||
{ | |||||
fread(&ix,sizeof(short),1,stream); // spare | |||||
fread(&ix,sizeof(short),1,stream); // spare | |||||
fread(&frame->nx, sizeof(short), 1, stream); | |||||
fread(&frame->markers, sizeof(short), 1, stream); | |||||
fread(&frame->amp_adjust, sizeof(short), 1, stream); | |||||
if (file_format_type == 2) { | |||||
fread(&ix, sizeof(short), 1, stream); // spare | |||||
fread(&ix, sizeof(short), 1, stream); // spare | |||||
} | } | ||||
for(ix=0; ix<N_PEAKS; ix++) | |||||
{ | |||||
fread(&frame->formants[ix].freq,sizeof(short),1,stream); | |||||
fread(&frame->formants[ix].bandw,sizeof(short),1,stream); | |||||
fread(&frame->peaks[ix].pkfreq,sizeof(short),1,stream); | |||||
fread(&frame->peaks[ix].pkheight,sizeof(short),1,stream); | |||||
fread(&frame->peaks[ix].pkwidth,sizeof(short),1,stream); | |||||
fread(&frame->peaks[ix].pkright,sizeof(short),1,stream); | |||||
if(frame->peaks[ix].pkheight > 0) | |||||
for (ix = 0; ix < N_PEAKS; ix++) { | |||||
fread(&frame->formants[ix].freq, sizeof(short), 1, stream); | |||||
fread(&frame->formants[ix].bandw, sizeof(short), 1, stream); | |||||
fread(&frame->peaks[ix].pkfreq, sizeof(short), 1, stream); | |||||
fread(&frame->peaks[ix].pkheight, sizeof(short), 1, stream); | |||||
fread(&frame->peaks[ix].pkwidth, sizeof(short), 1, stream); | |||||
fread(&frame->peaks[ix].pkright, sizeof(short), 1, stream); | |||||
if (frame->peaks[ix].pkheight > 0) | |||||
frame->keyframe = 1; | frame->keyframe = 1; | ||||
if(file_format_type == 2) | |||||
{ | |||||
fread(&frame->peaks[ix].klt_bw,sizeof(short),1,stream); | |||||
fread(&frame->peaks[ix].klt_ap,sizeof(short),1,stream); | |||||
fread(&frame->peaks[ix].klt_bp,sizeof(short),1,stream); | |||||
if (file_format_type == 2) { | |||||
fread(&frame->peaks[ix].klt_bw, sizeof(short), 1, stream); | |||||
fread(&frame->peaks[ix].klt_ap, sizeof(short), 1, stream); | |||||
fread(&frame->peaks[ix].klt_bp, sizeof(short), 1, stream); | |||||
} | } | ||||
} | } | ||||
if(file_format_type > 0) | |||||
{ | |||||
for(ix=0; ix<N_KLATTP2; ix++) | |||||
{ | |||||
fread(frame->klatt_param + ix,sizeof(short),1,stream); | |||||
if (file_format_type > 0) { | |||||
for (ix = 0; ix < N_KLATTP2; ix++) { | |||||
fread(frame->klatt_param + ix, sizeof(short), 1, stream); | |||||
} | } | ||||
} | } | ||||
spect_data = malloc(sizeof(USHORT) * frame->nx); | spect_data = malloc(sizeof(USHORT) * frame->nx); | ||||
if(spect_data == NULL) | |||||
{ | |||||
fprintf(stderr,"Failed to allocate memory\n"); | |||||
return(1); | |||||
if (spect_data == NULL) { | |||||
fprintf(stderr, "Failed to allocate memory\n"); | |||||
return (1); | |||||
} | } | ||||
frame->max_y = 0; | frame->max_y = 0; | ||||
for(ix=0; ix<frame->nx; ix++) | |||||
{ | |||||
fread(&x,sizeof(short),1,stream); | |||||
for (ix = 0; ix < frame->nx; ix++) { | |||||
fread(&x, sizeof(short), 1, stream); | |||||
spect_data[ix] = x; | spect_data[ix] = x; | ||||
if(x > frame->max_y) frame->max_y = x; | |||||
if (x > frame->max_y) frame->max_y = x; | |||||
} | } | ||||
frame->spect = spect_data; | frame->spect = spect_data; | ||||
return(0); | |||||
return (0); | |||||
} | } | ||||
double GetFrameRms(SpectFrame *frame, int seq_amplitude) | double GetFrameRms(SpectFrame *frame, int seq_amplitude) | ||||
{ | { | ||||
int h; | int h; | ||||
float total=0; | |||||
float total = 0; | |||||
int maxh; | int maxh; | ||||
int height; | int height; | ||||
int htab[400]; | int htab[400]; | ||||
wavegen_peaks_t wpeaks[9]; | wavegen_peaks_t wpeaks[9]; | ||||
for(h=0; h<9; h++) | |||||
{ | |||||
for (h = 0; h < 9; h++) { | |||||
height = (frame->peaks[h].pkheight * seq_amplitude * frame->amp_adjust)/10000; | height = (frame->peaks[h].pkheight * seq_amplitude * frame->amp_adjust)/10000; | ||||
wpeaks[h].height = height << 8; | wpeaks[h].height = height << 8; | ||||
wpeaks[h].right = frame->peaks[h].pkright << 16; | wpeaks[h].right = frame->peaks[h].pkright << 16; | ||||
} | } | ||||
maxh = PeaksToHarmspect(wpeaks,90<<16,htab,0); | |||||
for(h=1; h<maxh; h++) | |||||
{ | |||||
maxh = PeaksToHarmspect(wpeaks, 90<<16, htab, 0); | |||||
for (h = 1; h < maxh; h++) { | |||||
total += ((htab[h] * htab[h]) >> 10); | total += ((htab[h] * htab[h]) >> 10); | ||||
} | } | ||||
frame->rms = sqrt(total) / 7.25; | frame->rms = sqrt(total) / 7.25; | ||||
return(frame->rms); | |||||
return (frame->rms); | |||||
} | } | ||||
void SpectSeqDestroy(SpectSeq *spect) | void SpectSeqDestroy(SpectSeq *spect) | ||||
{ | { | ||||
int ix; | int ix; | ||||
if(spect->frames != NULL) | |||||
{ | |||||
for(ix=0; ix<spect->numframes; ix++) | |||||
{ | |||||
if(spect->frames[ix] != NULL) | |||||
if (spect->frames != NULL) { | |||||
for (ix = 0; ix < spect->numframes; ix++) { | |||||
if (spect->frames[ix] != NULL) | |||||
SpectFrameDestroy(spect->frames[ix]); | SpectFrameDestroy(spect->frames[ix]); | ||||
} | } | ||||
free(spect->frames); | free(spect->frames); | ||||
static float GetFrameLength(SpectSeq *spect, int frame) | static float GetFrameLength(SpectSeq *spect, int frame) | ||||
{ | { | ||||
int ix; | int ix; | ||||
float adjust=0; | |||||
float adjust = 0; | |||||
if(frame >= spect->numframes-1) return(0); | |||||
if (frame >= spect->numframes-1) return (0); | |||||
for(ix=frame+1; ix<spect->numframes-1; ix++) | |||||
{ | |||||
if(spect->frames[ix]->keyframe) break; // reached next keyframe | |||||
for (ix = frame+1; ix < spect->numframes-1; ix++) { | |||||
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); | ||||
short n, temp; | short n, temp; | ||||
int ix; | int ix; | ||||
uint32_t id1, id2, name_len; | uint32_t id1, id2, name_len; | ||||
int set_max_y=0; | |||||
int set_max_y = 0; | |||||
float time_offset; | float time_offset; | ||||
FILE *stream = fopen(filename, "rb"); | FILE *stream = fopen(filename, "rb"); | ||||
if(stream == NULL) | |||||
{ | |||||
if (stream == NULL) { | |||||
fprintf(stderr, "Failed to open: '%s'", filename); | fprintf(stderr, "Failed to open: '%s'", filename); | ||||
return(0); | |||||
return (0); | |||||
} | } | ||||
fread(&id1,sizeof(uint32_t),1,stream); | |||||
fread(&id2,sizeof(uint32_t),1,stream); | |||||
fread(&id1, 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); | ||||
return(1); | |||||
return (1); | |||||
} | } | ||||
fread(&name_len,sizeof(uint32_t),1,stream); | |||||
if (name_len > 0) | |||||
{ | |||||
fread(&name_len, sizeof(uint32_t), 1, stream); | |||||
if (name_len > 0) { | |||||
spect->name = (char *)malloc(name_len); | spect->name = (char *)malloc(name_len); | ||||
fread(spect->name,sizeof(char),name_len,stream); | |||||
} | |||||
else | |||||
fread(spect->name, sizeof(char), name_len, stream); | |||||
} else | |||||
spect->name = NULL; | spect->name = NULL; | ||||
fread(&n,sizeof(short),1,stream); | |||||
fread(&spect->amplitude,sizeof(short),1,stream); | |||||
fread(&spect->max_y,sizeof(short),1,stream); | |||||
fread(&temp,sizeof(short),1,stream); // unused | |||||
fread(&n, sizeof(short), 1, stream); | |||||
fread(&spect->amplitude, sizeof(short), 1, stream); | |||||
fread(&spect->max_y, sizeof(short), 1, stream); | |||||
fread(&temp, sizeof(short), 1, stream); // unused | |||||
if(n==0) | |||||
{ | |||||
if (n == 0) { | |||||
fclose(stream); | fclose(stream); | ||||
return(0); | |||||
return (0); | |||||
} | } | ||||
if(spect->frames != NULL) | |||||
{ | |||||
for(ix=0; ix<spect->numframes; ix++) | |||||
{ | |||||
if(spect->frames[ix] != NULL) | |||||
if (spect->frames != NULL) { | |||||
for (ix = 0; ix < spect->numframes; ix++) { | |||||
if (spect->frames[ix] != NULL) | |||||
SpectFrameDestroy(spect->frames[ix]); | SpectFrameDestroy(spect->frames[ix]); | ||||
} | } | ||||
free(spect->frames); | free(spect->frames); | ||||
spect->numframes = 0; | spect->numframes = 0; | ||||
spect->max_x = 3000; | spect->max_x = 3000; | ||||
if(spect->max_y == 0) | |||||
{ | |||||
if (spect->max_y == 0) { | |||||
set_max_y = 1; | set_max_y = 1; | ||||
spect->max_y = 1; | spect->max_y = 1; | ||||
} | } | ||||
for(ix = 0; ix < n; ix++) | |||||
{ | |||||
for (ix = 0; ix < n; ix++) { | |||||
SpectFrame *frame = SpectFrameCreate(); | SpectFrame *frame = SpectFrameCreate(); | ||||
if(LoadFrame(frame, stream, spect->file_format) != 0) | |||||
{ | |||||
if (LoadFrame(frame, stream, spect->file_format) != 0) { | |||||
free(frame); | free(frame); | ||||
break; | break; | ||||
} | } | ||||
spect->frames[spect->numframes++] = frame; | spect->frames[spect->numframes++] = frame; | ||||
if(set_max_y && (frame->max_y > spect->max_y)) | |||||
if (set_max_y && (frame->max_y > spect->max_y)) | |||||
spect->max_y = frame->max_y; | spect->max_y = frame->max_y; | ||||
if(frame->nx * frame->dx > spect->max_x) spect->max_x = (int)(frame->nx * frame->dx); | |||||
if (frame->nx * frame->dx > spect->max_x) spect->max_x = (int)(frame->nx * frame->dx); | |||||
} | } | ||||
spect->max_x = 9000; // disable auto-xscaling | spect->max_x = 9000; // disable auto-xscaling | ||||
frame_width = (int)((FRAME_WIDTH*spect->max_x)/MAX_DISPLAY_FREQ); | frame_width = (int)((FRAME_WIDTH*spect->max_x)/MAX_DISPLAY_FREQ); | ||||
if(frame_width > FRAME_WIDTH) frame_width = FRAME_WIDTH; | |||||
if (frame_width > FRAME_WIDTH) frame_width = FRAME_WIDTH; | |||||
// start times from zero | // start times from zero | ||||
time_offset = spect->frames[0]->time; | time_offset = spect->frames[0]->time; | ||||
for(ix=0; ix<spect->numframes; ix++) | |||||
for (ix = 0; ix < spect->numframes; ix++) | |||||
spect->frames[ix]->time -= time_offset; | spect->frames[ix]->time -= time_offset; | ||||
spect->pitch1 = spect->pitchenv.pitch1; | spect->pitch1 = spect->pitchenv.pitch1; | ||||
spect->pitch2 = spect->pitchenv.pitch2; | spect->pitch2 = spect->pitchenv.pitch2; | ||||
spect->duration = (int)(spect->frames[spect->numframes-1]->time * 1000); | spect->duration = (int)(spect->frames[spect->numframes-1]->time * 1000); | ||||
if(spect->max_y < 400) | |||||
if (spect->max_y < 400) | |||||
spect->max_y = 200; | spect->max_y = 200; | ||||
else | else | ||||
spect->max_y = 29000; // disable auto height scaling | spect->max_y = 29000; // disable auto height scaling | ||||
for(ix=0; ix<spect->numframes; ix++) | |||||
{ | |||||
if(spect->frames[ix]->keyframe) | |||||
spect->frames[ix]->length_adjust = spect->frames[ix]->length - GetFrameLength(spect,ix); | |||||
for (ix = 0; ix < spect->numframes; ix++) { | |||||
if (spect->frames[ix]->keyframe) | |||||
spect->frames[ix]->length_adjust = spect->frames[ix]->length - GetFrameLength(spect, ix); | |||||
} | } | ||||
fclose(stream); | fclose(stream); | ||||
return(0); | |||||
return (0); | |||||
} | } |
} peak_t; | } peak_t; | ||||
typedef struct | |||||
{ | |||||
typedef struct { | |||||
int keyframe; | int keyframe; | ||||
short amp_adjust; | short amp_adjust; | ||||
float length_adjust; | float length_adjust; | ||||
double GetFrameRms(SpectFrame *frame, int amp); | double GetFrameRms(SpectFrame *frame, int amp); | ||||
typedef struct | |||||
{ | |||||
typedef struct { | |||||
int numframes; | int numframes; | ||||
short amplitude; | short amplitude; | ||||
int spare; | int spare; |
extern char path_home[N_PATH_HOME]; // this is the espeak-data directory | extern char path_home[N_PATH_HOME]; // this is the espeak-data directory | ||||
extern void strncpy0(char *to,const char *from, int size); | |||||
extern void strncpy0(char *to, const char *from, int size); | |||||
int GetFileLength(const char *filename); | int GetFileLength(const char *filename); | ||||
char *Alloc(int size); | char *Alloc(int size); | ||||
void Free(void *ptr); | void Free(void *ptr); |
typedef void (WINAPI *PROCVF)(float); | typedef void (WINAPI *PROCVF)(float); | ||||
typedef int (WINAPI *PROCIV)(); | typedef int (WINAPI *PROCIV)(); | ||||
typedef int (WINAPI *PROCIC)(char *); | typedef int (WINAPI *PROCIC)(char *); | ||||
typedef int (WINAPI *PROCISI)(short *,int); | |||||
typedef char* (WINAPI *PROCVCI)(char *,int); | |||||
typedef int (WINAPI *PROCISI)(short *, int); | |||||
typedef char * (WINAPI *PROCVCI)(char *, int); | |||||
PROCIC init_MBR; | PROCIC init_MBR; | ||||
PROCIC write_MBR; | PROCIC write_MBR; | ||||
BOOL load_MBR() | BOOL load_MBR() | ||||
{ | { | ||||
if(hinstDllMBR != NULL) | |||||
if (hinstDllMBR != NULL) | |||||
return TRUE; // already loaded | return TRUE; // already loaded | ||||
if ((hinstDllMBR=LoadLibraryA("mbrola.dll")) == 0) | |||||
if ((hinstDllMBR = LoadLibraryA("mbrola.dll")) == 0) | |||||
return FALSE; | return FALSE; | ||||
init_MBR =(PROCIC) GetProcAddress(hinstDllMBR,"init_MBR"); | |||||
write_MBR =(PROCIC) GetProcAddress(hinstDllMBR,"write_MBR"); | |||||
flush_MBR =(PROCIV) GetProcAddress(hinstDllMBR,"flush_MBR"); | |||||
read_MBR =(PROCISI) GetProcAddress(hinstDllMBR,"read_MBR"); | |||||
close_MBR =(PROCVV) GetProcAddress(hinstDllMBR,"close_MBR"); | |||||
reset_MBR =(PROCVV) GetProcAddress(hinstDllMBR,"reset_MBR"); | |||||
lastError_MBR =(PROCIV) GetProcAddress(hinstDllMBR,"lastError_MBR"); | |||||
lastErrorStr_MBR =(PROCVCI) GetProcAddress(hinstDllMBR,"lastErrorStr_MBR"); | |||||
setNoError_MBR =(PROCVI) GetProcAddress(hinstDllMBR,"setNoError_MBR"); | |||||
setVolumeRatio_MBR =(PROCVF) GetProcAddress(hinstDllMBR,"setVolumeRatio_MBR"); | |||||
init_MBR = (PROCIC)GetProcAddress(hinstDllMBR, "init_MBR"); | |||||
write_MBR = (PROCIC)GetProcAddress(hinstDllMBR, "write_MBR"); | |||||
flush_MBR = (PROCIV)GetProcAddress(hinstDllMBR, "flush_MBR"); | |||||
read_MBR = (PROCISI)GetProcAddress(hinstDllMBR, "read_MBR"); | |||||
close_MBR = (PROCVV)GetProcAddress(hinstDllMBR, "close_MBR"); | |||||
reset_MBR = (PROCVV)GetProcAddress(hinstDllMBR, "reset_MBR"); | |||||
lastError_MBR = (PROCIV)GetProcAddress(hinstDllMBR, "lastError_MBR"); | |||||
lastErrorStr_MBR = (PROCVCI)GetProcAddress(hinstDllMBR, "lastErrorStr_MBR"); | |||||
setNoError_MBR = (PROCVI)GetProcAddress(hinstDllMBR, "setNoError_MBR"); | |||||
setVolumeRatio_MBR = (PROCVF)GetProcAddress(hinstDllMBR, "setVolumeRatio_MBR"); | |||||
return TRUE; | return TRUE; | ||||
} | } | ||||
void unload_MBR() | void unload_MBR() | ||||
{ | { | ||||
if (hinstDllMBR) | |||||
{ | |||||
FreeLibrary (hinstDllMBR); | |||||
hinstDllMBR=NULL; | |||||
if (hinstDllMBR) { | |||||
FreeLibrary(hinstDllMBR); | |||||
hinstDllMBR = NULL; | |||||
} | } | ||||
} | } | ||||
mbrola_delay = 0; | mbrola_delay = 0; | ||||
mbr_name_prefix = 0; | mbr_name_prefix = 0; | ||||
if(mbrola_voice == NULL) | |||||
{ | |||||
if (mbrola_voice == NULL) { | |||||
samplerate = samplerate_native; | samplerate = samplerate_native; | ||||
SetParameter(espeakVOICETYPE,0,0); | |||||
return(EE_OK); | |||||
SetParameter(espeakVOICETYPE, 0, 0); | |||||
return (EE_OK); | |||||
} | } | ||||
sprintf(path,"%s/mbrola/%s",path_home,mbrola_voice); | |||||
sprintf(path, "%s/mbrola/%s", path_home, mbrola_voice); | |||||
#ifdef PLATFORM_POSIX | #ifdef PLATFORM_POSIX | ||||
// if not found, then also look in | // if not found, then also look in | ||||
// usr/share/mbrola/xx, /usr/share/mbrola/xx/xx, /usr/share/mbrola/voices/xx | // usr/share/mbrola/xx, /usr/share/mbrola/xx/xx, /usr/share/mbrola/voices/xx | ||||
if(GetFileLength(path) <= 0) | |||||
{ | |||||
sprintf(path,"/usr/share/mbrola/%s",mbrola_voice); | |||||
if (GetFileLength(path) <= 0) { | |||||
sprintf(path, "/usr/share/mbrola/%s", mbrola_voice); | |||||
if(GetFileLength(path) <= 0) | |||||
{ | |||||
sprintf(path,"/usr/share/mbrola/%s/%s",mbrola_voice,mbrola_voice); | |||||
if (GetFileLength(path) <= 0) { | |||||
sprintf(path, "/usr/share/mbrola/%s/%s", mbrola_voice, mbrola_voice); | |||||
if(GetFileLength(path) <= 0) | |||||
{ | |||||
sprintf(path,"/usr/share/mbrola/voices/%s",mbrola_voice); | |||||
if (GetFileLength(path) <= 0) { | |||||
sprintf(path, "/usr/share/mbrola/voices/%s", mbrola_voice); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
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 | |||||
return(EE_NOT_FOUND); | |||||
if (init_MBR(path) != 0) // initialise the required mbrola voice | |||||
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); | |||||
size = GetFileLength(path); | size = GetFileLength(path); | ||||
if((f_in = fopen(path,"rb")) == NULL) { | |||||
if ((f_in = fopen(path, "rb")) == NULL) { | |||||
close_MBR(); | close_MBR(); | ||||
return(EE_NOT_FOUND); | |||||
return (EE_NOT_FOUND); | |||||
} | } | ||||
if((mbrola_tab = (MBROLA_TAB *)realloc(mbrola_tab,size)) == NULL) | |||||
{ | |||||
if ((mbrola_tab = (MBROLA_TAB *)realloc(mbrola_tab, size)) == NULL) { | |||||
fclose(f_in); | fclose(f_in); | ||||
close_MBR(); | close_MBR(); | ||||
return(EE_INTERNAL_ERROR); | |||||
return (EE_INTERNAL_ERROR); | |||||
} | } | ||||
mbrola_control = Read4Bytes(f_in); | mbrola_control = Read4Bytes(f_in); | ||||
pw = (int *)mbrola_tab; | pw = (int *)mbrola_tab; | ||||
for(ix=4; ix<size; ix+=4) | |||||
{ | |||||
for (ix = 4; ix < size; ix += 4) { | |||||
*pw++ = Read4Bytes(f_in); | *pw++ = Read4Bytes(f_in); | ||||
} | } | ||||
size = fread(mbrola_tab,1,size,f_in); | |||||
size = fread(mbrola_tab, 1, size, f_in); | |||||
fclose(f_in); | fclose(f_in); | ||||
setVolumeRatio_MBR((float)(mbrola_control & 0xff) /16.0f); | setVolumeRatio_MBR((float)(mbrola_control & 0xff) /16.0f); | ||||
samplerate = *srate = getFreq_MBR(); | samplerate = *srate = getFreq_MBR(); | ||||
if(*srate == 22050) | |||||
SetParameter(espeakVOICETYPE,0,0); | |||||
if (*srate == 22050) | |||||
SetParameter(espeakVOICETYPE, 0, 0); | |||||
else | else | ||||
SetParameter(espeakVOICETYPE,1,0); | |||||
strcpy(mbrola_name,mbrola_voice); | |||||
SetParameter(espeakVOICETYPE, 1, 0); | |||||
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); | |||||
} | } | ||||
// bit 5 only in stressed syllable | // bit 5 only in stressed syllable | ||||
// bit 6 only at the end of a word | // bit 6 only at the end of a word | ||||
*name2=0; | |||||
*split=0; | |||||
*control=0; | |||||
*name2 = 0; | |||||
*split = 0; | |||||
*control = 0; | |||||
mnem = ph->mnemonic; | mnem = ph->mnemonic; | ||||
pr = mbrola_tab; | pr = mbrola_tab; | ||||
while(pr->name != 0) | |||||
{ | |||||
if(mnem == pr->name) | |||||
{ | |||||
if(pr->next_phoneme == 0) | |||||
while (pr->name != 0) { | |||||
if (mnem == pr->name) { | |||||
if (pr->next_phoneme == 0) | |||||
found = 1; | found = 1; | ||||
else | |||||
if((pr->next_phoneme == ':') && (plist->synthflags & SFLAG_LENGTHEN)) | |||||
{ | |||||
else if ((pr->next_phoneme == ':') && (plist->synthflags & SFLAG_LENGTHEN)) { | |||||
found = 1; | found = 1; | ||||
} | |||||
else | |||||
{ | |||||
if(pr->control & 2) | |||||
} else { | |||||
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; | ||||
if((pr->next_phoneme == other_ph->mnemonic) || | |||||
((pr->next_phoneme == 2) && (other_ph->type == phVOWEL)) || | |||||
((pr->next_phoneme == '_') && (other_ph->type == phPAUSE))) | |||||
{ | |||||
if ((pr->next_phoneme == other_ph->mnemonic) || | |||||
((pr->next_phoneme == 2) && (other_ph->type == phVOWEL)) || | |||||
((pr->next_phoneme == '_') && (other_ph->type == phPAUSE))) { | |||||
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; | ||||
*split = pr->percent; | *split = pr->percent; | ||||
*control = pr->control; | *control = pr->control; | ||||
if(pr->control & 0x10) | |||||
{ | |||||
if (pr->control & 0x10) { | |||||
mbr_name_prefix = pr->mbr_name; | mbr_name_prefix = pr->mbr_name; | ||||
return(0); | |||||
return (0); | |||||
} | } | ||||
mnem = pr->mbr_name; | mnem = pr->mbr_name; | ||||
break; | break; | ||||
pr++; | pr++; | ||||
} | } | ||||
if(mbr_name_prefix != 0) | |||||
{ | |||||
if (mbr_name_prefix != 0) { | |||||
mnem = (mnem << 8) | (mbr_name_prefix & 0xff); | mnem = (mnem << 8) | (mbr_name_prefix & 0xff); | ||||
} | } | ||||
mbr_name_prefix = 0; | mbr_name_prefix = 0; | ||||
return(mnem); | |||||
return (mnem); | |||||
} | } | ||||
int ix; | int ix; | ||||
int pitch_base; | int pitch_base; | ||||
int pitch_range; | int pitch_range; | ||||
int p1,p2,p_end; | |||||
int p1, p2, p_end; | |||||
unsigned char *pitch_env; | unsigned char *pitch_env; | ||||
int max = -1; | int max = -1; | ||||
int min = 999; | int min = 999; | ||||
int y_max=0; | |||||
int y_min=0; | |||||
int y_max = 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]; | ||||
env_split = (split * 128)/100; | env_split = (split * 128)/100; | ||||
if(env_split < 0) | |||||
if (env_split < 0) | |||||
env_split = 0-env_split; | env_split = 0-env_split; | ||||
// find max and min in the pitch envelope | // find max and min in the pitch envelope | ||||
for(x=0; x<128; x++) | |||||
{ | |||||
if(pitch_env[x] > max) | |||||
{ | |||||
for (x = 0; x < 128; x++) { | |||||
if (pitch_env[x] > max) { | |||||
max = pitch_env[x]; | max = pitch_env[x]; | ||||
y_max = x; | y_max = x; | ||||
} | } | ||||
if(pitch_env[x] < min) | |||||
{ | |||||
if (pitch_env[x] < min) { | |||||
min = pitch_env[x]; | min = pitch_env[x]; | ||||
y_min = x; | y_min = x; | ||||
} | } | ||||
// set an additional pitch point half way through the phoneme. | // set an additional pitch point half way through the phoneme. | ||||
// but look for a maximum or a minimum and use that instead | // but look for a maximum or a minimum and use that instead | ||||
y[2] = 64; | y[2] = 64; | ||||
if((y_max > 0) && (y_max < 127)) | |||||
{ | |||||
if ((y_max > 0) && (y_max < 127)) { | |||||
y[2] = y_max; | y[2] = y_max; | ||||
} | } | ||||
if((y_min > 0) && (y_min < 127)) | |||||
{ | |||||
if ((y_min > 0) && (y_min < 127)) { | |||||
y[2] = y_min; | y[2] = y_min; | ||||
} | } | ||||
y[1] = y[2] / 2; | y[1] = y[2] / 2; | ||||
p_end = ((pitch_env[127]*pitch_range)>>8) + pitch_base; | p_end = ((pitch_env[127]*pitch_range)>>8) + pitch_base; | ||||
if(split >= 0) | |||||
{ | |||||
sprintf(buf," 0 %d",p1/4096); | |||||
strcat(output,buf); | |||||
if (split >= 0) { | |||||
sprintf(buf, " 0 %d", p1/4096); | |||||
strcat(output, buf); | |||||
} | } | ||||
// don't use intermediate pitch points for linear rise and fall | // don't use intermediate pitch points for linear rise and fall | ||||
if(env > 1) | |||||
{ | |||||
for(ix=1; ix<4; ix++) | |||||
{ | |||||
if (env > 1) { | |||||
for (ix = 1; ix < 4; ix++) { | |||||
p2 = ((pitch_env[y[ix]]*pitch_range)>>8) + pitch_base; | p2 = ((pitch_env[y[ix]]*pitch_range)>>8) + pitch_base; | ||||
if(split > 0) | |||||
{ | |||||
if (split > 0) { | |||||
y2 = (y[ix] * env100)/env_split; | y2 = (y[ix] * env100)/env_split; | ||||
} | |||||
else | |||||
if(split < 0) | |||||
{ | |||||
} else if (split < 0) { | |||||
y2 = ((y[ix]-env_split) * env100)/env_split; | y2 = ((y[ix]-env_split) * env100)/env_split; | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
y2 = (y[ix] * env100)/128; | y2 = (y[ix] * env100)/128; | ||||
} | } | ||||
if((y2 > 0) && (y2 <= env100)) | |||||
{ | |||||
sprintf(buf," %d %d",y2,p2/4096); | |||||
strcat(output,buf); | |||||
if ((y2 > 0) && (y2 <= env100)) { | |||||
sprintf(buf, " %d %d", y2, p2/4096); | |||||
strcat(output, buf); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
p_end = p_end/4096; | p_end = p_end/4096; | ||||
if(split <= 0) | |||||
{ | |||||
sprintf(buf," %d %d",env100,p_end); | |||||
strcat(output,buf); | |||||
if (split <= 0) { | |||||
sprintf(buf, " %d %d", env100, p_end); | |||||
strcat(output, buf); | |||||
} | } | ||||
if(env100 < 100) | |||||
{ | |||||
sprintf(buf," %d %d",100,p_end); | |||||
strcat(output,buf); | |||||
if (env100 < 100) { | |||||
sprintf(buf, " %d %d", 100, p_end); | |||||
strcat(output, buf); | |||||
} | } | ||||
strcat(output,"\n"); | |||||
strcat(output, "\n"); | |||||
if(final) | |||||
sprintf(output,"\t100 %d\n",p_end); | |||||
return(output); | |||||
if (final) | |||||
sprintf(output, "\t100 %d\n", p_end); | |||||
return (output); | |||||
} | } | ||||
word_count = 0; | word_count = 0; | ||||
} | } | ||||
while (phix < n_phonemes) | |||||
{ | |||||
while (phix < n_phonemes) { | |||||
if (WcmdqFree() < MIN_WCMDQ) | if (WcmdqFree() < MIN_WCMDQ) | ||||
return 1; | return 1; | ||||
ph_prev = plist[phix-1].ph; | ph_prev = plist[phix-1].ph; | ||||
ph_next = plist[phix+1].ph; | ph_next = plist[phix+1].ph; | ||||
if(p->synthflags & SFLAG_EMBEDDED) | |||||
{ | |||||
if (p->synthflags & SFLAG_EMBEDDED) { | |||||
DoEmbedded(&embedded_ix, p->sourceix); | DoEmbedded(&embedded_ix, p->sourceix); | ||||
} | } | ||||
if(p->newword & 4) | |||||
if (p->newword & 4) | |||||
DoMarker(espeakEVENT_SENTENCE, (p->sourceix & 0x7ff) + clause_start_char, 0, count_sentences); | DoMarker(espeakEVENT_SENTENCE, (p->sourceix & 0x7ff) + clause_start_char, 0, count_sentences); | ||||
if(p->newword & 1) | |||||
if (p->newword & 1) | |||||
DoMarker(espeakEVENT_WORD, (p->sourceix & 0x7ff) + clause_start_char, p->sourceix >> 11, clause_start_word + word_count++); | DoMarker(espeakEVENT_WORD, (p->sourceix & 0x7ff) + clause_start_char, p->sourceix >> 11, clause_start_word + word_count++); | ||||
name = GetMbrName(p,ph,ph_prev,ph_next,&name2,&len_percent,&control); | |||||
if(control & 1) | |||||
name = GetMbrName(p, ph, ph_prev, ph_next, &name2, &len_percent, &control); | |||||
if (control & 1) | |||||
phix++; | phix++; | ||||
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)) { | |||||
// a pause phoneme, which has not been changed by the translation | // a pause phoneme, which has not been changed by the translation | ||||
name = '_'; | name = '_'; | ||||
len = (p->length * speed.pause_factor)/256; | len = (p->length * speed.pause_factor)/256; | ||||
if(len == 0) | |||||
if (len == 0) | |||||
len = 1; | len = 1; | ||||
} | |||||
else | |||||
} else | |||||
len = (80 * speed.wav_factor)/256; | len = (80 * speed.wav_factor)/256; | ||||
if(ph->code != phonEND_WORD) | |||||
{ | |||||
if (ph->code != phonEND_WORD) { | |||||
char phoneme_name[16]; | char phoneme_name[16]; | ||||
WritePhMnemonic(phoneme_name, p->ph, p, option_phoneme_events & espeakINITIALIZE_PHONEME_IPA, NULL); | WritePhMnemonic(phoneme_name, p->ph, p, option_phoneme_events & espeakINITIALIZE_PHONEME_IPA, NULL); | ||||
DoPhonemeMarker(espeakEVENT_PHONEME, (p->sourceix & 0x7ff) + clause_start_char, 0, phoneme_name); | DoPhonemeMarker(espeakEVENT_PHONEME, (p->sourceix & 0x7ff) + clause_start_char, 0, phoneme_name); | ||||
} | } | ||||
ptr += sprintf(ptr,"%s\t",WordToString(name)); | |||||
ptr += sprintf(ptr, "%s\t", WordToString(name)); | |||||
if(name2 == '_') | |||||
{ | |||||
if (name2 == '_') { | |||||
// add a pause after this phoneme | // add a pause after this phoneme | ||||
pause = len_percent; | pause = len_percent; | ||||
name2 = 0; | name2 = 0; | ||||
done = 0; | done = 0; | ||||
final_pitch = ""; | final_pitch = ""; | ||||
switch(ph->type) | |||||
switch (ph->type) | |||||
{ | { | ||||
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) | |||||
{ | |||||
char *pitch = WritePitch(p->env,p->pitch1,p->pitch2,0,0); | |||||
ptr += sprintf(ptr,"%d\t%s", len, pitch); | |||||
} | |||||
else | |||||
{ | |||||
if (name2 == 0) { | |||||
char *pitch = WritePitch(p->env, p->pitch1, p->pitch2, 0, 0); | |||||
ptr += sprintf(ptr, "%d\t%s", len, pitch); | |||||
} else { | |||||
char *pitch; | char *pitch; | ||||
pitch = WritePitch(p->env,p->pitch1,p->pitch2,len_percent,0); | |||||
pitch = WritePitch(p->env, p->pitch1, p->pitch2, len_percent, 0); | |||||
len1 = (len * len_percent)/100; | len1 = (len * len_percent)/100; | ||||
ptr += sprintf(ptr,"%d\t%s", len1, pitch); | |||||
ptr += sprintf(ptr, "%d\t%s", len1, pitch); | |||||
pitch = WritePitch(p->env,p->pitch1,p->pitch2,-len_percent,0); | |||||
ptr += sprintf(ptr,"%s\t%d\t%s", WordToString(name2), len-len1, pitch); | |||||
pitch = WritePitch(p->env, p->pitch1, p->pitch2, -len_percent, 0); | |||||
ptr += sprintf(ptr, "%s\t%d\t%s", WordToString(name2), len-len1, pitch); | |||||
} | } | ||||
done = 1; | done = 1; | ||||
break; | break; | ||||
case phSTOP: | case phSTOP: | ||||
released = 0; | released = 0; | ||||
if(next->type==phVOWEL) released = 1; | |||||
if(next->type==phLIQUID && !next->newword) released = 1; | |||||
if (next->type == phVOWEL) released = 1; | |||||
if (next->type == phLIQUID && !next->newword) released = 1; | |||||
if(released == 0) | |||||
if (released == 0) | |||||
p->synthflags |= SFLAG_NEXT_PAUSE; | p->synthflags |= SFLAG_NEXT_PAUSE; | ||||
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: | ||||
case phFRICATIVE: | case phFRICATIVE: | ||||
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); | ||||
break; | break; | ||||
case phNASAL: | case phNASAL: | ||||
if(next->type != phVOWEL) | |||||
{ | |||||
if (next->type != phVOWEL) { | |||||
memset(&fmtp, 0, sizeof(fmtp)); | memset(&fmtp, 0, sizeof(fmtp)); | ||||
InterpretPhoneme(NULL, 0, p, &phdata, NULL); | InterpretPhoneme(NULL, 0, p, &phdata, NULL); | ||||
fmtp.fmt_addr = phdata.sound_addr[pd_FMT]; | fmtp.fmt_addr = phdata.sound_addr[pd_FMT]; | ||||
len = DoSpect2(p->ph, 0, &fmtp, p, -1); | len = DoSpect2(p->ph, 0, &fmtp, p, -1); | ||||
len = (len * 1000)/samplerate; | len = (len * 1000)/samplerate; | ||||
if(next->type == phPAUSE) | |||||
if (next->type == phPAUSE) | |||||
len += 50; | len += 50; | ||||
final_pitch = WritePitch(p->env,p->pitch1,p->pitch2,0,1); | |||||
final_pitch = WritePitch(p->env, p->pitch1, p->pitch2, 0, 1); | |||||
} | } | ||||
break; | break; | ||||
case phLIQUID: | case phLIQUID: | ||||
if(next->type == phPAUSE) | |||||
{ | |||||
if (next->type == phPAUSE) { | |||||
len += 50; | len += 50; | ||||
final_pitch = WritePitch(p->env,p->pitch1,p->pitch2,0,1); | |||||
final_pitch = WritePitch(p->env, p->pitch1, p->pitch2, 0, 1); | |||||
} | } | ||||
break; | break; | ||||
} | } | ||||
if(!done) | |||||
{ | |||||
if(name2 != 0) | |||||
{ | |||||
if (!done) { | |||||
if (name2 != 0) { | |||||
len1 = (len * len_percent)/100; | len1 = (len * len_percent)/100; | ||||
ptr += sprintf(ptr,"%d\n%s\t",len1,WordToString(name2)); | |||||
ptr += sprintf(ptr, "%d\n%s\t", len1, WordToString(name2)); | |||||
len -= len1; | len -= len1; | ||||
} | } | ||||
ptr += sprintf(ptr,"%d%s\n",len,final_pitch); | |||||
ptr += sprintf(ptr, "%d%s\n", len, final_pitch); | |||||
} | } | ||||
if(pause) | |||||
{ | |||||
len += PauseLength(pause,0); | |||||
ptr += sprintf(ptr,"_ \t%d\n",PauseLength(pause,0)); | |||||
if (pause) { | |||||
len += PauseLength(pause, 0); | |||||
ptr += sprintf(ptr, "_ \t%d\n", PauseLength(pause, 0)); | |||||
pause = 0; | pause = 0; | ||||
} | } | ||||
if(f_mbrola) | |||||
{ | |||||
fwrite(mbr_buf,1,(ptr-mbr_buf),f_mbrola); // write .pho to a file | |||||
} | |||||
else | |||||
{ | |||||
if (f_mbrola) { | |||||
fwrite(mbr_buf, 1, (ptr-mbr_buf), f_mbrola); // write .pho to a file | |||||
} 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 */ | ||||
phix++; | phix++; | ||||
} | } | ||||
if(!f_mbrola) | |||||
{ | |||||
if (!f_mbrola) { | |||||
flush_MBR(); | flush_MBR(); | ||||
// flush the mbrola output buffer | // flush the mbrola output buffer | ||||
{ | { | ||||
FILE *f_mbrola = NULL; | FILE *f_mbrola = NULL; | ||||
if(*n_ph == 0) | |||||
return(0); | |||||
if (*n_ph == 0) | |||||
return (0); | |||||
if(option_phonemes & espeakPHONEMES_MBROLA) | |||||
{ | |||||
if (option_phonemes & espeakPHONEMES_MBROLA) { | |||||
// send mbrola data to a file, not to the mbrola library | // send mbrola data to a file, not to the mbrola library | ||||
f_mbrola = f_trans; | f_mbrola = f_trans; | ||||
} | } | ||||
if (result <= 0) | if (result <= 0) | ||||
return 0; | return 0; | ||||
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) | |||||
value = 0x8000; | value = 0x8000; | ||||
out_ptr[0] = value; | out_ptr[0] = value; | ||||
out_ptr[1] = value >> 8; | out_ptr[1] = value >> 8; | ||||
espeak_ERROR LoadMbrolaTable(const char *mbrola_voice, const char *phtrans, int *srate) | espeak_ERROR LoadMbrolaTable(const char *mbrola_voice, const char *phtrans, int *srate) | ||||
{ | { | ||||
return(EE_INTERNAL_ERROR); | |||||
return (EE_INTERNAL_ERROR); | |||||
} | } | ||||
int MbrolaGenerate(PHONEME_LIST *phoneme_list, int *n_ph, int resume) | int MbrolaGenerate(PHONEME_LIST *phoneme_list, int *n_ph, int resume) | ||||
{ | { | ||||
return(0); | |||||
return (0); | |||||
} | } | ||||
int MbrolaFill(int length, int resume, int amplitude) | int MbrolaFill(int length, int resume, int amplitude) | ||||
{ | { | ||||
return(0); | |||||
return (0); | |||||
} | } | ||||
void MbrolaReset(void) | void MbrolaReset(void) |
int WavegenCloseSound(); | int WavegenCloseSound(); | ||||
int WavegenInitSound(); | int WavegenInitSound(); | ||||
void WavegenInit(int rate, int wavemult_fact); | void WavegenInit(int rate, int wavemult_fact); | ||||
float polint(float xa[],float ya[],int n,float x); | |||||
float polint(float xa[], float ya[], int n, float x); | |||||
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); | ||||
void Write4Bytes(FILE *f, int value); | void Write4Bytes(FILE *f, int value); | ||||
int Read4Bytes(FILE *f); | int Read4Bytes(FILE *f); | ||||
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 | ||||
extern unsigned char *out_end; | extern unsigned char *out_end; | ||||
extern int event_list_ix; | extern int event_list_ix; | ||||
extern espeak_EVENT *event_list; | extern espeak_EVENT *event_list; | ||||
extern t_espeak_callback* synth_callback; | |||||
extern t_espeak_callback *synth_callback; | |||||
extern const char *version_string; | extern const char *version_string; | ||||
extern const int version_phdata; | extern const int version_phdata; | ||||
extern double sonicSpeed; | extern double sonicSpeed; |
{ | { | ||||
#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 CTRL_EMBEDDED 0x01 // control character at the start of an embedded command | ||||
#define REPLACED_E 'E' // 'e' replaced by silent e | #define REPLACED_E 'E' // 'e' replaced by silent e | ||||
// match 1 pre 2 post 0 - use common phoneme string | // match 1 pre 2 post 0 - use common phoneme string | ||||
// match 1 pre 2 post 3 0 - empty phoneme string | // match 1 pre 2 post 3 0 - empty phoneme string | ||||
typedef const char * constcharptr; | |||||
typedef const char *constcharptr; | |||||
typedef struct { | typedef struct { | ||||
int points; | int points; | ||||
typedef struct | |||||
{ | |||||
typedef struct { | |||||
LANGUAGE_OPTIONS langopts; | LANGUAGE_OPTIONS langopts; | ||||
int translator_name; | int translator_name; | ||||
extern wchar_t *p_wchar_input; | extern wchar_t *p_wchar_input; | ||||
extern int dictionary_skipwords; | extern int dictionary_skipwords; | ||||
extern int (* uri_callback)(int, const char *, const char *); | |||||
extern int (* phoneme_callback)(const char *); | |||||
extern int (*uri_callback)(int, const char *, const char *); | |||||
extern int (*phoneme_callback)(const char *); | |||||
extern void SetLengthMods(Translator *tr, int value); | extern void SetLengthMods(Translator *tr, int value); | ||||
void LoadConfig(void); | void LoadConfig(void); | ||||
int utf8_in2(int *c, const char *buf, int backwards); | int utf8_in2(int *c, const char *buf, int backwards); | ||||
int utf8_out(unsigned int c, char *buf); | int utf8_out(unsigned int c, char *buf); | ||||
int utf8_nbytes(const char *buf); | int utf8_nbytes(const char *buf); | ||||
int lookupwchar(const unsigned short *list,int c); | |||||
int lookupwchar2(const unsigned short *list,int c); | |||||
int lookupwchar(const unsigned short *list, int c); | |||||
int lookupwchar2(const unsigned short *list, int c); | |||||
int Eof(void); | int Eof(void); | ||||
char *strchr_w(const char *s, int c); | char *strchr_w(const char *s, int c); | ||||
int IsBracket(int c); | int IsBracket(int c); |
extern int option_device_number; | extern int option_device_number; | ||||
extern int wave_init(int samplerate); | extern int wave_init(int samplerate); | ||||
extern void* wave_open(const char* the_api); | |||||
extern void *wave_open(const char *the_api); | |||||
extern size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSize); | |||||
extern int wave_close(void* theHandler); | |||||
extern void wave_flush(void* theHandler); | |||||
extern int wave_is_busy(void* theHandler); | |||||
extern size_t wave_write(void *theHandler, char *theMono16BitsWaveBuffer, size_t theSize); | |||||
extern int wave_close(void *theHandler); | |||||
extern void wave_flush(void *theHandler); | |||||
extern int wave_is_busy(void *theHandler); | |||||
extern void wave_terminate(); | extern void wave_terminate(); | ||||
extern uint32_t wave_get_read_position(void* theHandler); | |||||
extern uint32_t wave_get_write_position(void* theHandler); | |||||
extern uint32_t wave_get_read_position(void *theHandler); | |||||
extern uint32_t wave_get_write_position(void *theHandler); | |||||
// Supply the remaining time in ms before the sample is played | // Supply the remaining time in ms before the sample is played | ||||
// (or 0 if the event has been already played). | // (or 0 if the event has been already played). | ||||
// time: supplied value in ms | // time: supplied value in ms | ||||
// | // | ||||
// return 0 if ok or -1 otherwise (stream not opened). | // return 0 if ok or -1 otherwise (stream not opened). | ||||
extern int wave_get_remaining_time(uint32_t sample, uint32_t* time); | |||||
extern int wave_get_remaining_time(uint32_t sample, uint32_t *time); | |||||
// set the callback which informs if the output is still enabled. | // set the callback which informs if the output is still enabled. | ||||
// Helpful if a new sample is waiting for free space whereas sound must be stopped. | // Helpful if a new sample is waiting for free space whereas sound must be stopped. | ||||
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 |
#include "wave.h" | #include "wave.h" | ||||
#include "debug.h" | #include "debug.h" | ||||
enum {ONE_BILLION=1000000000}; | |||||
enum { ONE_BILLION = 1000000000 }; | |||||
enum { | enum { | ||||
/* return value */ | /* return value */ | ||||
#ifdef USE_PULSEAUDIO | #ifdef USE_PULSEAUDIO | ||||
static t_wave_callback* my_callback_is_output_enabled=NULL; | |||||
static t_wave_callback *my_callback_is_output_enabled = NULL; | |||||
#define SAMPLE_RATE 22050 | #define SAMPLE_RATE 22050 | ||||
#define ESPEAK_FORMAT PA_SAMPLE_S16LE | #define ESPEAK_FORMAT PA_SAMPLE_S16LE | ||||
SHOW("Connection died: %s\n", context ? pa_strerror(pa_context_errno(context)) : "NULL"); \ | SHOW("Connection died: %s\n", context ? pa_strerror(pa_context_errno(context)) : "NULL"); \ | ||||
goto label; \ | goto label; \ | ||||
} \ | } \ | ||||
} while(0); | |||||
} while (0); | |||||
#define CHECK_CONNECTED(retval) \ | #define CHECK_CONNECTED(retval) \ | ||||
do { \ | do { \ | ||||
ENTER(__FUNCTION__); | ENTER(__FUNCTION__); | ||||
assert(c); | assert(c); | ||||
switch (pa_context_get_state(c)) { | |||||
switch (pa_context_get_state(c)) | |||||
{ | |||||
case PA_CONTEXT_READY: | case PA_CONTEXT_READY: | ||||
case PA_CONTEXT_TERMINATED: | case PA_CONTEXT_TERMINATED: | ||||
case PA_CONTEXT_FAILED: | case PA_CONTEXT_FAILED: | ||||
} | } | ||||
} | } | ||||
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); | ||||
switch (pa_stream_get_state(s)) { | |||||
switch (pa_stream_get_state(s)) | |||||
{ | |||||
case PA_STREAM_READY: | case PA_STREAM_READY: | ||||
case PA_STREAM_FAILED: | case PA_STREAM_FAILED: | ||||
assert(s); | assert(s); | ||||
if (userdata) | if (userdata) | ||||
*(int*) userdata = success; | |||||
*(int *)userdata = success; | |||||
pa_threaded_mainloop_signal(mainloop, 0); | pa_threaded_mainloop_signal(mainloop, 0); | ||||
} | } | ||||
assert(c); | assert(c); | ||||
if (userdata) | if (userdata) | ||||
*(int*) userdata = success; | |||||
*(int *)userdata = success; | |||||
pa_threaded_mainloop_signal(mainloop, 0); | pa_threaded_mainloop_signal(mainloop, 0); | ||||
} | } | ||||
pa_threaded_mainloop_lock(mainloop); | pa_threaded_mainloop_lock(mainloop); | ||||
CHECK_DEAD_GOTO(fail, 1); | CHECK_DEAD_GOTO(fail, 1); | ||||
if ((l = pa_stream_writable_size(stream)) == (size_t) -1) { | |||||
if ((l = pa_stream_writable_size(stream)) == (size_t)-1) { | |||||
SHOW("pa_stream_writable_size() failed: %s", pa_strerror(pa_context_errno(context))); | SHOW("pa_stream_writable_size() failed: %s", pa_strerror(pa_context_errno(context))); | ||||
l = 0; | l = 0; | ||||
goto fail; | goto fail; | ||||
do_trigger = !!l; | do_trigger = !!l; | ||||
SHOW("pulse_free: %d (ret)\n", (int)l); | SHOW("pulse_free: %d (ret)\n", (int)l); | ||||
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) { | ||||
pa_threaded_mainloop_lock(mainloop); | pa_threaded_mainloop_lock(mainloop); | ||||
for (;; ) { | |||||
for (;;) { | |||||
CHECK_DEAD_GOTO(fail, 1); | CHECK_DEAD_GOTO(fail, 1); | ||||
if ((i = pa_stream_get_timing_info(stream))) | |||||
{ | |||||
if ((i = pa_stream_get_timing_info(stream))) { | |||||
break; | break; | ||||
} | } | ||||
if (pa_context_errno(context) != PA_ERR_NODATA) { | if (pa_context_errno(context) != PA_ERR_NODATA) { | ||||
} | } | ||||
r = i->playing; | r = i->playing; | ||||
memcpy((void*)the_timing_info, (void*)i, sizeof(pa_timing_info)); | |||||
memcpy((void *)the_timing_info, (void *)i, sizeof(pa_timing_info)); | |||||
fail: | fail: | ||||
pa_threaded_mainloop_unlock(mainloop); | pa_threaded_mainloop_unlock(mainloop); | ||||
return r; | return r; | ||||
} | } | ||||
static void pulse_write(void* ptr, int length) { | |||||
static void pulse_write(void *ptr, int length) { | |||||
ENTER(__FUNCTION__); | ENTER(__FUNCTION__); | ||||
if (!success) { | if (!success) { | ||||
SHOW("pa_stream_drain() failed: %s\n", pa_strerror(pa_context_errno(context))); | SHOW("pa_stream_drain() failed: %s\n", pa_strerror(pa_context_errno(context))); | ||||
} | |||||
else { | |||||
} else { | |||||
ret = PULSE_OK; | ret = PULSE_OK; | ||||
} | } | ||||
assert(!stream); | assert(!stream); | ||||
assert(!connected); | assert(!connected); | ||||
pthread_mutex_init( &pulse_mutex, (const pthread_mutexattr_t *)NULL); | |||||
pthread_mutex_init(&pulse_mutex, (const pthread_mutexattr_t *)NULL); | |||||
ss.format = ESPEAK_FORMAT; | ss.format = ESPEAK_FORMAT; | ||||
ss.rate = wave_samplerate; | ss.rate = wave_samplerate; | ||||
SHOW_TIME("pa_threaded_mainloop_new (call)"); | SHOW_TIME("pa_threaded_mainloop_new (call)"); | ||||
if (!(mainloop = pa_threaded_mainloop_new())) { | if (!(mainloop = pa_threaded_mainloop_new())) { | ||||
SHOW("Failed to allocate main loop\n",""); | |||||
SHOW("Failed to allocate main loop\n", ""); | |||||
goto fail; | goto fail; | ||||
} | } | ||||
SHOW_TIME("pa_context_new (call)"); | SHOW_TIME("pa_context_new (call)"); | ||||
if (!(context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "eSpeak"))) { | if (!(context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "eSpeak"))) { | ||||
SHOW("Failed to allocate context\n",""); | |||||
SHOW("Failed to allocate context\n", ""); | |||||
goto unlock_and_fail; | goto unlock_and_fail; | ||||
} | } | ||||
SHOW_TIME("pa_threaded_mainloop_start (call)"); | SHOW_TIME("pa_threaded_mainloop_start (call)"); | ||||
if (pa_threaded_mainloop_start(mainloop) < 0) { | if (pa_threaded_mainloop_start(mainloop) < 0) { | ||||
SHOW("Failed to start main loop",""); | |||||
SHOW("Failed to start main loop", ""); | |||||
goto unlock_and_fail; | goto unlock_and_fail; | ||||
} | } | ||||
pa_threaded_mainloop_free(mainloop); | pa_threaded_mainloop_free(mainloop); | ||||
mainloop = NULL; | mainloop = NULL; | ||||
} | } | ||||
} | |||||
else { | |||||
} else { | |||||
pulse_close(); | pulse_close(); | ||||
} | } | ||||
} | } | ||||
void wave_flush(void* theHandler) | |||||
void wave_flush(void *theHandler) | |||||
{ | { | ||||
ENTER("wave_flush"); | ENTER("wave_flush"); | ||||
} | } | ||||
void wave_set_callback_is_output_enabled(t_wave_callback* cb) | |||||
void wave_set_callback_is_output_enabled(t_wave_callback *cb) | |||||
{ | { | ||||
my_callback_is_output_enabled = cb; | my_callback_is_output_enabled = cb; | ||||
} | } | ||||
return pulse_open() == PULSE_OK; | return pulse_open() == PULSE_OK; | ||||
} | } | ||||
void* wave_open(const char* the_api) | |||||
void *wave_open(const char *the_api) | |||||
{ | { | ||||
ENTER("wave_open"); | ENTER("wave_open"); | ||||
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) | |||||
{ | { | ||||
ENTER("wave_write"); | ENTER("wave_write"); | ||||
size_t bytes_to_write = theSize; | size_t bytes_to_write = theSize; | ||||
char* aBuffer=theMono16BitsWaveBuffer; | |||||
char *aBuffer = theMono16BitsWaveBuffer; | |||||
assert(stream); | assert(stream); | ||||
size_t aTotalFreeMem=0; | |||||
size_t aTotalFreeMem = 0; | |||||
pthread_mutex_lock(&pulse_mutex); | pthread_mutex_lock(&pulse_mutex); | ||||
while (1) | |||||
{ | |||||
while (1) { | |||||
if (my_callback_is_output_enabled | if (my_callback_is_output_enabled | ||||
&& (0==my_callback_is_output_enabled())) | |||||
{ | |||||
&& (0 == my_callback_is_output_enabled())) { | |||||
SHOW_TIME("wave_write > my_callback_is_output_enabled: no!"); | SHOW_TIME("wave_write > my_callback_is_output_enabled: no!"); | ||||
theSize=0; | |||||
theSize = 0; | |||||
goto terminate; | goto terminate; | ||||
} | } | ||||
aTotalFreeMem = pulse_free(); | aTotalFreeMem = pulse_free(); | ||||
if (aTotalFreeMem >= bytes_to_write) | |||||
{ | |||||
if (aTotalFreeMem >= bytes_to_write) { | |||||
SHOW("wave_write > aTotalFreeMem(%d) >= bytes_to_write(%d)\n", aTotalFreeMem, bytes_to_write); | SHOW("wave_write > aTotalFreeMem(%d) >= bytes_to_write(%d)\n", aTotalFreeMem, bytes_to_write); | ||||
break; | break; | ||||
} | } | ||||
// TBD: check if really helpful | // TBD: check if really helpful | ||||
if (aTotalFreeMem >= MAXLENGTH*2) | |||||
{ | |||||
if (aTotalFreeMem >= MAXLENGTH*2) { | |||||
aTotalFreeMem = MAXLENGTH*2; | aTotalFreeMem = MAXLENGTH*2; | ||||
} | } | ||||
SHOW("wave_write > wait: aTotalFreeMem(%d) < bytes_to_write(%d)\n", aTotalFreeMem, bytes_to_write); | SHOW("wave_write > wait: aTotalFreeMem(%d) < bytes_to_write(%d)\n", aTotalFreeMem, bytes_to_write); | ||||
// 500: threshold for avoiding too many calls to pulse_write | // 500: threshold for avoiding too many calls to pulse_write | ||||
if (aTotalFreeMem>500) | |||||
{ | |||||
if (aTotalFreeMem > 500) { | |||||
pulse_write(aBuffer, aTotalFreeMem); | pulse_write(aBuffer, aTotalFreeMem); | ||||
bytes_to_write -= aTotalFreeMem; | bytes_to_write -= aTotalFreeMem; | ||||
aBuffer += aTotalFreeMem; | aBuffer += aTotalFreeMem; | ||||
return theSize; | return theSize; | ||||
} | } | ||||
int wave_close(void* theHandler) | |||||
int wave_close(void *theHandler) | |||||
{ | { | ||||
SHOW_TIME("wave_close > ENTER"); | SHOW_TIME("wave_close > ENTER"); | ||||
static int aStopStreamCount = 0; | static int aStopStreamCount = 0; | ||||
// Avoid race condition by making sure this function only | // Avoid race condition by making sure this function only | ||||
// gets called once at a time | // gets called once at a time | ||||
aStopStreamCount++; | aStopStreamCount++; | ||||
if (aStopStreamCount != 1) | |||||
{ | |||||
if (aStopStreamCount != 1) { | |||||
SHOW_TIME("wave_close > LEAVE (stopStreamCount)"); | SHOW_TIME("wave_close > LEAVE (stopStreamCount)"); | ||||
return 0; | return 0; | ||||
} | } | ||||
int a_status = pthread_mutex_lock(&pulse_mutex); | int a_status = pthread_mutex_lock(&pulse_mutex); | ||||
if (a_status) | |||||
{ | |||||
if (a_status) { | |||||
SHOW("Error: pulse_mutex lock=%d (%s)\n", a_status, __FUNCTION__); | SHOW("Error: pulse_mutex lock=%d (%s)\n", a_status, __FUNCTION__); | ||||
aStopStreamCount = 0; // last action | aStopStreamCount = 0; // last action | ||||
return PULSE_ERROR; | return PULSE_ERROR; | ||||
return PULSE_OK; | return PULSE_OK; | ||||
} | } | ||||
int wave_is_busy(void* theHandler) | |||||
int wave_is_busy(void *theHandler) | |||||
{ | { | ||||
SHOW_TIME("wave_is_busy"); | SHOW_TIME("wave_is_busy"); | ||||
pa_timing_info a_timing_info; | pa_timing_info a_timing_info; | ||||
int active = pulse_playing(&a_timing_info); | int active = pulse_playing(&a_timing_info); | ||||
SHOW("wave_is_busy: %d\n",active); | |||||
SHOW("wave_is_busy: %d\n", active); | |||||
return active; | return active; | ||||
} | } | ||||
ENTER("wave_terminate"); | ENTER("wave_terminate"); | ||||
int a_status; | int a_status; | ||||
pthread_mutex_t* a_mutex = NULL; | |||||
pthread_mutex_t *a_mutex = NULL; | |||||
a_mutex = &pulse_mutex; | a_mutex = &pulse_mutex; | ||||
a_status = pthread_mutex_lock(a_mutex); | a_status = pthread_mutex_lock(a_mutex); | ||||
pthread_mutex_destroy(a_mutex); | pthread_mutex_destroy(a_mutex); | ||||
} | } | ||||
uint32_t wave_get_read_position(void* theHandler) | |||||
uint32_t wave_get_read_position(void *theHandler) | |||||
{ | { | ||||
pa_timing_info a_timing_info; | pa_timing_info a_timing_info; | ||||
pulse_playing(&a_timing_info); | pulse_playing(&a_timing_info); | ||||
return a_timing_info.read_index; | return a_timing_info.read_index; | ||||
} | } | ||||
uint32_t wave_get_write_position(void* theHandler) | |||||
uint32_t wave_get_write_position(void *theHandler) | |||||
{ | { | ||||
pa_timing_info a_timing_info; | pa_timing_info a_timing_info; | ||||
pulse_playing(&a_timing_info); | pulse_playing(&a_timing_info); | ||||
return a_timing_info.write_index; | return a_timing_info.write_index; | ||||
} | } | ||||
int wave_get_remaining_time(uint32_t sample, uint32_t* time) | |||||
int wave_get_remaining_time(uint32_t sample, uint32_t *time) | |||||
{ | { | ||||
double a_time=0; | |||||
double a_time = 0; | |||||
if (!time || !stream) | |||||
{ | |||||
SHOW("event get_remaining_time> %s\n","audio device not available"); | |||||
if (!time || !stream) { | |||||
SHOW("event get_remaining_time> %s\n", "audio device not available"); | |||||
return -1; | return -1; | ||||
} | } | ||||
pa_timing_info a_timing_info; | pa_timing_info a_timing_info; | ||||
pulse_playing(&a_timing_info); | pulse_playing(&a_timing_info); | ||||
if (sample > a_timing_info.read_index) | |||||
{ | |||||
if (sample > a_timing_info.read_index) { | |||||
// 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; | ||||
} | } | ||||
int wave_init(return 1; ) { | int wave_init(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; | ||||
} | } | ||||
int wave_get_remaining_time(uint32_t sample, uint32_t* time) | |||||
int wave_get_remaining_time(uint32_t sample, uint32_t *time) | |||||
{ | { | ||||
if (!time) return(-1); | |||||
if (!time) return (-1); | |||||
*time = (uint32_t)0; | *time = (uint32_t)0; | ||||
return 0; | return 0; | ||||
} | } | ||||
{ | { | ||||
struct timeval tv; | struct timeval tv; | ||||
if (!ts) | |||||
{ | |||||
if (!ts) { | |||||
return; | return; | ||||
} | } | ||||
assert (gettimeofday(&tv, NULL) != -1); | |||||
assert(gettimeofday(&tv, NULL) != -1); | |||||
ts->tv_sec = tv.tv_sec; | ts->tv_sec = tv.tv_sec; | ||||
ts->tv_nsec = tv.tv_usec*1000; | ts->tv_nsec = tv.tv_usec*1000; | ||||
} | } | ||||
void add_time_in_ms(struct timespec *ts, int time_in_ms) | void add_time_in_ms(struct timespec *ts, int time_in_ms) | ||||
{ | { | ||||
if (!ts) | |||||
{ | |||||
if (!ts) { | |||||
return; | return; | ||||
} | } | ||||
uint64_t t_ns = (uint64_t)ts->tv_nsec + 1000000 * (uint64_t)time_in_ms; | uint64_t t_ns = (uint64_t)ts->tv_nsec + 1000000 * (uint64_t)time_in_ms; | ||||
while(t_ns >= ONE_BILLION) | |||||
{ | |||||
while (t_ns >= ONE_BILLION) { | |||||
SHOW("event > add_time_in_ms ns: %d sec %Lu nsec \n", ts->tv_sec, t_ns); | SHOW("event > add_time_in_ms ns: %d sec %Lu nsec \n", ts->tv_sec, t_ns); | ||||
ts->tv_sec += 1; | ts->tv_sec += 1; | ||||
t_ns -= ONE_BILLION; | t_ns -= ONE_BILLION; |
#include "wave.h" | #include "wave.h" | ||||
#include "debug.h" | #include "debug.h" | ||||
enum {ONE_BILLION=1000000000}; | |||||
enum { ONE_BILLION = 1000000000 }; | |||||
#define SAMPLE_RATE 22050 | #define SAMPLE_RATE 22050 | ||||
#define SAMPLE_SIZE 16 | #define SAMPLE_SIZE 16 | ||||
#ifdef USE_SADA | #ifdef USE_SADA | ||||
static t_wave_callback* my_callback_is_output_enabled=NULL; | |||||
static t_wave_callback *my_callback_is_output_enabled = NULL; | |||||
static const char *sun_audio_device = "/dev/audio"; | static const char *sun_audio_device = "/dev/audio"; | ||||
static int sun_audio_fd = -1; | static int sun_audio_fd = -1; | ||||
// The last known playing index after a call to wave_close. | // The last known playing index after a call to wave_close. | ||||
// | // | ||||
static uint32_t last_play_position=0; | |||||
static uint32_t last_play_position = 0; | |||||
static uint32_t wave_samplerate; | static uint32_t wave_samplerate; | ||||
SHOW("wave_init() sun_audio_fd: %d\n", sun_audio_fd); | SHOW("wave_init() sun_audio_fd: %d\n", sun_audio_fd); | ||||
if (sun_audio_fd < 0) { | if (sun_audio_fd < 0) { | ||||
return(0); | |||||
return (0); | |||||
} | } | ||||
ioctl(sun_audio_fd, AUDIO_GETINFO, &ainfo); | ioctl(sun_audio_fd, AUDIO_GETINFO, &ainfo); | ||||
if (ioctl(sun_audio_fd, AUDIO_SETINFO, &ainfo) == -1) { | if (ioctl(sun_audio_fd, AUDIO_SETINFO, &ainfo) == -1) { | ||||
SHOW("wave_init() failed to set audio params: %s\n", strerror(errno)); | SHOW("wave_init() failed to set audio params: %s\n", strerror(errno)); | ||||
close(sun_audio_fd); | close(sun_audio_fd); | ||||
return(0); | |||||
return (0); | |||||
} | } | ||||
return(1); | |||||
return (1); | |||||
} | } | ||||
// wave_open | // wave_open | ||||
// sun_audio_fd opened in wave_init, which is passed in as theHandler | // sun_audio_fd opened in wave_init, which is passed in as theHandler | ||||
// parameter in all other methods | // parameter in all other methods | ||||
// | // | ||||
void* wave_open(const char* the_api) | |||||
void *wave_open(const char *the_api) | |||||
{ | { | ||||
ENTER("wave_open"); | ENTER("wave_open"); | ||||
return((void*) sun_audio_fd); | |||||
return ((void *)sun_audio_fd); | |||||
} | } | ||||
// wave_write | // wave_write | ||||
// | // | ||||
// the number of bytes (not 16-bit samples) sent | // the number of bytes (not 16-bit samples) sent | ||||
// | // | ||||
size_t wave_write(void* theHandler, | |||||
char* theMono16BitsWaveBuffer, | |||||
size_t wave_write(void *theHandler, | |||||
char *theMono16BitsWaveBuffer, | |||||
size_t theSize) | size_t theSize) | ||||
{ | { | ||||
size_t num; | size_t num; | ||||
ENTER("wave_write"); | ENTER("wave_write"); | ||||
if (my_callback_is_output_enabled && (0==my_callback_is_output_enabled())) { | |||||
if (my_callback_is_output_enabled && (0 == my_callback_is_output_enabled())) { | |||||
SHOW_TIME("wave_write > my_callback_is_output_enabled: no!"); | SHOW_TIME("wave_write > my_callback_is_output_enabled: no!"); | ||||
return 0; | return 0; | ||||
} | } | ||||
char *out_end; | char *out_end; | ||||
out_ptr = (char *)theMono16BitsWaveBuffer; | out_ptr = (char *)theMono16BitsWaveBuffer; | ||||
out_end = out_ptr + theSize; | out_end = out_ptr + theSize; | ||||
while(out_ptr < out_end) | |||||
{ | |||||
while (out_ptr < out_end) { | |||||
c = out_ptr[0]; | c = out_ptr[0]; | ||||
out_ptr[0] = out_ptr[1]; | out_ptr[0] = out_ptr[1]; | ||||
out_ptr[1] = c; | out_ptr[1] = c; | ||||
} | } | ||||
#endif | #endif | ||||
num = write((int) theHandler, theMono16BitsWaveBuffer, theSize); | |||||
num = write((int)theHandler, theMono16BitsWaveBuffer, theSize); | |||||
// Keep track of the total number of samples sent -- we use this in | // Keep track of the total number of samples sent -- we use this in | ||||
// wave_get_read_position and also use it to help calculate the | // wave_get_read_position and also use it to help calculate the | ||||
// | // | ||||
// The result of the ioctl call (non-0 means failure) | // The result of the ioctl call (non-0 means failure) | ||||
// | // | ||||
int wave_close(void* theHandler) | |||||
int wave_close(void *theHandler) | |||||
{ | { | ||||
int ret; | int ret; | ||||
audio_info_t ainfo; | audio_info_t ainfo; | ||||
int audio_fd = (int) theHandler; | |||||
int audio_fd = (int)theHandler; | |||||
if (!audio_fd) { | if (!audio_fd) { | ||||
audio_fd = sun_audio_fd; | audio_fd = sun_audio_fd; | ||||
} | } | ||||
// | // | ||||
// A non-0 value if audio is being played | // A non-0 value if audio is being played | ||||
// | // | ||||
int wave_is_busy(void* theHandler) | |||||
int wave_is_busy(void *theHandler) | |||||
{ | { | ||||
uint32_t time; | uint32_t time; | ||||
if (total_samples_sent >= 1) { | if (total_samples_sent >= 1) { | ||||
// | // | ||||
// theHandler: the audio device file descriptor | // theHandler: the audio device file descriptor | ||||
// | // | ||||
void wave_flush(void* theHandler) | |||||
void wave_flush(void *theHandler) | |||||
{ | { | ||||
ENTER("wave_flush"); | ENTER("wave_flush"); | ||||
SHOW_TIME("wave_flush > LEAVE"); | SHOW_TIME("wave_flush > LEAVE"); | ||||
// | // | ||||
// cb: the callback to call from wave_write | // cb: the callback to call from wave_write | ||||
// | // | ||||
void wave_set_callback_is_output_enabled(t_wave_callback* cb) | |||||
void wave_set_callback_is_output_enabled(t_wave_callback *cb) | |||||
{ | { | ||||
my_callback_is_output_enabled = cb; | my_callback_is_output_enabled = cb; | ||||
} | } | ||||
// The total number of 16-bit samples played by the audio system | // The total number of 16-bit samples played by the audio system | ||||
// so far. | // so far. | ||||
// | // | ||||
uint32_t wave_get_read_position(void* theHandler) | |||||
uint32_t wave_get_read_position(void *theHandler) | |||||
{ | { | ||||
audio_info_t ainfo; | audio_info_t ainfo; | ||||
ENTER("wave_get_read_position"); | ENTER("wave_get_read_position"); | ||||
ioctl((int) theHandler, AUDIO_GETINFO, &ainfo); | |||||
ioctl((int)theHandler, AUDIO_GETINFO, &ainfo); | |||||
SHOW("wave_get_read_position: %d\n", ainfo.play.samples); | SHOW("wave_get_read_position: %d\n", ainfo.play.samples); | ||||
SHOW_TIME("wave_get_read_position > LEAVE"); | SHOW_TIME("wave_get_read_position > LEAVE"); | ||||
return ainfo.play.samples; | return ainfo.play.samples; | ||||
// the index wraps back to 0. We don't handle that wrapping, so | // the index wraps back to 0. We don't handle that wrapping, so | ||||
// the behavior after 54 hours of play time is undefined.]]] | // the behavior after 54 hours of play time is undefined.]]] | ||||
// | // | ||||
uint32_t wave_get_write_position(void* theHandler) | |||||
uint32_t wave_get_write_position(void *theHandler) | |||||
{ | { | ||||
ENTER("wave_get_write_position"); | ENTER("wave_get_write_position"); | ||||
SHOW("wave_get_write_position: %d\n", total_samples_sent); | SHOW("wave_get_write_position: %d\n", total_samples_sent); | ||||
// Time in milliseconds before the sample is played or 0 if the sample | // Time in milliseconds before the sample is played or 0 if the sample | ||||
// is currently playing or has already been played. | // is currently playing or has already been played. | ||||
// | // | ||||
int wave_get_remaining_time(uint32_t sample, uint32_t* time) | |||||
int wave_get_remaining_time(uint32_t sample, uint32_t *time) | |||||
{ | { | ||||
uint32_t a_time=0; | |||||
uint32_t a_time = 0; | |||||
uint32_t actual_index; | uint32_t actual_index; | ||||
audio_info_t ainfo; | audio_info_t ainfo; | ||||
ENTER("wave_get_remaining_time"); | ENTER("wave_get_remaining_time"); | ||||
if (!time) { | if (!time) { | ||||
return(-1); | |||||
return (-1); | |||||
SHOW_TIME("wave_get_remaining_time > LEAVE"); | SHOW_TIME("wave_get_remaining_time > LEAVE"); | ||||
} | } | ||||
*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; | |||||
} | } | ||||
SHOW("wave_get_remaining_time for %d: %d\n", sample, *time); | SHOW("wave_get_remaining_time for %d: %d\n", sample, *time); | ||||
SHOW_TIME("wave_get_remaining_time > LEAVE"); | SHOW_TIME("wave_get_remaining_time > LEAVE"); | ||||
init wave_init() { | init wave_init() { | ||||
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; | ||||
} | } | ||||
int wave_get_remaining_time(uint32_t sample, uint32_t* time) | |||||
int wave_get_remaining_time(uint32_t sample, uint32_t *time) | |||||
{ | { | ||||
if (!time) return(-1); | |||||
if (!time) return (-1); | |||||
*time = (uint32_t)0; | *time = (uint32_t)0; | ||||
return 0; | return 0; | ||||
} | } | ||||
{ | { | ||||
struct timeval tv; | struct timeval tv; | ||||
if (!ts) | |||||
{ | |||||
if (!ts) { | |||||
return; | return; | ||||
} | } | ||||
assert (gettimeofday(&tv, NULL) != -1); | |||||
assert(gettimeofday(&tv, NULL) != -1); | |||||
ts->tv_sec = tv.tv_sec; | ts->tv_sec = tv.tv_sec; | ||||
ts->tv_nsec = tv.tv_usec*1000; | ts->tv_nsec = tv.tv_usec*1000; | ||||
} | } | ||||
void add_time_in_ms(struct timespec *ts, int time_in_ms) | void add_time_in_ms(struct timespec *ts, int time_in_ms) | ||||
{ | { | ||||
if (!ts) | |||||
{ | |||||
if (!ts) { | |||||
return; | return; | ||||
} | } | ||||
uint64_t t_ns = (uint64_t)ts->tv_nsec + 1000000 * (uint64_t)time_in_ms; | uint64_t t_ns = (uint64_t)ts->tv_nsec + 1000000 * (uint64_t)time_in_ms; | ||||
while(t_ns >= ONE_BILLION) | |||||
{ | |||||
while (t_ns >= ONE_BILLION) { | |||||
SHOW("event > add_time_in_ms ns: %d sec %Lu nsec \n", ts->tv_sec, t_ns); | SHOW("event > add_time_in_ms ns: %d sec %Lu nsec \n", ts->tv_sec, t_ns); | ||||
ts->tv_sec += 1; | ts->tv_sec += 1; | ||||
t_ns -= ONE_BILLION; | t_ns -= ONE_BILLION; |
const espeak_VOICE **voices; | const espeak_VOICE **voices; | ||||
espeak_VOICE voice_select; | espeak_VOICE voice_select; | ||||
static char genders[4] = {'-','M','F','-'}; | |||||
static char genders[4] = { '-', 'M', 'F', '-' }; | |||||
if((language != NULL) && (language[0] != 0)) | |||||
{ | |||||
if ((language != NULL) && (language[0] != 0)) { | |||||
// display only voices for the specified language, in order of priority | // display only voices for the specified language, in order of priority | ||||
voice_select.languages = language; | voice_select.languages = language; | ||||
voice_select.age = 0; | voice_select.age = 0; | ||||
voice_select.gender = 0; | voice_select.gender = 0; | ||||
voice_select.name = NULL; | voice_select.name = NULL; | ||||
voices = espeak_ListVoices(&voice_select); | voices = espeak_ListVoices(&voice_select); | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
voices = espeak_ListVoices(NULL); | voices = espeak_ListVoices(NULL); | ||||
} | } | ||||
fprintf(f_out,"Pty Language Age/Gender VoiceName File Other Languages\n"); | |||||
fprintf(f_out, "Pty Language Age/Gender VoiceName File Other Languages\n"); | |||||
for(ix=0; (v = voices[ix]) != NULL; ix++) | |||||
{ | |||||
for (ix = 0; (v = voices[ix]) != NULL; ix++) { | |||||
count = 0; | count = 0; | ||||
p = v->languages; | p = v->languages; | ||||
while(*p != 0) | |||||
{ | |||||
while (*p != 0) { | |||||
len = strlen(p+1); | len = strlen(p+1); | ||||
lang_name = p+1; | lang_name = p+1; | ||||
if(v->age == 0) | |||||
strcpy(age_buf," "); | |||||
if (v->age == 0) | |||||
strcpy(age_buf, " "); | |||||
else | else | ||||
sprintf(age_buf,"%3d",v->age); | |||||
sprintf(age_buf, "%3d", v->age); | |||||
if(count==0) | |||||
{ | |||||
for(j=0; j < sizeof(buf); j++) | |||||
{ | |||||
if (count == 0) { | |||||
for (j = 0; j < sizeof(buf); j++) { | |||||
// replace spaces in the name | // replace spaces in the name | ||||
if((c = v->name[j]) == ' ') | |||||
if ((c = v->name[j]) == ' ') | |||||
c = '_'; | c = '_'; | ||||
if((buf[j] = c) == 0) | |||||
if ((buf[j] = c) == 0) | |||||
break; | break; | ||||
} | } | ||||
fprintf(f_out,"%2d %-12s%s%c %-20s %-13s ", | |||||
p[0],lang_name,age_buf,genders[v->gender],buf,v->identifier); | |||||
} | |||||
else | |||||
{ | |||||
fprintf(f_out,"(%s %d)",lang_name,p[0]); | |||||
fprintf(f_out, "%2d %-12s%s%c %-20s %-13s ", | |||||
p[0], lang_name, age_buf, genders[v->gender], buf, v->identifier); | |||||
} else { | |||||
fprintf(f_out, "(%s %d)", lang_name, p[0]); | |||||
} | } | ||||
count++; | count++; | ||||
p += len+2; | p += len+2; | ||||
} | } | ||||
fputc('\n',f_out); | |||||
fputc('\n', f_out); | |||||
} | } | ||||
} | } | ||||
// Set the length of 0x7ffff000 for --stdout | // Set the length of 0x7ffff000 for --stdout | ||||
// This will be changed to the correct length for -w (write to file) | // This will be changed to the correct length for -w (write to file) | ||||
static unsigned char wave_hdr[44] = { | static unsigned char wave_hdr[44] = { | ||||
'R','I','F','F',0x24,0xf0,0xff,0x7f,'W','A','V','E','f','m','t',' ', | |||||
0x10,0,0,0,1,0,1,0, 9,0x3d,0,0,0x12,0x7a,0,0, | |||||
2,0,0x10,0,'d','a','t','a', 0x00,0xf0,0xff,0x7f | |||||
'R', 'I', 'F', 'F', 0x24, 0xf0, 0xff, 0x7f, 'W', 'A', 'V', 'E', 'f', 'm', 't', ' ', | |||||
0x10, 0, 0, 0, 1, 0, 1, 0, 9, 0x3d, 0, 0, 0x12, 0x7a, 0, 0, | |||||
2, 0, 0x10, 0, 'd', 'a', 't', 'a', 0x00, 0xf0, 0xff, 0x7f | |||||
}; | }; | ||||
if(path == NULL) | |||||
return(2); | |||||
if (path == NULL) | |||||
return (2); | |||||
while(isspace(*path)) path++; | |||||
while (isspace(*path)) path++; | |||||
f_wave = NULL; | f_wave = NULL; | ||||
if(path[0] != 0) | |||||
{ | |||||
if(strcmp(path,"stdout")==0) | |||||
{ | |||||
if (path[0] != 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; | ||||
} | |||||
else | |||||
f_wave = fopen(path,"wb"); | |||||
} else | |||||
f_wave = fopen(path, "wb"); | |||||
} | } | ||||
if(f_wave != NULL) | |||||
{ | |||||
fwrite(wave_hdr,1,24,f_wave); | |||||
Write4Bytes(f_wave,rate); | |||||
Write4Bytes(f_wave,rate * 2); | |||||
fwrite(&wave_hdr[32],1,12,f_wave); | |||||
return(0); | |||||
if (f_wave != NULL) { | |||||
fwrite(wave_hdr, 1, 24, f_wave); | |||||
Write4Bytes(f_wave, rate); | |||||
Write4Bytes(f_wave, rate * 2); | |||||
fwrite(&wave_hdr[32], 1, 12, f_wave); | |||||
return (0); | |||||
} | } | ||||
return(1); | |||||
return (1); | |||||
} | } | ||||
{ | { | ||||
unsigned int pos; | unsigned int pos; | ||||
if((f_wave == NULL) || (f_wave == stdout)) | |||||
if ((f_wave == NULL) || (f_wave == stdout)) | |||||
return; | return; | ||||
fflush(f_wave); | fflush(f_wave); | ||||
pos = ftell(f_wave); | pos = ftell(f_wave); | ||||
fseek(f_wave,4,SEEK_SET); | |||||
Write4Bytes(f_wave,pos - 8); | |||||
fseek(f_wave, 4, SEEK_SET); | |||||
Write4Bytes(f_wave, pos - 8); | |||||
fseek(f_wave,40,SEEK_SET); | |||||
Write4Bytes(f_wave,pos - 44); | |||||
fseek(f_wave, 40, SEEK_SET); | |||||
Write4Bytes(f_wave, pos - 44); | |||||
fclose(f_wave); | fclose(f_wave); | ||||
finished = WavegenFill(0); | finished = WavegenFill(0); | ||||
if(quiet) | |||||
return(finished); | |||||
if (quiet) | |||||
return (finished); | |||||
if(f_wave == NULL) | |||||
{ | |||||
sprintf(fname,"%s_%.2d%s",wavefile,++wavefile_count,filetype); | |||||
if(OpenWaveFile(fname, samplerate) != 0) | |||||
return(1); | |||||
if (f_wave == NULL) { | |||||
sprintf(fname, "%s_%.2d%s", wavefile, ++wavefile_count, filetype); | |||||
if (OpenWaveFile(fname, samplerate) != 0) | |||||
return (1); | |||||
} | } | ||||
if(end_of_sentence) | |||||
{ | |||||
if (end_of_sentence) { | |||||
end_of_sentence = 0; | end_of_sentence = 0; | ||||
if((samples_split > 0 ) && (samples_total > samples_split)) | |||||
{ | |||||
if ((samples_split > 0 ) && (samples_total > samples_split)) { | |||||
CloseWaveFile(); | CloseWaveFile(); | ||||
samples_total = 0; | samples_total = 0; | ||||
} | } | ||||
} | } | ||||
if(f_wave != NULL) | |||||
{ | |||||
if (f_wave != NULL) { | |||||
samples_total += (out_ptr - wav_outbuf)/2; | samples_total += (out_ptr - wav_outbuf)/2; | ||||
fwrite(wav_outbuf, 1, out_ptr - wav_outbuf, f_wave); | fwrite(wav_outbuf, 1, out_ptr - wav_outbuf, f_wave); | ||||
} | } | ||||
return(finished); | |||||
return (finished); | |||||
} | } | ||||
static void init_path(char *argv0, char *path_specified) | static void init_path(char *argv0, char *path_specified) | ||||
{ | { | ||||
if(path_specified) | |||||
{ | |||||
sprintf(path_home,"%s/espeak-data",path_specified); | |||||
if (path_specified) { | |||||
sprintf(path_home, "%s/espeak-data", path_specified); | |||||
return; | return; | ||||
} | } | ||||
char *env; | char *env; | ||||
unsigned char buf[sizeof(path_home)-12]; | unsigned char buf[sizeof(path_home)-12]; | ||||
if(((env = getenv("ESPEAK_DATA_PATH")) != NULL) && ((strlen(env)+12) < sizeof(path_home))) | |||||
{ | |||||
sprintf(path_home,"%s\\espeak-data",env); | |||||
if(GetFileLength(path_home) == -2) | |||||
if (((env = getenv("ESPEAK_DATA_PATH")) != NULL) && ((strlen(env)+12) < sizeof(path_home))) { | |||||
sprintf(path_home, "%s\\espeak-data", env); | |||||
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); | |||||
if((p = strrchr(path_home,'\\')) != NULL) | |||||
{ | |||||
strcpy(&p[1],"espeak-data"); | |||||
if(GetFileLength(path_home) == -2) | |||||
strcpy(path_home, argv0); | |||||
if ((p = strrchr(path_home, '\\')) != NULL) { | |||||
strcpy(&p[1], "espeak-data"); | |||||
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 | ||||
} | } | ||||
var_type = REG_SZ; | var_type = REG_SZ; | ||||
RegQueryValueEx(RegKey, "path", 0, &var_type, buf, &size); | RegQueryValueEx(RegKey, "path", 0, &var_type, buf, &size); | ||||
sprintf(path_home,"%s\\espeak-data",buf); | |||||
sprintf(path_home, "%s\\espeak-data", buf); | |||||
#else | #else | ||||
#ifdef PLATFORM_DOS | #ifdef PLATFORM_DOS | ||||
strcpy(path_home,PATH_ESPEAK_DATA); | |||||
strcpy(path_home, PATH_ESPEAK_DATA); | |||||
#else | #else | ||||
char *env; | char *env; | ||||
if((env = getenv("ESPEAK_DATA_PATH")) != NULL) | |||||
{ | |||||
snprintf(path_home,sizeof(path_home),"%s/espeak-data",env); | |||||
if(GetFileLength(path_home) == -2) | |||||
if ((env = getenv("ESPEAK_DATA_PATH")) != NULL) { | |||||
snprintf(path_home, sizeof(path_home), "%s/espeak-data", env); | |||||
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")); | |||||
if(access(path_home,R_OK) != 0) | |||||
{ | |||||
strcpy(path_home,PATH_ESPEAK_DATA); | |||||
snprintf(path_home, sizeof(path_home), "%s/espeak-data", getenv("HOME")); | |||||
if (access(path_home, R_OK) != 0) { | |||||
strcpy(path_home, PATH_ESPEAK_DATA); | |||||
} | } | ||||
#endif | #endif | ||||
#endif | #endif | ||||
// 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 | ||||
// other characters give the correct results with iswalpha() etc. | // other characters give the correct results with iswalpha() etc. | ||||
#ifdef PLATFORM_RISCOS | #ifdef PLATFORM_RISCOS | ||||
setlocale(LC_CTYPE,"ISO8859-1"); | |||||
setlocale(LC_CTYPE, "ISO8859-1"); | |||||
#else | #else | ||||
if(setlocale(LC_CTYPE,"en_US.UTF-8") == NULL) | |||||
{ | |||||
if(setlocale(LC_CTYPE,"UTF-8") == NULL) | |||||
setlocale(LC_CTYPE,""); | |||||
if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL) { | |||||
if (setlocale(LC_CTYPE, "UTF-8") == NULL) | |||||
setlocale(LC_CTYPE, ""); | |||||
} | } | ||||
#endif | #endif | ||||
if((result = LoadPhData(&srate)) != 1) | |||||
{ | |||||
if(result == -1) | |||||
{ | |||||
fprintf(stderr,"Failed to load espeak-data\n"); | |||||
if ((result = LoadPhData(&srate)) != 1) { | |||||
if (result == -1) { | |||||
fprintf(stderr, "Failed to load espeak-data\n"); | |||||
exit(1); | exit(1); | ||||
} | |||||
else | |||||
fprintf(stderr,"Wrong version of espeak-data 0x%x (expects 0x%x) at %s\n",result,version_phdata,path_home); | |||||
} else | |||||
fprintf(stderr, "Wrong version of espeak-data 0x%x (expects 0x%x) at %s\n", result, version_phdata, path_home); | |||||
} | } | ||||
WavegenInit(srate,0); | |||||
WavegenInit(srate, 0); | |||||
LoadConfig(); | LoadConfig(); | ||||
SetVoiceStack(NULL, ""); | SetVoiceStack(NULL, ""); | ||||
SynthesizeInit(); | SynthesizeInit(); | ||||
for(param=0; param<N_SPEECH_PARAM; param++) | |||||
for (param = 0; param < N_SPEECH_PARAM; param++) | |||||
param_stack[0].parameter[param] = param_defaults[param]; | param_stack[0].parameter[param] = param_defaults[param]; | ||||
return(0); | |||||
return (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 | ||||
#define optional_argument 2 | #define optional_argument 2 | ||||
#endif | #endif | ||||
int main (int argc, char **argv) | |||||
int main(int argc, char **argv) | |||||
{ | { | ||||
static struct option long_options[] = | |||||
{ | |||||
{"help", no_argument, 0, 'h'}, | |||||
{"stdin", no_argument, 0, 0x100}, | |||||
{"compile-debug", optional_argument, 0, 0x101}, | |||||
{"compile", optional_argument, 0, 0x102}, | |||||
{"punct", optional_argument, 0, 0x103}, | |||||
{"voices", optional_argument, 0, 0x104}, | |||||
{"stdout", no_argument, 0, 0x105}, | |||||
{"split", optional_argument, 0, 0x106}, | |||||
{"path", required_argument, 0, 0x107}, | |||||
{"phonout", required_argument, 0, 0x108}, | |||||
{"pho", no_argument, 0, 0x109}, | |||||
{"ipa", optional_argument, 0, 0x10a}, | |||||
{"version", no_argument, 0, 0x10b}, | |||||
{"sep", optional_argument, 0, 0x10c}, | |||||
{"tie", optional_argument, 0, 0x10d}, | |||||
{0, 0, 0, 0} | |||||
static struct option long_options[] = { | |||||
{ "help", no_argument, 0, 'h' }, | |||||
{ "stdin", no_argument, 0, 0x100 }, | |||||
{ "compile-debug", optional_argument, 0, 0x101 }, | |||||
{ "compile", optional_argument, 0, 0x102 }, | |||||
{ "punct", optional_argument, 0, 0x103 }, | |||||
{ "voices", optional_argument, 0, 0x104 }, | |||||
{ "stdout", no_argument, 0, 0x105 }, | |||||
{ "split", optional_argument, 0, 0x106 }, | |||||
{ "path", required_argument, 0, 0x107 }, | |||||
{ "phonout", required_argument, 0, 0x108 }, | |||||
{ "pho", no_argument, 0, 0x109 }, | |||||
{ "ipa", optional_argument, 0, 0x10a }, | |||||
{ "version", no_argument, 0, 0x10b }, | |||||
{ "sep", optional_argument, 0, 0x10c }, | |||||
{ "tie", optional_argument, 0, 0x10d }, | |||||
{ 0, 0, 0, 0 } | |||||
}; | }; | ||||
static const char *err_load = "Failed to read "; | static const char *err_load = "Failed to read "; | ||||
FILE *f_text=NULL; | |||||
const char *p_text=NULL; | |||||
FILE *f_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 value; | int value; | ||||
int speed=175; | |||||
int speed = 175; | |||||
int ix; | int ix; | ||||
char *optarg2; | char *optarg2; | ||||
int amp = 100; // default | int amp = 100; // default | ||||
#ifdef NEED_GETOPT | #ifdef NEED_GETOPT | ||||
optind = 1; | optind = 1; | ||||
opt_string = ""; | opt_string = ""; | ||||
while(optind < argc) | |||||
{ | |||||
while (optind < argc) { | |||||
int len; | int len; | ||||
char *p; | char *p; | ||||
if((c = *opt_string) == 0) | |||||
{ | |||||
if ((c = *opt_string) == 0) { | |||||
opt_string = argv[optind]; | opt_string = argv[optind]; | ||||
if(opt_string[0] != '-') | |||||
if (opt_string[0] != '-') | |||||
break; | break; | ||||
optind++; | optind++; | ||||
opt_string++; | opt_string++; | ||||
p = optarg2 = opt_string; | p = optarg2 = opt_string; | ||||
if(c == '-') | |||||
{ | |||||
if(p[0] == 0) | |||||
if (c == '-') { | |||||
if (p[0] == 0) | |||||
break; // -- means don't interpret further - as commands | break; // -- means don't interpret further - as commands | ||||
opt_string=""; | |||||
for(ix=0;; ix++) | |||||
{ | |||||
if(long_options[ix].name == 0) | |||||
opt_string = ""; | |||||
for (ix = 0;; ix++) { | |||||
if (long_options[ix].name == 0) | |||||
break; | break; | ||||
len = strlen(long_options[ix].name); | len = strlen(long_options[ix].name); | ||||
if(memcmp(long_options[ix].name,p,len)==0) | |||||
{ | |||||
if (memcmp(long_options[ix].name, p, len) == 0) { | |||||
c = long_options[ix].val; | c = long_options[ix].val; | ||||
optarg2 = NULL; | optarg2 = NULL; | ||||
if((long_options[ix].has_arg != 0) && (p[len]=='=')) | |||||
{ | |||||
if ((long_options[ix].has_arg != 0) && (p[len] == '=')) { | |||||
optarg2 = &p[len+1]; | optarg2 = &p[len+1]; | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
} | |||||
else | |||||
if(strchr(arg_opts,c) != NULL) | |||||
{ | |||||
opt_string=""; | |||||
if(optarg2[0]==0) | |||||
{ | |||||
} else if (strchr(arg_opts, c) != NULL) { | |||||
opt_string = ""; | |||||
if (optarg2[0] == 0) { | |||||
// the option's value is in the next argument | // the option's value is in the next argument | ||||
optarg2 = argv[optind++]; | optarg2 = argv[optind++]; | ||||
} | } | ||||
} | } | ||||
#else | #else | ||||
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 | |||||
long_options, &option_index); | |||||
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 | |||||
long_options, &option_index); | |||||
/* Detect the end of the options. */ | /* Detect the end of the options. */ | ||||
if (c == -1) | if (c == -1) | ||||
case 'b': | case 'b': | ||||
// input character encoding, 8bit, 16bit, UTF8 | // input character encoding, 8bit, 16bit, UTF8 | ||||
option_multibyte = espeakCHARS_8BIT; | option_multibyte = espeakCHARS_8BIT; | ||||
if((sscanf(optarg2,"%d",&value) == 1) && (value <= 4)) | |||||
option_multibyte= value; | |||||
if ((sscanf(optarg2, "%d", &value) == 1) && (value <= 4)) | |||||
option_multibyte = value; | |||||
break; | break; | ||||
case 'h': | case 'h': | ||||
init_path(argv[0],data_path); | |||||
printf("\nspeak text-to-speech: %s Data at: %s\n%s",version_string,path_home,help_text); | |||||
init_path(argv[0], data_path); | |||||
printf("\nspeak text-to-speech: %s Data at: %s\n%s", version_string, path_home, help_text); | |||||
exit(0); | exit(0); | ||||
case 'k': | case 'k': | ||||
case 'p': | case 'p': | ||||
pitch_adjustment = atoi(optarg2); | pitch_adjustment = atoi(optarg2); | ||||
if(pitch_adjustment > 99) pitch_adjustment = 99; | |||||
if (pitch_adjustment > 99) pitch_adjustment = 99; | |||||
break; | break; | ||||
case 'q': | case 'q': | ||||
break; | break; | ||||
case 'f': | case 'f': | ||||
strncpy0(filename,optarg2,sizeof(filename)); | |||||
strncpy0(filename, optarg2, sizeof(filename)); | |||||
break; | break; | ||||
case 'l': | case 'l': | ||||
break; | break; | ||||
case 'v': | case 'v': | ||||
strncpy0(voicename,optarg2,sizeof(voicename)); | |||||
strncpy0(voicename, optarg2, sizeof(voicename)); | |||||
break; | break; | ||||
case 'w': | case 'w': | ||||
option_waveout = 1; | option_waveout = 1; | ||||
strncpy0(wavefile,optarg2,sizeof(wavefile)); | |||||
strncpy0(wavefile, optarg2, sizeof(wavefile)); | |||||
break; | break; | ||||
case 'z': | case 'z': | ||||
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 0x101: // --compile-debug | ||||
case 0x102: // --compile | case 0x102: // --compile | ||||
if(optarg2 != NULL) | |||||
strncpy0(voicename,optarg2,sizeof(voicename)); | |||||
if (optarg2 != NULL) | |||||
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; | ||||
while((ix < N_PUNCTLIST) && ((option_punctlist[ix] = optarg2[ix]) != 0)) ix++; | |||||
while ((ix < N_PUNCTLIST) && ((option_punctlist[ix] = optarg2[ix]) != 0)) ix++; | |||||
option_punctlist[N_PUNCTLIST-1] = 0; | option_punctlist[N_PUNCTLIST-1] = 0; | ||||
option_punctuation = 2; | option_punctuation = 2; | ||||
} | } | ||||
break; | break; | ||||
case 0x104: // --voices | case 0x104: // --voices | ||||
init_path(argv[0],data_path); | |||||
DisplayVoices(stdout,optarg2); | |||||
init_path(argv[0], data_path); | |||||
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 0x108: // --phonout | case 0x108: // --phonout | ||||
if((f_trans = fopen(optarg2,"w")) == NULL) | |||||
{ | |||||
fprintf(stderr,"Can't write to: %s\n",optarg2); | |||||
if ((f_trans = fopen(optarg2, "w")) == NULL) { | |||||
fprintf(stderr, "Can't write to: %s\n", optarg2); | |||||
f_trans = stderr; | f_trans = stderr; | ||||
} | } | ||||
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 | ||||
switch(atoi(optarg2)) | |||||
switch (atoi(optarg2)) | |||||
{ | { | ||||
case 1: | case 1: | ||||
phonemes_separator = '_'; | phonemes_separator = '_'; | ||||
break; | break; | ||||
case 0x10b: // --version | case 0x10b: // --version | ||||
init_path(argv[0],data_path); | |||||
printf("speak text-to-speech: %s Data at: %s\n",version_string,path_home); | |||||
init_path(argv[0], data_path); | |||||
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; | ||||
} | } | ||||
} | } | ||||
init_path(argv[0],data_path); | |||||
init_path(argv[0], data_path); | |||||
initialise(); | initialise(); | ||||
if(voicename[0] == 0) | |||||
strcpy(voicename,"default"); | |||||
if (voicename[0] == 0) | |||||
strcpy(voicename, "default"); | |||||
if(SetVoiceByName(voicename) != EE_OK) | |||||
{ | |||||
memset(&voice_select,0,sizeof(voice_select)); | |||||
if (SetVoiceByName(voicename) != EE_OK) { | |||||
memset(&voice_select, 0, sizeof(voice_select)); | |||||
voice_select.languages = voicename; | voice_select.languages = voicename; | ||||
if(SetVoiceByProperties(&voice_select) != EE_OK) | |||||
{ | |||||
fprintf(stderr,"%svoice '%s'\n",err_load,voicename); | |||||
if (SetVoiceByProperties(&voice_select) != EE_OK) { | |||||
fprintf(stderr, "%svoice '%s'\n", err_load, voicename); | |||||
exit(2); | exit(2); | ||||
} | } | ||||
} | } | ||||
if(flag_compile) | |||||
{ | |||||
if (flag_compile) { | |||||
#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\\"); | |||||
CompileDictionary(path_dsource,dictionary_name,NULL,NULL, flag_compile & 0x1); | |||||
strcat(path_dsource, "dictsource\\"); | |||||
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\\"); | |||||
CompileDictionary(path_dsource,dictionary_name,NULL,NULL, flag_compile & 0x1); | |||||
strcat(path_dsource, "dictsource\\"); | |||||
CompileDictionary(path_dsource, dictionary_name, NULL, NULL, flag_compile & 0x1); | |||||
#else | #else | ||||
CompileDictionary(NULL,dictionary_name,NULL,NULL, flag_compile & 0x1); | |||||
CompileDictionary(NULL, dictionary_name, NULL, NULL, flag_compile & 0x1); | |||||
#endif | #endif | ||||
#endif | #endif | ||||
exit(0); | exit(0); | ||||
} | } | ||||
SetParameter(espeakRATE,speed,0); | |||||
SetParameter(espeakVOLUME,amp,0); | |||||
SetParameter(espeakCAPITALS,option_capitals,0); | |||||
SetParameter(espeakPUNCTUATION,option_punctuation,0); | |||||
SetParameter(espeakWORDGAP,wordgap,0); | |||||
SetParameter(espeakRATE, speed, 0); | |||||
SetParameter(espeakVOLUME, amp, 0); | |||||
SetParameter(espeakCAPITALS, option_capitals, 0); | |||||
SetParameter(espeakPUNCTUATION, option_punctuation, 0); | |||||
SetParameter(espeakWORDGAP, wordgap, 0); | |||||
option_phonemes = phoneme_options | (phonemes_separator << 8); | option_phonemes = phoneme_options | (phonemes_separator << 8); | ||||
if(pitch_adjustment != 50) | |||||
{ | |||||
SetParameter(espeakPITCH,pitch_adjustment,0); | |||||
if (pitch_adjustment != 50) { | |||||
SetParameter(espeakPITCH, pitch_adjustment, 0); | |||||
} | } | ||||
DoVoiceChange(voice); | DoVoiceChange(voice); | ||||
if(filename[0]==0) | |||||
{ | |||||
if((optind < argc) && (flag_stdin == 0)) | |||||
{ | |||||
if (filename[0] == 0) { | |||||
if ((optind < argc) && (flag_stdin == 0)) { | |||||
// there's a non-option parameter, and no -f or --stdin | // there's a non-option parameter, and no -f or --stdin | ||||
// use it as text | // use it as text | ||||
p_text = argv[optind]; | p_text = argv[optind]; | ||||
} | |||||
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 | |||||
{ | |||||
f_text = fopen(filename,"r"); | |||||
} else { | |||||
f_text = fopen(filename, "r"); | |||||
} | } | ||||
if((f_text == NULL) && (p_text == NULL)) | |||||
{ | |||||
fprintf(stderr,"%sfile '%s'\n",err_load,filename); | |||||
if ((f_text == NULL) && (p_text == NULL)) { | |||||
fprintf(stderr, "%sfile '%s'\n", err_load, filename); | |||||
exit(1); | exit(1); | ||||
} | } | ||||
if(option_waveout || quiet) | |||||
{ | |||||
if(quiet) | |||||
{ | |||||
if (option_waveout || quiet) { | |||||
if (quiet) { | |||||
// no sound output | // no sound output | ||||
OpenWaveFile(NULL,samplerate); | |||||
OpenWaveFile(NULL, samplerate); | |||||
option_waveout = 1; | option_waveout = 1; | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
// write sound output to a WAV file | // write sound output to a WAV file | ||||
samples_split = (samplerate * samples_split) * 60; | samples_split = (samplerate * samples_split) * 60; | ||||
if(samples_split) | |||||
{ | |||||
if (samples_split) { | |||||
// don't open the wav file until we start generating speech | // don't open the wav file until we start generating speech | ||||
char *extn; | char *extn; | ||||
extn = strrchr(wavefile,'.'); | |||||
if((extn != NULL) && ((wavefile + strlen(wavefile) - extn) <= 4)) | |||||
{ | |||||
strcpy(filetype,extn); | |||||
extn = strrchr(wavefile, '.'); | |||||
if ((extn != NULL) && ((wavefile + strlen(wavefile) - extn) <= 4)) { | |||||
strcpy(filetype, extn); | |||||
*extn = 0; | *extn = 0; | ||||
} | } | ||||
} | |||||
else | |||||
if(OpenWaveFile(wavefile,samplerate) != 0) | |||||
{ | |||||
fprintf(stderr,"Can't write to output file '%s'\n'",wavefile); | |||||
} else if (OpenWaveFile(wavefile, samplerate) != 0) { | |||||
fprintf(stderr, "Can't write to output file '%s'\n'", wavefile); | |||||
exit(3); | exit(3); | ||||
} | } | ||||
} | } | ||||
InitText(0); | InitText(0); | ||||
SpeakNextClause(f_text,p_text,0); | |||||
SpeakNextClause(f_text, p_text, 0); | |||||
ix = 1; | ix = 1; | ||||
for(;; ) | |||||
{ | |||||
if(WavegenFile() != 0) | |||||
{ | |||||
if(ix == 0) | |||||
for (;;) { | |||||
if (WavegenFile() != 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) | |||||
{ | |||||
ix = SpeakNextClause(NULL,NULL,1); | |||||
if (Generate(phoneme_list, &n_phoneme_list, 1) == 0) { | |||||
ix = SpeakNextClause(NULL, NULL, 1); | |||||
} | } | ||||
} | } | ||||
CloseWaveFile(); | CloseWaveFile(); | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
WavegenInitSound(); | WavegenInitSound(); | ||||
InitText(0); | InitText(0); | ||||
SpeakNextClause(f_text,p_text,0); | |||||
SpeakNextClause(f_text, p_text, 0); | |||||
if(option_quiet) | |||||
{ | |||||
while(SpeakNextClause(NULL,NULL,1) != 0); | |||||
return(0); | |||||
if (option_quiet) { | |||||
while (SpeakNextClause(NULL, NULL, 1) != 0) ; | |||||
return (0); | |||||
} | } | ||||
speaking = 1; | speaking = 1; | ||||
while(speaking) | |||||
{ | |||||
while (speaking) { | |||||
// 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 | ||||
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); | ||||
#endif | #endif | ||||
#endif | #endif | ||||
if(SynthOnTimer() != 0) | |||||
if (SynthOnTimer() != 0) | |||||
speaking = 0; | speaking = 0; | ||||
} | } | ||||
} | } | ||||
if((f_trans != stdout) && (f_trans != stderr)) | |||||
if ((f_trans != stdout) && (f_trans != stderr)) | |||||
fclose(f_trans); // needed for WinCe | fclose(f_trans); // needed for WinCe | ||||
return(0); | |||||
return (0); | |||||
} | } |