[1.30.04] SSML <audio src=""> now works without using a UriCallback() function, and works with the command-line espeak.

Pitch changes for mbrola are now applied over the first 80% of the phoneme, not over the whole length of the phoneme.  This seems to give better intonation for mbrola voices.


git-svn-id: https://espeak.svn.sourceforge.net/svnroot/espeak/trunk@118 d46cf337-b52f-0410-862d-fd96e6ae7743
This commit is contained in:
jonsd
2008-01-07 20:06:32 +00:00
parent 5634dd1891
commit af575bc1db
12 changed files with 243 additions and 65 deletions
+1 -15
View File
@@ -73,6 +73,7 @@
#define tINCLUDE 31
extern void Write4Bytes(FILE *f, int value);
extern int Read4Bytes(FILE *f);
extern void MakeVowelLists(void);
extern void FindPhonemesUsed(void);
extern void DrawEnvelopes();
@@ -309,21 +310,6 @@ static unsigned int StringToWord(const char *string)
}
int Read4Bytes(FILE *f)
{//====================
// Read 4 bytes (least significant first) into a word
int ix;
unsigned char c;
int acc=0;
for(ix=0; ix<4; ix++)
{
c = fgetc(f) & 0xff;
acc += (c << (ix*8));
}
return(acc);
}
static FILE *fopen_log(FILE *f_log, const char *fname,const char *access)
{//==================================================
+102 -10
View File
@@ -511,6 +511,21 @@ const char *Translator::LookupCharName(int c)
return(buf);
}
int Read4Bytes(FILE *f)
{//====================
// Read 4 bytes (least significant first) into a word
int ix;
unsigned char c;
int acc=0;
for(ix=0; ix<4; ix++)
{
c = fgetc(f) & 0xff;
acc += (c << (ix*8));
}
return(acc);
}
static int LoadSoundFile(const char *fname, int index)
{//===================================================
@@ -520,7 +535,7 @@ static int LoadSoundFile(const char *fname, int index)
int length;
char fname_temp[100];
char fname2[sizeof(path_home)+13+40];
char command[sizeof(fname2)+sizeof(fname2)+30];
char command[sizeof(fname2)+sizeof(fname2)+40];
if(fname == NULL)
{
@@ -542,16 +557,27 @@ static int LoadSoundFile(const char *fname, int index)
#ifdef PLATFORM_POSIX
if((f = fopen(fname,"rb")) != NULL)
{
int header[11];
fread(header, 1, 44, f);
int ix;
const char *resample;
int header[3];
if(header[6] != samplerate)
fseek(f,20,SEEK_SET);
for(ix=0; ix<3; ix++)
header[ix] = Read4Bytes(f);
// if the sound file is not mono, 16 bit signed, at the correct sample rate, then convert it
if((header[0] != 0x10001) || (header[1] != samplerate) || (header[2] != samplerate*2))
{
fclose(f);
f = NULL;
if(header[2] == samplerate)
resample = "";
else
resample = "polyphase";
sprintf(fname_temp,"%s.wav",tmpnam(NULL));
sprintf(command,"sox \"%s\" -r %d -w %s polyphase\n",fname,samplerate,fname_temp);
sprintf(command,"sox \"%s\" -r %d -w -s -c1 %s %s\n", fname, samplerate, fname_temp, resample);
if(system(command) == 0)
{
fname = fname_temp;
@@ -572,14 +598,18 @@ static int LoadSoundFile(const char *fname, int index)
length = GetFileLength(fname);
fseek(f,0,SEEK_SET);
p = Alloc(length);
if((p = (char *)realloc(soundicon_tab[index].data, length)) == NULL)
{
fclose(f);
return(4);
}
fread(p,length,1,f);
fclose(f);
remove(fname_temp);
ip = (int *)(&p[40]);
soundicon_tab[index].length = (*ip) / 2; // length in samples
soundicon_tab[index].data = p+44; // skip WAV header
soundicon_tab[index].data = p;
return(0);
} // end of LoadSoundFile
@@ -589,7 +619,7 @@ static int LookupSoundicon(int c)
// Find the sound icon number for a punctuation chatacter
int ix;
for(ix=0; ix<n_soundicon_tab; ix++)
for(ix=N_SOUNDICON_SLOTS; ix<n_soundicon_tab; ix++)
{
if(soundicon_tab[ix].name == c)
{
@@ -605,6 +635,34 @@ static int LookupSoundicon(int c)
}
static int LoadSoundFile2(const char *fname)
{//=========================================
// Load a sound file into one of the reserved slots in the sound icon table
// (if it'snot already loaded)
int ix;
static int slot = -1;
for(ix=0; ix<n_soundicon_tab; ix++)
{
if(((soundicon_tab[ix].filename != NULL) && strcmp(fname, soundicon_tab[ix].filename) == 0))
return(ix); // already loaded
}
// load the file into the next slot
slot++;
if(slot >= N_SOUNDICON_SLOTS)
slot = 0;
if(LoadSoundFile(fname, slot) != 0)
return(-1);
soundicon_tab[slot].filename = (char *)realloc(soundicon_tab[ix].filename, strlen(fname)+1);
strcpy(soundicon_tab[slot].filename, fname);
return(slot);
}
int Translator::AnnouncePunctuation(int c1, int c2, char *buf, int bufix)
{//======================================================================
@@ -1462,12 +1520,27 @@ static int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int &outix, int n_outb
if((attr1 = GetSsmlAttribute(px,"src")) != NULL)
{
char fname[256];
attrcopy_utf8(buf,attr1,sizeof(buf));
if(uri_callback == NULL)
{
// we could load the audio file as a sound icon (assuming it's a local WAV file)
// But when to free() the data?
if((xmlbase != NULL) && (buf[0] != '/'))
{
sprintf(fname,"%s/%s",xmlbase,buf);
index = LoadSoundFile2(fname);
}
else
{
index = LoadSoundFile2(buf);
}
if(index >= 0)
{
sprintf(buf,"%c%dI",CTRL_EMBEDDED,index);
strcpy(&outbuf[outix],buf);
outix += strlen(buf);
sp->parameter[espeakSILENCE] = 1;
}
}
else
{
@@ -1853,6 +1926,25 @@ f_input = f_in; // for GetC etc
if(iswalnum(c1))
any_alnum = 1;
else
if(iswspace(c1))
{
if(translator_name == 0x6a626f)
{
// language jbo : lojban
// treat "i" or ".i" as end-of-sentence
if(buf[ix-1] == 'i')
{
if(iswspace(buf[ix-2]) || ((buf[ix-2] == '.') && iswspace(buf[ix-3])))
{
UngetC(c2);
buf[ix] = ' ';
buf[ix+1] = 0;
return(CLAUSE_PERIOD);
}
}
}
}
if(iswupper(c1))
{
+1 -3
View File
@@ -305,9 +305,7 @@ static int initialise(void)
else
fprintf(stderr,"Wrong version of espeak-data 0x%x (expects 0x%x)\n",result,version_phdata);
}
#ifndef PLATFORM_WINDOWS
LoadConfig(); // causes problem on Windows, don't know why
#endif
LoadConfig();
SetVoiceStack(NULL);
SynthesizeInit();
-2
View File
@@ -335,9 +335,7 @@ static int initialise(void)
int param;
int result;
#ifndef __WIN32__
LoadConfig();
#endif
WavegenInit(22050,0); // 22050
if((result = LoadPhData()) != 1)
{
+39 -15
View File
@@ -252,8 +252,9 @@ int GetMbrName(PHONEME_LIST *plist, PHONEME_TAB *ph, PHONEME_TAB *ph_prev, PHONE
}
static char *WritePitch(int env, int pitch1, int pitch2, int split)
{//================================================================
static char *WritePitch(int env, int pitch1, int pitch2, int split, int final)
{//===========================================================================
// final=1: only give the final pitch value.
int x;
int ix;
int pitch_base;
@@ -264,11 +265,12 @@ static char *WritePitch(int env, int pitch1, int pitch2, int split)
int min = 999;
int y_max=0;
int y_min=0;
int env100 = 80; // apply the pitch change only over this proportion of the mbrola phoneme(s)
int y2;
int y[4];
int env_split;
char buf[40];
static char output[40];
char buf[50];
static char output[50];
output[0] = 0;
pitch_env = envelope_data[env];
@@ -335,18 +337,18 @@ static char *WritePitch(int env, int pitch1, int pitch2, int split)
if(split > 0)
{
y2 = (y[ix] * 100)/env_split;
y2 = (y[ix] * env100)/env_split;
}
else
if(split < 0)
{
y2 = ((y[ix]-env_split) * 100)/env_split;
y2 = ((y[ix]-env_split) * env100)/env_split;
}
else
{
y2 = (y[ix] * 100)/128;
y2 = (y[ix] * env100)/128;
}
if((y2 > 0) && (y2 <= 100))
if((y2 > 0) && (y2 <= env100))
{
sprintf(buf," %d %d",y2,p2/4096);
strcat(output,buf);
@@ -354,14 +356,23 @@ static char *WritePitch(int env, int pitch1, int pitch2, int split)
}
}
p_end = p_end/4096;
if(split <= 0)
{
sprintf(buf," 100 %d",p_end/4096);
sprintf(buf," %d %d",env100,p_end);
strcat(output,buf);
}
if(env100 < 100)
{
sprintf(buf," %d %d",100,p_end);
strcat(output,buf);
}
strcat(output,"\n");
if(final)
sprintf(output,"\t100 %d\n",p_end);
return(output);
} // end of SetPitch
} // end of WritePitch
#ifdef USE_MBROLA_LIB
@@ -506,6 +517,7 @@ void MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, FILE *f_mbrola)
int control;
int done;
int len_percent;
const char *final_pitch;
char buf[80];
char mbr_buf[120];
@@ -562,7 +574,7 @@ void MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, FILE *f_mbrola)
len = 1;
}
else
len = (70 * speed_factor2)/256;
len = (80 * speed_factor2)/256;
sprintf(buf,"%s\t",WordToString(name));
strcat(mbr_buf,buf);
@@ -575,6 +587,7 @@ void MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, FILE *f_mbrola)
}
done = 0;
final_pitch = "";
switch(ph->type)
{
@@ -589,16 +602,16 @@ void MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, FILE *f_mbrola)
if(name2 == 0)
{
sprintf(buf,"%d\t%s", len, WritePitch(p->env,p->pitch1,p->pitch2,0));
sprintf(buf,"%d\t%s", len, WritePitch(p->env,p->pitch1,p->pitch2,0,0));
strcat(mbr_buf,buf);
}
else
{
len1 = (len * len_percent)/100;
sprintf(buf,"%d\t%s", len1, WritePitch(p->env,p->pitch1,p->pitch2,len_percent));
sprintf(buf,"%d\t%s", len1, WritePitch(p->env,p->pitch1,p->pitch2,len_percent,0));
strcat(mbr_buf,buf);
sprintf(buf,"%s\t%d\t%s", WordToString(name2), len-len1, WritePitch(p->env,p->pitch1,p->pitch2,-len_percent));
sprintf(buf,"%s\t%d\t%s", WordToString(name2), len-len1, WritePitch(p->env,p->pitch1,p->pitch2,-len_percent,0));
strcat(mbr_buf,buf);
}
done = 1;
@@ -635,6 +648,17 @@ void MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, FILE *f_mbrola)
{
len = DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE],2,p,-1);
len = (len * 1000)/samplerate;
if(next->type == phPAUSE)
len += 50;
final_pitch = WritePitch(p->env,p->pitch1,p->pitch2,0,1);
}
break;
case phLIQUID:
if(next->type == phPAUSE)
{
len += 50;
final_pitch = WritePitch(p->env,p->pitch1,p->pitch2,0,1);
}
break;
}
@@ -648,7 +672,7 @@ void MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, FILE *f_mbrola)
strcat(mbr_buf,buf);
len -= len1;
}
sprintf(buf,"%d\n",len);
sprintf(buf,"%d%s\n",len,final_pitch);
strcat(mbr_buf,buf);
}
+7 -1
View File
@@ -35,7 +35,7 @@
#include "translate.h"
#include "wave.h"
const char *version_string = "1.30.03 04.Jan.08";
const char *version_string = "1.30.04 07.Jan.08";
const int version_phdata = 0x013000;
int option_device_number = -1;
@@ -622,6 +622,12 @@ void LoadConfig(void)
char *p;
char string[200];
for(ix=0; ix<N_SOUNDICON_SLOTS; ix++)
{
soundicon_tab[ix].filename = NULL;
soundicon_tab[ix].data = NULL;
}
sprintf(buf,"%s%c%s",path_home,PATHSEP,"config");
if((f = fopen(buf,"r"))==NULL)
{
+4 -4
View File
@@ -62,7 +62,7 @@ static int syllable_centre;
static voice_t *new_voice=NULL;
int n_soundicon_tab=0;
int n_soundicon_tab=N_SOUNDICON_SLOTS;
SOUND_ICON soundicon_tab[N_SOUNDICON_TAB];
#define RMS_GLOTTAL1 35 // vowel before glottal stop
@@ -1041,13 +1041,13 @@ static void DoEmbedded(int &embix, int sourceix)
case EMBED_I: // play dynamically loaded wav data (sound icon)
if((int)value < n_soundicon_tab)
{
if((wcmdq[wcmdq_tail][1] = soundicon_tab[value].length) != 0)
if(soundicon_tab[value].length != 0)
{
DoPause(10); // ensure a break in the speech
wcmdq[wcmdq_tail][0] = WCMD_WAVE;
wcmdq[wcmdq_tail][1] = soundicon_tab[value].length;
wcmdq[wcmdq_tail][2] = (long)soundicon_tab[value].data;
wcmdq[wcmdq_tail][3] = 0x1f00; // 16 bit data, amp=32
wcmdq[wcmdq_tail][2] = (long)soundicon_tab[value].data + 44; // skip WAV header
wcmdq[wcmdq_tail][3] = 0x1500; // 16 bit data, amp=21
WcmdqInc();
}
}
+2 -1
View File
@@ -262,7 +262,8 @@ extern int option_log_frames;
extern const char *version_string;
extern const int version_phdata;
#define N_SOUNDICON_TAB 100
#define N_SOUNDICON_TAB 80 // total entries in soundicon_tab
#define N_SOUNDICON_SLOTS 4 // number of slots reserved for dynamic loading of autio files
extern int n_soundicon_tab;
extern SOUND_ICON soundicon_tab[N_SOUNDICON_TAB];
+1 -1
View File
@@ -1724,7 +1724,7 @@ void *Translator::TranslateClause(FILE *f_text, const void *vp_input, int *tone_
WORD_TAB words[N_CLAUSE_WORDS];
int word_count=0; // index into words
char sbuf[512];
char sbuf[N_TR_SOURCE];
int terminator;
int tone;