Merge branch 'master' into build

Conflicts:
	src/compiledata.cpp
This commit is contained in:
Reece Dunn
2010-12-19 17:04:24 +00:00
63 changed files with 7484 additions and 2676 deletions
+5 -4
View File
@@ -6,7 +6,7 @@ INCDIR=$(PREFIX)/include/espeak
LIBDIR=$(PREFIX)/lib
DATADIR=$(PREFIX)/share/espeak-data
RELEASE = 1.43
RELEASE = 1.44
BIN_NAME = speak
BIN2_NAME = espeak
LIB_NAME = libespeak.so
@@ -80,10 +80,10 @@ all: $(BIN_NAME) $(LIB_NAME) $(STATIC_LIB_NAME) $(BIN2_NAME)
$(CXX) $(CXXFLAGS) $(USE_AUDIO) -D PATH_ESPEAK_DATA=\"$(DATADIR)\" -Wall -pedantic -I. -c -fno-exceptions $<
$(BIN_NAME): $(OBJS1)
$(CXX) -o $@ $(OBJS1) $(LIBS1)
$(CXX) $(LDFLAGS) -o $@ $(OBJS1) $(LIBS1)
$(BIN2_NAME): $(OBJS3) $(LIB_NAME)
$(CXX) -o $@ $(OBJS3) $(LIBS3)
$(CXX) $(LDFLAGS) -o $@ $(OBJS3) $(LIBS3)
@@ -92,7 +92,8 @@ x_%.o: %.cpp
-I. -D LIBRARY -c -fno-exceptions $< -o x_$*.o
$(LIB_NAME): $(OBJS2)
$(CXX) -shared $(SONAME_OPT)$(LIB_NAME).$(LIB_VERSION) -o $@ $(OBJS2) $(LIBS2)
$(CXX) $(LDFLAGS) -shared $(SONAME_OPT)$(LIB_NAME).$(LIB_VERSION) -o $@ \
$(OBJS2) $(LIBS2)
$(STATIC_LIB_NAME): $(OBJS2)
$(AR) cqs $(STATIC_LIB_NAME) $(OBJS2)
+2 -1
View File
@@ -140,6 +140,7 @@ static keywtab_t k_properties[] = {
{"isNotVowel", 0, i_isNotVowel},
{"isFinalVowel", 0, i_isFinalVowel},
{"isVoiced", 0, i_isVoiced}, // voiced consonant, or vowel
{"isFirstVowel", 0, i_isFirstVowel},
{NULL, 0, 0}
};
@@ -3259,7 +3260,7 @@ static void CompileEquivalents()
}
*p_equivalence++ = 0;
p_equivalence = (char *)((intptr_t)(p_equivalence + 3) & ~0x3); // align to word boundary
p_equivalence = (char *)((long int)(p_equivalence + 3) & ~0x3); // align to word boundary
n_bytes = p_equivalence - p_start;
p_start[1] = remove_stress;
n_bytes = n_bytes / 4;
+23 -5
View File
@@ -208,14 +208,15 @@ char *DecodeRule(const char *group_chars, int group_length, char *rule, int cont
int flags;
int suffix_char;
int condition_num=0;
int at_start = 0;
const char *name;
char buf[60];
char buf_pre[60];
char suffix[20];
static char output[60];
static char symbols[] = {' ',' ',' ',' ',' ',' ',' ',' ',' ',
'@','&','%','+','#','S','D','Z','A','L','!',' ','?','?','J','N','K','V','?','T','X','?','W'};
static char symbols[] = {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
'&','%','+','#','S','D','Z','A','L','!',' ','@','?','J','N','K','V','?','T','X','?','W'};
static char symbols_lg[] = {'A','B','C','H','F','G','Y'};
@@ -241,6 +242,8 @@ char *DecodeRule(const char *group_chars, int group_length, char *rule, int cont
case RULE_PHONEMES:
finished=1;
break;
case RULE_PRE_ATSTART:
at_start = 1; // drop through to next case
case RULE_PRE:
match_type = RULE_PRE;
*p = 0;
@@ -342,8 +345,10 @@ char *DecodeRule(const char *group_chars, int group_length, char *rule, int cont
sprintf(p,"?%d ",condition_num);
p = &p[strlen(p)];
}
if((ix = strlen(buf_pre)) > 0)
if(((ix = strlen(buf_pre)) > 0) || at_start)
{
if(at_start)
*p++ = '_';
while(--ix >= 0)
*p++ = buf_pre[ix];
*p++ = ')';
@@ -1158,6 +1163,7 @@ static char *compile_rule(char *input)
char *prule;
int len;
int len_name;
int start;
int state=2;
int finish=0;
int pre_bracket=0;
@@ -1308,9 +1314,21 @@ static char *compile_rule(char *input)
}
if(rule_pre[0] != 0)
{
output[len++] = RULE_PRE;
start = 0;
if(rule_pre[0] == RULE_SPACE)
{
// omit '_' at the beginning of the pre-string and imply it by using RULE_PRE_ATSTART
c = RULE_PRE_ATSTART;
start = 1;
}
else
{
c = RULE_PRE;
}
output[len++] = c;
// output PRE string in reverse order
for(ix = strlen(rule_pre)-1; ix>=0; ix--)
for(ix = strlen(rule_pre)-1; ix>=start; ix--)
output[len++] = rule_pre[ix];
}
+54 -27
View File
@@ -1090,6 +1090,7 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
int done;
int stressflags;
int dflags = 0;
int first_primary;
signed char vowel_stress[N_WORD_PHONEMES/2];
char syllable_weight[N_WORD_PHONEMES/2];
@@ -1433,6 +1434,7 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
}
done = 0;
first_primary = 0;
for(v=1; v<vowel_count; v++)
{
if(vowel_stress[v] < 0)
@@ -1469,6 +1471,18 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
stress = 3; /* use secondary stress for remaining syllables */
}
}
if(vowel_stress[v] >= 4)
{
if(first_primary == 0)
first_primary = v;
else
if(stressflags & S_FIRST_PRIMARY)
{
// reduce primary stresses after the first to secondary
vowel_stress[v] = 3;
}
}
}
if((unstressed_word) && (tonic < 0))
@@ -1737,7 +1751,6 @@ static void MatchRule(Translator *tr, char *word[], char *word_start, int group_
int failed;
int unpron_ignore;
int consumed; /* number of letters consumed from input */
int count; /* count through rules in the group */
int syllable_count;
int vowel;
int letter_group;
@@ -1747,6 +1760,7 @@ static void MatchRule(Translator *tr, char *word[], char *word_start, int group_
int n_bytes;
int add_points;
int command;
int check_atstart;
MatchRecord match;
static MatchRecord best;
@@ -1769,7 +1783,6 @@ static void MatchRule(Translator *tr, char *word[], char *word_start, int group_
total_consumed = 0;
count = 0;
common_phonemes = NULL;
match_type = 0;
@@ -1787,7 +1800,7 @@ static void MatchRule(Translator *tr, char *word[], char *word_start, int group_
letter = 0;
distance_right= -6; /* used to reduce points for matches further away the current letter */
distance_left= -2;
count++;
check_atstart = 0;
match.points = 1;
match.end_type = 0;
@@ -1827,17 +1840,24 @@ static void MatchRule(Translator *tr, char *word[], char *word_start, int group_
rule--; // so we are still pointing at the 0
failed=2; // matched OK
break;
case RULE_PRE_ATSTART: // pre rule with implied 'start of word'
check_atstart = 1;
unpron_ignore = 0;
match_type = RULE_PRE;
break;
case RULE_PRE:
match_type = RULE_PRE;
if(word_flags & FLAG_UNPRON_TEST)
{
// checking the start of the word for unpronouncable character sequences, only
// consider rules which explicitly match the start of a word
if(rule[0] != ' ')
failed = 1;
unpron_ignore = 0;
// Note: Those rules now use RULE_PRE_ATSTART
failed = 1;
}
break;
case RULE_POST:
match_type = RULE_POST;
break;
@@ -2306,31 +2326,34 @@ static void MatchRule(Translator *tr, char *word[], char *word_start, int group_
match.points += add_points;
}
if((failed == 2) && (unpron_ignore == 0))
{
/* matched OK, is this better than the last best match ? */
if(match.points >= best.points)
// do we also need to check for 'start of word' ?
if((check_atstart==0) || (pre_ptr[-1] == ' '))
{
memcpy(&best,&match,sizeof(match));
total_consumed = consumed;
if(check_atstart)
match.points += 4;
/* matched OK, is this better than the last best match ? */
if(match.points >= best.points)
{
memcpy(&best,&match,sizeof(match));
total_consumed = consumed;
}
if((option_phonemes == 2) && (match.points > 0) && ((word_flags & FLAG_NO_TRACE) == 0))
{
// show each rule that matches, and it's points score
int pts;
char decoded_phonemes[80];
pts = match.points;
if(group_length > 1)
pts += 35; // to account for an extra letter matching
DecodePhonemes(match.phonemes,decoded_phonemes);
fprintf(f_trans,"%3d\t%s [%s]\n",pts,DecodeRule(group_chars, group_length, rule_start, word_flags), decoded_phonemes);
}
}
if((option_phonemes == 2) && (match.points > 0) && ((word_flags & FLAG_NO_TRACE) == 0))
{
// show each rule that matches, and it's points score
int pts;
char decoded_phonemes[80];
// note: 'count' contains the rule number, if we want to include it
pts = match.points;
if(group_length > 1)
pts += 35; // to account for an extra letter matching
DecodePhonemes(match.phonemes,decoded_phonemes);
fprintf(f_trans,"%3d\t%s [%s]\n",pts,DecodeRule(group_chars, group_length, rule_start, word_flags), decoded_phonemes);
}
}
/* skip phoneme string to reach start of next template */
@@ -3313,6 +3336,10 @@ int LookupDictList(Translator *tr, char **wordptr, char *ph_out, unsigned int *f
fprintf(f_trans,"Replace: %s %s\n",word,*wordptr);
}
}
else
{
flags[0] &= ~FLAG_SKIPWORDS;
}
ph_out[0] = 0;
return(0);
+4 -2
View File
@@ -232,7 +232,6 @@ BEGIN_EVENT_TABLE(MyFrame, wxMDIParentFrame)
EVT_MENU(MENU_LEXICON_BG, MyFrame::OnTools)
EVT_MENU(MENU_LEXICON_DE, MyFrame::OnTools)
EVT_MENU(MENU_LEXICON_IT, MyFrame::OnTools)
EVT_MENU(MENU_LEXICON_IT2, MyFrame::OnTools)
EVT_MENU(MENU_LEXICON_TEST, MyFrame::OnTools)
EVT_MENU(MENU_TO_UTF8, MyFrame::OnTools)
EVT_MENU(MENU_COUNT_WORDS, MyFrame::OnTools)
@@ -610,7 +609,6 @@ void MyFrame::OnTools(wxCommandEvent& event)
case MENU_LEXICON_BG:
case MENU_LEXICON_DE:
case MENU_LEXICON_IT:
case MENU_LEXICON_IT2:
case MENU_LEXICON_TEST:
CompareLexicon(event.GetId()); // Compare a lexicon with _rules translation
break;
@@ -699,6 +697,7 @@ void MyFrame::OnSpeak(wxCommandEvent& event)
break;
case MENU_SPEAK_FILE:
out_ptr = NULL;
transldlg->SpeakFile();
break;
@@ -707,11 +706,14 @@ void MyFrame::OnSpeak(wxCommandEvent& event)
break;
case MENU_SPEAK_PAUSE:
out_ptr = NULL;
SpeakNextClause(NULL,NULL,3);
if(SynthStatus() & 2)
speak_menu->SetLabel(MENU_SPEAK_PAUSE,_T("&Resume"));
else
{
speak_menu->SetLabel(MENU_SPEAK_PAUSE,_T("&Pause"));
}
break;
case MENU_SPEAK_VOICE:
+98 -29
View File
@@ -289,11 +289,14 @@ static void DecodePhonemes2(const char *inptr, char *outptr)
void Lexicon_It(int pass)
{//======================
// Reads a lexicon of pairs of words: normal spelling and spelling with accents
// Creates file: dictsource/it_listx which includes corrections for stress position and [E]/[e] and [O]/[o] phonemes
// Words which are still in error are listed in file: it_compare (in the directory of the lexicon file).
int count=0;
int matched=0;
int ix;
int c;
char *p;
char *p, *p2;
int len;
int vowel_ix;
int stress_posn1;
@@ -305,8 +308,11 @@ void Lexicon_It(int pass)
FILE *f_out;
FILE *f_listx;
FILE *f_list_in = NULL;
int listx_count;
long int displ;
const char *alt_string;
wxString str;
static wxString fname_lex;
char buf[200];
char word[80];
char word1[80];
@@ -315,7 +321,10 @@ void Lexicon_It(int pass)
char temp[80];
char phonemes[80];
char phonemes2[80];
char buf_out[120];
char phonemes3[80];
char buf_out[200];
char buf_error[200];
char last_listx[200];
static const char *vowels1 = "aeiou";
static const char *vowels2 = "aeou";
@@ -333,31 +342,42 @@ void Lexicon_It(int pass)
static const char *exceptions[] = {ex1, ex2, ex3, ex4, ex5, ex6, ex7, ex8, ex9, ex10, NULL};
wxString fname = wxFileSelector(_T("Italian Lexicon"),path_dir1,_T(""),_T(""),_T("*"),wxOPEN);
if(pass == 1)
{
fname_lex = wxFileSelector(_T("Italian Lexicon"),path_dir1,_T(""),_T(""),_T("*"),wxOPEN);
}
strcpy(buf,fname.mb_str(wxConvLocal));
strcpy(buf,fname_lex.mb_str(wxConvLocal));
if((f_in = fopen(buf,"r")) == NULL)
{
wxLogError(_T("Can't read file ")+fname);
wxLogError(_T("Can't read file ")+fname_lex);
return;
}
path_dir1 = wxFileName(fname).GetPath();
path_dir1 = wxFileName(fname_lex).GetPath();
strcpy(buf_out, path_dir1.mb_str(wxConvLocal));
sprintf(buf, "%s/IT_errors", buf_out);
if((f_out = fopen("compare_it","w")) == NULL)
if((f_out = fopen(buf,"w")) == NULL)
{
wxLogError(_T("Can't write file: compare_it "));
str = wxString(buf, wxConvLocal);
wxLogError(_T("Can't write file: ") + str);
return;
}
listx_count = 0;
last_listx[0] = 0;
if(pass == 1)
{
sprintf(buf,"%s/it_listx",path_dsource);
remove(buf);
CompileDictionary(path_dsource,"it",NULL,NULL,0);
f_listx = fopen(buf,"w");
wxLogStatus(_T("Pass 1"));
}
else
{
CompileDictionary(path_dsource,"it",NULL,NULL,0);
sprintf(buf,"%s/it_listx2",path_dsource);
f_listx = fopen(buf,"w");
sprintf(buf,"%s/it_listx",path_dsource);
@@ -390,9 +410,9 @@ void Lexicon_It(int pass)
continue;
if(strlen(word) < 8)
sprintf(buf_out,"%s\t\t%s\t",word,temp);
sprintf(buf_error,"%s\t\t%s\t",word,temp);
else
sprintf(buf_out,"%s\t%s",word,temp);
sprintf(buf_error,"%s\t%s",word,temp);
sprintf(word1," %s ",word);
@@ -550,23 +570,38 @@ void Lexicon_It(int pass)
sscanf(buf, "%s", word1);
if(strcmp(word1, word_stem) < 0)
{
fprintf(f_listx,"%s",buf); // copy it_listx from pass 1 until we reach the matching word
}
else
if(strcmp(word1, word_stem) == 0)
{
p = buf;
while((*p != '\n') && (*p != 0)) *p++;
*p = 0;
fprintf(f_listx,"%s %s\n",buf,alt_string); // add $alt or $alt2 to the entry
break;
sprintf(buf_out,"%s",buf); // copy it_listx from pass 1 until we reach the matching word
}
else
{
fprintf(f_listx,"%s %s\n", word_stem, alt_string); // add a new word with $alt or $alt2
fseek(f_list_in, displ, SEEK_SET);
if(strcmp(word1, word_stem) == 0)
{
p = buf;
while((*p != '\n') && (*p != 0)) *p++;
*p = 0;
sprintf(buf_out,"%s %s\n",buf,alt_string); // add $alt or $alt2 to the entry
}
else
{
sprintf(buf_out,"%s %s\n", word_stem, alt_string); // add a new word with $alt or $alt2
fseek(f_list_in, displ, SEEK_SET);
}
if(strcmp(buf_out, last_listx) != 0)
{
fprintf(f_listx, "%s", buf_out);
listx_count++;
strcpy(last_listx, buf_out);
}
break;
}
if(strcmp(buf_out, last_listx) != 0)
{
fprintf(f_listx, "%s", buf_out);
listx_count++;
strcpy(last_listx, buf_out);
}
}
}
}
@@ -574,7 +609,26 @@ void Lexicon_It(int pass)
}
else
{
fprintf(f_out,"%s\t%s\t%s\n",buf_out,phonemes,phonemes2);
// allow if the only difference is no primary stress
p2 = phonemes2;
p = phonemes3;
while(*p2 != 0)
{
*p = *p2++;
if((*p2 == ':') && (strchr("aeiouEO", *p) != NULL)) p2++; // change lone vowels to short by removing ':'
if(*p == '\'') *p = ','; // change primary to secondary stress
p++;
}
*p = 0;
if(strcmp(phonemes, phonemes3) == 0)
{
matched++;
}
else
{
// still doesn't match, report this word
fprintf(f_out,"%s\t%s\t%s\n",buf_error,phonemes,phonemes2);
}
}
}
@@ -582,15 +636,32 @@ void Lexicon_It(int pass)
{
while(fgets(buf, sizeof(buf), f_list_in) != NULL)
{
fprintf(f_listx, "%s", buf); // copy the remaining entries from pass 1
if(strcmp(buf, last_listx) != 0) // check for duplicate entries
{
fprintf(f_listx, "%s", buf); // copy the remaining entries from pass 1
listx_count++;
strcpy(last_listx, buf);
}
}
fclose(f_list_in);
}
fclose(f_in);
fclose(f_out);
fclose(f_listx);
wxLogStatus(_T("Completed, equal=%d different=%d"),matched,count-matched);
}
if(pass == 2)
{
sprintf(buf,"%s/it_listx",path_dsource);
remove(buf);
sprintf(buf_out,"%s/it_listx2",path_dsource);
rename(buf_out, buf);
wxLogStatus(_T("Created file 'it_listx', entries=%d errors=%d total words=%d"),listx_count, count-matched, count);
}
else
{
wxLogStatus(_T("Pass 1, equal=%d different=%d"),matched,count-matched);
}
} // end of Lexicon_It
void Lexicon_De()
@@ -760,7 +831,7 @@ void Lexicon_De()
fclose(f_in);
fclose(f_out);
wxLogStatus(_T("Completed, equal=%d different=%d"),matched,count-matched);
}
} // end of Lexicon_De
extern int IsVowel(Translator *tr, int letter);
@@ -1340,8 +1411,6 @@ void CompareLexicon(int id)
break;
case MENU_LEXICON_IT:
Lexicon_It(1);
break;
case MENU_LEXICON_IT2:
Lexicon_It(2);
break;
case MENU_LEXICON_TEST:
-1
View File
@@ -160,7 +160,6 @@ enum {
MENU_LEXICON_BG,
MENU_LEXICON_DE,
MENU_LEXICON_IT,
MENU_LEXICON_IT2,
MENU_LEXICON_TEST,
MENU_TO_UTF8,
MENU_COUNT_WORDS,
-1
View File
@@ -127,7 +127,6 @@ wxMenuBar *MakeMenu(int type, const char *dict_name)
lexicon_menu->Append(MENU_LEXICON_BG, _("Bulgarian"));
lexicon_menu->Append(MENU_LEXICON_DE, _("German"));
lexicon_menu->Append(MENU_LEXICON_IT, _("Italian"));
lexicon_menu->Append(MENU_LEXICON_IT2, _("Italian, pass 2"));
// lexicon_menu->Append(MENU_LEXICON_TEST, _("Test"));
tools_menu = new wxMenu;
+44 -19
View File
@@ -808,14 +808,18 @@ if((tr->prev_dict_flags & FLAG_ALT_TRANS) && ((c2 == 0) || (wtab[0].flags & FLAG
} // end of CheckDotOrdinal
static int hu_number_e(const char *word)
{//=====================================
static int hu_number_e(const char *word, int thousandplex, int value)
{//==================================================================
// lang-hu: variant form of numbers when followed by hyphen and a suffix starting with 'a' or 'e' (but not a, e, az, ez, azt, ezt, att. ett
if((word[0] == 'a') || (word[0] == 'e'))
{
if((word[1] == ' ') || (word[1] == 'z') || ((word[1] == 't') && (word[2] == 't')))
return(0);
if(((thousandplex==1) || ((value % 1000) == 0)) && (word[1] == 'l'))
return(0); // 1000-el
return(1);
}
return(0);
@@ -927,7 +931,7 @@ int TranslateRoman(Translator *tr, char *word, char *ph_out, WORD_TAB *wtab)
{
if(!(wtab[0].flags & FLAG_ORDINAL))
{
if((wtab[0].flags & FLAG_HYPHEN_AFTER) && hu_number_e(word))
if((wtab[0].flags & FLAG_HYPHEN_AFTER) && hu_number_e(word, 0, acc))
{
// should use the 'e' form of the number
num_control |= 1;
@@ -1202,12 +1206,19 @@ static int LookupNum2(Translator *tr, int value, int control, char *ph_out)
else
{
if((control & 1) && ((units == 0) || (tr->langopts.numbers & NUM_SWAP_TENS)))
if((control & 1) &&
((units == 0) || (tr->langopts.numbers & NUM_SWAP_TENS) || (tr->langopts.numbers2 & NUM2_MULTIPLE_ORDINAL)))
{
sprintf(string,"_%dX%c", tens, ord_type);
if(Lookup(tr, string, ph_tens) != 0)
{
found_ordinal = 1;
if((units != 0) && (tr->langopts.numbers2 & NUM2_MULTIPLE_ORDINAL))
{
// Use the ordinal form of tens as well as units. Add the ordinal ending
strcat(ph_tens, ph_ordinal2);
}
}
}
if(found_ordinal == 0)
@@ -1290,6 +1301,10 @@ static int LookupNum2(Translator *tr, int value, int control, char *ph_out)
if((tr->langopts.numbers & (NUM_SWAP_TENS | NUM_AND_UNITS)) && (ph_tens[0] != 0) && (ph_digits[0] != 0))
{
Lookup(tr, "_0and", ph_and);
if(tr->langopts.numbers2 & NUM2_MULTIPLE_ORDINAL)
ph_and[0] = 0;
if(tr->langopts.numbers & NUM_SWAP_TENS)
sprintf(ph_out,"%s%s%s%s",ph_digits, ph_and, ph_tens, ph_ordinal);
else
@@ -1355,7 +1370,7 @@ static int LookupNum3(Translator *tr, int value, char *ph_out, int suppress_null
char ph_thousands[50];
char ph_hundred_and[12];
char ph_thousand_and[12];
ordinal = control & 0x22;
hundreds = value / 100;
tensunits = value % 100;
@@ -1422,22 +1437,28 @@ static int LookupNum3(Translator *tr, int value, char *ph_out, int suppress_null
suppress_null = 1;
found = 0;
if(tensunits == 0)
if((ordinal)
&& ((tensunits == 0) || (tr->langopts.numbers2 & NUM2_MULTIPLE_ORDINAL)))
{
// is there a special pronunciation for exactly n00 ?
// ordinal number
sprintf(string, "_%dCo", hundreds);
found = Lookup(tr, string, ph_digits);
if(ordinal)
if((tr->langopts.numbers2 & NUM2_MULTIPLE_ORDINAL) && (tensunits > 0))
{
// ordinal number
sprintf(string, "_%dCo", hundreds);
found = Lookup(tr, string, ph_digits);
}
if(!found)
{
sprintf(string,"_%dC0",hundreds);
found = Lookup(tr, string, ph_digits);
// Use ordinal form of hundreds, as well as for tens and units
// Add ordinal suffix to the hundreds
strcat(ph_digits, ph_ordinal2);
}
}
if((!found) && (tensunits == 0))
{
// is there a special pronunciation for exactly n00 ?
sprintf(string,"_%dC0",hundreds);
found = Lookup(tr, string, ph_digits);
}
if(!found)
{
sprintf(string,"_%dC",hundreds);
@@ -1536,6 +1557,7 @@ static int TranslateNumber_1(Translator *tr, char *word, char *ph_out, unsigned
int thousands_inc = 0;
int prev_thousands = 0;
int ordinal = 0;
int dot_ordinal;
int this_value;
int decimal_count;
int max_decimal_count;
@@ -1590,7 +1612,8 @@ static int TranslateNumber_1(Translator *tr, char *word, char *ph_out, unsigned
if(prev_thousands || (word[0] != '0'))
{
// don't check for ordinal if the number has a leading zero
ordinal = CheckDotOrdinal(tr, word, &word[ix], wtab, 0);
if((ordinal = CheckDotOrdinal(tr, word, &word[ix], wtab, 0)) != 0)
dot_ordinal = 1;
}
if((word[ix] == '.') && !isdigit(word[ix+1]) && !isdigit(word[ix+2]) && !(wtab[1].flags & FLAG_NOSPACE))
@@ -1709,7 +1732,7 @@ static int TranslateNumber_1(Translator *tr, char *word, char *ph_out, unsigned
if(tr->translator_name == L('h','u'))
{
// variant form of numbers when followed by hyphen and a suffix starting with 'a' or 'e' (but not a, e, az, ez, azt, ezt
if((wtab[thousandplex].flags & FLAG_HYPHEN_AFTER) && (thousands_exact==1) && hu_number_e(&word[suffix_ix]))
if((wtab[thousandplex].flags & FLAG_HYPHEN_AFTER) && (thousands_exact==1) && hu_number_e(&word[suffix_ix], thousandplex, value))
{
number_control |= 1; // use _1e variant of number
}
@@ -1769,9 +1792,11 @@ static int TranslateNumber_1(Translator *tr, char *word, char *ph_out, unsigned
n_digit_lookup = 2;
}
}
// if((buf_digit_lookup[0] == 0) && (*p != '0') && (dot_ordinal==0))
if((buf_digit_lookup[0] == 0) && (*p != '0'))
{
// not found, lookup only the last digit
// not found, lookup only the last digit (?? but not if dot-ordinal has been found)
if(LookupDictList(tr, &p, buf_digit_lookup, flags, FLAG_SUFX, wtab)) // don't match '0', or entries with $only
{
n_digit_lookup = 1;
+14 -12
View File
@@ -150,6 +150,20 @@ void SetSpeed(int control)
if(control == 2)
wpm = embedded_value[EMBED_S2];
// fast_settings[0] wpm where speedup starts
// fast_settings[1] wpm for maximum espeak speed
// fast_settings[2] maximum espeak speed
sonicSpeed = 1.0;
if(wpm > speed.fast_settings[0])
{
wpm2 = wpm;
wpm = speed.fast_settings[2];
// Maybe we can do better than a 10 wpm fudge factor?
sonicSpeed = ((double)wpm2 + 10)/wpm;
}
#ifdef TEST_SPEED
if(wpm > 1000)
{
@@ -262,18 +276,6 @@ void SetSpeed(int control)
if((speed.clause_pause_factor = speed.pause_factor) < 16)
speed.clause_pause_factor = 16;
}
if(wpm >= 370)
{
// TESTING
// use experimental fast settings if they have been specified in the Voice
if(speed.fast_settings[0] > 0)
speed.pause_factor = speed.fast_settings[0];
if(speed.fast_settings[1] > 0)
speed.wav_factor = speed.fast_settings[1];
if(speed.fast_settings[2] > 0)
speed.lenmod_factor = speed.lenmod2_factor = speed.fast_settings[2];
}
}
#ifdef TEST_SPEED
+967
View File
@@ -0,0 +1,967 @@
/* Sonic library
Copyright 2010
Bill Cox
This file is part of the Sonic Library.
The Sonic Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
#include "StdAfx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "sonic.h"
struct sonicStreamStruct {
short *inputBuffer;
short *outputBuffer;
short *pitchBuffer;
short *downSampleBuffer;
float speed;
float volume;
float pitch;
int numChannels;
int inputBufferSize;
int pitchBufferSize;
int outputBufferSize;
int numInputSamples;
int numOutputSamples;
int numPitchSamples;
int minPeriod;
int maxPeriod;
int maxRequired;
int remainingInputToCopy;
int sampleRate;
int prevPeriod;
int prevMaxDiff;
int prevMinDiff;
};
/* Just used for debugging */
void sonicMSG(char *format, ...)
{
char buffer[4096];
va_list ap;
FILE *file;
va_start(ap, format);
vsprintf((char *)buffer, (char *)format, ap);
va_end(ap);
file=fopen("/tmp/sonic.log", "a");
fprintf(file, "%s", buffer);
fclose(file);
}
/* Scale the samples by the factor. */
static void scaleSamples(
short *samples,
int numSamples,
float volume)
{
int fixedPointVolume = volume*4096.0f;
int value;
while(numSamples--) {
value = (*samples*fixedPointVolume) >> 12;
if(value > 32767) {
value = 32767;
} else if(value < -32767) {
value = -32767;
}
*samples++ = value;
}
}
/* Get the speed of the stream. */
float sonicGetSpeed(
sonicStream stream)
{
return stream->speed;
}
/* Set the speed of the stream. */
void sonicSetSpeed(
sonicStream stream,
float speed)
{
stream->speed = speed;
}
/* Get the pitch of the stream. */
float sonicGetPitch(
sonicStream stream)
{
return stream->pitch;
}
/* Set the pitch of the stream. */
void sonicSetPitch(
sonicStream stream,
float pitch)
{
stream->pitch = pitch;
}
/* Get the scaling factor of the stream. */
float sonicGetVolume(
sonicStream stream)
{
return stream->volume;
}
/* Set the scaling factor of the stream. */
void sonicSetVolume(
sonicStream stream,
float volume)
{
stream->volume = volume;
}
/* Get the sample rate of the stream. */
int sonicGetSampleRate(
sonicStream stream)
{
return stream->sampleRate;
}
/* Get the number of channels. */
int sonicGetNumChannels(
sonicStream stream)
{
return stream->numChannels;
}
/* Destroy the sonic stream. */
void sonicDestroyStream(
sonicStream stream)
{
if(stream->inputBuffer != NULL) {
free(stream->inputBuffer);
}
if(stream->outputBuffer != NULL) {
free(stream->outputBuffer);
}
if(stream->pitchBuffer != NULL) {
free(stream->pitchBuffer);
}
if(stream->downSampleBuffer != NULL) {
free(stream->downSampleBuffer);
}
free(stream);
}
/* Create a sonic stream. Return NULL only if we are out of memory and cannot
allocate the stream. */
sonicStream sonicCreateStream(
int sampleRate,
int numChannels)
{
sonicStream stream = (sonicStream)calloc(1, sizeof(struct sonicStreamStruct));
int minPeriod = sampleRate/SONIC_MAX_PITCH;
int maxPeriod = sampleRate/SONIC_MIN_PITCH;
int maxRequired = 2*maxPeriod;
if(stream == NULL) {
return NULL;
}
stream->inputBufferSize = maxRequired;
stream->inputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
if(stream->inputBuffer == NULL) {
sonicDestroyStream(stream);
return NULL;
}
stream->outputBufferSize = maxRequired;
stream->outputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
if(stream->outputBuffer == NULL) {
sonicDestroyStream(stream);
return NULL;
}
stream->pitchBufferSize = maxRequired;
stream->pitchBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
if(stream->pitchBuffer == NULL) {
sonicDestroyStream(stream);
return NULL;
}
stream->downSampleBuffer = (short *)calloc(maxRequired, sizeof(short));
stream->speed = 1.0f;
stream->pitch = 1.0f;
stream->volume = 1.0f;
stream->sampleRate = sampleRate;
stream->numChannels = numChannels;
stream->minPeriod = minPeriod;
stream->maxPeriod = maxPeriod;
stream->maxRequired = maxRequired;
return stream;
}
/* Enlarge the output buffer if needed. */
static int enlargeOutputBufferIfNeeded(
sonicStream stream,
int numSamples)
{
if(stream->numOutputSamples + numSamples > stream->outputBufferSize) {
stream->outputBufferSize += (stream->outputBufferSize >> 1) + numSamples;
stream->outputBuffer = (short *)realloc(stream->outputBuffer,
stream->outputBufferSize*sizeof(short)*stream->numChannels);
if(stream->outputBuffer == NULL) {
return 0;
}
}
return 1;
}
/* Enlarge the input buffer if needed. */
static int enlargeInputBufferIfNeeded(
sonicStream stream,
int numSamples)
{
if(stream->numInputSamples + numSamples > stream->inputBufferSize) {
stream->inputBufferSize += (stream->inputBufferSize >> 1) + numSamples;
stream->inputBuffer = (short *)realloc(stream->inputBuffer,
stream->inputBufferSize*sizeof(short)*stream->numChannels);
if(stream->inputBuffer == NULL) {
return 0;
}
}
return 1;
}
/* Add the input samples to the input buffer. */
static int addFloatSamplesToInputBuffer(
sonicStream stream,
float *samples,
int numSamples)
{
short *buffer;
int count = numSamples*stream->numChannels;
if(numSamples == 0) {
return 1;
}
if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
return 0;
}
buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
while(count--) {
*buffer++ = (*samples++)*32767.0f;
}
stream->numInputSamples += numSamples;
return 1;
}
/* Add the input samples to the input buffer. */
static int addShortSamplesToInputBuffer(
sonicStream stream,
short *samples,
int numSamples)
{
if(numSamples == 0) {
return 1;
}
if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
return 0;
}
memcpy(stream->inputBuffer + stream->numInputSamples*stream->numChannels, samples,
numSamples*sizeof(short)*stream->numChannels);
stream->numInputSamples += numSamples;
return 1;
}
/* Add the input samples to the input buffer. */
static int addUnsignedCharSamplesToInputBuffer(
sonicStream stream,
unsigned char *samples,
int numSamples)
{
short *buffer;
int count = numSamples*stream->numChannels;
if(numSamples == 0) {
return 1;
}
if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
return 0;
}
buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
while(count--) {
*buffer++ = (*samples++ - 128) << 8;
}
stream->numInputSamples += numSamples;
return 1;
}
/* Remove input samples that we have already processed. */
static void removeInputSamples(
sonicStream stream,
int position)
{
int remainingSamples = stream->numInputSamples - position;
if(remainingSamples > 0) {
memmove(stream->inputBuffer, stream->inputBuffer + position*stream->numChannels,
remainingSamples*sizeof(short)*stream->numChannels);
}
stream->numInputSamples = remainingSamples;
}
/* Just copy from the array to the output buffer */
static int copyToOutput(
sonicStream stream,
short *samples,
int numSamples)
{
if(!enlargeOutputBufferIfNeeded(stream, numSamples)) {
return 0;
}
memcpy(stream->outputBuffer + stream->numOutputSamples*stream->numChannels,
samples, numSamples*sizeof(short)*stream->numChannels);
stream->numOutputSamples += numSamples;
return numSamples;
}
/* Just copy from the input buffer to the output buffer. Return 0 if we fail to
resize the output buffer. Otherwise, return numSamples */
static int copyInputToOutput(
sonicStream stream,
int position)
{
int numSamples = stream->remainingInputToCopy;
if(numSamples > stream->maxRequired) {
numSamples = stream->maxRequired;
}
if(!copyToOutput(stream, stream->inputBuffer + position*stream->numChannels,
numSamples)) {
return 0;
}
stream->remainingInputToCopy -= numSamples;
return numSamples;
}
/* Read data out of the stream. Sometimes no data will be available, and zero
is returned, which is not an error condition. */
int sonicReadFloatFromStream(
sonicStream stream,
float *samples,
int maxSamples)
{
int numSamples = stream->numOutputSamples;
int remainingSamples = 0;
short *buffer;
int count;
if(numSamples == 0) {
return 0;
}
if(numSamples > maxSamples) {
remainingSamples = numSamples - maxSamples;
numSamples = maxSamples;
}
buffer = stream->outputBuffer;
count = numSamples*stream->numChannels;
while(count--) {
*samples++ = (*buffer++)/32767.0f;
}
if(remainingSamples > 0) {
memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
remainingSamples*sizeof(short)*stream->numChannels);
}
stream->numOutputSamples = remainingSamples;
return numSamples;
}
/* Read short data out of the stream. Sometimes no data will be available, and zero
is returned, which is not an error condition. */
int sonicReadShortFromStream(
sonicStream stream,
short *samples,
int maxSamples)
{
int numSamples = stream->numOutputSamples;
int remainingSamples = 0;
if(numSamples == 0) {
return 0;
}
if(numSamples > maxSamples) {
remainingSamples = numSamples - maxSamples;
numSamples = maxSamples;
}
memcpy(samples, stream->outputBuffer, numSamples*sizeof(short)*stream->numChannels);
if(remainingSamples > 0) {
memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
remainingSamples*sizeof(short)*stream->numChannels);
}
stream->numOutputSamples = remainingSamples;
return numSamples;
}
/* Read unsigned char data out of the stream. Sometimes no data will be available, and zero
is returned, which is not an error condition. */
int sonicReadUnsignedCharFromStream(
sonicStream stream,
unsigned char *samples,
int maxSamples)
{
int numSamples = stream->numOutputSamples;
int remainingSamples = 0;
short *buffer;
int count;
if(numSamples == 0) {
return 0;
}
if(numSamples > maxSamples) {
remainingSamples = numSamples - maxSamples;
numSamples = maxSamples;
}
buffer = stream->outputBuffer;
count = numSamples*stream->numChannels;
while(count--) {
*samples++ = (char)((*buffer++) >> 8) + 128;
}
if(remainingSamples > 0) {
memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
remainingSamples*sizeof(short)*stream->numChannels);
}
stream->numOutputSamples = remainingSamples;
return numSamples;
}
/* Force the sonic stream to generate output using whatever data it currently
has. No extra delay will be added to the output, but flushing in the middle of
words could introduce distortion. */
int sonicFlushStream(
sonicStream stream)
{
int maxRequired = stream->maxRequired;
int numSamples = stream->numInputSamples;
int remainingSpace, numOutputSamples, expectedSamples;
if(numSamples == 0) {
return 1;
}
if(numSamples >= maxRequired && !sonicWriteShortToStream(stream, NULL, 0)) {
return 0;
}
numSamples = stream->numInputSamples; /* Now numSamples < maxRequired */
if(numSamples == 0) {
return 1;
}
remainingSpace = maxRequired - numSamples;
memset(stream->inputBuffer + numSamples*stream->numChannels, 0,
remainingSpace*sizeof(short)*stream->numChannels);
stream->numInputSamples = maxRequired;
numOutputSamples = stream->numOutputSamples;
if(!sonicWriteShortToStream(stream, NULL, 0)) {
return 0;
}
/* Throw away any extra samples we generated due to the silence we added */
expectedSamples = (int)(numSamples*stream->speed + 0.5);
if(stream->numOutputSamples > numOutputSamples + expectedSamples) {
stream->numOutputSamples = numOutputSamples + expectedSamples;
}
return 1;
}
/* Return the number of samples in the output buffer */
int sonicSamplesAvailable(
sonicStream stream)
{
return stream->numOutputSamples;
}
/* If skip is greater than one, average skip samples togther and write them to
the down-sample buffer. If numChannels is greater than one, mix the channels
together as we down sample. */
static void downSampleInput(
sonicStream stream,
short *samples,
int skip)
{
int numSamples = stream->maxRequired/skip;
int samplesPerValue = stream->numChannels*skip;
int i, j;
int value;
short *downSamples = stream->downSampleBuffer;
for(i = 0; i < numSamples; i++) {
value = 0;
for(j = 0; j < samplesPerValue; j++) {
value += *samples++;
}
value /= samplesPerValue;
*downSamples++ = value;
}
}
/* Find the best frequency match in the range, and given a sample skip multiple.
For now, just find the pitch of the first channel. */
static int findPitchPeriodInRange(
short *samples,
int minPeriod,
int maxPeriod,
int *retMinDiff,
int *retMaxDiff)
{
int period, bestPeriod = 0;
short *s, *p, sVal, pVal;
unsigned long diff, minDiff = 1, maxDiff = 0;
int i;
for(period = minPeriod; period <= maxPeriod; period++) {
diff = 0;
s = samples;
p = samples + period;
for(i = 0; i < period; i++) {
sVal = *s++;
pVal = *p++;
diff += sVal >= pVal? (unsigned short)(sVal - pVal) :
(unsigned short)(pVal - sVal);
}
/* Note that the highest number of samples we add into diff will be less
than 256, since we skip samples. Thus, diff is a 24 bit number, and
we can safely multiply by numSamples without overflow */
if(diff*bestPeriod < minDiff*period) {
minDiff = diff;
bestPeriod = period;
}
if(diff*bestPeriod > maxDiff*period) {
maxDiff = diff;
}
}
*retMinDiff = minDiff;
*retMaxDiff = maxDiff;
return bestPeriod;
}
/* At abrupt ends of voiced words, we can have pitch periods that are better
aproximated by the previous pitch period estimate. Try to detect this case. */
static int prevPeriodBetter(
sonicStream stream,
int period,
int minDiff,
int maxDiff)
{
if(maxDiff*3/2 < stream->prevMaxDiff && (maxDiff*3.0f)*stream->prevMinDiff <
(float)stream->prevMaxDiff*minDiff*2) {
return 1;
}
return 0;
}
/* Find the pitch period. This is a critical step, and we may have to try
multiple ways to get a good answer. This version uses AMDF. To improve
speed, we down sample by an integer factor get in the 11KHz range, and then
do it again with a narrower frequency range without down sampling */
static int findPitchPeriod(
sonicStream stream,
short *samples)
{
int minPeriod = stream->minPeriod;
int maxPeriod = stream->maxPeriod;
int sampleRate = stream->sampleRate;
int minDiff, maxDiff, retPeriod;
int skip = 1;
int period;
if(sampleRate > SONIC_AMDF_FREQ) {
skip = sampleRate/SONIC_AMDF_FREQ;
}
if(stream->numChannels == 1 && skip == 1) {
period = findPitchPeriodInRange(samples, minPeriod, maxPeriod, &minDiff, &maxDiff);
} else {
downSampleInput(stream, samples, skip);
period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod/skip,
maxPeriod/skip, &minDiff, &maxDiff);
if(skip != 1) {
period *= skip;
minPeriod = period - (skip << 2);
maxPeriod = period + (skip << 2);
if(minPeriod < stream->minPeriod) {
minPeriod = stream->minPeriod;
}
if(maxPeriod > stream->maxPeriod) {
maxPeriod = stream->maxPeriod;
}
if(stream->numChannels == 1) {
period = findPitchPeriodInRange(samples, minPeriod, maxPeriod,
&minDiff, &maxDiff);
} else {
downSampleInput(stream, samples, 1);
period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod,
maxPeriod, &minDiff, &maxDiff);
}
}
}
if(prevPeriodBetter(stream, period, minDiff, maxDiff)) {
retPeriod = stream->prevPeriod;
} else {
retPeriod = period;
}
stream->prevMinDiff = minDiff;
stream->prevMaxDiff = maxDiff;
stream->prevPeriod = period;
return retPeriod;
}
/* Overlap two sound segments, ramp the volume of one down, while ramping the
other one from zero up, and add them, storing the result at the output. */
static void overlapAdd(
int numSamples,
int numChannels,
short *out,
short *rampDown,
short *rampUp)
{
short *o, *u, *d;
int i, t;
for(i = 0; i < numChannels; i++) {
o = out + i;
u = rampUp + i;
d = rampDown + i;
for(t = 0; t < numSamples; t++) {
*o = (*d*(numSamples - t) + *u*t)/numSamples;
o += numChannels;
d += numChannels;
u += numChannels;
}
}
}
/* Overlap two sound segments, ramp the volume of one down, while ramping the
other one from zero up, and add them, storing the result at the output. */
static void overlapAddWithSeparation(
int numSamples,
int numChannels,
int separation,
short *out,
short *rampDown,
short *rampUp)
{
short *o, *u, *d;
int i, t;
for(i = 0; i < numChannels; i++) {
o = out + i;
u = rampUp + i;
d = rampDown + i;
for(t = 0; t < numSamples + separation; t++) {
if(t < separation) {
*o = *d*(numSamples - t)/numSamples;
d += numChannels;
} else if(t < numSamples) {
*o = (*d*(numSamples - t) + *u*(t - separation))/numSamples;
d += numChannels;
u += numChannels;
} else {
*o = *u*(t - separation)/numSamples;
u += numChannels;
}
o += numChannels;
}
}
}
/* Just move the new samples in the output buffer to the pitch bufer */
static int moveNewSamplesToPitchBuffer(
sonicStream stream,
int originalNumOutputSamples)
{
int numSamples = stream->numOutputSamples - originalNumOutputSamples;
int numChannels = stream->numChannels;
if(stream->numPitchSamples + numSamples > stream->pitchBufferSize) {
stream->pitchBufferSize += (stream->pitchBufferSize >> 1) + numSamples;
stream->pitchBuffer = (short *)realloc(stream->pitchBuffer,
stream->pitchBufferSize*sizeof(short)*numChannels);
if(stream->pitchBuffer == NULL) {
return 0;
}
}
memcpy(stream->pitchBuffer + stream->numPitchSamples*numChannels,
stream->outputBuffer + originalNumOutputSamples*numChannels,
numSamples*sizeof(short)*numChannels);
stream->numOutputSamples = originalNumOutputSamples;
stream->numPitchSamples += numSamples;
return 1;
}
/* Remove processed samples from the pitch buffer. */
static void removePitchSamples(
sonicStream stream,
int numSamples)
{
int numChannels = stream->numChannels;
short *source = stream->pitchBuffer + numSamples*numChannels;
if(numSamples == 0) {
return;
}
if(numSamples != stream->numPitchSamples) {
memmove(stream->pitchBuffer, source, (stream->numPitchSamples -
numSamples)*sizeof(short)*numChannels);
}
stream->numPitchSamples -= numSamples;
}
/* Change the pitch. The latency this introduces could be reduced by looking at
past samples to determine pitch, rather than future. */
static int adjustPitch(
sonicStream stream,
int originalNumOutputSamples)
{
float pitch = stream->pitch;
int numChannels = stream->numChannels;
int period, newPeriod, separation;
int position = 0;
short *out, *rampDown, *rampUp;
if(stream->numOutputSamples == originalNumOutputSamples) {
return 1;
}
if(!moveNewSamplesToPitchBuffer(stream, originalNumOutputSamples)) {
return 0;
}
while(stream->numPitchSamples - position >= stream->maxRequired) {
period = findPitchPeriod(stream, stream->pitchBuffer + position*numChannels);
newPeriod = period/pitch;
if(!enlargeOutputBufferIfNeeded(stream, newPeriod)) {
return 0;
}
out = stream->outputBuffer + stream->numOutputSamples*numChannels;
if(pitch >= 1.0f) {
rampDown = stream->pitchBuffer + position*numChannels;
rampUp = stream->pitchBuffer + (position + period - newPeriod)*numChannels;
overlapAdd(newPeriod, numChannels, out, rampDown, rampUp);
} else {
rampDown = stream->pitchBuffer + position*numChannels;
rampUp = stream->pitchBuffer + position*numChannels;
separation = newPeriod - period;
overlapAddWithSeparation(period, numChannels, separation, out, rampDown, rampUp);
}
stream->numOutputSamples += newPeriod;
position += period;
}
removePitchSamples(stream, position);
return 1;
}
/* Skip over a pitch period, and copy period/speed samples to the output */
static int skipPitchPeriod(
sonicStream stream,
short *samples,
float speed,
int period)
{
long newSamples;
int numChannels = stream->numChannels;
if(speed >= 2.0f) {
newSamples = period/(speed - 1.0f);
} else if(speed > 1.0f) {
newSamples = period;
stream->remainingInputToCopy = period*(2.0f - speed)/(speed - 1.0f);
}
if(!enlargeOutputBufferIfNeeded(stream, newSamples)) {
return 0;
}
overlapAdd(newSamples, numChannels, stream->outputBuffer +
stream->numOutputSamples*numChannels, samples, samples + period*numChannels);
stream->numOutputSamples += newSamples;
return newSamples;
}
/* Insert a pitch period, and determine how much input to copy directly. */
static int insertPitchPeriod(
sonicStream stream,
short *samples,
float speed,
int period)
{
long newSamples;
short *out;
int numChannels = stream->numChannels;
if(speed < 0.5f) {
newSamples = period*speed/(1.0f - speed);
} else {
newSamples = period;
stream->remainingInputToCopy = period*(2.0f*speed - 1.0f)/(1.0f - speed);
}
if(!enlargeOutputBufferIfNeeded(stream, period + newSamples)) {
return 0;
}
out = stream->outputBuffer + stream->numOutputSamples*numChannels;
memcpy(out, samples, period*sizeof(short)*numChannels);
out = stream->outputBuffer + (stream->numOutputSamples + period)*numChannels;
overlapAdd(newSamples, numChannels, out, samples + period*numChannels, samples);
stream->numOutputSamples += period + newSamples;
return newSamples;
}
/* Resample as many pitch periods as we have buffered on the input. Return 0 if
we fail to resize an input or output buffer. Also scale the output by the volume. */
static int changeSpeed(
sonicStream stream,
float speed)
{
short *samples;
int numSamples = stream->numInputSamples;
int position = 0, period, newSamples;
int maxRequired = stream->maxRequired;
if(stream->numInputSamples < maxRequired) {
return 1;
}
do {
if(stream->remainingInputToCopy > 0) {
newSamples = copyInputToOutput(stream, position);
position += newSamples;
} else {
samples = stream->inputBuffer + position*stream->numChannels;
period = findPitchPeriod(stream, samples);
if(speed > 1.0) {
newSamples = skipPitchPeriod(stream, samples, speed, period);
position += period + newSamples;
} else {
newSamples = insertPitchPeriod(stream, samples, speed, period);
position += newSamples;
}
}
if(newSamples == 0) {
return 0; /* Failed to resize output buffer */
}
} while(position + maxRequired <= numSamples);
removeInputSamples(stream, position);
return 1;
}
/* Resample as many pitch periods as we have buffered on the input. Return 0 if
we fail to resize an input or output buffer. Also scale the output by the volume. */
static int processStreamInput(
sonicStream stream)
{
int originalNumOutputSamples = stream->numOutputSamples;
float speed = stream->speed/stream->pitch;
if(speed > 1.00001 || speed < 0.99999) {
changeSpeed(stream, speed);
} else {
if(!copyToOutput(stream, stream->inputBuffer, stream->numInputSamples)) {
return 0;
}
stream->numInputSamples = 0;
}
if(stream->pitch != 1.0f) {
if(!adjustPitch(stream, originalNumOutputSamples)) {
return 0;
}
}
if(stream->volume != 1.0f) {
/* Adjust output volume. */
scaleSamples(stream->outputBuffer + originalNumOutputSamples*stream->numChannels,
(stream->numOutputSamples - originalNumOutputSamples)*stream->numChannels,
stream->volume);
}
return 1;
}
/* Write floating point data to the input buffer and process it. */
int sonicWriteFloatToStream(
sonicStream stream,
float *samples,
int numSamples)
{
if(!addFloatSamplesToInputBuffer(stream, samples, numSamples)) {
return 0;
}
return processStreamInput(stream);
}
/* Simple wrapper around sonicWriteFloatToStream that does the short to float
conversion for you. */
int sonicWriteShortToStream(
sonicStream stream,
short *samples,
int numSamples)
{
if(!addShortSamplesToInputBuffer(stream, samples, numSamples)) {
return 0;
}
return processStreamInput(stream);
}
/* Simple wrapper around sonicWriteFloatToStream that does the unsigned char to float
conversion for you. */
int sonicWriteUnsignedCharToStream(
sonicStream stream,
unsigned char *samples,
int numSamples)
{
if(!addUnsignedCharSamplesToInputBuffer(stream, samples, numSamples)) {
return 0;
}
return processStreamInput(stream);
}
/* This is a non-stream oriented interface to just change the speed of a sound sample */
int sonicChangeFloatSpeed(
float *samples,
int numSamples,
float speed,
float pitch,
float volume,
int sampleRate,
int numChannels)
{
sonicStream stream = sonicCreateStream(sampleRate, numChannels);
sonicSetSpeed(stream, speed);
sonicSetPitch(stream, pitch);
sonicSetVolume(stream, volume);
sonicWriteFloatToStream(stream, samples, numSamples);
sonicFlushStream(stream);
numSamples = sonicSamplesAvailable(stream);
sonicReadFloatFromStream(stream, samples, numSamples);
sonicDestroyStream(stream);
return numSamples;
}
/* This is a non-stream oriented interface to just change the speed of a sound sample */
int sonicChangeShortSpeed(
short *samples,
int numSamples,
float speed,
float pitch,
float volume,
int sampleRate,
int numChannels)
{
sonicStream stream = sonicCreateStream(sampleRate, numChannels);
sonicSetSpeed(stream, speed);
sonicSetPitch(stream, pitch);
sonicSetVolume(stream, volume);
sonicWriteShortToStream(stream, samples, numSamples);
sonicFlushStream(stream);
numSamples = sonicSamplesAvailable(stream);
sonicReadShortFromStream(stream, samples, numSamples);
sonicDestroyStream(stream);
return numSamples;
}
+138
View File
@@ -0,0 +1,138 @@
/* Sonic library
Copyright 2010
Bill Cox
This file is part of the Sonic Library.
The Sonic Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
/*
The Sonic Library implements Pitch Based Resampling, which is a new algorithm
invented by Bill Cox for the specific purpose of speeding up speech by high
factors at high quality. It generates smooth speech at speed up factors as high
as 6X, possibly more. It is also capable of slowing down speech, and generates
high quality results regardless of the speed up or slow down factor. For
speeding up speech by 2X or more, the following equation is used:
newSamples = period/(speed - 1.0)
scale = 1.0/newSamples;
where period is the current pitch period, determined using AMDF or any other
pitch estimator, and speed is the speedup factor. If the current position in
the input stream is pointed to by "samples", and the current output stream
position is pointed to by "out", then newSamples number of samples can be
generated with:
out[t] = (samples[t]*(newSamples - t) + samples[t + period]*t)/newSamples;
where t = 0 to newSamples - 1.
For speed factors < 2X, an algorithm similar to PICOLA is used. The above
algorithm is first used to double the speed of one pitch period. Then, enough
input is directly copied from the input to the output to achieve the desired
speed up facter, where 1.0 < speed < 2.0. The amount of data copied is derived:
speed = (2*period + length)/(period + length)
speed*length + speed*period = 2*period + length
length(speed - 1) = 2*period - speed*period
length = period*(2 - speed)/(speed - 1)
For slowing down speech where 0.5 < speed < 1.0, a pitch period is inserted into
the output twice, and length of input is copied from the input to the output
until the output desired speed is reached. The length of data copied is:
length = period*(speed - 0.5)/(1 - speed)
For slow down factors between 0.5 and 0.5, no data is copied, and an algorithm
similar to high speed factors is used.
*/
#ifdef __cplusplus
extern "C" {
#endif
/* This specifies the range of voice pitches we try to match.
Note that if we go lower than 65, we could overflow in findPitchInRange */
#define SONIC_MIN_PITCH 65
#define SONIC_MAX_PITCH 400
/* These are used to down-sample some inputs to improve speed */
#define SONIC_AMDF_FREQ 4000
struct sonicStreamStruct;
typedef struct sonicStreamStruct *sonicStream;
/* For all of the following functions, numChannels is multiplied by numSamples
to determine the actual number of values read or returned. */
/* Create a sonic stream. Return NULL only if we are out of memory and cannot
allocate the stream. Set numChannels to 1 for mono, and 2 for stereo. */
sonicStream sonicCreateStream(int sampleRate, int numChannels);
/* Destroy the sonic stream. */
void sonicDestroyStream(sonicStream stream);
/* Use this to write floating point data to be speed up or down into the stream.
Values must be between -1 and 1. Return 0 if memory realloc failed, otherwise 1 */
int sonicWriteFloatToStream(sonicStream stream, float *samples, int numSamples);
/* Use this to write 16-bit data to be speed up or down into the stream.
Return 0 if memory realloc failed, otherwise 1 */
int sonicWriteShortToStream(sonicStream stream, short *samples, int numSamples);
/* Use this to write 8-bit unsigned data to be speed up or down into the stream.
Return 0 if memory realloc failed, otherwise 1 */
int sonicWriteUnsignedCharToStream(sonicStream stream, unsigned char *samples, int numSamples);
/* Use this to read floating point data out of the stream. Sometimes no data
will be available, and zero is returned, which is not an error condition. */
int sonicReadFloatFromStream(sonicStream stream, float *samples, int maxSamples);
/* Use this to read 16-bit data out of the stream. Sometimes no data will
be available, and zero is returned, which is not an error condition. */
int sonicReadShortFromStream(sonicStream stream, short *samples, int maxSamples);
/* Use this to read 8-bit unsigned data out of the stream. Sometimes no data will
be available, and zero is returned, which is not an error condition. */
int sonicReadUnsignedCharFromStream(sonicStream stream, unsigned char *samples, int maxSamples);
/* Force the sonic stream to generate output using whatever data it currently
has. No extra delay will be added to the output, but flushing in the middle of
words could introduce distortion. */
int sonicFlushStream(sonicStream stream);
/* Return the number of samples in the output buffer */
int sonicSamplesAvailable(sonicStream stream);
/* Get the speed of the stream. */
float sonicGetSpeed(sonicStream stream);
/* Set the speed of the stream. */
void sonicSetSpeed(sonicStream stream, float speed);
/* Get the pitch of the stream. */
float sonicGetPitch(sonicStream stream);
/* Set the pitch of the stream. */
void sonicSetPitch(sonicStream stream, float pitch);
/* Get the scaling factor of the stream. */
float sonicGetVolume(sonicStream stream);
/* Set the scaling factor of the stream. */
void sonicSetVolume(sonicStream stream, float volume);
/* Get the sample rate of the stream. */
int sonicGetSampleRate(sonicStream stream);
/* Get the number of channels. */
int sonicGetNumChannels(sonicStream stream);
/* This is a non-stream oriented interface to just change the speed of a sound
sample. It works in-place on the sample array, so there must be at least
speed*numSamples available space in the array. Returns the new number of samples. */
int sonicChangeFloatSpeed(float *samples, int numSamples, float speed, float pitch,
float volume, int sampleRate, int numChannels);
/* This is a non-stream oriented interface to just change the speed of a sound
sample. It works in-place on the sample array, so there must be at least
speed*numSamples available space in the array. Returns the new number of samples. */
int sonicChangeShortSpeed(short *samples, int numSamples, float speed, float pitch,
float volume, int sampleRate, int numChannels);
#ifdef __cplusplus
}
#endif
+1 -1
View File
@@ -289,7 +289,7 @@ void MarkerEvent(int type, unsigned int char_position, int value, unsigned char
static int WavegenFile(void)
{//=========================
int finished;
unsigned char wav_outbuf[512];
unsigned char wav_outbuf[1024];
char fname[210];
out_ptr = out_start = wav_outbuf;
+2 -2
View File
@@ -528,8 +528,8 @@ static espeak_ERROR Synthesize(unsigned int unique_identifier, const void *text,
}
}
}
}
return(EE_OK);
}
return(EE_OK);
} // end of Synthesize
#ifdef DEBUG_ENABLED
+15 -2
View File
@@ -35,8 +35,8 @@
#include "translate.h"
#include "wave.h"
const char *version_string = "1.44.04 14.Sep.10";
const int version_phdata = 0x014404;
const char *version_string = "1.44.18 03.Dec.10";
const int version_phdata = 0x014415;
int option_device_number = -1;
FILE *f_logespeak = NULL;
@@ -593,6 +593,7 @@ static bool InterpretCondition(Translator *tr, int control, PHONEME_LIST *plist,
int which;
unsigned int data;
int instn2;
int count;
PHONEME_TAB *ph;
PHONEME_LIST *plist_this;
static int ph_position[8] = {0, 1, 2, 3, 2, 0, 1, 3}; // prevPh, thisPh, nextPh, next2Ph, nextPhW, prevPhW, nextVowel, next2PhW
@@ -737,6 +738,18 @@ static bool InterpretCondition(Translator *tr, int control, PHONEME_LIST *plist,
case 12: // isVoiced
return((ph->type == phVOWEL) || (ph->type == phLIQUID) || (ph->phflags & phVOICED));
}
case 13: // isFirstVowel
count = 0;
for(;;)
{
if(plist->ph->type == phVOWEL)
count++;
if(plist->sourceix != 0)
break;
plist--;
}
return(count==1);
break;
}
+2
View File
@@ -326,6 +326,7 @@ typedef struct {
#define i_isNotVowel 0x8a
#define i_isFinalVowel 0x8b
#define i_isVoiced 0x8c
#define i_isFirstVowel 0x8d
// place of articulation
#define i_isVel 0x28
@@ -535,6 +536,7 @@ extern t_espeak_callback* synth_callback;
extern int option_log_frames;
extern const char *version_string;
extern const int version_phdata;
extern double sonicSpeed;
#define N_SOUNDICON_TAB 80 // total entries in soundicon_tab
#define N_SOUNDICON_SLOTS 4 // number of slots reserved for dynamic loading of audio files
+22 -6
View File
@@ -328,6 +328,9 @@ Translator *SelectTranslator(const char *name)
int name2 = 0;
Translator *tr;
static const short stress_lengths_fr[8] = {190, 170, 190, 200, 0, 0, 190, 240};
static const unsigned char stress_amps_fr[8] = {18,16, 18,18, 18,18, 18,18 };
static const unsigned char stress_amps_sk[8] = {17,17, 20,20, 20,22, 22,21 };
static const short stress_lengths_sk[8] = {190,190, 210,210, 0,0, 210,210};
@@ -355,6 +358,17 @@ Translator *SelectTranslator(const char *name)
}
break;
case L('a','m'): // Amharic, Ethiopia
{
SetupTranslator(tr,stress_lengths_fr,stress_amps_fr);
tr->langopts.stress_rule = STRESSPOSN_1L;
tr->langopts.stress_flags = 0x0024; // don't use secondary stress
tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words
}
break;
case L('a','r'): // Arabic
tr->letter_bits_offset = OFFSET_ARABIC;
tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words
@@ -496,7 +510,7 @@ Translator *SelectTranslator(const char *name)
tr->langopts.param[LOPT_SONORANT_MIN] = 130; // limit the shortening of sonorants before short vowels
tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA;
tr->langopts.numbers2 = 0x2; // variant form of numbers before thousands
tr->langopts.numbers2 = 0x2 | NUM2_MULTIPLE_ORDINAL; // variant form of numbers before thousands
if(name2 == L_grc)
{
@@ -550,6 +564,7 @@ Translator *SelectTranslator(const char *name)
tr->langopts.param[LOPT_SONORANT_MIN] = 120; // limit the shortening of sonorants before short vowels
tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_AND_UNITS | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_ROMAN | NUM_ROMAN_AFTER;
tr->langopts.numbers2 = NUM2_MULTIPLE_ORDINAL;
if(name2 == L('c','a'))
{
@@ -611,16 +626,12 @@ Translator *SelectTranslator(const char *name)
case L('f','r'): // french
{
static const short stress_lengths_fr[8] = {190, 170, 190, 200, 0, 0, 190, 240};
static const unsigned char stress_amps_fr[8] = {18,16, 20,20, 20,22, 18,18 };
SetupTranslator(tr,stress_lengths_fr,stress_amps_fr);
tr->langopts.stress_rule = STRESSPOSN_1R; // stress on final syllable
tr->langopts.stress_flags = 0x0024; // don't use secondary stress
tr->langopts.param[LOPT_IT_LENGTHEN] = 1; // remove lengthen indicator from unstressed syllables
tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
tr->langopts.accents = 2; // Say "Capital" after the letter.
tr->langopts.vowel_pause = 0;
tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_ALLOW_SPACE | NUM_OMIT_1_HUNDRED | NUM_NOPAUSE | NUM_ROMAN | NUM_ROMAN_CAPITALS | NUM_ROMAN_AFTER | NUM_VIGESIMAL | NUM_DFRACTION_4;
SetLetterVowel(tr,'y');
@@ -685,7 +696,6 @@ Translator *SelectTranslator(const char *name)
case L('h','t'): // Haitian Creole
// static const short stress_lengths_fr[8] = {190, 170, 190, 200, 0, 0, 235, 240};
// memcpy(tr->stress_lengths,stress_lengths_fr,sizeof(tr->stress_lengths));
tr->langopts.stress_rule = STRESSPOSN_1R; // stress on final syllable
tr->langopts.stress_flags = 0x0024; // don't use secondary stress
@@ -905,6 +915,7 @@ SetLengthMods(tr,3); // all equal
tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_SWAP_TENS | NUM_OMIT_1_HUNDRED | NUM_ALLOW_SPACE | NUM_1900 | NUM_ORDINAL_DOT;
tr->langopts.ordinal_indicator = "e";
tr->langopts.stress_flags = S_FIRST_PRIMARY;
memcpy(tr->stress_lengths,stress_lengths_nl,sizeof(tr->stress_lengths));
}
break;
@@ -1201,6 +1212,11 @@ SetLengthMods(tr,3); // all equal
}
break;
case L('w','o'):
tr->langopts.stress_rule = STRESSPOSN_1L;
tr->langopts.numbers = NUM_AND_UNITS | NUM_HUNDRED_AND | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_SINGLE_STRESS;
break;
case L('z','h'):
case L_zhy:
{
+2 -2
View File
@@ -2779,8 +2779,8 @@ if((c == '/') && (tr->langopts.testing & 2) && IsDigit09(next_in) && IsAlpha(pre
// dot after a word, with space following, probably an abbreviation
words[word_count-1].flags |= FLAG_HAS_DOT;
if(IsSpace(next_in))
c = ' '; // remove the dot if it's followed by a space, so that it's not pronounced
if(IsSpace(next_in) || (next_in == '-'))
c = ' '; // remove the dot if it's followed by a space or hyphen, so that it's not pronounced
}
}
else
+7 -2
View File
@@ -145,10 +145,11 @@
#define RULE_CONDITION 5 // followed by condition number (byte)
#define RULE_GROUP_START 6
#define RULE_GROUP_END 7
#define RULE_LINENUM 8 // next 2 bytes give a line number, for debugging purposes
#define RULE_PRE_ATSTART 8 // as RULE_PRE but also match with 'start of word'
#define RULE_LINENUM 9 // next 2 bytes give a line number, for debugging purposes
#define RULE_SPACE 32 // ascii space
#define RULE_SYLLABLE 9 // @
#define RULE_SYLLABLE 21 // @
#define RULE_STRESSED 10 // &
#define RULE_DOUBLE 11 // %
#define RULE_INC_SCORE 12 // +
@@ -342,6 +343,7 @@ typedef struct {
#define S_FINAL_NO_2 0x10
#define S_NO_AUTO_2 0x20
#define S_2_TO_HEAVY 0x40
#define S_FIRST_PRIMARY 0x80
#define S_2_SYL_2 0x1000
#define S_INITIAL_2 0x2000
#define S_NO_AUTO_DIM 0x10000
@@ -353,6 +355,7 @@ typedef struct {
// bit4=don't allow secondary stress on last syllable
// bit5-don't use automatic secondary stress
// bit6=light syllable followed by heavy, move secondary stress to the heavy syllable. LANG=Finnish
// bit7=if more than one primary stress, make the subsequent primaries to secondary stress
// bit8=stress last syllable if it doesn't end in a vowel
// bit9=stress last syllable if it doesn't end in vowel or "s" or "n" LANG=Spanish
// bit12= In a 2-syllable word, if one has primary stress then give the other secondary stress
@@ -427,11 +430,13 @@ typedef struct {
// bit27= Roman numbers are ordinal numbers
int numbers;
#define NUM2_MULTIPLE_ORDINAL 0x1000
// bits 1-4 use variant form of numbers before thousands,millions,etc.
// bit6=(LANG=pl) two forms of plural, M or MA
// bit7=(LANG-ru) use MB for 1 thousand, million, etc
// bit8=(LANG=cs,sk) two forms of plural, M or MA
// bit9=(LANG=rw) say "thousand" and "million" before its number, not after
// bit12=(LANG=el,es) use ordinal form of hundreds and tens as well as units
int numbers2;
#define BREAK_THOUSANDS 0x49249248
+6 -2
View File
@@ -152,7 +152,7 @@ static MNEM_TAB keyword_tab[] = {
{"mbrola", V_MBROLA},
{"consonants", V_CONSONANTS},
{"klatt", V_KLATT},
{"fast_test", V_FAST},
{"fast_test2", V_FAST},
{"speed", V_SPEED},
// these just set a value in langopts.param[]
@@ -410,7 +410,10 @@ void VoiceReset(int tone_only)
voice->consonant_ampv = 100;
voice->samplerate = 22050;
memset(voice->klattv,0,sizeof(voice->klattv));
memset(speed.fast_settings,0,sizeof(speed.fast_settings));
speed.fast_settings[0] = 350;
speed.fast_settings[1] = 800;
speed.fast_settings[2] = 175;
#ifdef PLATFORM_RISCOS
voice->roughness = 1;
@@ -509,6 +512,7 @@ static void PhonemeReplacement(int type, char *p)
static int Read8Numbers(char *data_in,int *data)
{//=============================================
// Read 8 integer numbers
memset(data, 0, 8+sizeof(int));
return(sscanf(data_in,"%d %d %d %d %d %d %d %d",
&data[0],&data[1],&data[2],&data[3],&data[4],&data[5],&data[6],&data[7]));
}
+119 -20
View File
@@ -28,11 +28,13 @@
#include <math.h>
#include "speak_lib.h"
#include "speech.h"
#include "phoneme.h"
#include "synthesize.h"
#include "voice.h"
#include "sonic.h"
//#undef INCLUDE_KLATT
@@ -134,7 +136,7 @@ int wcmdq_tail=0;
// pitch,speed,
int embedded_default[N_EMBEDDED_VALUES] = {0, 50,175,100,50, 0, 0, 0,175,0,0,0,0,0,0};
static int embedded_max[N_EMBEDDED_VALUES] = {0,0x7fff,600,300,99,99,99, 0,600,0,0,0,0,4,0};
static int embedded_max[N_EMBEDDED_VALUES] = {0,0x7fff,750,300,99,99,99, 0,750,0,0,0,0,4,0};
#define N_CALLBACK_IX N_WAV_BUF-2 // adjust this delay to match display with the currently spoken word
int current_source_index=0;
@@ -148,7 +150,8 @@ static PortAudioStream *pa_stream=NULL;
static PaStream *pa_stream=NULL;
#endif
static sonicStream sonicSpeedupStream = NULL;
double sonicSpeed = 1.0;
// 1st index=roughness
// 2nd index=modulation_type
@@ -256,8 +259,6 @@ unsigned char pitch_adjust_tab[MAX_PITCH_VALUE+1] = {
217,220,223,226,229,232,236,239,
242,246,249,252, 254,255 };
int WavegenFill(int fill_zeros);
#ifdef LOG_FRAMES
static void LogMarker(int type, int value)
@@ -285,6 +286,11 @@ void WcmdqStop()
{//=============
wcmdq_head = 0;
wcmdq_tail = 0;
if(sonicSpeedupStream != NULL)
{
sonicDestroyStream(sonicSpeedupStream);
sonicSpeedupStream = NULL;
}
#ifdef USE_PORTAUDIO
Pa_AbortStream(pa_stream);
#endif
@@ -413,6 +419,10 @@ static int userdata[4];
static PaError pa_init_err=0;
static int out_channels=1;
unsigned char *outbuffer = NULL;
int outbuffer_size = 0;
#if USE_PORTAUDIO == 18
static int WaveCallback(void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer, PaTimestamp outTime, void *userData )
@@ -425,9 +435,30 @@ static int WaveCallback(const void *inputBuffer, void *outputBuffer,
int ix;
int result;
unsigned char *p;
unsigned char *out_buf;
unsigned char *out_end2;
int pa_size;
out_ptr = out_start = (unsigned char *)outputBuffer;
out_end = out_ptr + framesPerBuffer*2;
pa_size = framesPerBuffer*2;
// make a buffer 3x size of the portaudio output
ix = pa_size*3;
if(ix > outbuffer_size)
{
outbuffer = (unsigned char *)realloc(outbuffer, ix);
if(outbuffer == NULL)
{
fprintf(stderr, "espeak: out of memory\n");
}
outbuffer_size = ix;
out_ptr = NULL;
}
if(out_ptr == NULL)
{
out_ptr = out_start = outbuffer;
out_end = out_start + outbuffer_size;
}
out_end2 = &outbuffer[pa_size]; // top of data needed for the portaudio buffer
#ifdef LIBRARY
event_list_ix = 0;
@@ -435,6 +466,24 @@ static int WaveCallback(const void *inputBuffer, void *outputBuffer,
result = WavegenFill(1);
// copy from the outbut buffer into the portaudio buffer
if(result && (out_ptr > out_end2))
{
result = 0; // don't end yet, there is more data in the buffer than can fit in portaudio
}
while(out_ptr < out_end2)
*out_ptr++ = 0; // fill with zeros up to the size of the portaudio buffer
memcpy(outputBuffer, outbuffer, pa_size);
// move the remaining contents of the start of the output buffer
for(p = out_end2; p < out_end; p++)
{
p[-pa_size] = p[0];
}
out_ptr -= pa_size;
#ifdef LIBRARY
count_samples += framesPerBuffer;
if(synth_callback)
@@ -455,14 +504,15 @@ static int WaveCallback(const void *inputBuffer, void *outputBuffer,
{
// swap the order of bytes in each sound sample in the portaudio buffer
int c;
out_ptr = (unsigned char *)outputBuffer;
out_end = out_ptr + framesPerBuffer*2;
while(out_ptr < out_end)
unsigned char *buf_end;
out_buf = (unsigned char *)outputBuffer;
buf_end = out_buf + framesPerBuffer*2;
while(out_buf < buf_end)
{
c = out_ptr[0];
out_ptr[0] = out_ptr[1];
out_ptr[1] = c;
out_ptr += 2;
c = out_buf[0];
out_buf[0] = out_buf[1];
out_buf[1] = c;
out_buf += 2;
}
}
#endif
@@ -471,12 +521,12 @@ static int WaveCallback(const void *inputBuffer, void *outputBuffer,
{
// sound output can only do stereo, not mono. Duplicate each sound sample to
// produce 2 channels.
out_ptr = (unsigned char *)outputBuffer;
out_buf = (unsigned char *)outputBuffer;
for(ix=framesPerBuffer-1; ix>=0; ix--)
{
p = &out_ptr[ix*4];
p[3] = p[1] = out_ptr[ix*2 + 1];
p[2] = p[0] = out_ptr[ix*2];
p = &out_buf[ix*4];
p[3] = p[1] = out_buf[ix*2 + 1];
p[2] = p[0] = out_buf[ix*2];
}
}
@@ -1791,8 +1841,7 @@ void Write4Bytes(FILE *f, int value)
int WavegenFill(int fill_zeros)
int WavegenFill2(int fill_zeros)
{//============================
// Pick up next wavegen commands from the queue
// return: 0 output buffer has been filled
@@ -1930,6 +1979,56 @@ int WavegenFill(int fill_zeros)
}
return(0);
} // end of WavegenFill2
/* Speed up the audio samples with libsonic. */
static int SpeedUp(short *outbuf, int length_in, int length_out, int end_of_text)
{//==============================================================================
if(length_in >0)
{
if(sonicSpeedupStream == NULL)
{
sonicSpeedupStream = sonicCreateStream(22050, 1);
}
if(sonicGetSpeed(sonicSpeedupStream) != sonicSpeed)
{
sonicSetSpeed(sonicSpeedupStream, sonicSpeed);
}
sonicWriteShortToStream(sonicSpeedupStream, outbuf, length_in);
}
if(end_of_text)
{
sonicFlushStream(sonicSpeedupStream);
}
return sonicReadShortFromStream(sonicSpeedupStream, outbuf, length_out);
} // end of SpeedUp
/* Call WavegenFill2, and then speed up the output samples. */
int WavegenFill(int fill_zeros)
{//============================
int finished;
unsigned char *p_start;
int length;
int max_length;
p_start = out_ptr;
// fill_zeros is ignored. It is now done in the portaudio callback
finished = WavegenFill2(0);
if(sonicSpeed > 1.0)
{
max_length = (out_end - p_start);
length = 2*SpeedUp((short *)p_start, (out_ptr-p_start)/2, max_length/2, finished);
out_ptr = p_start + length;
if(length >= max_length)
finished = 0; // there may be more data to flush
}
return finished;
} // end of WavegenFill