mirror of
https://github.com/rhasspy/espeak-ng.git
synced 2026-11-07 15:24:02 +00:00
Merge branch 'master' into build
Conflicts: src/compiledata.cpp
This commit is contained in:
+5
-4
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user