[1.42.21]

New format for phoneme source files.
Update phoneme data for Klatt synthesizer.


git-svn-id: https://espeak.svn.sourceforge.net/svnroot/espeak/trunk@235 d46cf337-b52f-0410-862d-fd96e6ae7743
This commit is contained in:
jonsd
2009-12-20 07:48:33 +00:00
parent dd1623232f
commit 20f52f0301
231 changed files with 10132 additions and 8946 deletions
+588 -227
View File
@@ -35,8 +35,8 @@
#include "translate.h"
#include "wave.h"
const char *version_string = "1.42.09 13.Dec.09";
const int version_phdata = 0x014200;
const char *version_string = "1.42.21 19.Dec.09";
const int version_phdata = 0x014220;
int option_device_number = -1;
FILE *f_logespeak = NULL;
@@ -48,7 +48,7 @@ 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;
static USHORT *phoneme_index=NULL;
char *spects_data=NULL;
unsigned char *wavefile_data=NULL;
static unsigned char *phoneme_tab_data = NULL;
@@ -116,7 +116,7 @@ int LoadPhData()
if((phoneme_tab_data = (unsigned char *)ReadPhFile((void *)(phoneme_tab_data),"phontab")) == NULL)
return(-1);
if((phoneme_index = (unsigned int *)ReadPhFile((void *)(phoneme_index),"phonindex")) == NULL)
if((phoneme_index = (USHORT *)ReadPhFile((void *)(phoneme_index),"phonindex")) == NULL)
return(-1);
if((spects_data = ReadPhFile((void *)(spects_data),"phondata")) == NULL)
return(-1);
@@ -204,172 +204,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;
@@ -380,19 +217,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 *)(&spects_data[fmt_params->fmt_addr]);
seqk = (SPECT_SEQK *)seq;
nf = seq->n_frames;
@@ -400,8 +228,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++)
{
@@ -431,46 +259,21 @@ frameref_t *LookupSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB
}
// 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 *)(&spects_data[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
@@ -493,18 +296,18 @@ 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
@@ -518,22 +321,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;
}
}
else
{
//not a vowel
if(fmt_params->std_length > 0)
{
seq_len_adjust += (fmt_params->std_length - length1);
}
}
if(seq_len_adjust != 0)
{
length_std = 0;
for(ix=0; ix<nf1; ix++)
{
length_std += frames[ix].length;
}
length_factor = ((length_std + seq_len_adjust) * 256)/length_std;
length_factor = ((length1 + seq_len_adjust) * 256)/length1;
for(ix=0; ix<nf1; ix++)
{
frames[ix].length = (frames[ix].length * length_factor)/256;
@@ -547,11 +357,12 @@ frameref_t *LookupSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB
} // end of LookupSpect
unsigned char *LookupEnvelope(int ix)
{//================================
if(ix==0)
return(NULL);
return((unsigned char *)&spects_data[phoneme_index[ix]]);
return((unsigned char *)&spects_data[ix]);
}
@@ -689,6 +500,556 @@ 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 bool InterpretCondition(Translator *tr, int control, PHONEME_LIST *plist, int instn)
{//========================================================================================
int which;
unsigned int data;
int instn2;
PHONEME_TAB *ph;
PHONEME_LIST *plist_this;
static int ph_position[7] = {0, 1, 2, 3, 2, 0, 1};
data = instn & 0xff;
instn2 = instn >> 8;
if(instn2 < 14)
{
which = (instn2) % 7;
if(which==4)
{
// nextPh not word boundary
if(plist[1].sourceix)
return(false);
}
if(which==5)
{
// prevPh, not word boundary
if(plist[0].sourceix)
return(false);
}
if(which==6)
{
// nextVowel, not word boundary
for(which=2;;which++)
{
if(plist[which-1].sourceix)
return(false);
if(phoneme_tab[plist[which-1].phcode]->type == phVOWEL)
break;
}
}
else
{
which = ph_position[which];
}
plist_this = plist;
plist = &plist[which-1];
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;
}
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);
}
}
return(false);
} // end of InterpretCondition
static void SwitchVowelType(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
static int NumInstnWords(USHORT *prog)
{//===================================
int instn;
int instn2;
int instn_type;
static char n_words[11] = {1,1,1,1,1,1,1,1,1,2,4};
instn = *prog;
instn_type = instn >> 12;
if(instn_type < 11)
return(n_words[instn_type]);
// 11 to 15, 2 words
instn2 = prog[2];
if((instn2 >> 12) == 0xf)
{
// 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)
{//==========================================================================================
// control:
//bit 0: PreVoicing
//bit 8: change phonemes
PHONEME_TAB *ph;
USHORT *prog;
USHORT instn;
int instn2;
int or_flag;
bool truth;
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;
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:
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 < 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))
{
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
if(or_flag)
truth = (truth || InterpretCondition(tr, control, plist, instn & 0xfff));
else
truth = (truth && InterpretCondition(tr, control, plist, instn & 0xfff));
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
SwitchVowelType(plist, phdata, &prog, 2);
break;
case 6: // PrevVowelTypeEndings
SwitchVowelType(plist, phdata, &prog, 3);
break;
}
break;
case 9:
data = ((instn & 0xf) << 16) + prog[1];
switch(instn2)
{
case 1:
// call a procedure or another phoneme
if(n_return < N_RETURN)
{
return_addr[n_return++] = prog+1;
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 == 4)
{
// addWav(), return if previous instruction was FMT() or WAV()
end_flag--;
}
if((instn2 != 1) && (instn2 != 4))
{
// FMT, 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];
}
}
} // 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);
} // end of InterpretPhoneme2