|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// indexed by (entry num. in punct_chars) + 1 |
|
|
// indexed by (entry num. in punct_chars) + 1 |
|
|
// bits 0-7 pause x 10mS, bits 8-10 intonation type, bit 11 don't need following space or bracket |
|
|
|
|
|
|
|
|
// bits 0-7 pause x 10mS, bits 12-14 intonation type, bit 15 don't need following space or bracket |
|
|
static const unsigned short punct_attributes [] = { 0, |
|
|
static const unsigned short punct_attributes [] = { 0, |
|
|
CLAUSE_COMMA, CLAUSE_PERIOD, CLAUSE_QUESTION, CLAUSE_EXCLAMATION, CLAUSE_COLON, CLAUSE_SEMICOLON, |
|
|
CLAUSE_COMMA, CLAUSE_PERIOD, CLAUSE_QUESTION, CLAUSE_EXCLAMATION, CLAUSE_COLON, CLAUSE_SEMICOLON, |
|
|
CLAUSE_SEMICOLON, // en-dash |
|
|
CLAUSE_SEMICOLON, // en-dash |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CLAUSE_QUESTION, // Greek question mark |
|
|
CLAUSE_QUESTION, // Greek question mark |
|
|
CLAUSE_SEMICOLON, // Greek semicolon |
|
|
CLAUSE_SEMICOLON, // Greek semicolon |
|
|
CLAUSE_PERIOD+0x800, // Devanagari Danda (fullstop) |
|
|
|
|
|
CLAUSE_COMMA+0x800, // ideograph comma |
|
|
|
|
|
CLAUSE_PERIOD+0x800, // ideograph period |
|
|
|
|
|
|
|
|
CLAUSE_PERIOD+0x8000, // Devanagari Danda (fullstop) |
|
|
|
|
|
CLAUSE_COMMA+0x8000, // ideograph comma |
|
|
|
|
|
CLAUSE_PERIOD+0x8000, // ideograph period |
|
|
|
|
|
|
|
|
CLAUSE_EXCLAMATION+0x800, // fullwidth |
|
|
|
|
|
CLAUSE_COMMA+0x800, |
|
|
|
|
|
CLAUSE_PERIOD+0x800, |
|
|
|
|
|
CLAUSE_COLON+0x800, |
|
|
|
|
|
CLAUSE_SEMICOLON+0x800, |
|
|
|
|
|
CLAUSE_QUESTION+0x800, |
|
|
|
|
|
|
|
|
CLAUSE_EXCLAMATION+0x8000, // fullwidth |
|
|
|
|
|
CLAUSE_COMMA+0x8000, |
|
|
|
|
|
CLAUSE_PERIOD+0x8000, |
|
|
|
|
|
CLAUSE_COLON+0x8000, |
|
|
|
|
|
CLAUSE_SEMICOLON+0x8000, |
|
|
|
|
|
CLAUSE_QUESTION+0x8000, |
|
|
|
|
|
|
|
|
CLAUSE_SEMICOLON, // spare |
|
|
CLAUSE_SEMICOLON, // spare |
|
|
0 }; |
|
|
0 }; |
|
|
|
|
|
|
|
|
char command[sizeof(fname2)+sizeof(fname2)+30]; |
|
|
char command[sizeof(fname2)+sizeof(fname2)+30]; |
|
|
|
|
|
|
|
|
if(fname == NULL) |
|
|
if(fname == NULL) |
|
|
|
|
|
{ |
|
|
|
|
|
// filename is already in the table |
|
|
fname = soundicon_tab[index].filename; |
|
|
fname = soundicon_tab[index].filename; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if((fname==NULL) || (GetFileLength(fname) <= 0)) |
|
|
|
|
|
|
|
|
if(fname==NULL) |
|
|
return(1); |
|
|
return(1); |
|
|
|
|
|
|
|
|
if(fname[0] != '/') |
|
|
if(fname[0] != '/') |
|
|
{ |
|
|
{ |
|
|
// a relative path, look in espeak-data/soundicons |
|
|
// a relative path, look in espeak-data/soundicons |
|
|
sprintf(fname2,"%s%csoundicons%c%s",path_home,PATHSEP,PATHSEP,fname); |
|
|
sprintf(fname2,"%s%csoundicons%c%s",path_home,PATHSEP,PATHSEP,fname); |
|
|
fname = fname2; |
|
|
fname = fname2; |
|
|
} |
|
|
} |
|
|
sprintf(fname_temp,"%s.wav",tmpnam(NULL)); |
|
|
|
|
|
sprintf(command,"sox \"%s\" -r %d -w %s polyphase\n",fname,samplerate,fname_temp); |
|
|
|
|
|
if(system(command) != 0) |
|
|
|
|
|
{ |
|
|
|
|
|
// resample has failed, use the original file |
|
|
|
|
|
fprintf(stderr,"Failed to resample: %s\n",command); |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
f = NULL; |
|
|
|
|
|
#ifdef PLATFORM_POSIX |
|
|
|
|
|
if((f = fopen(fname,"rb")) != NULL) |
|
|
{ |
|
|
{ |
|
|
fname = fname_temp; |
|
|
|
|
|
|
|
|
int header[11]; |
|
|
|
|
|
fread(header, 1, 44, f); |
|
|
|
|
|
|
|
|
|
|
|
if(header[6] != samplerate) |
|
|
|
|
|
{ |
|
|
|
|
|
fclose(f); |
|
|
|
|
|
f = NULL; |
|
|
|
|
|
|
|
|
|
|
|
sprintf(fname_temp,"%s.wav",tmpnam(NULL)); |
|
|
|
|
|
sprintf(command,"sox \"%s\" -r %d -w %s polyphase\n",fname,samplerate,fname_temp); |
|
|
|
|
|
if(system(command) == 0) |
|
|
|
|
|
{ |
|
|
|
|
|
fname = fname_temp; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
length = GetFileLength(fname); |
|
|
|
|
|
f = fopen(fname,"rb"); |
|
|
|
|
|
if(f == NULL) |
|
|
if(f == NULL) |
|
|
{ |
|
|
{ |
|
|
fprintf(stderr,"Can't read temp file: %s",fname); |
|
|
|
|
|
return(3); |
|
|
|
|
|
|
|
|
f = fopen(fname,"rb"); |
|
|
|
|
|
if(f == NULL) |
|
|
|
|
|
{ |
|
|
|
|
|
fprintf(stderr,"Can't read temp file: %s\n",fname); |
|
|
|
|
|
return(3); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
length = GetFileLength(fname); |
|
|
fseek(f,0,SEEK_SET); |
|
|
fseek(f,0,SEEK_SET); |
|
|
p = Alloc(length); |
|
|
p = Alloc(length); |
|
|
fread(p,length,1,f); |
|
|
fread(p,length,1,f); |
|
|
|
|
|
|
|
|
} // end of LoadSoundFile |
|
|
} // end of LoadSoundFile |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int LookupSoundicon(int c) |
|
|
static int LookupSoundicon(int c) |
|
|
{//============================== |
|
|
{//============================== |
|
|
// Find the sound icon number for a punctuation chatacter |
|
|
// Find the sound icon number for a punctuation chatacter |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int Translator::AnnouncePunctuation(int c1, int c2, char *buf, int bufix) |
|
|
int Translator::AnnouncePunctuation(int c1, int c2, char *buf, int bufix) |
|
|
{//====================================================================== |
|
|
{//====================================================================== |
|
|
// announce punctuation names |
|
|
// announce punctuation names |
|
|
|
|
|
|
|
|
#define SSML_CLOSE 0x10 // for a closing tag, OR this with the tag type |
|
|
#define SSML_CLOSE 0x10 // for a closing tag, OR this with the tag type |
|
|
|
|
|
|
|
|
// these tags have no effect if they are self-closing, eg. <voice /> |
|
|
// these tags have no effect if they are self-closing, eg. <voice /> |
|
|
static char ignore_if_self_closing[] = {0,1,1,1,1,0,0,0,0,1,1,0,1,0}; |
|
|
|
|
|
|
|
|
static char ignore_if_self_closing[] = {0,1,1,1,1,0,0,0,0,1,1,0,1,0,0,0,0}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MNEM_TAB ssmltags[] = { |
|
|
MNEM_TAB ssmltags[] = { |
|
|
|
|
|
|
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case SSML_AUDIO: |
|
|
case SSML_AUDIO: |
|
|
if(uri_callback == NULL) |
|
|
|
|
|
break; |
|
|
|
|
|
sp = PushParamStack(tag_type); |
|
|
sp = PushParamStack(tag_type); |
|
|
|
|
|
|
|
|
if((attr1 = GetSsmlAttribute(px,"src")) != NULL) |
|
|
if((attr1 = GetSsmlAttribute(px,"src")) != NULL) |
|
|
{ |
|
|
{ |
|
|
attrcopy_utf8(buf,attr1,sizeof(buf)); |
|
|
attrcopy_utf8(buf,attr1,sizeof(buf)); |
|
|
if((index = AddNameData(buf,0)) >= 0) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(uri_callback == NULL) |
|
|
|
|
|
{ |
|
|
|
|
|
// we could load the audio file as a sound icon (assuming it's a local WAV file) |
|
|
|
|
|
// But when to free() the data? |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
{ |
|
|
{ |
|
|
uri = &namedata[index]; |
|
|
|
|
|
if(uri_callback(1,uri,xmlbase) == 0) |
|
|
|
|
|
|
|
|
if((index = AddNameData(buf,0)) >= 0) |
|
|
{ |
|
|
{ |
|
|
sprintf(buf,"%c%dU",CTRL_EMBEDDED,index); |
|
|
|
|
|
strcpy(&outbuf[outix],buf); |
|
|
|
|
|
outix += strlen(buf); |
|
|
|
|
|
sp->parameter[espeakSILENCE] = 1; |
|
|
|
|
|
|
|
|
uri = &namedata[index]; |
|
|
|
|
|
if(uri_callback(1,uri,xmlbase) == 0) |
|
|
|
|
|
{ |
|
|
|
|
|
sprintf(buf,"%c%dU",CTRL_EMBEDDED,index); |
|
|
|
|
|
strcpy(&outbuf[outix],buf); |
|
|
|
|
|
outix += strlen(buf); |
|
|
|
|
|
sp->parameter[espeakSILENCE] = 1; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return(CLAUSE_NONE); |
|
|
return(CLAUSE_NONE); |
|
|
|
|
|
|
|
|
case SSML_AUDIO + SSML_CLOSE: |
|
|
case SSML_AUDIO + SSML_CLOSE: |
|
|
if(uri_callback == NULL) |
|
|
|
|
|
break; |
|
|
|
|
|
PopParamStack(tag_type, outbuf, outix); |
|
|
PopParamStack(tag_type, outbuf, outix); |
|
|
return(CLAUSE_NONE); |
|
|
return(CLAUSE_NONE); |
|
|
|
|
|
|
|
|
case SSML_BREAK: |
|
|
case SSML_BREAK: |
|
|
value = 50; |
|
|
|
|
|
|
|
|
value = 21; |
|
|
terminator = CLAUSE_NONE; |
|
|
terminator = CLAUSE_NONE; |
|
|
|
|
|
|
|
|
if((attr1 = GetSsmlAttribute(px,"strength")) != NULL) |
|
|
if((attr1 = GetSsmlAttribute(px,"strength")) != NULL) |
|
|
|
|
|
|
|
|
outix += 3; |
|
|
outix += 3; |
|
|
terminator = 0; |
|
|
terminator = 0; |
|
|
} |
|
|
} |
|
|
// if(value > 3) |
|
|
|
|
|
// terminator = CLAUSE_COMMA & 0xff00; |
|
|
|
|
|
value = break_value[value]; |
|
|
value = break_value[value]; |
|
|
} |
|
|
} |
|
|
if((attr2 = GetSsmlAttribute(px,"time")) != NULL) |
|
|
if((attr2 = GetSsmlAttribute(px,"time")) != NULL) |
|
|
{ |
|
|
{ |
|
|
value2 = attrnumber(px,0,1) / 10; |
|
|
|
|
|
|
|
|
value = (attrnumber(attr2,0,1) * 25) / speed_factor1; // compensate for speaking speed to keep constant pause length |
|
|
|
|
|
|
|
|
if(terminator == 0) |
|
|
if(terminator == 0) |
|
|
terminator = CLAUSE_NONE; |
|
|
terminator = CLAUSE_NONE; |
|
|
} |
|
|
} |
|
|
if(terminator) |
|
|
if(terminator) |
|
|
|
|
|
{ |
|
|
|
|
|
if(value > 0xfff) |
|
|
|
|
|
value = 0xfff; |
|
|
return(terminator + value); |
|
|
return(terminator + value); |
|
|
|
|
|
} |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case SSML_SPEAK: |
|
|
case SSML_SPEAK: |
|
|
|
|
|
|
|
|
char buf2[40]; |
|
|
char buf2[40]; |
|
|
wchar_t xml_buf[N_XML_BUF+1]; |
|
|
wchar_t xml_buf[N_XML_BUF+1]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(clear_skipping_text) |
|
|
if(clear_skipping_text) |
|
|
{ |
|
|
{ |
|
|
skipping_text = 0; |
|
|
skipping_text = 0; |
|
|
|
|
|
|
|
|
if(xml_buf[n_xml_buf-1] == '/') |
|
|
if(xml_buf[n_xml_buf-1] == '/') |
|
|
{ |
|
|
{ |
|
|
// a self-closing tag |
|
|
// a self-closing tag |
|
|
|
|
|
xml_buf[n_xml_buf-1] = ' '; |
|
|
self_closing = 1; |
|
|
self_closing = 1; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if((phoneme_mode==0) && (sayas_mode==0) && ((punct = lookupwchar(punct_chars,c1)) != 0)) |
|
|
if((phoneme_mode==0) && (sayas_mode==0) && ((punct = lookupwchar(punct_chars,c1)) != 0)) |
|
|
{ |
|
|
{ |
|
|
if((iswspace(c2) || (punct_attributes[punct] & 0x800) || IsBracket(c2) || (c2=='?') || (c2=='-') || Eof())) |
|
|
|
|
|
|
|
|
if((iswspace(c2) || (punct_attributes[punct] & 0x8000) || IsBracket(c2) || (c2=='?') || (c2=='-') || Eof())) |
|
|
{ |
|
|
{ |
|
|
// note: (c2='?') is for when a smart-quote has been replaced by '?' |
|
|
// note: (c2='?') is for when a smart-quote has been replaced by '?' |
|
|
buf[ix] = ' '; |
|
|
buf[ix] = ' '; |