diff options
Diffstat (limited to 'navit/support/espeak/synthdata.c')
-rw-r--r--[-rwxr-xr-x] | navit/support/espeak/synthdata.c | 1114 |
1 files changed, 861 insertions, 253 deletions
diff --git a/navit/support/espeak/synthdata.c b/navit/support/espeak/synthdata.c index 4f8234beb..4508bab92 100755..100644 --- a/navit/support/espeak/synthdata.c +++ b/navit/support/espeak/synthdata.c @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005 to 2007 by Jonathan Duddington * + * Copyright (C) 2005 to 2013 by Jonathan Duddington * * email: jonsd@users.sourceforge.net * * * * This program is free software; you can redistribute it and/or modify * @@ -23,6 +23,7 @@ #include <stdio.h> #include <stdlib.h> #include <ctype.h> +#include <stdbool.h> #include <wctype.h> #include <string.h> @@ -35,10 +36,14 @@ #include "translate.h" #include "wave.h" -const char *version_string = "1.41.01 25.Aug.09"; -const int version_phdata = 0x014100; +#include "synthdata.h" + +const char *version_string = "1.48.03 04.Mar.14"; +const int version_phdata = 0x014801; int option_device_number = -1; +FILE *f_logespeak = NULL; +int logging_type; // copy the current phoneme table into here int n_phoneme_tab; @@ -46,8 +51,8 @@ int current_phoneme_table; PHONEME_TAB *phoneme_tab[N_PHONEME_TAB]; unsigned char phoneme_tab_flags[N_PHONEME_TAB]; // bit 0: not inherited -static unsigned int *phoneme_index=NULL; -char *spects_data=NULL; +USHORT *phoneme_index=NULL; +char *phondata_ptr=NULL; unsigned char *wavefile_data=NULL; static unsigned char *phoneme_tab_data = NULL; @@ -65,12 +70,12 @@ int vowel_transition[4]; int vowel_transition0; int vowel_transition1; -int FormantTransition2(frameref_t *seq, int *n_frames, unsigned int data1, unsigned int data2, PHONEME_TAB *other_ph, int which); +extern int FormantTransition2(frameref_t *seq, int *n_frames, unsigned int data1, unsigned int data2, PHONEME_TAB *other_ph, int which); -static char *ReadPhFile(void *ptr, const char *fname) -{//================================================== +static char *ReadPhFile(void *ptr, const char *fname, int *size) +{//============================================================= FILE *f_in; char *p; unsigned int length; @@ -78,7 +83,7 @@ static char *ReadPhFile(void *ptr, const char *fname) sprintf(buf,"%s%c%s",path_home,PATHSEP,fname); length = GetFileLength(buf); - + if((f_in = fopen(buf,"rb")) == NULL) { fprintf(stderr,"Can't read data file: '%s'\n",buf); @@ -87,7 +92,7 @@ static char *ReadPhFile(void *ptr, const char *fname) if(ptr != NULL) Free(ptr); - + if((p = Alloc(length)) == NULL) { fclose(f_in); @@ -100,31 +105,41 @@ static char *ReadPhFile(void *ptr, const char *fname) } fclose(f_in); + if(size != NULL) + *size = length; return(p); } // end of ReadPhFile -int LoadPhData() -{//============= +int LoadPhData(int *srate) +{//======================== int ix; int n_phonemes; int version; int result = 1; + int length; + int rate; unsigned char *p; + int *pw; - if((phoneme_tab_data = (unsigned char *)ReadPhFile((void *)(phoneme_tab_data),"phontab")) == NULL) + if((phoneme_tab_data = (unsigned char *)ReadPhFile((void *)(phoneme_tab_data),"phontab",NULL)) == NULL) return(-1); - if((phoneme_index = (unsigned int *)ReadPhFile((void *)(phoneme_index),"phonindex")) == NULL) + if((phoneme_index = (USHORT *)ReadPhFile((void *)(phoneme_index),"phonindex",NULL)) == NULL) return(-1); - if((spects_data = ReadPhFile((void *)(spects_data),"phondata")) == NULL) + if((phondata_ptr = ReadPhFile((void *)(phondata_ptr),"phondata",NULL)) == NULL) return(-1); - wavefile_data = (unsigned char *)spects_data; + if((tunes = (TUNE *)ReadPhFile((void *)(tunes),"intonations",&length)) == NULL) + return(-1); + wavefile_data = (unsigned char *)phondata_ptr; + n_tunes = length / sizeof(TUNE); - // read the version number from the first 4 bytes of phondata - version = 0; + // read the version number and sample rate from the first 8 bytes of phondata + version = 0; // bytes 0-3, version number + rate = 0; // bytes 4-7, sample rate for(ix=0; ix<4; ix++) { version += (wavefile_data[ix] << (ix*8)); + rate += (wavefile_data[ix+4] << (ix*8)); } if(version != version_phdata) @@ -142,7 +157,9 @@ int LoadPhData() n_phonemes = p[0]; phoneme_tab_list[ix].n_phonemes = p[0]; phoneme_tab_list[ix].includes = p[1]; - p += 4; + pw = (int *)p; + phoneme_tab_list[ix].equivalence_tables = Reverse4Bytes(pw[1]); + p += 8; memcpy(phoneme_tab_list[ix].name,p,N_PHONEME_TAB_NAME); p += N_PHONEME_TAB_NAME; phoneme_tab_list[ix].phoneme_tab_ptr = (PHONEME_TAB *)p; @@ -152,6 +169,10 @@ int LoadPhData() if(phoneme_tab_number >= n_phoneme_tables) phoneme_tab_number = 0; + if(srate != NULL) + { + *srate = rate; + } return(result); } // end of LoadPhData @@ -160,10 +181,12 @@ void FreePhData(void) {//================== Free(phoneme_tab_data); Free(phoneme_index); - Free(spects_data); + Free(phondata_ptr); + Free(tunes); phoneme_tab_data=NULL; phoneme_index=NULL; - spects_data=NULL; + phondata_ptr=NULL; + tunes=NULL; } @@ -202,172 +225,9 @@ int LookupPhonemeString(const char *string) -static unsigned int LookupSound2(int index, unsigned int other_phcode, int control) -{//================================================================================ -// control=1 get formant transition data only - unsigned int code; - unsigned int value, value2; - - while((value = phoneme_index[index++]) != 0) - { - if((code = (value & 0xff)) == other_phcode) - { - while(((value2 = phoneme_index[index]) != 0) && ((value2 & 0xff) < 8)) - { - switch(value2 & 0xff) - { - case 0: - // next entry is a wavefile to be played along with the synthesis - if(control==0) - { - wavefile_ix = value2 >> 8; - } - break; - case 1: - if(control==0) - { - seq_len_adjust = value2 >> 8; - } - break; - case 2: - if(control==0) - { - seq_len_adjust = value2 >> 8; - seq_len_adjust = -seq_len_adjust; - } - break; - case 3: - if(control==0) - { - wavefile_amp = value2 >> 8; - } - break; - case 4: - // formant transition data, 2 words - vowel_transition[0] = value2 >> 8; - vowel_transition[1] = phoneme_index[index++ + 1]; - break; - case 5: - // formant transition data, 2 words - vowel_transition[2] = value2 >> 8; - vowel_transition[3] = phoneme_index[index++ + 1]; - break; - } - index++; - } - return(value >> 8); - } - else - if((code == 4) || (code == 5)) - { - // formant transition data, ignore next word of data - index++; - } - } - return(3); // not found -} // end of LookupSound2 - - -unsigned int LookupSound(PHONEME_TAB *this_ph, PHONEME_TAB *other_ph, int which, int *match_level, int control) -{//============================================================================================================ - // follows, 1 other_ph preceeds this_ph, 2 other_ph follows this_ph - // control: 1= get formant transition data only - int spect_list; - int spect_list2; - int s_list; - unsigned char virtual_ph; - int result; - int level=0; - unsigned int other_code; - unsigned int other_virtual; - - if(control==0) - { - wavefile_ix = 0; - wavefile_amp = 32; - seq_len_adjust = 0; - } - memset(vowel_transition,0,sizeof(vowel_transition)); - - other_code = other_ph->code; - if(phoneme_tab[other_code]->type == phPAUSE) - other_code = phonPAUSE_SHORT; // use this version of Pause for matching - - if(which==1) - { - spect_list = this_ph->after; - virtual_ph = this_ph->start_type; - spect_list2 = phoneme_tab[virtual_ph]->after; - other_virtual = other_ph->end_type; - } - else - { - spect_list = this_ph->before; - virtual_ph = this_ph->end_type; - spect_list2 = phoneme_tab[virtual_ph]->before; - other_virtual = other_ph->start_type; - } - - result = 3; - // look for ph1-ph2 combination - if((s_list = spect_list) != 0) - { - if((result = LookupSound2(s_list,other_code,control)) != 3) - { - level = 2; - } - else - if(other_virtual != 0) - { - if((result = LookupSound2(spect_list,other_virtual,control)) != 3) - { - level = 1; - } - } - } - // not found, look in a virtual phoneme if one is given for this phoneme - if((result==3) && (virtual_ph != 0) && ((s_list = spect_list2) != 0)) - { - if((result = LookupSound2(s_list,other_code,control)) != 3) - { - level = 1; - } - else - if(other_virtual != 0) - { - if((result = LookupSound2(spect_list2,other_virtual,control)) != 3) - { - level = 1; - } - } - } - - if(match_level != NULL) - *match_level = level; - - if(result==0) - return(0); // NULL was given in the phoneme source - - // note: values = 1 indicates use the default for this phoneme, even though we found a match - // which set a secondary reference - if(result >= 4) - { - // values 1-3 can be used for special codes - // 1 = DFT from the phoneme source file - return(result); - } - - // no match found for other_ph, return the default - return(LookupSound2(this_ph->spect,phonPAUSE,control)); - -} // end of LookupSound - - - -frameref_t *LookupSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB *next_ph, - int which, int *match_level, int *n_frames, PHONEME_LIST *plist) -{//========================================================================================================= +frameref_t *LookupSpect(PHONEME_TAB *this_ph, int which, FMT_PARAMS *fmt_params, int *n_frames, PHONEME_LIST *plist) +{//=================================================================================================================== int ix; int nf; int nf1; @@ -378,19 +238,10 @@ frameref_t *LookupSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB int length_factor; SPECT_SEQ *seq, *seq2; SPECT_SEQK *seqk, *seqk2; - PHONEME_TAB *next2_ph; frame_t *frame; static frameref_t frames_buf[N_SEQ_FRAMES]; - - PHONEME_TAB *other_ph; - if(which == 1) - other_ph = prev_ph; - else - other_ph = next_ph; - if((ix = LookupSound(this_ph,other_ph,which,match_level,0)) < 4) - return(NULL); - seq = (SPECT_SEQ *)(&spects_data[ix]); + seq = (SPECT_SEQ *)(&phondata_ptr[fmt_params->fmt_addr]); seqk = (SPECT_SEQK *)seq; nf = seq->n_frames; @@ -398,8 +249,8 @@ frameref_t *LookupSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB if(nf >= N_SEQ_FRAMES) nf = N_SEQ_FRAMES - 1; + seq_len_adjust = fmt_params->fmt2_lenadj + fmt_params->fmt_length; seq_break = 0; - length1 = 0; for(ix=0; ix<nf; ix++) { @@ -427,50 +278,25 @@ frameref_t *LookupSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB nf -= seq_break; } } - + // do we need to modify a frame for blending with a consonant? - if(this_ph->type == phVOWEL) + if((this_ph->type == phVOWEL) && (fmt_params->fmt2_addr == 0) && (fmt_params->use_vowelin)) { - if((which==2) && ((frames[nf-1].frflags & FRFLAG_BREAK) == 0)) - { - // lookup formant transition for the following phoneme - - if((*match_level == 0) || (next_ph->type == phNASAL)) - { - LookupSound(next_ph,this_ph,1,NULL,1); - seq_len_adjust += FormantTransition2(frames,&nf,vowel_transition[2],vowel_transition[3],next_ph,which); - } - else - if(next_ph->phflags == phVOWEL2) - { - // not really a consonant, rather a coloured vowel - if(LookupSound(next_ph,this_ph,1,NULL,1) == 0) - { - next2_ph = plist[2].ph; - LookupSound(next2_ph,next_ph,1,NULL,1); - seq_len_adjust += FormantTransition2(frames,&nf,vowel_transition[2],vowel_transition[3],next2_ph,which); - } - } - } - else - { - if(*match_level == 0) - seq_len_adjust = FormantTransition2(frames,&nf,vowel_transition0,vowel_transition1,prev_ph,which); - } + seq_len_adjust += FormantTransition2(frames,&nf,fmt_params->transition0,fmt_params->transition1,NULL,which); } + length1 = 0; nf1 = nf - 1; for(ix=0; ix<nf1; ix++) length1 += frames[ix].length; - - if((wavefile_ix != 0) && ((wavefile_ix & 0x800000)==0)) + if(fmt_params->fmt2_addr != 0) { // a secondary reference has been returned, which is not a wavefile // add these spectra to the main sequence - seq2 = (SPECT_SEQ *)(&spects_data[wavefile_ix]); + seq2 = (SPECT_SEQ *)(&phondata_ptr[fmt_params->fmt2_addr]); seqk2 = (SPECT_SEQK *)seq2; - + // first frame of the addition just sets the length of the last frame of the main seq nf--; for(ix=0; ix<seq2->n_frames; ix++) @@ -490,25 +316,25 @@ frameref_t *LookupSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB } wavefile_ix = 0; } - - if((this_ph->type == phVOWEL) && (length1 > 0)) + + if(length1 > 0) { if(which==2) { // adjust the length of the main part to match the standard length specified for the vowel // less the front part of the vowel and any added suffix - - length_std = this_ph->std_length + seq_len_adjust - 45; + + length_std = fmt_params->std_length + seq_len_adjust - 45; if(length_std < 10) length_std = 10; if(plist->synthflags & SFLAG_LENGTHEN) - length_std += phoneme_tab[phonLENGTHEN]->std_length; // phoneme was followed by an extra : symbol + length_std += (phoneme_tab[phonLENGTHEN]->std_length * 2); // phoneme was followed by an extra : symbol // can adjust vowel length for stressed syllables here length_factor = (length_std * 256)/ length1; - + for(ix=0; ix<nf1; ix++) { frames[ix].length = (frames[ix].length * length_factor)/256; @@ -516,22 +342,29 @@ frameref_t *LookupSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB } else { - // front of a vowel - if(*match_level == 0) + if(which == 1) { - // allow very short vowels to have shorter front parts - if(this_ph->std_length < 130) - frames[0].length = (frames[0].length * this_ph->std_length)/130; + // front of a vowel + if(fmt_params->fmt_control == 1) + { + // This is the default start of a vowel. + // Allow very short vowels to have shorter front parts + if(fmt_params->std_length < 130) + frames[0].length = (frames[0].length * fmt_params->std_length)/130; + } } - - if(seq_len_adjust != 0) + else { - length_std = 0; - for(ix=0; ix<nf1; ix++) + //not a vowel + if(fmt_params->std_length > 0) { - length_std += frames[ix].length; + seq_len_adjust += (fmt_params->std_length - length1); } - length_factor = ((length_std + seq_len_adjust) * 256)/length_std; + } + + if(seq_len_adjust != 0) + { + length_factor = ((length1 + seq_len_adjust) * 256)/length1; for(ix=0; ix<nf1; ix++) { frames[ix].length = (frames[ix].length * length_factor)/256; @@ -539,17 +372,21 @@ frameref_t *LookupSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB } } } - + *n_frames = nf; return(frames); } // end of LookupSpect -unsigned char *LookupEnvelope(int ix) -{//================================ - if(ix==0) - return(NULL); - return((unsigned char *)&spects_data[phoneme_index[ix]]); + +unsigned char *GetEnvelope(int index) +{//================================== + if(index==0) + { + fprintf(stderr,"espeak: No envelope\n"); + return(envelope_data[0]); // not found, use a default envelope + } + return((unsigned char *)&phondata_ptr[index]); } @@ -640,6 +477,8 @@ void LoadConfig(void) char *p; char string[200]; + logging_type = 0; + for(ix=0; ix<N_SOUNDICON_SLOTS; ix++) { soundicon_tab[ix].filename = NULL; @@ -654,6 +493,14 @@ void LoadConfig(void) while(fgets(buf,sizeof(buf),f)!=NULL) { + if(buf[0] == '/') continue; + + if(memcmp(buf,"log",3)==0) + { + if(sscanf(&buf[4],"%d %s",&logging_type,string)==2) + f_logespeak = fopen(string,"w"); + } + else if(memcmp(buf,"tone",4)==0) { ReadTonePoints(&buf[5],tone_points); @@ -661,7 +508,7 @@ void LoadConfig(void) else if(memcmp(buf,"pa_device",9)==0) { - sscanf(&buf[7],"%d",&option_device_number); + sscanf(&buf[10],"%d",&option_device_number); } else if(memcmp(buf,"soundicon",9)==0) @@ -680,3 +527,764 @@ void LoadConfig(void) fclose(f); } // end of LoadConfig + + + +PHONEME_DATA this_ph_data; + + +static void InvalidInstn(PHONEME_TAB *ph, int instn) +{//==================================================== + fprintf(stderr,"Invalid instruction %.4x for phoneme '%s'\n", instn, WordToString(ph->mnemonic)); +} + + +static bool StressCondition(Translator *tr, PHONEME_LIST *plist, int condition, int control) +{//======================================================================================== +// condition: +// 0 if diminished, 1 if unstressed, 2 if not stressed, 3 if stressed, 4 if max stress + + int stress_level; + PHONEME_LIST *pl; + static int condition_level[4] = {1,2,4,15}; + + if(phoneme_tab[plist[0].phcode]->type == phVOWEL) + { + pl = plist; + } + else + { + // consonant, get stress from the following vowel + if(phoneme_tab[plist[1].phcode]->type == phVOWEL) + { + pl = &plist[1]; + } + else + return(false); // no stress elevel for this consonant + } + + stress_level = pl->stresslevel & 0xf; + + if(tr != NULL) + { + if((control & 1) && (plist->synthflags & SFLAG_DICTIONARY) && ((tr->langopts.param[LOPT_REDUCE] & 1)==0)) + { + // change phoneme. Don't change phonemes which are given for the word in the dictionary. + return(false); + } + + if((tr->langopts.param[LOPT_REDUCE] & 0x2) && (stress_level >= pl->wordstress)) + { + // treat the most stressed syllable in an unstressed word as stressed + stress_level = 4; + } + } + + if(condition == 4) + { + return(stress_level >= pl->wordstress); + } + + if(condition == 3) + { + // if stressed + if(stress_level > 3) + return(true); + } + else + { + if(stress_level < condition_level[condition]) + return(true); + } + return(false); + +} // end of StressCondition + + +static int CountVowelPosition(PHONEME_LIST *plist) +{//=============================================== + int count = 0; + + for(;;) + { + if(plist->ph->type == phVOWEL) + count++; + if(plist->sourceix != 0) + break; + plist--; + } + return(count); +} // end of CoundVowelPosition + + +static bool InterpretCondition(Translator *tr, int control, PHONEME_LIST *plist, USHORT *p_prog, WORD_PH_DATA *worddata) +{//======================================================================================================================== + int which; + int ix; + unsigned int data; + int instn; + int instn2; + int count; + PHONEME_TAB *ph; + PHONEME_LIST *plist_this; + + // instruction: 2xxx, 3xxx + + // bits 8-10 = 0 to 5, which phoneme, =6 the 'which' information is in the next instruction. + // bit 11 = 0, bits 0-7 are a phoneme code + // bit 11 = 1, bits 5-7 type of data, bits 0-4 data value + + // bits 8-10 = 7, other conditions + + instn = (*p_prog) & 0xfff; + data = instn & 0xff; + instn2 = instn >> 8; + + if(instn2 < 14) + { + plist_this = plist; + which = (instn2) % 7; + + if(which==6) + { + // the 'which' code is in the next instruction + p_prog++; + which = (*p_prog); + } + + if(which==4) + { + // nextPhW not word boundary + if(plist[1].sourceix) + return(false); + } + if(which==5) + { + // prevPhW, not word boundary + if(plist[0].sourceix) + return(false); + } + if(which==6) + { + // next2PhW, not word boundary + if(plist[1].sourceix || plist[2].sourceix) + return(false); + } + + + switch(which) + { + case 0: // prevPh + case 5: // prevPhW + plist--; + break; + + case 1: // thisPh + break; + + case 2: // nextPh + case 4: // nextPhW + plist++; + break; + + case 3: // next2Ph + case 6: // next2PhW + plist += 2; + break; + + case 7: + // nextVowel, not word boundary + for(which=1;;which++) + { + if(plist[which].sourceix) + return(false); + if(phoneme_tab[plist[which].phcode]->type == phVOWEL) + { + plist = &plist[which]; + break; + } + } + break; + + case 8: // prevVowel in this word + if((worddata==NULL) || (worddata->prev_vowel.ph == NULL)) + return(false); // no previous vowel + plist = &(worddata->prev_vowel); + break; + + case 9: // next3PhW + for(ix=1; ix<=3; ix++) + { + if(plist[ix].sourceix) + return(false); + } + plist = &plist[3]; + break; + + case 10: // prev2PhW + if((plist[0].sourceix) || (plist[-1].sourceix)) + return(false); + plist-=2; + break; + } + + if((which == 0) || (which == 5)) + { + if(plist->phcode == 1) + { + // This is a NULL phoneme, a phoneme has been deleted so look at the previous phoneme + plist--; + } + } + + if(control & 0x100) + { + // "change phonemes" pass + plist->ph = phoneme_tab[plist->phcode]; + } + ph = plist->ph; + + if(instn2 < 7) + { + // 'data' is a phoneme number + if((phoneme_tab[data]->mnemonic == ph->mnemonic) == true) + return(true); + if((which == 0) && (ph->type == phVOWEL)) + return(data == ph->end_type); // prevPh() match on end_type + return(data == ph->start_type); // thisPh() or nextPh(), match on start_type + } + + data = instn & 0x1f; + + switch(instn & 0xe0) + { + case 0x00: + // phoneme type, vowel, nasal, fricative, etc + return(ph->type == data); + break; + + case 0x20: + // place of articulation + return(((ph->phflags >> 16) & 0xf) == data); + break; + + case 0x40: + // is a bit set in phoneme flags + return((ph->phflags & (1 << data)) != 0); + break; + + case 0x80: + switch(data) + { + case 0: + case 1: + case 2: + case 3: + case 4: + return(StressCondition(tr, plist, data, 0)); + + case 5: // isBreak, Either pause phoneme, or (stop/vstop/vfric not followed by vowel or (liquid in same word)) + return((ph->type == phPAUSE) || (plist_this->synthflags & SFLAG_NEXT_PAUSE)); + + case 6: // isWordStart + return(plist->sourceix != 0); + + case 7: // notWordStart + return(plist->sourceix == 0); + + case 8: // isWordEnd + return(plist[1].sourceix || (plist[1].ph->type == phPAUSE)); + break; + + case 9: // isAfterStress + if(plist->sourceix != 0) + return(false); + do { + plist--; + if((plist->stresslevel & 0xf) >= 4) + return(true); + + } while (plist->sourceix == 0); + break; + + case 10: // isNotVowel + return(ph->type != phVOWEL); + + case 11: // isFinalVowel + for(;;) + { + plist++; + plist->ph = phoneme_tab[plist->phcode]; + if(plist->sourceix != 0) + return(true); // start of next word, without finding another vowel + if(plist->ph->type == phVOWEL) + return(false); + } + break; + + case 12: // isVoiced + return((ph->type == phVOWEL) || (ph->type == phLIQUID) || (ph->phflags & phVOICED)); + + case 13: // isFirstVowel + return(CountVowelPosition(plist)==1); + + case 14: // isSecondVowel + return(CountVowelPosition(plist)==2); + + case 15: // isSeqFlag1 + // is this preceded by a sequence if 1 or more vowels which have 'flag1' ? (lang=hi) + if(plist->sourceix != 0) + return(false); // this is the first phoneme in the word, so no. + + count = 0; + for(;;) + { + plist--; + if(plist->ph->type == phVOWEL) + { + if(plist->ph->phflags & phFLAG1) + count++; + else + break; // stop when we find a vowel without flag1 + } + if(plist->sourceix != 0) + break; + } + return(count > 0); + + case 0x10: // isTranslationGiven + return((plist->synthflags & SFLAG_DICTIONARY) != 0); + } + break; + + } + return(false); + } + else + if(instn2 == 0xf) + { + // Other conditions + switch(data) + { + case 1: // PreVoicing + return(control & 1); + case 2: // KlattSynth + return(voice->klattv[0] != 0); + case 3: // MbrolaSynth + return(mbrola_name[0] != 0); + } + } + return(false); +} // end of InterpretCondition + + +static void SwitchOnVowelType(PHONEME_LIST *plist, PHONEME_DATA *phdata, USHORT **p_prog, int instn_type) +{//======================================================================================================== + USHORT *prog; + int voweltype; + signed char x; + + if(instn_type == 2) + { + phdata->pd_control |= pd_FORNEXTPH; + voweltype = plist[1].ph->start_type; // SwitchNextVowelType + } + else + { + voweltype = plist[-1].ph->end_type; // SwitchPrevVowelType + } + + voweltype -= phonVOWELTYPES; + if((voweltype >= 0) && (voweltype < 6)) + { + prog = *p_prog + voweltype*2; + phdata->sound_addr[instn_type] = (((prog[1] & 0xf) << 16) + prog[2]) * 4; + x = (prog[1] >> 4) & 0xff; + phdata->sound_param[instn_type] = x; // sign extend + } + + *p_prog += 12; +} // end of SwitchVowelType + + +int NumInstnWords(USHORT *prog) +{//============================ + int instn; + int instn2; + int instn_type; + int n; + int type2; + static const char n_words[16] = {0,1,0,0,1,1,0,1,1,2,4,0,0,0,0,0}; + + instn = *prog; + instn_type = instn >> 12; + if((n = n_words[instn_type]) > 0) + return(n); + + switch(instn_type) + { + case 0: + if(((instn & 0xf00) >> 8) == i_IPA_NAME) + { + n = ((instn & 0xff) + 1) / 2; + return(n+1); + } + return(1);; + + case 6: + type2 = (instn & 0xf00) >> 9; + if((type2 == 5) || (type2 == 6)) + return(12); // switch on vowel type + return(1); + + case 2: + case 3: + // a condition, check for a 2-word instruction + if(((n = instn & 0x0f00) == 0x600) || (n == 0x0d00)) + return(2); + return(1); + + default: + // instn_type 11 to 15, 2 words + instn2 = prog[2]; + if((instn2 >> 12) == 0xf) + { + // This instruction is followed by addWav(), 2 more words + return(4); + } + if(instn2 == i_CONTINUE) + { + return(3); + } + return(2); + } +} // end of NumInstnWords + + + +void InterpretPhoneme(Translator *tr, int control, PHONEME_LIST *plist, PHONEME_DATA *phdata, WORD_PH_DATA *worddata) +{//=================================================================================================================== +// control: +//bit 0: PreVoicing +//bit 8: change phonemes + PHONEME_TAB *ph; + USHORT *prog; + USHORT instn; + int instn2; + int or_flag; + bool truth; + bool truth2; + int data; + int end_flag; + int ix; + signed char param_sc; + + #define N_RETURN 10 + int n_return=0; + USHORT *return_addr[N_RETURN]; // return address stack + + ph = plist->ph; + + if((worddata != NULL) && (plist->sourceix)) + { + // start of a word, reset word data + worddata->prev_vowel.ph = NULL; + } + + memset(phdata, 0, sizeof(PHONEME_DATA)); + phdata->pd_param[i_SET_LENGTH] = ph->std_length; + phdata->pd_param[i_LENGTH_MOD] = ph->length_mod; + + if(ph->program == 0) + { + return; + } + + end_flag = 0; + + for(prog = &phoneme_index[ph->program]; end_flag != 1; prog++) + { + instn = *prog; + instn2 = (instn >> 8) & 0xf; + or_flag = 0; + + switch(instn >> 12) + { + case 0: // 0xxx + data = instn & 0xff; + + if(instn2 == 0) + { + // instructions with no operand + switch(data) + { + case i_RETURN: + end_flag = 1; + break; + + case i_CONTINUE: + break; + + default: + InvalidInstn(ph,instn); + break; + } + } + else + if(instn2 == i_APPEND_IFNEXTVOWEL) + { + if(phoneme_tab[plist[1].phcode]->type == phVOWEL) + phdata->pd_param[i_APPEND_PHONEME] = data; + } + else + if(instn2 == i_ADD_LENGTH) + { + if(data & 0x80) + { + // a negative value, do sign extension + data = -(0x100 - data); + } + phdata->pd_param[i_SET_LENGTH] += data; + } + else + if(instn2 == i_IPA_NAME) + { + // followed by utf-8 characters, 2 per instn word + for(ix=0; (ix < data) && (ix < 16); ix += 2) + { + prog++; + phdata->ipa_string[ix] = prog[0] >> 8; + phdata->ipa_string[ix+1] = prog[0] & 0xff; + } + phdata->ipa_string[ix] = 0; + } + else + if(instn2 < N_PHONEME_DATA_PARAM) + { + if(instn2 == i_CHANGE_PHONEME2) + { + phdata->pd_param[i_CHANGE_PHONEME] = data; // also set ChangePhoneme + } + phdata->pd_param[instn2] = data; + if((instn2 == i_CHANGE_PHONEME) && (control & 0x100)) + { + // found ChangePhoneme() in PhonemeList mode, exit + end_flag = 1; + } + } + else + { + InvalidInstn(ph,instn); + } + break; + + case 1: + if(tr == NULL) + break; // ignore if in synthesis stage + + if(instn2 < 8) + { + // ChangeIf + if(StressCondition(tr, plist, instn2 & 7, 1) == true) + { + phdata->pd_param[i_CHANGE_PHONEME] = instn & 0xff; + end_flag = 1; // change phoneme, exit + } + } + break; + + case 2: + case 3: + // conditions + or_flag = 0; + truth = true; + while((instn & 0xe000) == 0x2000) + { + // process a sequence of conditions, using boolean accumulator + truth2 = InterpretCondition(tr, control, plist, prog, worddata); + prog += NumInstnWords(prog); + if(*prog == i_NOT) + { + truth2 = truth2 ^ 1; + prog++; + } + + if(or_flag) + truth = truth || truth2; + else + truth = truth && truth2; + or_flag = instn & 0x1000; + instn = *prog; + } + + if(truth == false) + { + if((instn & 0xf800) == i_JUMP_FALSE) + { + prog += instn & 0xff; + } + else + { + // instruction after a condition is not JUMP_FALSE, so skip the instruction. + prog += NumInstnWords(prog); + if((prog[0] & 0xfe00) == 0x6000) + prog++; // and skip ELSE jump + } + } + prog--; + break; + + case 6: + // JUMP + switch(instn2 >> 1) + { + case 0: + prog += (instn & 0xff) - 1; + break; + + case 4: + // conditional jumps should have been processed in the Condition section + break; + + case 5: // NexttVowelStarts + SwitchOnVowelType(plist, phdata, &prog, 2); + break; + + case 6: // PrevVowelTypeEndings + SwitchOnVowelType(plist, phdata, &prog, 3); + break; + } + break; + + case 9: + data = ((instn & 0xf) << 16) + prog[1]; + prog++; + switch(instn2) + { + case 1: + // call a procedure or another phoneme + if(n_return < N_RETURN) + { + return_addr[n_return++] = prog; + prog = &phoneme_index[data] - 1; + } + break; + + case 2: + // pitch envelope + phdata->pitch_env = data; + break; + + case 3: + // amplitude envelope + phdata->amp_env = data; + break; + } + break; + + case 10: // Vowelin, Vowelout + if(instn2 == 1) + ix = 0; + else + ix = 2; + + phdata->vowel_transition[ix] = ((prog[0] & 0xff) << 16) + prog[1]; + phdata->vowel_transition[ix+1] = (prog[2] << 16) + prog[3]; + prog += 3; + break; + + case 11: // FMT + case 12: // WAV + case 13: // VowelStart + case 14: // VowelEnd + case 15: // addWav + instn2 = (instn >> 12) - 11; + phdata->sound_addr[instn2] = ((instn & 0xf) << 18) + (prog[1] << 2); + param_sc = phdata->sound_param[instn2] = (instn >> 4) & 0xff; + prog++; + + if(prog[1] != i_CONTINUE) + { + if(instn2 < 2) + { + // FMT() and WAV() imply Return + end_flag = 1; + if((prog[1] >> 12) == 0xf) + { + // Return after the following addWav() + end_flag = 2; + } + } + else + if(instn2 ==pd_ADDWAV) + { + // addWav(), return if previous instruction was FMT() or WAV() + end_flag--; + } + + if((instn2 == pd_VWLSTART) || (instn2 == pd_VWLEND)) + { + // VowelStart or VowelEnding. + phdata->sound_param[instn2] = param_sc; // sign extend + } + } + break; + + default: + InvalidInstn(ph,instn); + break; + } + + if(ph->phflags & phSINGLE_INSTN) + { + end_flag = 1; // this phoneme has a one-instruction program, with an implicit Return + } + + if((end_flag == 1) && (n_return > 0)) + { + // return from called procedure or phoneme + end_flag = 0; + prog = return_addr[--n_return]; + } + } + + if((worddata != NULL) && (plist->type == phVOWEL)) + { + memcpy(&worddata->prev_vowel, &plist[0], sizeof(PHONEME_LIST)); + } + +#ifdef _ESPEAKEDIT + plist->std_length = phdata->pd_param[i_SET_LENGTH]; + if(phdata->sound_addr[0] != 0) + { + plist->phontab_addr = phdata->sound_addr[0]; // FMT address + plist->sound_param = phdata->sound_param[0]; + } + else + { + plist->phontab_addr = phdata->sound_addr[1]; // WAV address + plist->sound_param = phdata->sound_param[1]; + } +#endif +} // end of InterpretPhoneme + + +void InterpretPhoneme2(int phcode, PHONEME_DATA *phdata) +{//===================================================== +// Examine the program of a single isolated phoneme + int ix; + PHONEME_LIST plist[4]; + memset(plist, 0, sizeof(plist)); + + for(ix=0; ix<4; ix++) + { + plist[ix].phcode = phonPAUSE; + plist[ix].ph = phoneme_tab[phonPAUSE]; + } + + plist[1].phcode = phcode; + plist[1].ph = phoneme_tab[phcode]; + plist[2].sourceix = 1; + + InterpretPhoneme(NULL, 0, &plist[1], phdata, NULL); +} // end of InterpretPhoneme2 |