git-svn-id: https://espeak.svn.sourceforge.net/svnroot/espeak/trunk@1 d46cf337-b52f-0410-862d-fd96e6ae7743

This commit is contained in:
jonsd
2007-05-05 11:00:27 +00:00
commit 8d59235f7a
874 changed files with 76097 additions and 0 deletions
+96
View File
@@ -0,0 +1,96 @@
BINDIR=/usr/bin
INCDIR=/usr/include/espeak
LIBDIR=/usr/lib
DATADIR=/usr/share/espeak-data
RELEASE = 1.24
BIN_NAME = speak
BIN2_NAME = espeak
LIB_NAME = libespeak.so
STATIC_LIB_NAME = libespeak.a
LIB_VERSION = 1
LIBTAG = $(LIB_VERSION).$(RELEASE)
INSTALL = /usr/bin/install
LN_SF = /bin/ln -sf
MKDIR = mkdir -p
speak_SOURCES = speak.cpp compiledict.cpp dictionary.cpp intonation.cpp \
readclause.cpp setlengths.cpp numbers.cpp synth_mbrola.cpp \
synthdata.cpp synthesize.cpp translate.cpp tr_english.cpp \
tr_languages.cpp voices.cpp wavegen.cpp phonemelist.cpp
libespeak_SOURCES = speak_lib.cpp compiledict.cpp dictionary.cpp intonation.cpp \
readclause.cpp setlengths.cpp numbers.cpp synth_mbrola.cpp \
synthdata.cpp synthesize.cpp translate.cpp tr_english.cpp \
tr_languages.cpp voices.cpp wavegen.cpp phonemelist.cpp \
espeak_command.cpp event.cpp fifo.cpp wave.cpp debug.cpp
SRCS1=$(speak_SOURCES)
OBJS1=$(patsubst %.cpp,%.o,$(SRCS1))
LIBS1=-lstdc++ -lportaudio -lpthread
SRCS2=$(libespeak_SOURCES)
OBJS2=$(patsubst %.cpp,x_%.o,$(SRCS2))
LIBS2=-lstdc++ -lportaudio -lpthread
SRCS3 = espeak.cpp
OBJS3=$(patsubst %.cpp,%.o,$(SRCS3))
LIBS3=-lstdc++ ./libespeak.so
CXXFLAGS=-O2
all: $(BIN_NAME) $(LIB_NAME) $(STATIC_LIB_NAME) $(BIN2_NAME)
mv $(LIB_NAME) $(LIB_NAME).$(LIBTAG)
.cpp.o:
$(CXX) $(CXXFLAGS) -D PATH_ESPEAK_DATA=\"$(DATADIR)\" -Wall -pedantic -I. -c -fno-exceptions $<
$(BIN_NAME): $(OBJS1)
$(CXX) -o $@ $(OBJS1) $(LIBS1)
$(BIN2_NAME): $(OBJS3) $(LIB_NAME)
$(CXX) -o $@ $(OBJS3) $(LIBS3)
x_%.o: %.cpp
$(CXX) $(CXXFLAGS) -Wall -fpic -fvisibility=hidden -pedantic \
-I. -D LIBRARY -c -fno-exceptions $< -o x_$*.o
$(LIB_NAME): $(OBJS2)
$(CXX) -shared -Wl,-soname,$(LIB_NAME).$(LIB_VERSION) -o $@ $(OBJS2) $(LIBS2)
$(STATIC_LIB_NAME): $(OBJS2)
$(AR) cqs $(STATIC_LIB_NAME) $(OBJS2)
clean:
rm -f *.o *.a *~
distclean: clean
rm -f $(BIN_NAME)
rm -f $(BIN2_NAME)
rm -f $(LIB_NAME)*
install: all
# Create directories
rm -rf $(DESTDIR)$(DATADIR)
$(MKDIR) $(DESTDIR)$(BINDIR)
$(MKDIR) $(DESTDIR)$(LIBDIR)
$(MKDIR) $(DESTDIR)$(INCDIR)
$(MKDIR) $(DESTDIR)$(DATADIR)
# Install espeak executable
$(INSTALL) -m 755 $(BIN2_NAME) $(DESTDIR)$(BINDIR)
# Install shared library
$(INSTALL) -m 755 $(LIB_NAME).$(LIBTAG) $(DESTDIR)$(LIBDIR)
# Install static library
$(INSTALL) -m 755 $(STATIC_LIB_NAME) $(DESTDIR)$(LIBDIR)
$(LN_SF) $(LIB_NAME).$(LIBTAG) $(DESTDIR)$(LIBDIR)/$(LIB_NAME).$(LIB_VERSION)
$(LN_SF) $(LIB_NAME).$(LIB_VERSION) $(DESTDIR)$(LIBDIR)/$(LIB_NAME)
# Install development headers
$(INSTALL) -pm 644 speak_lib.h $(DESTDIR)$(INCDIR)
# Install data files
cp -prf ../espeak-data/* $(DESTDIR)$(DATADIR)
+3
View File
@@ -0,0 +1,3 @@
// This is a dummy file.
// A file of this name is needed on Windows
+2138
View File
File diff suppressed because it is too large Load Diff
+1287
View File
File diff suppressed because it is too large Load Diff
+74
View File
@@ -0,0 +1,74 @@
#include <stdio.h>
#include <stdarg.h>
#include "speech.h"
#include "debug.h"
#ifdef DEBUG_ENABLED
#include <sys/time.h>
#include <unistd.h>
static FILE* fd_log=NULL;
static const char* FILENAME="/tmp/espeak.log";
void debug_init()
{
fd_log = fopen(FILENAME,"a");
setvbuf(fd_log, NULL, _IONBF, 0);
}
void debug_enter(const char* text)
{
struct timeval tv;
gettimeofday(&tv, NULL);
// fd_log = fopen(FILENAME,"a");
if (!fd_log)
{
debug_init();
}
if (fd_log)
{
fprintf(fd_log, "%03d.%03dms > ENTER %s\n",(int)(tv.tv_sec%1000), (int)(tv.tv_usec/1000), text);
// fclose(fd_log);
}
}
void debug_show(const char *format, ...)
{
va_list args;
va_start(args, format);
// fd_log = fopen(FILENAME,"a");
if (!fd_log)
{
debug_init();
}
if (fd_log)
{
vfprintf(fd_log, format, args);
// fclose(fd_log);
}
va_end(args);
}
void debug_time(const char* text)
{
struct timeval tv;
gettimeofday(&tv, NULL);
// fd_log = fopen(FILENAME,"a");
if (!fd_log)
{
debug_init();
}
if (fd_log)
{
fprintf(fd_log, "%03d.%03dms > %s\n",(int)(tv.tv_sec%1000), (int)(tv.tv_usec/1000), text);
// fclose(fd_log);
}
}
#endif
+26
View File
@@ -0,0 +1,26 @@
#ifndef DEBUG_H
#define DEBUG_H
//#define DEBUG_ENABLED
#ifdef DEBUG_ENABLED
#define ENTER(text) debug_enter(text)
#define SHOW(format,...) debug_show(format,__VA_ARGS__);
#define SHOW_TIME(text) debug_time(text);
extern void debug_enter(const char* text);
extern void debug_show(const char* format,...);
extern void debug_time(const char* text);
#else
#ifdef PLATFORM_WINDOWS
#define SHOW(format) // VC6 doesn't allow "..."
#else
#define SHOW(format,...)
#endif
#define SHOW_TIME(text)
#define ENTER(text)
#endif
#endif
+2805
View File
File diff suppressed because it is too large Load Diff
+573
View File
@@ -0,0 +1,573 @@
/***************************************************************************
* Copyright (C) 2005,2006 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <time.h>
#include "sys/stat.h"
#include "speak_lib.h"
// This version of the command-line speak program uses the
// libespeak.so.1 library
static const char *help_text =
"\nspeak [options] [\"<words>\"]\n\n"
"-f <text file> Text file to speak\n"
"--stdin Read text input from stdin instead of a file\n\n"
"If neither -f nor --stdin, <words> are spoken, or if none then text is\n"
"spoken from stdin, each line separately.\n\n"
"-a <integer>\n"
"\t Amplitude, 0 to 200, default is 100\n"
"-l <integer>\n"
"\t Line length. If not zero (which is the default), consider\n"
"\t lines less than this length as end-of-clause\n"
"-p <integer>\n"
"\t Pitch adjustment, 0 to 99, default is 50\n"
"-s <integer>\n"
"\t Speed in words per minute, 80 to 370, default is 170\n"
"-v <voice name>\n"
"\t Use voice file of this name from espeak-data/voices\n"
"-w <wave file name>\n"
"\t Write output to this WAV file, rather than speaking it directly\n"
"-b\t Input text is 8-bit encoding\n"
"-m\t Interpret SSML markup, and ignore other < > tags\n"
"-q\t Quiet, don't produce any speech (may be useful with -x)\n"
"-x\t Write phoneme mnemonics to stdout\n"
"-X\t Write phonemes mnemonics and translation trace to stdout\n"
"-z\t No final sentence pause at the end of the text\n"
"--stdout Write speech output to stdout\n"
"--compile=<voice name>\n"
"\t Compile the pronunciation rules and dictionary in the current\n"
"\t directory. =<voice name> is optional and specifies which language\n"
"--punct=\"<characters>\"\n"
"\t Speak the names of punctuation characters during speaking. If\n"
"\t =<characters> is omitted, all punctuation is spoken.\n"
"--voices=<langauge>\n"
"\t List the available voices for the specified language.\n"
"\t If <language> is omitted, then list all voices.\n"
"-k <integer>\n"
"\t Indicate capital letters with: 1=sound, 2=the word \"capitals\",\n"
"\t higher values = a pitch increase (try -k20).\n";
int samplerate;
FILE *f_wavfile = NULL;
char wavefile[160];
int GetFileLength(const char *filename)
{//====================================
struct stat statbuf;
if(stat(filename,&statbuf) != 0)
return(0);
if((statbuf.st_mode & S_IFMT) == S_IFDIR)
return(-2); // a directory
return(statbuf.st_size);
} // end of GetFileLength
void strncpy0(char *dest, const char *source, int size)
{//====================================================
if(source!=NULL)
{
strncpy(dest,source,size);
dest[size-1] = 0;
}
}
void DisplayVoices(FILE *f_out, char *language)
{//============================================
int ix;
char *p;
int len;
int count;
int scores = 0;
const espeak_VOICE *v;
char *lang_name;
char age_buf[12];
const espeak_VOICE **voices;
espeak_VOICE voice_select;
static char genders[4] = {' ','M','F',' '};
if((language != NULL) && (language[0] != 0))
{
// display only voices for the specified language, in order of priority
voice_select.languages = language;
voice_select.age = 0;
voice_select.gender = 0;
voice_select.name = NULL;
voices = espeak_ListVoices(&voice_select);
scores = 1;
}
else
{
voices = espeak_ListVoices(NULL);
}
fprintf(f_out,"Pty Language Age/Gender VoiceName File Other Langs\n");
for(ix=0; (v = voices[ix]) != NULL; ix++)
{
count = 0;
p = v->languages;
while(*p != 0)
{
len = strlen(p+1);
lang_name = p+1;
if(v->age == 0)
strcpy(age_buf," ");
else
sprintf(age_buf,"%3d",v->age);
if(count==0)
{
fprintf(f_out,"%2d %-12s%s%c %-17s %-11s ",
p[0],lang_name,age_buf,genders[v->gender],v->name,v->identifier);
}
else
{
fprintf(f_out,"(%s %d)",lang_name,p[0]);
}
count++;
p += len+2;
}
// if(scores)
// fprintf(f_out,"%3d ",v->score);
fputc('\n',f_out);
}
} // end of DisplayVoices
static void Write4Bytes(FILE *f, int value)
{//=================================
// Write 4 bytes to a file, least significant first
int ix;
for(ix=0; ix<4; ix++)
{
fputc(value & 0xff,f);
value = value >> 8;
}
}
int OpenWavFile(const char *path, int rate)
//=========================================
{
static unsigned char wave_hdr[44] = {
'R','I','F','F',0,0,0,0,'W','A','V','E','f','m','t',' ',
0x10,0,0,0,1,0,1,0, 0,0,0,0, 0,0,0,0,
2,0,0x10,0,'d','a','t','a', 0,0,0,0 };
if(path == NULL)
return(2);
if(path[0] == 0)
return(0);
if(path == "stdout")
f_wavfile = stdout;
else
f_wavfile = fopen(path,"wb");
if(f_wavfile != NULL)
{
fwrite(wave_hdr,1,24,f_wavfile);
Write4Bytes(f_wavfile,rate);
Write4Bytes(f_wavfile,rate * 2);
fwrite(&wave_hdr[32],1,8,f_wavfile);
return(0);
}
return(1);
} // end of OpenWavFile
static void CloseWavFile(int rate)
//================================
{
unsigned int pos;
if((f_wavfile==NULL) || (wavefile == "stdout"))
return;
fflush(f_wavfile);
pos = ftell(f_wavfile);
fseek(f_wavfile,4,SEEK_SET);
Write4Bytes(f_wavfile,pos - 8);
fseek(f_wavfile,40,SEEK_SET);
Write4Bytes(f_wavfile,pos - 44);
fclose(f_wavfile);
f_wavfile = NULL;
} // end of CloseWavFile
static int SynthCallback(short *wav, int numsamples, espeak_EVENT *events)
{//========================================================================
if(f_wavfile == NULL) return(0); // -q quiet mode
if(wav == NULL)
{
CloseWavFile(samplerate);
return(0);
}
if(numsamples > 0)
{
fwrite(wav,numsamples*2,1,f_wavfile);
}
return(0);
}
int main (int argc, char **argv)
//==============================
{
static struct option long_options[] =
{
/* These options set a flag. */
// {"verbose", no_argument, &verbose_flag, 1},
// {"brief", no_argument, &verbose_flag, 0},
/* These options don't set a flag.
We distinguish them by their indices. */
{"help", no_argument, 0, 'h'},
{"stdin", no_argument, 0, 0x100},
{"stdout", no_argument, 0, 0x101},
{"compile", optional_argument, 0, 0x102},
{"punct", optional_argument, 0, 0x103},
{"voices", optional_argument, 0, 0x104},
{0, 0, 0, 0}
};
static const char* err_load = "Failed to read ";
FILE *f_text=NULL;
char *p_text=NULL;
int option_index = 0;
int c;
int ix;
int quiet = 0;
int flag_stdin = 0;
int flag_compile = 0;
int filesize = 0;
int synth_flags = espeakCHARS_AUTO | espeakPHONEMES | espeakENDPAUSE;
int volume = -1;
int speed = -1;
int pitch = -1;
int option_capitals = -1;
int option_punctuation = -1;
int option_phonemes = -1;
int option_linelength = 0;
int option_waveout = 0;
char filename[120];
char voicename[40];
char voice_mbrola[20];
char dictname[40];
#define N_PUNCTLIST 100
wchar_t option_punctlist[N_PUNCTLIST];
voicename[0] = 0;
voice_mbrola[0] = 0;
dictname[0] = 0;
wavefile[0] = 0;
filename[0] = 0;
option_punctlist[0] = 0;
while(true)
{
c = getopt_long (argc, argv, "a:bf:hk:l:mp:qs:v:w:xXz",
long_options, &option_index);
/* Detect the end of the options. */
if (c == -1)
break;
switch (c)
{
case 'b':
synth_flags |= espeakCHARS_8BIT;
break;
case 'h':
printf("\n");
printf("eSpeak text-to-speech: %s\n%s",espeak_Info(NULL),help_text);
exit(0);
break;
case 'k':
option_capitals = atoi(optarg);
break;
case 'x':
option_phonemes = 1;
break;
case 'X':
option_phonemes = 2;
break;
case 'm':
synth_flags |= espeakSSML;
break;
case 'p':
pitch = atoi(optarg);
break;
case 'q':
quiet = 1;
break;
case 'f':
strncpy0(filename,optarg,sizeof(filename));
break;
case 'l':
option_linelength = atoi(optarg);
break;
case 'a':
volume = atoi(optarg);
break;
case 's':
speed = atoi(optarg);
break;
case 'v':
strncpy0(voicename,optarg,sizeof(voicename));
break;
case 'w':
option_waveout = 1;
strncpy0(wavefile,optarg,sizeof(filename));
break;
case 'z': // remove pause from the end of a sentence
synth_flags &= ~espeakENDPAUSE;
break;
case 0x100: // --stdin
flag_stdin = 1;
break;
case 0x101: // --stdout
option_waveout = 1;
strcpy(wavefile,"stdout");
break;
case 0x102: // --compile
strncpy0(voicename,optarg,sizeof(voicename));
flag_compile = 1;
quiet = 1;
break;
case 0x103: // --punct
option_punctuation = 1;
if(optarg != NULL)
{
ix = 0;
while((ix < N_PUNCTLIST) && ((option_punctlist[ix] = optarg[ix]) != 0)) ix++;
option_punctlist[N_PUNCTLIST-1] = 0;
option_punctuation = 2;
}
break;
case 0x104: // --voices
espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS,0,NULL);
DisplayVoices(stdout,optarg);
exit(0);
default:
exit(0);
}
}
if(option_waveout || quiet)
{
// writing to a file (or no output), we can use synchronous mode
samplerate = espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS,0,NULL);
espeak_SetSynthCallback(SynthCallback);
if(option_waveout)
{
if(OpenWavFile(wavefile,samplerate) != 0)
exit(4);
}
}
else
{
// play the sound output
samplerate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK,0,NULL);
}
if(voicename[0] == 0)
strcpy(voicename,"default");
if(espeak_SetVoiceByName(voicename) != EE_OK)
{
fprintf(stderr,"%svoice '%s'\n",err_load,voicename);
exit(2);
}
if(flag_compile)
{
// This must be done after the voice is set
espeak_CompileDictionary("",stderr);
exit(0);
}
// set any non-default values of parameters. This must be done after espeak_Initialize()
if(speed > 0)
espeak_SetParameter(espeakRATE,speed,0);
if(volume >= 0)
espeak_SetParameter(espeakVOLUME,volume,0);
if(pitch >= 0)
espeak_SetParameter(espeakPITCH,pitch,0);
if(option_capitals >= 0)
espeak_SetParameter(espeakCAPITALS,option_capitals,0);
if(option_punctuation >= 0)
espeak_SetParameter(espeakPUNCTUATION,option_punctuation,0);
if(option_linelength > 0)
espeak_SetParameter(espeakLINELENGTH,option_linelength,0);
if(option_punctuation == 2)
espeak_SetPunctuationList(option_punctlist);
if(option_phonemes >= 0)
espeak_SetPhonemeTrace(option_phonemes,stderr);
if(filename[0]==0)
{
if((optind < argc) && (flag_stdin == 0))
{
// there's a non-option parameter, and no -f or --stdin
// use it as text
p_text = argv[optind];
}
else
{
f_text = stdin;
if(flag_stdin == 0)
{
flag_stdin = 2;
}
}
}
else
{
filesize = GetFileLength(filename);
f_text = fopen(filename,"r");
}
if((f_text == NULL) && (p_text == NULL))
{
fprintf(stderr,"%sfile '%s'\n",err_load,filename);
exit(1);
}
if(p_text != NULL)
{
int size;
size = strlen(p_text);
espeak_Synth(p_text,size+1,0,POS_CHARACTER,0,synth_flags,NULL,NULL);
}
else
if(flag_stdin)
{
int max = 1000;
p_text = (char *)malloc(max);
if(flag_stdin == 2)
{
// line by line input on stdin
while(fgets(p_text,max,stdin) != NULL)
{
p_text[max-1] = 0;
espeak_Synth(p_text,max,0,POS_CHARACTER,0,synth_flags,NULL,NULL);
}
}
else
{
// bulk input on stdin
ix = 0;
while(!feof(stdin))
{
p_text[ix++] = fgetc(stdin);
if(ix >= (max-1))
{
max += 1000;
p_text = (char *)realloc(p_text,max);
}
}
if(ix > 0)
{
p_text[ix-1] = 0;
espeak_Synth(p_text,ix+1,0,POS_CHARACTER,0,synth_flags,NULL,NULL);
}
}
}
else
if(f_text != NULL)
{
if((p_text = (char *)malloc(filesize+1)) == NULL)
{
fprintf(stderr,"Failed to allocate memory %d bytes",filesize);
exit(3);
}
fread(p_text,1,filesize,f_text);
p_text[filesize]=0;
espeak_Synth(p_text,filesize+1,0,POS_CHARACTER,0,synth_flags,NULL,NULL);
fclose(f_text);
}
espeak_Synchronize();
return(0);
}
+703
View File
@@ -0,0 +1,703 @@
/***************************************************************************
* Copyright (C) 2007, Gilles Casse <gcasse@oralux.org> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "speech.h"
#ifdef USE_ASYNC
// This source file is only used for asynchronious modes
#include "espeak_command.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <wchar.h>
#include "debug.h"
static unsigned int my_current_text_id=0;
//<create_espeak_text
t_espeak_command* create_espeak_text(const void *text, size_t size, unsigned int position, espeak_POSITION_TYPE position_type, unsigned int end_position, unsigned int flags, void* user_data)
{
ENTER("create_espeak_text");
int a_error=1;
void* a_text = NULL;
t_espeak_text* data = NULL;
t_espeak_command* a_command = (t_espeak_command*)malloc(sizeof(t_espeak_command));
if (!text || !size || !a_command)
{
goto text_error;
}
a_text = malloc( size );
if (!a_text)
{
goto text_error;
}
memcpy(a_text, text, size);
a_command->type = ET_TEXT;
a_command->state = CS_UNDEFINED;
data = &(a_command->u.my_text);
data->unique_identifier = ++my_current_text_id;
data->text = a_text;
data->size = size;
data->position = position;
data->position_type = position_type;
data->end_position = end_position;
data->flags = flags;
data->user_data = user_data;
a_error=0;
SHOW("ET_TEXT malloc text=%x, command=%x (uid=%d)\n", a_text, a_command, data->unique_identifier);
text_error:
if (a_error)
{
if (a_text)
{
free (a_text);
}
if (a_command)
{
free (a_command);
}
a_command = NULL;
}
SHOW("command=0x%x\n", a_command);
return a_command;
}
//>
t_espeak_command* create_espeak_terminated_msg(unsigned int unique_identifier, void* user_data)
{
ENTER("create_espeak_terminated_msg");
int a_error=1;
t_espeak_terminated_msg* data = NULL;
t_espeak_command* a_command = (t_espeak_command*)malloc(sizeof(t_espeak_command));
if (!a_command)
{
goto msg_error;
}
a_command->type = ET_TERMINATED_MSG;
a_command->state = CS_UNDEFINED;
data = &(a_command->u.my_terminated_msg);
data->unique_identifier = unique_identifier;
data->user_data = user_data;
a_error=0;
SHOW("ET_TERMINATED_MSG command=%x (uid=%d, user_data=0x%x)\n", a_command, unique_identifier, (int)user_data);
msg_error:
if (a_error)
{
if (a_command)
{
free (a_command);
}
a_command = NULL;
}
SHOW("command=0x%x\n", a_command);
return a_command;
}
//<create_espeak_mark
t_espeak_command* create_espeak_mark(const void *text, size_t size, const char *index_mark, unsigned int end_position, unsigned int flags, void* user_data)
{
ENTER("create_espeak_mark");
int a_error=1;
void* a_text = NULL;
char *a_index_mark = NULL;
t_espeak_mark* data = NULL;
t_espeak_command* a_command = (t_espeak_command*)malloc(sizeof(t_espeak_command));
if (!text || !size || !index_mark || !a_command)
{
goto mark_error;
}
a_text = malloc( size );
if (!a_text)
{
goto mark_error;
}
memcpy(a_text, text, size);
a_index_mark = strdup( index_mark);
a_command->type = ET_MARK;
a_command->state = CS_UNDEFINED;
data = &(a_command->u.my_mark);
data->unique_identifier = ++my_current_text_id;
data->text = a_text;
data->size = size;
data->index_mark = a_index_mark;
data->end_position = end_position;
data->flags = flags;
data->user_data = user_data;
a_error=0;
mark_error:
if (a_error)
{
if (a_text)
{
free (a_text);
}
if (a_command)
{
free (a_command);
}
a_command = NULL;
if (a_index_mark)
{
free (a_index_mark);
}
}
SHOW("ET_MARK malloc text=%x, command=%x (uid=%d)\n", a_text, a_command, data->unique_identifier);
return a_command;
}
//>
//< create_espeak_key, create_espeak_char
t_espeak_command* create_espeak_key(const char *key_name)
{
ENTER("create_espeak_key");
int a_error=1;
t_espeak_command* a_command = (t_espeak_command*)malloc(sizeof(t_espeak_command));
if (!key_name || !a_command)
{
goto key_error;
}
a_command->type = ET_KEY;
a_command->state = CS_UNDEFINED;
a_command->u.my_key = strdup( key_name);
a_error=0;
key_error:
if (a_error)
{
if (a_command)
{
free (a_command);
}
a_command = NULL;
}
SHOW("command=0x%x\n", a_command);
return a_command;
}
t_espeak_command* create_espeak_char(wchar_t character)
{
ENTER("create_espeak_char");
int a_error=1;
t_espeak_command* a_command = (t_espeak_command*)malloc(sizeof(t_espeak_command));
if (!a_command)
{
goto char_error;
}
a_command->type = ET_CHAR;
a_command->state = CS_UNDEFINED;
a_command->u.my_char = character;
a_error=0;
char_error:
if (a_error)
{
if (a_command)
{
free (a_command);
}
a_command = NULL;
}
SHOW("command=0x%x\n", a_command);
return a_command;
}
//>
//< create_espeak_parameter
t_espeak_command* create_espeak_parameter(espeak_PARAMETER parameter, int value, int relative)
{
ENTER("create_espeak_parameter");
int a_error=1;
t_espeak_parameter* data = NULL;
t_espeak_command* a_command = (t_espeak_command*)malloc(sizeof(t_espeak_command));
if (!a_command)
{
goto param_error;
}
a_command->type = ET_PARAMETER;
a_command->state = CS_UNDEFINED;
data = &(a_command->u.my_param);
data->parameter = parameter;
data->value = value;
data->relative = relative;
a_error=0;
param_error:
if (a_error)
{
if (a_command)
{
free (a_command);
}
a_command = NULL;
}
SHOW("command=0x%x\n", a_command);
return a_command;
}
//>
//< create_espeak_punctuation_list
t_espeak_command* create_espeak_punctuation_list(const wchar_t *punctlist)
{
ENTER("create_espeak_punctuation_list");
int a_error=1;
// wchar_t *a_list = NULL;
t_espeak_command* a_command = (t_espeak_command*)malloc(sizeof(t_espeak_command));
if (!punctlist || !a_command)
{
goto list_error;
}
a_command->type = ET_PUNCTUATION_LIST;
a_command->state = CS_UNDEFINED;
{
size_t len = (wcslen(punctlist) + 1)*sizeof(wchar_t);
wchar_t* a_list = (wchar_t*)malloc(len);
memcpy(a_list, punctlist, len);
a_command->u.my_punctuation_list = a_list;
}
a_error=0;
list_error:
if (a_error)
{
if (a_command)
{
free (a_command);
}
a_command = NULL;
}
SHOW("command=0x%x\n", a_command);
return a_command;
}
//>
//< create_espeak_voice_name, create_espeak_voice_spec
t_espeak_command* create_espeak_voice_name(const char *name)
{
ENTER("create_espeak_voice_name");
int a_error=1;
t_espeak_command* a_command = (t_espeak_command*)malloc(sizeof(t_espeak_command));
if (!name || !a_command)
{
goto name_error;
}
a_command->type = ET_VOICE_NAME;
a_command->state = CS_UNDEFINED;
a_command->u.my_voice_name = strdup( name);
a_error=0;
name_error:
if (a_error)
{
if (a_command)
{
free (a_command);
}
a_command = NULL;
}
SHOW("command=0x%x\n", a_command);
return a_command;
}
t_espeak_command* create_espeak_voice_spec(espeak_VOICE *voice)
{
ENTER("create_espeak_voice_spec");
int a_error=1;
t_espeak_command* a_command = (t_espeak_command*)malloc(sizeof(t_espeak_command));
if (!voice || !a_command)
{
goto spec_error;
}
a_command->type = ET_VOICE_SPEC;
a_command->state = CS_UNDEFINED;
{
espeak_VOICE* data = &(a_command->u.my_voice_spec);
memcpy(data, voice, sizeof(espeak_VOICE));
if (voice->name)
{
data->name = strdup(voice->name);
}
if (voice->languages)
{
data->languages = strdup(voice->languages);
}
if (voice->identifier)
{
data->identifier = strdup(voice->identifier);
}
a_error=0;
}
spec_error:
if (a_error)
{
if (a_command)
{
free (a_command);
}
a_command = NULL;
}
SHOW("command=0x%x\n", a_command);
return a_command;
}
//>
//< delete_espeak_command
int delete_espeak_command( t_espeak_command* the_command)
{
ENTER("delete_espeak_command");
int a_status = 0;
if (the_command)
{
switch(the_command->type)
{
case ET_TEXT:
if (the_command->u.my_text.text)
{
SHOW("delete_espeak_command > ET_TEXT free text=%x, command=%x, uid=%d\n", the_command->u.my_text.text, the_command, the_command->u.my_text.unique_identifier);
free(the_command->u.my_text.text);
}
break;
case ET_MARK:
if (the_command->u.my_mark.text)
{
free(the_command->u.my_mark.text);
}
if (the_command->u.my_mark.index_mark)
{
free((void*)(the_command->u.my_mark.index_mark));
}
break;
case ET_TERMINATED_MSG:
{
// if the terminated msg is pending,
// it must be processed here for informing the calling program
// that its message is finished.
// This can be important for cleaning the related user data.
t_espeak_terminated_msg* data = &(the_command->u.my_terminated_msg);
if (the_command->state == CS_PENDING)
{
the_command->state = CS_PROCESSED;
SHOW("delete_espeak_command > ET_TERMINATED_MSG callback (command=0x%x, uid=%d) \n", the_command, data->unique_identifier);
sync_espeak_terminated_msg( data->unique_identifier, data->user_data);
}
}
break;
case ET_KEY:
if (the_command->u.my_key)
{
free((void*)(the_command->u.my_key));
}
break;
case ET_CHAR:
case ET_PARAMETER:
// No allocation
break;
case ET_PUNCTUATION_LIST:
if (the_command->u.my_punctuation_list)
{
free((void*)(the_command->u.my_punctuation_list));
}
break;
case ET_VOICE_NAME:
if (the_command->u.my_voice_name)
{
free((void*)(the_command->u.my_voice_name));
}
break;
case ET_VOICE_SPEC:
{
espeak_VOICE* data = &(the_command->u.my_voice_spec);
if (data->name)
{
free(data->name);
}
if (data->languages)
{
free(data->languages);
}
if (data->identifier)
{
free(data->identifier);
}
}
break;
default:
assert(0);
}
SHOW("delete_espeak_command > free command=0x%x\n", the_command);
free(the_command);
a_status = 1;
}
return a_status;
}
//>
//< process_espeak_command
void process_espeak_command( t_espeak_command* the_command)
{
ENTER("process_espeak_command");
SHOW("command=0x%x\n", the_command);
if (the_command == NULL)
{
return;
}
the_command->state = CS_PROCESSED;
switch(the_command->type)
{
case ET_TEXT:
{
t_espeak_text* data = &(the_command->u.my_text);
sync_espeak_Synth( data->unique_identifier, data->text, data->size,
data->position, data->position_type,
data->end_position, data->flags, data->user_data);
}
break;
case ET_MARK:
{
t_espeak_mark* data = &(the_command->u.my_mark);
sync_espeak_Synth_Mark( data->unique_identifier, data->text, data->size,
data->index_mark, data->end_position, data->flags,
data->user_data);
}
break;
case ET_TERMINATED_MSG:
{
t_espeak_terminated_msg* data = &(the_command->u.my_terminated_msg);
sync_espeak_terminated_msg( data->unique_identifier, data->user_data);
}
break;
case ET_KEY:
{
const char* data = the_command->u.my_key;
sync_espeak_Key(data);
}
break;
case ET_CHAR:
{
const wchar_t data = the_command->u.my_char;
sync_espeak_Char( data);
}
break;
case ET_PARAMETER:
{
t_espeak_parameter* data = &(the_command->u.my_param);
SetParameter( data->parameter, data->value, data->relative);
}
break;
case ET_PUNCTUATION_LIST:
{
const wchar_t* data = the_command->u.my_punctuation_list;
sync_espeak_SetPunctuationList( data);
}
break;
case ET_VOICE_NAME:
{
const char* data = the_command->u.my_voice_name;
SetVoiceByName( data);
}
break;
case ET_VOICE_SPEC:
{
espeak_VOICE* data = &(the_command->u.my_voice_spec);
SetVoiceByProperties(data);
}
break;
default:
assert(0);
break;
}
}
//>
//< process_espeak_command
void display_espeak_command( t_espeak_command* the_command)
{
ENTER("display_espeak_command");
#ifdef DEBUG_ENABLED
if (the_command == NULL)
{
SHOW("display_espeak_command > command=%s\n","NULL");
return;
}
SHOW("display_espeak_command > state=%d\n",the_command->state);
switch(the_command->type)
{
case ET_TEXT:
{
t_espeak_text* data = &(the_command->u.my_text);
SHOW("display_espeak_command > (0x%x) uid=%d, TEXT=%s, user_data=0x%x\n", the_command, data->unique_identifier, (char*)data->text, (int)(data->user_data));
}
break;
case ET_MARK:
{
t_espeak_mark* data = &(the_command->u.my_mark);
SHOW("display_espeak_command > (0x%x) uid=%d, MARK=%s, user_data=0x%x\n", the_command, data->unique_identifier, (char*)data->text, (int)(data->user_data));
}
break;
case ET_KEY:
{
const char* data = the_command->u.my_key;
SHOW("display_espeak_command > (0x%x) KEY=%c\n", the_command, data);
}
break;
case ET_TERMINATED_MSG:
{
t_espeak_terminated_msg* data = &(the_command->u.my_terminated_msg);
SHOW("display_espeak_command > (0x%x) TERMINATED_MSG uid=%d, user_data=0x%x, state=%d\n",
the_command, data->unique_identifier, data->user_data,
the_command->state);
}
break;
case ET_CHAR:
{
const wchar_t data = the_command->u.my_char;
SHOW("display_espeak_command > (0x%x) CHAR=%c\n", the_command, (char)data);
}
break;
case ET_PARAMETER:
{
t_espeak_parameter* data = &(the_command->u.my_param);
SHOW("display_espeak_command > (0x%x) PARAMETER=%d, value=%d, relative=%d\n",
the_command, data->parameter, data->value, data->relative);
}
break;
case ET_PUNCTUATION_LIST:
{
const wchar_t* data = the_command->u.my_punctuation_list;
sync_espeak_SetPunctuationList( data);
SHOW("display_espeak_command > (0x%x) PUNCTLIST=%s\n", the_command, (char*)data);
}
break;
case ET_VOICE_NAME:
{
const char* data = the_command->u.my_voice_name;
SHOW("display_espeak_command > (0x%x) VOICE_NAME=%s\n", the_command, data);
}
break;
case ET_VOICE_SPEC:
{
SHOW("display_espeak_command > (0x%x) VOICE_SPEC", the_command);
}
break;
default:
assert(0);
break;
}
#endif
}
#endif
//>
+129
View File
@@ -0,0 +1,129 @@
#ifndef ESPEAK_COMMAND_H
#define ESPEAK_COMMAND_H
#ifndef PLATFORM_WINDOWS
#include <unistd.h>
#endif
#include "speak_lib.h"
enum t_espeak_type
{
ET_TEXT,
ET_MARK,
ET_KEY,
ET_CHAR,
ET_PARAMETER,
ET_PUNCTUATION_LIST,
ET_VOICE_NAME,
ET_VOICE_SPEC,
ET_TERMINATED_MSG
};
typedef struct
{
unsigned int unique_identifier;
void* text;
size_t size;
unsigned int position;
espeak_POSITION_TYPE position_type;
unsigned int end_position;
unsigned int flags;
void* user_data;
} t_espeak_text;
typedef struct
{
unsigned int unique_identifier;
void* text;
size_t size;
const char* index_mark;
unsigned int end_position;
unsigned int flags;
void* user_data;
} t_espeak_mark;
typedef struct
{
unsigned int unique_identifier;
void* user_data;
} t_espeak_terminated_msg;
typedef struct
{
espeak_PARAMETER parameter;
int value;
int relative;
} t_espeak_parameter;
enum t_command_state
{
CS_UNDEFINED, // The command has just been created
CS_PENDING, // stored in the fifo
CS_PROCESSED // processed
};
typedef struct
{
enum t_espeak_type type;
t_command_state state;
union command
{
t_espeak_text my_text;
t_espeak_mark my_mark;
const char* my_key;
wchar_t my_char;
t_espeak_parameter my_param;
const wchar_t* my_punctuation_list;
const char *my_voice_name;
espeak_VOICE my_voice_spec;
t_espeak_terminated_msg my_terminated_msg;
} u;
} t_espeak_command;
t_espeak_command* create_espeak_text(const void *text, size_t size, unsigned int position, espeak_POSITION_TYPE position_type, unsigned int end_position, unsigned int flags, void* user_data);
t_espeak_command* create_espeak_mark(const void *text, size_t size, const char *index_mark, unsigned int end_position, unsigned int flags, void* user_data);
t_espeak_command* create_espeak_terminated_msg(unsigned int unique_identifier, void* user_data);
t_espeak_command* create_espeak_key(const char *key_name);
t_espeak_command* create_espeak_char(wchar_t character);
t_espeak_command* create_espeak_parameter(espeak_PARAMETER parameter, int value, int relative);
t_espeak_command* create_espeak_punctuation_list(const wchar_t *punctlist);
t_espeak_command* create_espeak_voice_name(const char *name);
t_espeak_command* create_espeak_voice_spec(espeak_VOICE *voice_spec);
void process_espeak_command( t_espeak_command* the_command);
int delete_espeak_command( t_espeak_command* the_command);
void display_espeak_command(t_espeak_command* the_command);
espeak_ERROR sync_espeak_Synth(unsigned int unique_identifier, const void *text, size_t size,
unsigned int position, espeak_POSITION_TYPE position_type,
unsigned int end_position, unsigned int flags, void* user_data);
espeak_ERROR sync_espeak_Synth_Mark(unsigned int unique_identifier, const void *text, size_t size,
const char *index_mark, unsigned int end_position,
unsigned int flags, void* user_data);
void sync_espeak_Key(const char *key);
void sync_espeak_Char(wchar_t character);
void sync_espeak_SetPunctuationList(const wchar_t *punctlist);
void sync_espeak_SetParameter(espeak_PARAMETER parameter, int value, int relative);
int sync_espeak_SetVoiceByName(const char *name);
int sync_espeak_SetVoiceByProperties(espeak_VOICE *voice_selector);
espeak_ERROR SetVoiceByName(const char *name);
espeak_ERROR SetVoiceByProperties(espeak_VOICE *voice_selector);
void SetParameter(int parameter, int value, int relative);
int sync_espeak_terminated_msg(unsigned int unique_identifier, void* user_data);
//>
#endif
+707
View File
@@ -0,0 +1,707 @@
/***************************************************************************
* Copyright (C) 2005, 2006 by Jonathan Duddington *
* jsd@clara.co.uk *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "wx/wx.h"
#include "wx/wfstream.h"
#include "wx/notebook.h"
#include "wx/image.h"
#include "wx/filename.h"
#include "wx/numdlg.h"
#include "wx/mdi.h"
#include "wx/laywin.h"
#include "wx/sashwin.h"
#include <locale.h>
#include "speak_lib.h"
#include "main.h"
#include "speech.h"
#include "voice.h"
#include "options.h"
#include "phoneme.h"
#include "synthesize.h"
#include "spect.h"
#include "translate.h"
#include "prosodydisplay.h"
static char *about_string = "espeakedit: %s\nAuthor: Jonathan Duddington (c) 2006";
const char *path_data = "/home/jsd1/speechdata/phsource";
extern void TestTest(int control);
extern void CompareLexicon(int);
extern void ConvertToUtf8();
extern void init_z();
extern void CompileInit(void);
extern void CompileMbrola();
extern void InitSpectrumDisplay();
extern void InitProsodyDisplay();
extern void InitWaveDisplay();
extern void VowelChart(int control, char *fname);
extern void MakeVowelLists(void);
extern void MakeWordFreqList();
extern const char *dict_name;
extern char voice_name[];
MyFrame *myframe = NULL;
SpectDisplay *currentcanvas = NULL;
ChildFrProsody *prosodyframe = NULL;
ProsodyDisplay *prosodycanvas = NULL;
wxNotebook *notebook = NULL;
wxProgressDialog *progress;
int progress_max;
int gui_flag = 0;
wxList my_children;
wxFont FONT_SMALL(8,wxSWISS,wxNORMAL,wxNORMAL);
wxFont FONT_MEDIUM(9,wxSWISS,wxNORMAL,wxNORMAL);
wxFont FONT_NORMAL(10,wxSWISS,wxNORMAL,wxNORMAL);
IMPLEMENT_APP(MyApp)
wxString AppName = _T("espeakedit");
int MyApp::OnExit()
{//================
ConfigSave(1);
return(0);
}
static const char *help_text =
"\n\nespeakedit\n"
"\tRun with GUI\n"
"espeakedit --compile\n"
"\tCompile phoneme data in espeak-data/phsource\n"
"\tand dictionary data in espeak-data/dictsource\n";
// Initialise this in OnInit, not statically
bool MyApp::OnInit(void)
{//=====================
int j;
wxChar *p;
char param[80];
if(argc > 1)
{
extern void VoiceReset(int control);
p = argv[1];
j = 0;
while((param[j] = p[j]) != 0) j++;
if((strcmp(param,"--help")==0) || (strcmp(param,"-h")==0))
{
printf(about_string,espeak_Info(NULL));
printf(help_text);
exit(0);
}
ConfigInit();
VoiceReset(0);
WavegenSetVoice(voice);
WavegenInitSound();
if(strcmp(param,"--compile")==0)
{
CompileInit();
exit(0);
}
if(strcmp(param,"--lex=ru")==0)
{
if(LoadPhData() != 1)
{
fprintf(stderr,"Failed to load phoneme data\n");
exit(1);
}
CompareLexicon(MENU_LEXICON_RU);
exit(0);
}
}
gui_flag = 1;
// It seems that the wctype functions don't work until the locale has been set
// to something other than the default "C". Then, not only Latin1 but also the
// other characters give the correct results with iswalpha() etc.
if(setlocale(LC_CTYPE,"en_US.UTF-8") == NULL)
setlocale(LC_CTYPE,"");
// Create the main frame window
myframe = new MyFrame(NULL, -1, AppName, wxPoint(0, 0), wxSize(1024, 768),
wxDEFAULT_FRAME_STYLE |
wxNO_FULL_REPAINT_ON_RESIZE |
wxHSCROLL | wxVSCROLL);
// Make a menubar
myframe->SetMenuBar(MakeMenu(0));
myframe->CreateStatusBar();
myframe->Show(TRUE);
SetTopWindow(myframe);
wxInitAllImageHandlers();
// wxImage::AddHandler(wxPNGHandler);
return TRUE;
}
BEGIN_EVENT_TABLE(MyFrame, wxMDIParentFrame)
EVT_CHAR(MyFrame::OnKey)
EVT_MENU(MENU_ABOUT, MyFrame::OnAbout)
EVT_MENU(MENU_SPECTRUM, MyFrame::OnNewWindow)
EVT_MENU(MENU_SPECTRUM2, MyFrame::OnNewWindow)
EVT_MENU(MENU_PROSODY, MyFrame::OnProsody)
EVT_MENU(MENU_PARAMS, MyFrame::OnOptions)
EVT_MENU(MENU_PATH1, MyFrame::OnOptions)
EVT_MENU(MENU_PATH2, MyFrame::OnOptions)
EVT_MENU(MENU_PATH3, MyFrame::OnOptions)
EVT_MENU(MENU_COMPILE_PH, MyFrame::OnTools)
EVT_MENU(MENU_COMPILE_DICT, MyFrame::OnTools)
EVT_MENU(MENU_COMPILE_MBROLA, MyFrame::OnTools)
EVT_MENU(MENU_CLOSE_ALL, MyFrame::OnQuit)
EVT_MENU(MENU_QUIT, MyFrame::OnQuit)
EVT_MENU(MENU_SPEAK_TRANSLATE, MyFrame::OnSpeak)
EVT_MENU(MENU_SPEAK_RULES, MyFrame::OnSpeak)
EVT_MENU(MENU_SPEAK_TEXT, MyFrame::OnSpeak)
EVT_MENU(MENU_SPEAK_FILE, MyFrame::OnSpeak)
EVT_MENU(MENU_SPEAK_STOP, MyFrame::OnSpeak)
EVT_MENU(MENU_SPEAK_PAUSE, MyFrame::OnSpeak)
EVT_MENU(MENU_SPEAK_VOICE, MyFrame::OnSpeak)
EVT_MENU(MENU_LOAD_WAV, MyFrame::OnTools)
EVT_MENU(MENU_VOWELCHART1, MyFrame::OnTools)
EVT_MENU(MENU_VOWELCHART2, MyFrame::OnTools)
EVT_MENU(MENU_VOWELCHART3, MyFrame::OnTools)
EVT_MENU(MENU_LEXICON_RU, MyFrame::OnTools)
EVT_MENU(MENU_TO_UTF8, MyFrame::OnTools)
EVT_MENU(MENU_COUNT_WORDS, MyFrame::OnTools)
EVT_MENU(MENU_TEST, MyFrame::OnTools)
EVT_MENU(MENU_TEST2, MyFrame::OnTools)
EVT_TIMER(1, MyFrame::OnTimer)
EVT_SIZE(MyFrame::OnSize)
EVT_SASH_DRAGGED_RANGE(ID_WINDOW_TOP, ID_WINDOW_BOTTOM, MyFrame::OnSashDrag)
END_EVENT_TABLE()
MyFrame::MyFrame(wxWindow *parent, const wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size,
const long style):
wxMDIParentFrame(parent, id, title, pos, size, style)
{//===================================================================================================================
// Main Frame constructor
int error_flag = 0;
int result;
// Create some dummy layout windows
wxSashLayoutWindow *win;
// Another window to the left of the client window
win = new wxSashLayoutWindow(this, ID_WINDOW_LEFT2,
wxDefaultPosition, wxSize(200, 30),
wxNO_BORDER | wxSW_3D | wxCLIP_CHILDREN);
win->SetDefaultSize(wxSize(208, 1000));
win->SetOrientation(wxLAYOUT_VERTICAL);
win->SetAlignment(wxLAYOUT_LEFT);
// win->SetBackgroundColour(wxColour(0, 255, 255));
win->SetSashVisible(wxSASH_RIGHT, TRUE);
m_leftWindow2 = win;
notebook = new wxNotebook(m_leftWindow2,-1);
// notebook->AddPage(voicedlg,_T("Voice"),FALSE);
formantdlg = new FormantDlg(notebook);
notebook->AddPage(formantdlg,_T(" Spect"),FALSE);
voicedlg = new VoiceDlg(notebook);
transldlg = new TranslDlg(notebook);
notebook->AddPage(transldlg,_T("Text"),TRUE);
ConfigInit();
WavegenInitSound();
if((result = LoadPhData()) != 1)
{
if(result == -1)
wxLogError(_T("Failed to load phoneme data,\nneeds espeak-data/phontab,phondata,phonindex"));
else
wxLogError(_T("Wrong version of espeak-data: 0x%x (expects 0x%x)"),result,version_phdata);
error_flag = 1;
}
f_trans = stdout;
option_ssml = 1;
// if(LoadVoice(voice_name,0) == NULL)
if(SetVoiceByName(voice_name) != EE_OK)
{
if(error_flag==0)
wxLogError(_T("Failed to load voice data"));
strcpy(dictionary_name,"en");
}
else
{
SetVoiceTitle(voice_name);
}
WavegenSetVoice(voice);
SetParameter(espeakRATE,option_speed,0);
SetSpeed(3);
SynthesizeInit();
InitSpectrumDisplay();
InitProsodyDisplay();
// InitWaveDisplay();
espeak_ListVoices(NULL);
m_timer.SetOwner(this,1);
m_timer.Start(500); /* 0.5 timer */
} // end of MyFrame::MyFrame
void MyFrame::SetVoiceTitle(char *voice_name)
{//==========================================
SetTitle(AppName + _T(" - ") + wxString(voice_name,wxConvLocal) + _T(" voice"));
}
void MyFrame::OnKey(wxKeyEvent& event)
{
int key;
key = event.GetKeyCode();
if((currentcanvas != NULL) && (currentcanvas != FindFocus()))
{
if((key == WXK_F1) || (key == WXK_F2))
{
currentcanvas->OnKey(event);
currentcanvas->SetFocus();
return;
}
}
event.Skip();
}
void MyFrame::OnTimer(wxTimerEvent &event)
//****************************************
{
SynthOnTimer();
}
void MyFrame::OnQuit(wxCommandEvent& event)
{
switch(event.GetId())
{
case MENU_QUIT:
Close(TRUE);
break;
case MENU_CLOSE_ALL:
break;
}
}
//#define xcharset
#ifdef xcharset
#include "iconv.h"
void CharsetToUnicode(const char *charset)
{//=======================================
// write a 8bit charset to unicode translation table to file
// charset: eg. "ISO-8859-1"
iconv_t cd;
unsigned char inbuf[4];
size_t n_inbuf;
unsigned char outbuf[12];
size_t n_outbuf;
int n;
int ix;
int x, y;
FILE *f;
char *p_inbuf;
char *p_outbuf;
f = fopen("/home/jsd1/tmp1/unicode1","a");
cd = iconv_open("WCHAR_T",charset);
if (cd == (iconv_t) -1)
{
fprintf(stderr,"Error - iconv_open\n");
return;
}
fprintf(f,"towlower_tab\n ");
for(ix=0x80; ix<=0x241; ix++)
{
y = 0;
if(iswalpha(ix))
{
x = towlower(ix);
if(x == ix)
y = 0xff;
else
y = x - ix;
}
if((y == 0xff) || (y < 0))
fprintf(f,"0xff,"); // ignore the 5 obscure cases where uc > lc
else
{
fprintf(f,"%4d,",y);
}
if((ix&15)==15)
fprintf(f," // %x\n ",ix & ~15);
}
fprintf(f,"\n%s\n ",charset);
for(ix=0x80; ix<0x100; ix++)
{
inbuf[0] = ix;
inbuf[1] = 0;
inbuf[2] = 0;
outbuf[0] = 0;
outbuf[1] = 0;
n_inbuf = 2;
n_outbuf = sizeof(outbuf);
p_inbuf = (char *)inbuf;
p_outbuf = (char *)outbuf;
n = iconv(cd, &p_inbuf, &n_inbuf, &p_outbuf, &n_outbuf);
fprintf(f,"0x%.2x%.2x, ",outbuf[1],outbuf[0]);
if((ix&7)==7)
fprintf(f,"// %.2x\n ",ix & ~7);
}
fclose(f);
iconv_close(cd);
}
#endif
void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
{//===================================================
//CharsetToUnicode("ISO-8859-3");
//CharsetToUnicode("ISCII");
char buf[120];
sprintf(buf,about_string,espeak_Info(NULL));
(void)wxMessageBox(wxString(buf,wxConvLocal), _T("About eSpeak Editor"));
}
void MyFrame::OnOptions(wxCommandEvent& event)
{
wxString string;
int value;
switch(event.GetId())
{
case MENU_PARAMS:
value = wxGetNumberFromUser(_T(""),_T(""),_T("Speed"),option_speed,80,320);
if(value > 0)
{
option_speed = value;
SetParameter(espeakRATE,option_speed,0);
SetSpeed(3);
}
break;
case MENU_PATH1:
string = wxDirSelector(_T("Phoneme source directory"),path_phsource);
if(!string.IsEmpty())
{
path_phsource = string;
}
break;
case MENU_PATH2:
string = wxDirSelector(_T("Dictionary source directory"),path_dictsource);
if(!string.IsEmpty())
{
path_dictsource = string;
}
break;
case MENU_PATH3:
string = wxFileSelector(_T("Sound output file"),wxFileName(path_speech).GetPath(),
_T(""),_T("WAV"),_T("*"),wxSAVE);
if(!string.IsEmpty())
{
path_speech = string;
}
break;
}
ConfigSetPaths();
}
void MyFrame::OnTools(wxCommandEvent& event)
{//=========================================
int err;
FILE *log;
char fname_log[80];
char err_fname[130];
switch(event.GetId())
{
case MENU_TEST:
TestTest(0);
break;
case MENU_TEST2:
TestTest(2);
break;
case MENU_TO_UTF8:
ConvertToUtf8();
break;
case MENU_COUNT_WORDS:
MakeWordFreqList();
break;
case MENU_LEXICON_RU:
CompareLexicon(event.GetId()); // Compare a lexicon with _rules translation
break;
case MENU_COMPILE_PH:
CompileInit();
break;
case MENU_COMPILE_MBROLA:
CompileMbrola();
break;
case MENU_COMPILE_DICT:
sprintf(fname_log,"%s%s",path_dsource,"dict_log");
log = fopen(fname_log,"w");
if((err = CompileDictionary(path_dsource,dictionary_name,log,err_fname)) < 0)
{
wxLogError(_T("Can't access file:\n")+wxString(err_fname,wxConvLocal));
wxString dir = wxDirSelector(_T("Directory containing dictionary files"),path_dictsource);
if(!dir.IsEmpty())
{
path_dictsource = dir;
strncpy0(path_dsource,path_dictsource.mb_str(wxConvLocal),sizeof(path_dsource)-1);
strcat(path_dsource,"/");
}
break;
}
wxLogStatus(_T("Compiled '")+wxString(dictionary_name,wxConvLocal)+_T("', %d errors"),err);
if(log != NULL)
fclose(log);
break;
case MENU_VOWELCHART1:
MakeVowelLists();
break;
case MENU_VOWELCHART2:
VowelChart(2,NULL);
break;
case MENU_VOWELCHART3:
VowelChart(3,NULL);
break;
case MENU_LOAD_WAV:
// LoadWavFile();
break;
}
}
extern wxMenu *speak_menu;
void MyFrame::OnSpeak(wxCommandEvent& event)
{//=========================================
switch(event.GetId())
{
case MENU_SPEAK_TRANSLATE:
case MENU_SPEAK_RULES:
case MENU_SPEAK_TEXT:
transldlg->OnCommand(event);
break;
case MENU_SPEAK_FILE:
transldlg->SpeakFile();
break;
case MENU_SPEAK_STOP:
SpeakNextClause(NULL,NULL,2);
break;
case MENU_SPEAK_PAUSE:
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:
transldlg->ReadVoice();
if(voicedlg != NULL)
voicedlg->WriteParams();
SetVoiceTitle(voice_name);
break;
}
}
void MyFrame::OnSashDrag(wxSashEvent& event)
{
if (event.GetDragStatus() == wxSASH_STATUS_OUT_OF_RANGE)
return;
switch (event.GetId())
{
case ID_WINDOW_TOP:
{
m_topWindow->SetDefaultSize(wxSize(1000, event.GetDragRect().height));
break;
}
case ID_WINDOW_LEFT1:
{
m_leftWindow1->SetDefaultSize(wxSize(event.GetDragRect().width, 1000));
break;
}
case ID_WINDOW_LEFT2:
{
m_leftWindow2->SetDefaultSize(wxSize(event.GetDragRect().width, 1000));
break;
}
case ID_WINDOW_BOTTOM:
{
m_bottomWindow->SetDefaultSize(wxSize(1000, event.GetDragRect().height));
break;
}
}
wxLayoutAlgorithm layout;
layout.LayoutMDIFrame(this);
// Leaves bits of itself behind sometimes
GetClientWindow()->Refresh();
}
void MyFrame::OnSize(wxSizeEvent& WXUNUSED(event))
{
wxLayoutAlgorithm layout;
layout.LayoutMDIFrame(this);
}
// Note that SASHTEST_NEW_WINDOW and SASHTEST_ABOUT commands get passed
// to the parent window for processing, so no need to
// duplicate event handlers here.
BEGIN_EVENT_TABLE(MyChild, wxMDIChildFrame)
EVT_MENU(SPECTSEQ_CLOSE, MyChild::OnQuit)
EVT_MENU(SPECTSEQ_SAVE, MyChild::OnSave)
EVT_MENU(SPECTSEQ_SAVEAS, MyChild::OnSaveAs)
EVT_MENU(SPECTSEQ_SAVESELECT, MyChild::OnSaveSelect)
EVT_MENU(SPECTSEQ_SAVEPITCH, MyChild::OnSavePitchenv)
EVT_ACTIVATE(MyChild::OnActivate)
END_EVENT_TABLE()
MyChild::MyChild(wxMDIParentFrame *parent, const wxString& title, const wxPoint& pos, const wxSize& size,
const long style):
wxMDIChildFrame(parent, -1, title, pos, size, style)
{
canvas = NULL;
my_children.Append(this);
}
MyChild::~MyChild(void)
{
wxWindow *w;
canvas = NULL;
#ifndef PLATFORM_WINDOWS
// bug in wxMDIChildFrame, we need to explicitly remove the ChildFrame from the ClientWindow
w = myframe->GetClientWindow();
w->RemoveChild(this);
#endif
my_children.DeleteObject(this);
}
//extern void CloseCanvas();
void MyChild::OnQuit(wxCommandEvent& WXUNUSED(event))
{
Destroy();
}
void MyChild::OnSave(wxCommandEvent& WXUNUSED(event))
{
canvas->Save(canvas->savepath);
}
void MyChild::OnSaveAs(wxCommandEvent& WXUNUSED(event))
{
canvas->Save();
}
void MyChild::OnSaveSelect(wxCommandEvent& WXUNUSED(event))
{
canvas->Save(_T(""),1);
}
void MyChild::OnSavePitchenv(wxCommandEvent& WXUNUSED(event))
{
canvas->SavePitchenv(canvas->spectseq->pitchenv);
}
void MyChild::OnActivate(wxActivateEvent& event)
{
if(canvas)
canvas->OnActivate(event.GetActive());
}
+55
View File
@@ -0,0 +1,55 @@
/***************************************************************************
* Copyright (C) 2004 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef _VOICEEDIT_H_
#define _VOICEEDIT_H_
/**
* @short Application Main Window
* @author Jonathan Duddington <jsd@clara.co.uk>
* @version 0.1
*/
class
voiceeditapp : public wxApp
{
public:
virtual bool OnInit();
};
class
voiceeditFrame : public wxFrame
{
public:
voiceeditFrame( const wxString& title, const wxPoint& pos, const wxSize& pos );
void OnQuit( wxCommandEvent& event );
void OnAbout( wxCommandEvent& event );
private:
DECLARE_EVENT_TABLE()
};
enum
{
Menu_File_Quit = 100,
Menu_File_About
};
#endif // _VOICEEDIT_H_
+716
View File
@@ -0,0 +1,716 @@
/***************************************************************************
* Copyright (C) 2007, Gilles Casse <gcasse@oralux.org> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "speech.h"
#ifdef USE_ASYNC
// This source file is only used for asynchronious modes
//<includes
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/time.h>
#include <errno.h>
#include "speak_lib.h"
#include "event.h"
#include "wave.h"
#include "debug.h"
//>
//<decls and function prototypes
// my_mutex: protects my_thread_is_talking,
static pthread_mutex_t my_mutex;
static sem_t my_sem_start_is_required;
static sem_t my_sem_stop_is_required;
static sem_t my_sem_stop_is_acknowledged;
// my_thread: polls the audio duration and compares it to the duration of the first event.
static pthread_t my_thread;
static t_espeak_callback* my_callback = NULL;
static int my_event_is_running=0;
enum {MIN_TIMEOUT_IN_MS=10,
ACTIVITY_TIMEOUT=50, // in ms, check that the stream is active
MAX_ACTIVITY_CHECK=6
};
typedef struct t_node
{
void* data;
t_node *next;
} node;
static node* head=NULL;
static node* tail=NULL;
static int node_counter=0;
static espeak_ERROR push(void* data);
static void* pop();
static void init();
static void* polling_thread(void*);
//>
//<event_init
void event_init(t_espeak_callback* SynthCallback)
{
ENTER("event_init");
my_callback = SynthCallback;
SHOW("event_init > my_callback=0x%x\n", my_callback);
my_event_is_running=0;
// security
pthread_mutex_init( &my_mutex, (const pthread_mutexattr_t *)NULL);
init();
assert(-1 != sem_init(&my_sem_start_is_required, 0, 0));
assert(-1 != sem_init(&my_sem_stop_is_required, 0, 0));
assert(-1 != sem_init(&my_sem_stop_is_acknowledged, 0, 0));
pthread_attr_t a_attrib;
if (pthread_attr_init (& a_attrib)
|| pthread_attr_setdetachstate(&a_attrib, PTHREAD_CREATE_JOINABLE)
|| pthread_create( &my_thread,
& a_attrib,
polling_thread,
(void*)NULL))
{
assert(0);
}
pthread_attr_destroy(&a_attrib);
}
//>
//<event_display
void event_display(espeak_EVENT* event)
{
ENTER("event_display");
#ifdef DEBUG_ENABLED
if (event==NULL)
{
SHOW("event_display > event=%s\n","NULL");
}
else
{
static const char* label[] = {
"LIST_TERMINATED",
"WORD",
"SENTENCE",
"MARK",
"PLAY",
"END",
"MSG_TERMINATED"
};
SHOW("event_display > event=0x%x\n",event);
SHOW("event_display > type=%s\n",label[event->type]);
SHOW("event_display > uid=%d\n",event->unique_identifier);
SHOW("event_display > text_position=%d\n",event->text_position);
SHOW("event_display > length=%d\n",event->length);
SHOW("event_display > audio_position=%d\n",event->audio_position);
SHOW("event_display > sample=%d\n",event->sample);
SHOW("event_display > user_data=0x%x\n",event->user_data);
}
#endif
}
//>
//<event_copy
static espeak_EVENT* event_copy (espeak_EVENT* event)
{
ENTER("event_copy");
if (event==NULL)
{
return NULL;
}
espeak_EVENT* a_event=(espeak_EVENT*)malloc(sizeof(espeak_EVENT));
if (a_event)
{
memcpy(a_event, event, sizeof(espeak_EVENT));
switch(event->type)
{
case espeakEVENT_MARK:
case espeakEVENT_PLAY:
if (event->id.name)
{
a_event->id.name = strdup(event->id.name);
}
break;
default:
break;
}
}
event_display(a_event);
return a_event;
}
//>
//<event_notify
// Call the user supplied callback
//
// Note: the current sequence is:
//
// * First call with: event->type = espeakEVENT_SENTENCE
// * 0, 1 or several calls: event->type = espeakEVENT_WORD
// * Last call: event->type = espeakEVENT_MSG_TERMINATED
//
static void event_notify(espeak_EVENT* event)
{
ENTER("event_notify");
static unsigned int a_old_uid = 0;
if (event && my_callback)
{
event_display(event);
switch(event->type)
{
case espeakEVENT_SENTENCE:
my_callback(NULL, 0, event);
a_old_uid = event->unique_identifier;
break;
case espeakEVENT_WORD:
case espeakEVENT_MSG_TERMINATED:
{
if (a_old_uid != event->unique_identifier)
{
espeak_EVENT_TYPE a_new_type = event->type;
event->type = espeakEVENT_SENTENCE;
my_callback(NULL, 0, event);
event->type = a_new_type;
usleep(50000);
}
my_callback(NULL, 0, event);
a_old_uid = event->unique_identifier;
}
break;
default:
case espeakEVENT_LIST_TERMINATED:
case espeakEVENT_MARK:
case espeakEVENT_PLAY:
case espeakEVENT_END:
break;
}
}
}
//>
//<event_delete
static int event_delete(espeak_EVENT* event)
{
ENTER("event_delete");
event_display(event);
if(event==NULL)
{
return 0;
}
switch(event->type)
{
case espeakEVENT_MSG_TERMINATED:
event_notify(event);
break;
case espeakEVENT_MARK:
case espeakEVENT_PLAY:
if(event->id.name)
{
free((void*)(event->id.name));
}
break;
default:
break;
}
free(event);
return 1;
}
//>
//<event_declare
espeak_ERROR event_declare (espeak_EVENT* event)
{
ENTER("event_declare");
event_display(event);
if (!event)
{
return EE_INTERNAL_ERROR;
}
int a_status = pthread_mutex_lock(&my_mutex);
espeak_ERROR a_error = EE_OK;
if (!a_status)
{
SHOW_TIME("event_declare > locked\n");
espeak_EVENT* a_event = event_copy(event);
a_error = push(a_event);
if (a_error != EE_OK)
{
event_delete(a_event);
}
SHOW_TIME("event_declare > unlocking\n");
a_status = pthread_mutex_unlock(&my_mutex);
}
// TBD: remove the comment
// reminder: code in comment.
// This wait can lead to an underrun
//
// if (!a_status && !my_event_is_running && (a_error == EE_OK))
// {
// // quit when command is actually started
// // (for possible forthcoming 'end of command' checks)
SHOW_TIME("event_declare > post my_sem_start_is_required\n");
sem_post(&my_sem_start_is_required);
// int val=1;
// while (val)
// {
// usleep(50000); // TBD: event?
// sem_getvalue(&my_sem_start_is_required, &val);
// }
// }
if (a_status != 0)
{
a_error = EE_INTERNAL_ERROR;
}
return a_error;
}
//>
//<event_clear_all
espeak_ERROR event_clear_all ()
{
ENTER("event_clear_all");
int a_status = pthread_mutex_lock(&my_mutex);
int a_event_is_running = 0;
SHOW_TIME("event_stop > locked\n");
if (a_status != 0)
{
return EE_INTERNAL_ERROR;
}
if (my_event_is_running)
{
SHOW_TIME("event_stop > post my_sem_stop_is_required\n");
sem_post(&my_sem_stop_is_required);
a_event_is_running = 1;
}
else
{
init(); // clear pending events
}
SHOW_TIME("event_stop > unlocking\n");
a_status = pthread_mutex_unlock(&my_mutex);
if (a_status != 0)
{
return EE_INTERNAL_ERROR;
}
if (a_event_is_running)
{
SHOW_TIME("event_stop > wait for my_sem_stop_is_acknowledged\n");
while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR)
{
continue; // Restart when interrupted by handler
}
SHOW_TIME("event_stop > get my_sem_stop_is_acknowledged\n");
}
SHOW_TIME("LEAVE event_stop\n");
return EE_OK;
}
//>
//<sleep_until_timeout_or_stop_request
static int sleep_until_timeout_or_stop_request(uint32_t time_in_ms)
{
ENTER("sleep_until_timeout_or_stop_request");
int a_stop_is_required=0;
struct timespec ts, to;
struct timeval tv;
int err=0;
clock_gettime2( &ts);
to.tv_sec = ts.tv_sec;
to.tv_nsec = ts.tv_nsec;
add_time_in_ms( &ts, time_in_ms);
SHOW("polling_thread > sleep_until_timeout_or_stop_request > start sem_timedwait from %d.%09lu to %d.%09lu \n",
to.tv_sec, to.tv_nsec,
ts.tv_sec, ts.tv_nsec);
while ((err = sem_timedwait(&my_sem_stop_is_required, &ts)) == -1
&& errno == EINTR)
{
continue; // Restart when interrupted by handler
}
assert (gettimeofday(&tv, NULL) != -1);
SHOW("polling_thread > sleep_until_timeout_or_stop_request > stop sem_timedwait %d.%09lu \n",
tv.tv_sec, tv.tv_usec*1000);
if (err == 0)
{
SHOW("polling_thread > sleep_until_timeout_or_stop_request > %s\n","stop required!");
a_stop_is_required=1; // stop required
}
return a_stop_is_required;
}
//>
//<get_remaining_time
// Asked for the time interval required for reaching the sample.
// If the stream is opened but the audio samples are not played,
// a timeout is started.
static int get_remaining_time(uint32_t sample, uint32_t* time_in_ms, int* stop_is_required)
{
ENTER("get_remaining_time");
int err = 0;
*stop_is_required = 0;
int i=0;
for (i=0; i < MAX_ACTIVITY_CHECK && (*stop_is_required == 0); i++)
{
err = wave_get_remaining_time( sample, time_in_ms);
if (err || wave_is_busy(NULL) || (*time_in_ms == 0))
{ // if err, stream not available: quit
// if wave is busy, time_in_ms is known: quit
// if wave is not busy but remaining time == 0, event is reached: quit
break;
}
// stream opened but not active
//
// Several possible states:
// * the stream is opened but not yet started:
//
// wait for the start of stream
//
// * some samples have already been played,
// ** the end of stream is reached
// ** or there is an underrun
//
// wait for the close of stream
*stop_is_required = sleep_until_timeout_or_stop_request( ACTIVITY_TIMEOUT);
}
return err;
}
//>
//<polling_thread
static void* polling_thread(void*)
{
ENTER("polling_thread");
while(1)
{
int a_stop_is_required=0;
SHOW_TIME("polling_thread > locking\n");
int a_status = pthread_mutex_lock(&my_mutex);
SHOW_TIME("polling_thread > locked (my_event_is_running = 0)\n");
my_event_is_running = 0;
pthread_mutex_unlock(&my_mutex);
SHOW_TIME("polling_thread > unlocked\n");
SHOW_TIME("polling_thread > wait for my_sem_start_is_required\n");
while ((sem_wait(&my_sem_start_is_required) == -1) && errno == EINTR)
{
continue; // Restart when interrupted by handler
}
SHOW_TIME("polling_thread > get my_sem_start_is_required\n");
a_status = pthread_mutex_lock(&my_mutex);
SHOW_TIME("polling_thread > locked (my_event_is_running = 1)\n");
my_event_is_running = 1;
pthread_mutex_unlock(&my_mutex);
SHOW_TIME("polling_thread > unlocked\n");
a_stop_is_required=0;
a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required);
if ((a_status==0) && a_stop_is_required)
{
SHOW("polling_thread > stop required (%d)\n", __LINE__);
while(0 == sem_trywait(&my_sem_stop_is_required))
{
};
}
else
{
a_stop_is_required=0;
}
// In this loop, my_event_is_running = 1
while (head && !a_stop_is_required)
{
SHOW_TIME("polling_thread > check head\n");
while(0 == sem_trywait(&my_sem_start_is_required))
{
};
espeak_EVENT* event = (espeak_EVENT*)(head->data);
assert(event);
uint32_t time_in_ms = 0;
int err = get_remaining_time((uint32_t)event->sample,
&time_in_ms,
&a_stop_is_required);
if (a_stop_is_required)
{
break;
}
else if (err != 0)
{
// No available time: the event is deleted.
SHOW("polling_thread > %s\n","audio device down");
a_status = pthread_mutex_lock(&my_mutex);
SHOW_TIME("polling_thread > locked\n");
event_delete( (espeak_EVENT*)pop());
a_status = pthread_mutex_unlock(&my_mutex);
SHOW_TIME("polling_thread > unlocked\n");
}
else if (time_in_ms==0)
{ // the event is already reached.
if (my_callback)
{
event_notify(event);
// the user_data (and the type) are cleaned to be sure
// that MSG_TERMINATED is called twice (at delete time too).
event->type=espeakEVENT_LIST_TERMINATED;
event->user_data=NULL;
}
a_status = pthread_mutex_lock(&my_mutex);
SHOW_TIME("polling_thread > locked\n");
event_delete( (espeak_EVENT*)pop());
a_status = pthread_mutex_unlock(&my_mutex);
SHOW_TIME("polling_thread > unlocked\n");
a_stop_is_required=0;
a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required);
if ((a_status==0) && a_stop_is_required)
{
SHOW("polling_thread > stop required (%d)\n", __LINE__);
while(0 == sem_trywait(&my_sem_stop_is_required))
{
};
}
else
{
a_stop_is_required=0;
}
}
else
{ // The event will be notified soon: sleep until timeout or stop request
a_stop_is_required = sleep_until_timeout_or_stop_request(time_in_ms);
}
}
a_status = pthread_mutex_lock(&my_mutex);
SHOW_TIME("polling_thread > locked\n");
SHOW_TIME("polling_thread > my_event_is_running = 0\n");
my_event_is_running = 0;
if(!a_stop_is_required)
{
a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required);
if ((a_status==0) && a_stop_is_required)
{
SHOW("polling_thread > stop required (%d)\n", __LINE__);
while(0 == sem_trywait(&my_sem_stop_is_required))
{
};
}
else
{
a_stop_is_required=0;
}
}
a_status = pthread_mutex_unlock(&my_mutex);
SHOW_TIME("polling_thread > unlocked\n");
if (a_stop_is_required)
{
SHOW("polling_thread > %s\n","stop required!");
// no mutex required since the stop command is synchronous
// and waiting for my_sem_stop_is_acknowledged
init();
// acknowledge the stop request
SHOW_TIME("polling_thread > post my_sem_stop_is_acknowledged\n");
a_status = sem_post(&my_sem_stop_is_acknowledged);
}
}
return NULL;
}
//>
//<push, pop, init
enum {MAX_NODE_COUNTER=1000};
// return 1 if ok, 0 otherwise
static espeak_ERROR push(void* the_data)
{
ENTER("event > push");
assert((!head && !tail) || (head && tail));
if (the_data == NULL)
{
SHOW("event > push > event=0x%x\n", NULL);
return EE_INTERNAL_ERROR;
}
if (node_counter >= MAX_NODE_COUNTER)
{
SHOW("event > push > %s\n", "EE_BUFFER_FULL");
return EE_BUFFER_FULL;
}
node *n = (node *)malloc(sizeof(node));
if (n == NULL)
{
return EE_INTERNAL_ERROR;
}
if (head == NULL)
{
head = n;
tail = n;
}
else
{
tail->next = n;
tail = n;
}
tail->next = NULL;
tail->data = the_data;
node_counter++;
SHOW("event > push > counter=%d (uid=%d)\n",node_counter,((espeak_EVENT*)the_data)->unique_identifier);
return EE_OK;
}
static void* pop()
{
ENTER("event > pop");
void* the_data = NULL;
assert((!head && !tail) || (head && tail));
if (head != NULL)
{
node* n = head;
the_data = n->data;
head = n->next;
free(n);
node_counter--;
SHOW("event > pop > event=0x%x (counter=%d, uid=%d)\n",the_data, node_counter,((espeak_EVENT*)the_data)->unique_identifier);
}
if(head == NULL)
{
tail = NULL;
}
return the_data;
}
static void init()
{
ENTER("event > init");
while (event_delete( (espeak_EVENT*)pop() ))
{}
node_counter = 0;
}
//>
//<event_terminate
void event_terminate()
{
ENTER("event_terminate");
if (my_thread)
{
pthread_cancel(my_thread);
pthread_join(my_thread,NULL);
pthread_mutex_destroy(&my_mutex);
sem_destroy(&my_sem_start_is_required);
sem_destroy(&my_sem_stop_is_required);
sem_destroy(&my_sem_stop_is_acknowledged);
init(); // purge event
}
}
#endif
//>
+50
View File
@@ -0,0 +1,50 @@
#ifndef EVENT_H
#define EVENT_H
/*
Manage events (sentence, word, mark, end,...), is responsible of calling the external
callback as soon as the relevant audio sample is played.
The audio stream is composed of samples from synthetised messages or audio icons.
Each event is associated to a sample.
Scenario:
- event_declare is called for each expected event.
- A timeout is started for the first pending event.
- When the timeout happens, the synth_callback is called.
Note: the timeout is checked against the real progress of the audio stream, which depends on pauses or underruns. If the real progress is lower than the expected one, a new timeout starts.
*/
#include "speak_lib.h"
// Initialize the event component.
// First function to be called.
// the callback will be called when the event actually occurs.
// The callback is detailled in speak_lib.h .
void event_init(t_espeak_callback* cb);
// Clear any pending event.
//
// Return: EE_OK: operation achieved
// EE_INTERNAL_ERROR.
espeak_ERROR event_clear_all ();
// Declare a future event
//
// Return: EE_OK: operation achieved
// EE_BUFFER_FULL: the event can not be buffered;
// you may try after a while to call the function again.
// EE_INTERNAL_ERROR.
espeak_ERROR event_declare (espeak_EVENT* event);
// Terminate the event component.
// Last function to be called.
void event_terminate();
#endif
+894
View File
@@ -0,0 +1,894 @@
/***************************************************************************
* Copyright (C) 2006 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "wx/wx.h"
#include <wx/dirdlg.h>
#include "wx/filename.h"
#include "wx/sound.h"
#include "sys/stat.h"
#include "speak_lib.h"
#include "main.h"
#include "speech.h"
#include "voice.h"
#include "spect.h"
#include "phoneme.h"
#include "synthesize.h"
#include "translate.h"
#include "options.h"
//******************************************************************************************************
FILE *f_wavtest = NULL;
FILE *f_events = NULL;
int OpenWaveFile3(const char *path, int rate)
/******************************************/
{
int *p;
static unsigned char wave_hdr[44] = {
'R','I','F','F',0,0,0,0,'W','A','V','E','f','m','t',' ',
0x10,0,0,0,1,0,1,0, 9,0x3d,0,0,0x12,0x7a,0,0,
2,0,0x10,0,'d','a','t','a', 0,0,0,0 };
if(path == NULL)
return(2);
// set the sample rate in the header
p = (int *)(&wave_hdr[24]);
p[0] = rate;
p[1] = rate * 2;
f_wavtest = fopen(path,"wb");
if(f_wavtest != NULL)
{
fwrite(wave_hdr,1,sizeof(wave_hdr),f_wavtest);
return(0);
}
return(1);
} // end of OpenWaveFile
void CloseWaveFile3(int rate)
/******************/
{
unsigned int pos;
static int value;
fflush(f_wavtest);
pos = ftell(f_wavtest);
value = pos - 8;
fseek(f_wavtest,4,SEEK_SET);
fwrite(&value,4,1,f_wavtest);
value = rate;
fseek(f_wavtest,24,SEEK_SET);
fwrite(&value,4,1,f_wavtest);
value = rate*2;
fseek(f_wavtest,28,SEEK_SET);
fwrite(&value,4,1,f_wavtest);
value = pos - 44;
fseek(f_wavtest,40,SEEK_SET);
fwrite(&value,4,1,f_wavtest);
fclose(f_wavtest);
f_wavtest = NULL;
} // end of CloseWaveFile2
int TestUriCallback(int type, const char *uri, const char *base)
{//=============================================================
if(strcmp(uri,"hello")==0)
return(1);
return(0);
}
int TestSynthCallback(short *wav, int numsamples, espeak_EVENT *events)
{//====================================================================
int type;
if(f_wavtest == NULL) return(0);
if(wav == NULL)
{
fprintf(f_events,"Finished\n");
CloseWaveFile3(samplerate);
fclose(f_events);
return(0);
}
fwrite(wav,numsamples*2,1,f_wavtest);
while((type = events->type) != 0)
{
fprintf(f_events,"%4d %4d (%2d): %d ",events->audio_position,events->text_position,events->length,type);
if((type==3) || (type==4))
fprintf(f_events,"'%s'\n",events->id.name);
else
fprintf(f_events,"%d\n",events->id.number);
events++;
}
return(0);
}
//******************************************************************************************************
#ifdef deleted
static int RuLex_sorter(char **a, char **b)
{//=======================================
char *pa, *pb;
int xa, xb;
int ix;
pa = *a;
pb = *b;
xa = strlen(pa)-1;
xb = strlen(pb)-1;
while((xa >= 0) && (xb >= 0))
{
if((ix = (pa[xa] - pb[xb])) != 0)
return(ix);
xa--;
xb--;
}
return(pa - pb);
} /* end of strcmp2 */
#endif
static const unsigned short KOI8_R[0x60] = {
0x2550, 0x2551, 0x2552, 0x0451, 0x2553, 0x2554, 0x2555, 0x2556, // a0
0x2557, 0x2558, 0x2559, 0x255a, 0x255b, 0x255c, 0x255d, 0x255e, // a8
0x255f, 0x2560, 0x2561, 0x0401, 0x2562, 0x2563, 0x2564, 0x2565, // b0
0x2566, 0x2567, 0x2568, 0x2569, 0x256a, 0x256b, 0x256c, 0x00a9, // b8
0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, // c0
0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, // c8
0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, // d0
0x044c, 0x044b, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a, // d8
0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, // e0
0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, // e8
0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, // f0
0x042c, 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042a, // f8
};
#define N_CHARS 34
int *p_unicode;
int unicode[80];
void Lexicon_Ru()
{//==============
// compare stress markings in Russian RuLex file with lookup in ru_rules
int ix;
char *p;
int c;
FILE *f_in;
FILE *f_out;
FILE *f_log;
FILE *f_roots;
PHONEME_TAB *ph;
int vcount;
int ru_stress;
int max_stress;
int max_stress_posn;
int n_words=0;
int n_wrong=0;
int wlength;
int input_length;
int sfx;
char *suffix;
int wlen;
int len;
int check_root;
WORD_TAB winfo;
char word[80];
char word2[80];
int counts[20][20][10];
char phonemes[N_WORD_PHONEMES];
char buf[100];
// KOI8-R codes for Russian vowels
static char vowels[] = {0xa3,0xc0,0xc1,0xc5,0xc9,0xcf,0xd1,0xd5,0xd9,0xdc,0};
typedef struct {
char *suffix;
int syllables;
} SUFFIX;
static SUFFIX suffixes[] = {
{NULL,0},
{"ичу",2},
{"ского",2},
{"ская",2},
{"ски",1},
{"ские",2},
{"ский",1},
{"ским",1},
{"ское",2},
{"ской",1},
{"ском",1},
{"скую",2},
{"а",1},
{"е",1},
{"и",1},
{NULL,0}};
memset(counts,0,sizeof(counts));
if(gui_flag)
{
wxString fname = wxFileSelector(_T("Read lexicon.dict"),path_dictsource,
_T(""),_T(""),_T("*"),wxOPEN);
if(fname.IsEmpty())
return;
strcpy(buf,fname.mb_str(wxConvLocal));
}
else
{
strcpy(buf,"lexicon.dict");
}
if((f_in = fopen(buf,"r")) == NULL)
{
if(gui_flag)
wxLogError(_T("Can't read file: ") + wxString(buf,wxConvLocal));
else
fprintf(stderr,"Can't read file: %s\n",buf);
return;
}
input_length = GetFileLength(buf);
sprintf(buf,"%s%c%s",path_dsource,PATHSEP,"ru_listx_1");
if((f_out = fopen(buf,"w")) == NULL)
{
wxLogError(_T("Can't write to: ")+wxString(buf,wxConvLocal));
fclose(f_in);
return;
}
sprintf(buf,"%s%c%s",path_dsource,PATHSEP,"ru_log");
f_log = fopen(buf,"w");
sprintf(buf,"%s%c%s",path_dsource,PATHSEP,"ru_roots_1");
f_roots = fopen(buf,"w");
LoadVoice("ru",0);
if(gui_flag)
progress = new wxProgressDialog(_T("Lexicon"),_T(""),input_length);
else
fprintf(stderr,"Processing lexicon.dict\n");
for(;;)
{
if(((n_words & 0x3ff) == 0) && gui_flag)
{
progress->Update(ftell(f_in));
}
if(fgets(buf,sizeof(buf),f_in) == NULL)
break;
if(isspace2(buf[0]))
continue;
// convert word from KOI8-R to UTF8
p = buf;
ix = 0;
wlength = 0;
p_unicode = unicode;
while(!isspace2(c = (*p++ & 0xff)))
{
if(c >= 0xa0)
{
c = KOI8_R[c-0xa0];
*p_unicode++ = c;
}
wlength++;
ix += utf8_out(c,&word[ix]);
}
word[ix] = 0;
*p_unicode=0;
sprintf(word2," %s ",word);
// find the marked stress position
vcount = 0;
ru_stress = 0;
while(*p == ' ') p++;
while((c = (*p++ & 0xff)) != '\n')
{
if(c == '+')
{
ru_stress = vcount;
break;
}
if(strchr(vowels,c) != NULL)
{
vcount++;
}
}
// translate
memset(&winfo,0,sizeof(winfo));
translator->TranslateWord(&word2[1],0,&winfo);
DecodePhonemes(translator->word_phonemes,phonemes);
// find the stress position in the translation
max_stress = 0;
max_stress_posn = -1;
vcount = 0;
check_root = 0;
ph = phoneme_tab[phonPAUSE];
for(p=translator->word_phonemes; *p != 0; p++)
{
ph = phoneme_tab[(unsigned int)*p];
if(ph == NULL)
continue;
if(ph->type == phVOWEL)
vcount++;
if(ph->type == phSTRESS)
{
if(ph->std_length > max_stress)
{
max_stress = ph->std_length;
max_stress_posn = vcount+1;
}
}
}
n_words++;
if(ru_stress > vcount)
{
if(f_log != NULL)
{
fprintf(f_log,"%s\t $%d\t // %s\n",word,ru_stress,phonemes);
}
}
else
{
counts[vcount][ru_stress][ph->type]++;
if((vcount > 1) && (ru_stress != max_stress_posn))
{
n_wrong++;
if((ru_stress==0) || (ru_stress > 7))
fprintf(f_out,"// "); // we only have $1 to $7 to indicate stress position
else
check_root = 1;
#define X_COMPACT
fprintf(f_out,"%s",word);
#ifdef X_COMPACT
if(wlength < 8) fputc('\t',f_out);
if(wlength < 16) fputc('\t',f_out);
fprintf(f_out," $%d\n",ru_stress);
#else
while(wlength++ < 20)
fputc(' ',f_out);
fprintf(f_out," $%d //%d %s\n",ru_stress,max_stress_posn,phonemes);
#endif
//CharStats();
}
}
if(check_root)
{
// does this word match any suffixes ?
wlen = strlen(word);
for(sfx=0;(suffix = suffixes[sfx].suffix) != NULL; sfx++)
{
len = strlen(suffix);
if(len >= (wlen-2))
continue;
if(ru_stress > (vcount - suffixes[sfx].syllables))
continue;
if(strcmp(suffix,&word[wlen-len])==0)
{
strcpy(word2,word);
word2[wlen-len] = 0;
// fprintf(f_roots,"%s\t $%d\t\\ %s\n",word2,ru_stress,suffix);
fprintf(f_roots,"%s\t $%d\n",word2,ru_stress);
}
}
}
}
fclose(f_in);
fclose(f_out);
fclose(f_roots);
sprintf(buf,"Lexicon: Total %d OK %d wrong %d",n_words,n_words-n_wrong,n_wrong);
if(gui_flag)
{
delete progress;
wxLogStatus(wxString(buf,wxConvLocal));
}
else
{
fprintf(stderr,"%s\n",buf);
}
if(f_log != NULL)
{
#ifdef deleted
// list tables of frequency of stress position for words of different syllable lengths
int j,k;
for(ix=0; ix<12; ix++)
{
fprintf(f_log,"%2d syl: ",ix);
for(k=0; k<10; k++)
{
fprintf(f_log," %2d :",k);
for(j=0; j<10; j++)
{
fprintf(f_log,"%6d ",counts[ix][j][k]);
}
fprintf(f_log,"\n");
}
fprintf(f_log,"\n\n");
}
#endif
fclose(f_log);
}
} // end of Lexicon_Ru
void CompareLexicon(int id)
{//========================
switch(id)
{
case MENU_LEXICON_RU:
Lexicon_Ru();
break;
}
} // end of CompareLexicon
//******************************************************************************************************
extern int HashDictionary(const char *string);
static int n_words;
struct wcount {
struct wcount *link;
int count;
char *word;
};
static int wfreq_sorter(wcount **p1, wcount **p2)
{//==============================================
int x;
wcount *a, *b;
a = *p1;
b = *p2;
if((x = b->count - a->count) != 0)
return(x);
return(strcmp(a->word,b->word));
}
static void wfreq_add(const char *word, wcount **hashtab)
{//======================================================
wcount *p;
wcount **p2;
int len;
int hash;
hash = HashDictionary(word);
p2 = &hashtab[hash];
p = *p2;
while(p != NULL)
{
if(strcmp(p->word,word)==0)
{
p->count++;
return;
}
p2 = &p->link;
p = *p2;
}
// word not found, add it to the list
len = strlen(word) + 1;
if((p = (wcount *)malloc(sizeof(wcount)+len)) == NULL)
return;
p->count = 1;
p->link = NULL;
p->word = (char *)p + sizeof(wcount);
strcpy(p->word,word);
*p2 = p;
n_words++;
}
void CountWordFreq(wxString path, wcount **hashtab)
{//================================================
// Count the occurances of words in this file
FILE *f_in;
unsigned char c;
int wc;
unsigned int ix, j, k;
int n_chars;
char buf[80];
char wbuf[80];
if((f_in = fopen(path.mb_str(wxConvLocal),"rb")) == NULL)
return;
while(!feof(f_in))
{
while((c = fgetc(f_in)) < 'A')
{
// skip leading spaces, numbers, etc
if(feof(f_in)) break;
}
// read utf8 bytes until a space, number or punctuation
ix = 0;
while(!feof(f_in) && (c >= 'A') && (ix < sizeof(buf)-1))
{
buf[ix++] = c;
c = fgetc(f_in);
}
buf[ix++] = 0;
buf[ix] = 0;
// the buf may contain non-alphabetic characters
j = 0;
n_chars = 0;
for(k=0; k<ix; )
{
k += utf8_in(&wc,&buf[k],0);
wc = towlower(wc); // convert to lower case
if(iswalpha(wc))
{
j += utf8_out(wc,&wbuf[j]);
n_chars++;
}
else
{
wbuf[j] = 0;
if(n_chars > 2)
{
wfreq_add(wbuf,hashtab);
}
j = 0;
n_chars = 0;
}
}
}
fclose(f_in);
} // end of CountWordFreq
void MakeWordFreqList()
{//====================
// Read text files from a specified directory and make a list of the most frequently occuring words.
struct wcount *whashtab[N_HASH_DICT];
wcount **w_list;
int ix;
int j;
int hash;
wcount *p;
FILE *f_out;
char buf[200];
char buf2[200];
wxString dir = wxDirSelector(_T("Directory of text files"),path_speaktext);
if(dir.IsEmpty()) return;
memset(whashtab,0,sizeof(whashtab));
wxString path = wxFindFirstFile(dir+_T("/*"),wxFILE);
while (!path.empty())
{
if(path.AfterLast(PATHSEP) != _T("!wordcounts"))
{
CountWordFreq(path,whashtab);
path = wxFindNextFile();
}
}
// put all the words into a list and then sort it
w_list = (wcount **)malloc(sizeof(wcount *) * n_words);
ix = 0;
for(hash=0; hash < N_HASH_DICT; hash++)
{
p = whashtab[hash];
while((p != NULL) && (ix < n_words))
{
w_list[ix++] = p;
p = p->link;
}
}
qsort((void *)w_list,ix,sizeof(wcount *),(int(*)(const void *,const void *))wfreq_sorter);
// write out the sorted list
strcpy(buf,dir.mb_str(wxConvLocal));
sprintf(buf2,"%s/!wordcounts",buf);
if((f_out = fopen(buf2,"w")) == NULL)
return;
for(j=0; j<ix; j++)
{
p = w_list[j];
fprintf(f_out,"%5d %s\n",p->count,p->word);
free(p);
}
fclose(f_out);
} // end of Make WorkFreqList
//******************************************************************************************************
void ConvertToUtf8()
{//=================
// Convert a file from 8bit to UTF8, according to the current voice
unsigned int c;
int ix;
FILE *f_in;
FILE *f_out;
char buf[80];
wxString fname = wxFileSelector(_T("Convert file to UTF8"),wxString(path_home,wxConvLocal),
_T(""),_T(""),_T("*"),wxOPEN);
if(fname.IsEmpty())
return;
strcpy(buf,fname.mb_str(wxConvLocal));
f_in = fopen(buf,"r");
if(f_in == NULL)
{
wxLogError(_T("Can't read file: ")+fname);
return;
}
strcat(buf,"_1");
f_out = fopen(buf,"w");
if(f_out == NULL)
{
wxLogError(_T("Can't create file: ")+wxString(buf,wxConvLocal));
fclose(f_in);
return;
}
while(!feof(f_in))
{
c = fgetc(f_in);
if(c >= 0xa0)
c = translator->charset_a0[c-0xa0];
ix = utf8_out(c,buf);
fwrite(buf,ix,1,f_out);
}
fclose(f_in);
fclose(f_out);
} // end of ConvertToItf8
//#define calcspeedtab
#ifdef calcspeedtab
// used to set up the presets in the speed_lookup table
// interpolate between a set of measured wpm values
void SetSpeedTab(void)
{//===================
#define N_WPM 19
// Interpolation table to translate from words-per-minute to internal speed
// words-per-minute values (measured)
static float wpm1[N_WPM] =
{0, 82, 96, 108, 124, 134, 147, 162, 174, 189, 224, 259, 273, 289, 307, 326, 346, 361, 370 };
// corresponding internal speed values
static float wpm2[N_WPM] =
{0,253,200, 170, 140, 125, 110, 95, 85, 75, 55, 40, 35, 30, 25, 20, 15, 10, 5 };
unsigned char speed_lookup[290];
unsigned int ix;
float x;
int speed_wpm;
FILE *f;
// convert from word-per-minute to internal speed code
for(speed_wpm=80; speed_wpm<370; speed_wpm++)
{
for(ix=2; ix<N_WPM-2; ix++)
{
if(speed_wpm < wpm1[ix])
break;
}
x = polint(&wpm1[ix-1], &wpm2[ix-1], 3, speed_wpm);
speed_lookup[speed_wpm-80] = (unsigned char)x;
}
f = fopen("speed_lookup","w");
if(f == NULL) return;
for(ix=0; ix<sizeof(speed_lookup); ix++)
{
fprintf(f,"%4d,",speed_lookup[ix]);
if((ix % 5) == 4)
fprintf(f,"\t//%4d\n\t",(ix / 5)*5 + 80);
}
fclose(f);
} // end of SetSpeedTab
#endif
char* text[]=
{
"<voice xml:lang=\"en\" variant=\"1\">eSpeak.</voice>",
" <voice xml:lang=\"en\" gender=\"male\" variant=\"1\"><prosody rate=\"1.0\"> normally would, </prosody></voice>"
};
#ifdef deleted
void Test2()
{
//
char buf[120];
FILE *f;
FILE *f_out;
unsigned char *p;
f = fopen("/home/jsd1/tmp1/list","r");
if(f == NULL) return;
f_out = fopen("/home/jsd1/tmp1/list_out","w");
if(f_out == NULL) return;
while(!feof(f))
{
if(fgets(buf,sizeof(buf),f) == NULL)
break;
p = (unsigned char *)buf;
while(*p > ' ') p++;
*p = 0;
fprintf(f_out,"%s . . .\n",buf);
}
fclose(f);
fclose(f_out);
}
void Test3()
{
espeak_VOICE voicespec;
espeak_VOICE *newvoice;
int x;
espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS,100,NULL);
memset(&voicespec,0,sizeof(voicespec));
voicespec.languages = "de";
espeak_SetVoiceByProperties(&voicespec);
newvoice = espeak_GetCurrentVoice();
x = 1;
}
#endif
void TestTest(int control)
{//=======================
FILE *f;
unsigned int c;
unsigned int ix=0;
char textbuf[2000];
espeak_VOICE voice;
//FindPhonemesUsed();
//return;
return;
if(control==2)
{
return;
}
memset(&voice,0,sizeof(voice));
f = fopen("/home/jsd1/speechdata/text/test","r");
if(f==NULL)
return;
while(!feof(f) && (ix < sizeof(textbuf)-2))
{
c = fgetc(f);
if(!feof(f))
textbuf[ix++] = c;
}
textbuf[ix] = 0;
fclose(f);
OpenWaveFile3("/home/jsd1/speechdata/text/test.wav",samplerate);
f_events = fopen("/home/jsd1/speechdata/text/events","w");
espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS,100,NULL);
espeak_SetSynthCallback(TestSynthCallback);
// espeak_SetUriCallback(TestUriCallback);
voice.languages = "fr";
voice.gender = 0;
voice.age = 0;
voice.variant = 0;
espeak_SetVoiceByName("en");
// espeak_SetVoiceByProperties(&voice);
espeak_SetParameter(espeakRANGE,0,0);
espeak_Synth(textbuf,ix+1,0,POS_CHARACTER,0,espeakCHARS_8BIT+espeakSSML,NULL,NULL);
// for(voice.variant = 1; voice.variant < 9; voice.variant++)
// {
// espeak_SetVoiceByProperties(&voice);
// espeak_Synth(textbuf,ix+1,0,POS_CHARACTER,0,espeakCHARS_8BIT+espeakSSML,NULL,NULL);
// }
}
+593
View File
@@ -0,0 +1,593 @@
/***************************************************************************
* Copyright (C) 2007, Gilles Casse <gcasse@oralux.org> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "speech.h"
#ifdef USE_ASYNC
// This source file is only used for asynchronious modes
//<includes
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <wchar.h>
#include <errno.h>
#include <sys/time.h>
#include <time.h>
#include "fifo.h"
#include "wave.h"
#include "debug.h"
//>
//<decls and function prototypes
// my_mutex: protects my_thread_is_talking,
// my_stop_is_required, and the command fifo
static pthread_mutex_t my_mutex;
static int my_command_is_running = 0;
static int my_stop_is_required = 0;
// + fifo
//
// my_thread: reads commands from the fifo, and runs them.
static pthread_t my_thread;
static sem_t my_sem_start_is_required;
static sem_t my_sem_stop_is_acknowledged;
static void* say_thread(void*);
static espeak_ERROR push(t_espeak_command* the_command);
static t_espeak_command* pop();
static void init();
static int node_counter=0;
enum {MAX_NODE_COUNTER=200,
INACTIVITY_TIMEOUT=50, // in ms, check that the stream is inactive
MAX_INACTIVITY_CHECK=2
};
//>
//<fifo_init
void fifo_init()
{
ENTER("fifo_init");
// security
pthread_mutex_init( &my_mutex, (const pthread_mutexattr_t *)NULL);
init();
assert(-1 != sem_init(&my_sem_start_is_required, 0, 0));
assert(-1 != sem_init(&my_sem_stop_is_acknowledged, 0, 0));
pthread_attr_t a_attrib;
if (pthread_attr_init (& a_attrib)
|| pthread_attr_setdetachstate(&a_attrib, PTHREAD_CREATE_JOINABLE)
|| pthread_create( &my_thread,
& a_attrib,
say_thread,
(void*)NULL))
{
assert(0);
}
pthread_attr_destroy(&a_attrib);
// leave once the thread is actually started
SHOW_TIME("fifo > wait for my_sem_stop_is_acknowledged\n");
while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR)
{
continue; // Restart when interrupted by handler
}
SHOW_TIME("fifo > get my_sem_stop_is_acknowledged\n");
}
//>
//<fifo_add_command
espeak_ERROR fifo_add_command (t_espeak_command* the_command)
{
ENTER("fifo_add_command");
int a_status = pthread_mutex_lock(&my_mutex);
espeak_ERROR a_error = EE_OK;
if (!a_status)
{
SHOW_TIME("fifo_add_command > locked\n");
a_error = push(the_command);
SHOW_TIME("fifo_add_command > unlocking\n");
a_status = pthread_mutex_unlock(&my_mutex);
}
if (!a_status && !my_command_is_running && (a_error == EE_OK))
{
// quit when command is actually started
// (for possible forthcoming 'end of command' checks)
SHOW_TIME("fifo_add_command > post my_sem_start_is_required\n");
sem_post(&my_sem_start_is_required);
int val=1;
while (val)
{
usleep(50000); // TBD: event?
sem_getvalue(&my_sem_start_is_required, &val);
}
}
if (a_status != 0)
{
a_error = EE_INTERNAL_ERROR;
}
SHOW_TIME("LEAVE fifo_add_command");
return a_error;
}
//>
//<fifo_add_commands
espeak_ERROR fifo_add_commands (t_espeak_command* command1, t_espeak_command* command2)
{
ENTER("fifo_add_command");
int a_status = pthread_mutex_lock(&my_mutex);
espeak_ERROR a_error = EE_OK;
if (!a_status)
{
SHOW_TIME("fifo_add_commands > locked\n");
if (node_counter+1 >= MAX_NODE_COUNTER)
{
SHOW("push > %s\n", "EE_BUFFER_FULL");
a_error = EE_BUFFER_FULL;
}
else
{
push(command1);
push(command2);
}
SHOW_TIME("fifo_add_command > unlocking\n");
a_status = pthread_mutex_unlock(&my_mutex);
}
if (!a_status && !my_command_is_running && (a_error == EE_OK))
{
// quit when one command is actually started
// (for possible forthcoming 'end of command' checks)
SHOW_TIME("fifo_add_command > post my_sem_start_is_required\n");
sem_post(&my_sem_start_is_required);
int val=1;
while (val)
{
usleep(50000); // TBD: event?
sem_getvalue(&my_sem_start_is_required, &val);
}
}
if (a_status != 0)
{
a_error = EE_INTERNAL_ERROR;
}
SHOW_TIME("LEAVE fifo_add_commands");
return a_error;
}
//>
//<fifo_stop
espeak_ERROR fifo_stop ()
{
ENTER("fifo_stop");
int a_command_is_running = 0;
int a_status = pthread_mutex_lock(&my_mutex);
SHOW_TIME("fifo_stop > locked\n");
if (a_status != 0)
{
return EE_INTERNAL_ERROR;
}
if (my_command_is_running)
{
a_command_is_running = 1;
my_stop_is_required = 1;
SHOW_TIME("fifo_stop > my_stop_is_required = 1\n");
}
SHOW_TIME("fifo_stop > unlocking\n");
a_status = pthread_mutex_unlock(&my_mutex);
if (a_status != 0)
{
return EE_INTERNAL_ERROR;
}
if (a_command_is_running)
{
SHOW_TIME("fifo_stop > wait for my_sem_stop_is_acknowledged\n");
while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR)
{
continue; // Restart when interrupted by handler
}
SHOW_TIME("fifo_stop > get my_sem_stop_is_acknowledged\n");
}
SHOW_TIME("fifo_stop > my_stop_is_required = 0\n");
my_stop_is_required = 0;
SHOW_TIME("LEAVE fifo_stop\n");
return EE_OK;
}
//>
//<fifo_is_speaking
int fifo_is_busy ()
{
// ENTER("isSpeaking");
// int aResult = (int) (my_command_is_running || WaveIsPlaying());
SHOW("fifo_is_busy > aResult = %d\n",my_command_is_running);
return my_command_is_running;
}
// int pause ()
// {
// ENTER("pause");
// // TBD
// // if (espeakPause (espeakHandle, 1))
// return true;
// }
// int resume ()
// {
// ENTER("resume");
// // TBD
// // if (espeakPause (espeakHandle, 0))
// return true;
// }
//>
//<sleep_until_start_request_or_inactivity
int sleep_until_start_request_or_inactivity()
{
SHOW_TIME("fifo > sleep_until_start_request_or_inactivity > ENTER");
int a_start_is_required=0;
// Wait for the start request (my_sem_start_is_required).
// Besides this, if the audio stream is still busy,
// check from time to time its end.
// The end of the stream is confirmed by several checks
// for filtering underflow.
//
int i=0;
while((i<= MAX_INACTIVITY_CHECK) && !a_start_is_required)
{
if (wave_is_busy( NULL) )
{
i = 0;
}
else
{
i++;
}
int err=0;
struct timespec ts, to;
struct timeval tv;
clock_gettime2( &ts);
to.tv_sec = ts.tv_sec;
to.tv_nsec = ts.tv_nsec;
add_time_in_ms( &ts, INACTIVITY_TIMEOUT);
SHOW("fifo > sleep_until_start_request_or_inactivity > start sem_timedwait (start_is_required) from %d.%09lu to %d.%09lu \n",
to.tv_sec, to.tv_nsec,
ts.tv_sec, ts.tv_nsec);
while ((err = sem_timedwait(&my_sem_start_is_required, &ts)) == -1
&& errno == EINTR)
{
continue;
}
assert (gettimeofday(&tv, NULL) != -1);
SHOW("fifo > sleep_until_start_request_or_inactivity > stop sem_timedwait (start_is_required, err=%d) %d.%09lu \n", err,
tv.tv_sec, tv.tv_usec*1000);
if (err==0)
{
a_start_is_required = 1;
}
}
SHOW_TIME("fifo > sleep_until_start_request_or_inactivity > LEAVE");
return a_start_is_required;
}
//>
//<close_stream
static void close_stream()
{
SHOW_TIME("fifo > close_stream > ENTER\n");
// Warning: a wave_close can be already required by
// an external command (espeak_Cancel + fifo_stop), if so:
// my_stop_is_required = 1;
int a_status = pthread_mutex_lock(&my_mutex);
assert (!a_status);
int a_stop_is_required = my_stop_is_required;
if (!a_stop_is_required)
{
my_command_is_running = 1;
}
a_status = pthread_mutex_unlock(&my_mutex);
if (!a_stop_is_required)
{
wave_close(NULL);
int a_status = pthread_mutex_lock(&my_mutex);
assert (!a_status);
my_command_is_running = 0;
a_stop_is_required = my_stop_is_required;
a_status = pthread_mutex_unlock(&my_mutex);
if (a_stop_is_required)
{
// acknowledge the stop request
SHOW_TIME("fifo > close_stream > post my_sem_stop_is_acknowledged\n");
int a_status = sem_post(&my_sem_stop_is_acknowledged);
assert( a_status != -1);
}
}
SHOW_TIME("fifo > close_stream > LEAVE\n");
}
//>
//<say_thread
static void* say_thread(void*)
{
ENTER("say_thread");
SHOW_TIME("say_thread > post my_sem_stop_is_acknowledged\n");
// announce that thread is started
sem_post(&my_sem_stop_is_acknowledged);
int look_for_inactivity=0;
while(1)
{
SHOW_TIME("say_thread > wait for my_sem_start_is_required\n");
int a_start_is_required = 0;
if (look_for_inactivity)
{
a_start_is_required = sleep_until_start_request_or_inactivity();
if (!a_start_is_required)
{
close_stream();
}
}
look_for_inactivity = 1;
if (!a_start_is_required)
{
while ((sem_wait(&my_sem_start_is_required) == -1) && errno == EINTR)
{
continue; // Restart when interrupted by handler
}
}
SHOW_TIME("say_thread > get my_sem_start_is_required\n");
SHOW_TIME("say_thread > my_command_is_running = 1\n");
my_command_is_running = 1;
while( my_command_is_running)
{
SHOW_TIME("say_thread > locking\n");
int a_status = pthread_mutex_lock(&my_mutex);
assert (!a_status);
t_espeak_command* a_command = (t_espeak_command*)pop();
if (a_command == NULL)
{
SHOW_TIME("say_thread > text empty (talking=0) \n");
a_status = pthread_mutex_unlock(&my_mutex);
SHOW_TIME("say_thread > unlocked\n");
SHOW_TIME("say_thread > my_command_is_running = 0\n");
my_command_is_running = 0;
}
else
{
display_espeak_command(a_command);
// purge start semaphore
SHOW_TIME("say_thread > purge my_sem_start_is_required\n");
while(0 == sem_trywait(&my_sem_start_is_required))
{
};
if (my_stop_is_required)
{
SHOW_TIME("say_thread > my_command_is_running = 0\n");
my_command_is_running = 0;
}
SHOW_TIME("say_thread > unlocking\n");
a_status = pthread_mutex_unlock(&my_mutex);
if (my_command_is_running)
{
process_espeak_command(a_command);
}
delete_espeak_command(a_command);
}
}
if (my_stop_is_required)
{
// no mutex required since the stop command is synchronous
// and waiting for my_sem_stop_is_acknowledged
init();
// purge start semaphore
SHOW_TIME("say_thread > purge my_sem_start_is_required\n");
while(0==sem_trywait(&my_sem_start_is_required))
{
};
// acknowledge the stop request
SHOW_TIME("say_thread > post my_sem_stop_is_acknowledged\n");
int a_status = sem_post(&my_sem_stop_is_acknowledged);
assert( a_status != -1);
}
// and wait for the next start
SHOW_TIME("say_thread > wait for my_sem_start_is_required\n");
}
return NULL;
}
int fifo_is_command_enabled()
{
SHOW("ENTER fifo_is_command_enabled=%d\n",(int)(0 == my_stop_is_required));
return (0 == my_stop_is_required);
}
//>
//<fifo
typedef struct t_node
{
t_espeak_command* data;
t_node *next;
} node;
static node* head=NULL;
static node* tail=NULL;
// return 1 if ok, 0 otherwise
static espeak_ERROR push(t_espeak_command* the_command)
{
ENTER("fifo > push");
assert((!head && !tail) || (head && tail));
if (the_command == NULL)
{
SHOW("push > command=0x%x\n", NULL);
return EE_INTERNAL_ERROR;
}
if (node_counter >= MAX_NODE_COUNTER)
{
SHOW("push > %s\n", "EE_BUFFER_FULL");
return EE_BUFFER_FULL;
}
node *n = (node *)malloc(sizeof(node));
if (n == NULL)
{
return EE_INTERNAL_ERROR;
}
if (head == NULL)
{
head = n;
tail = n;
}
else
{
tail->next = n;
tail = n;
}
tail->next = NULL;
tail->data = the_command;
node_counter++;
SHOW("push > counter=%d\n",node_counter);
the_command->state = CS_PENDING;
display_espeak_command(the_command);
return EE_OK;
}
static t_espeak_command* pop()
{
ENTER("fifo > pop");
t_espeak_command* the_command = NULL;
assert((!head && !tail) || (head && tail));
if (head != NULL)
{
node* n = head;
the_command = n->data;
head = n->next;
free(n);
node_counter--;
SHOW("pop > command=0x%x (counter=%d)\n",the_command, node_counter);
}
if(head == NULL)
{
tail = NULL;
}
display_espeak_command(the_command);
return the_command;
}
static void init()
{
ENTER("fifo > init");
while (delete_espeak_command( pop() ))
{}
node_counter = 0;
}
//>
//<fifo_init
void fifo_terminate()
{
ENTER("fifo_terminate");
pthread_cancel(my_thread);
pthread_join(my_thread,NULL);
pthread_mutex_destroy(&my_mutex);
sem_destroy(&my_sem_start_is_required);
sem_destroy(&my_sem_stop_is_acknowledged);
init(); // purge fifo
}
#endif
//>
+58
View File
@@ -0,0 +1,58 @@
#ifndef FIFO_H
#define FIFO_H
// Helps to add espeak commands in a first-in first-out queue
// and run them asynchronously.
#include "espeak_command.h"
#include "speak_lib.h"
// Initialize the fifo component.
// First function to be called.
void fifo_init();
// Add an espeak command.
//
// Note: this function fails if too many commands are already buffered.
// In such a case, the calling function could wait and then add again its command.
//
// Return: EE_OK: operation achieved
// EE_BUFFER_FULL: the command can not be buffered;
// you may try after a while to call the function again.
// EE_INTERNAL_ERROR.
espeak_ERROR fifo_add_command (t_espeak_command* c);
// Add two espeak commands in a single transaction.
//
// Note: this function fails if too many commands are already buffered.
// In such a case, the calling function could wait and then add again these commands.
//
// Return: EE_OK: operation achieved
// EE_BUFFER_FULL: at least one command can not be buffered;
// you may try after a while to call the function again.
// EE_INTERNAL_ERROR.
espeak_ERROR fifo_add_commands (t_espeak_command* c1, t_espeak_command* c2);
// The current running command must be stopped and the awaiting commands are cleared.
// Return: EE_OK: operation achieved
// EE_INTERNAL_ERROR.
espeak_ERROR fifo_stop ();
// Is there a running command?
// Returns 1 if yes; 0 otherwise.
int fifo_is_busy ();
// Terminate the fifo component.
// Last function to be called.
void fifo_terminate();
// Indicates if the running command is still enabled.
//
// Note: this function is mainly called by the SynthCallback (speak_lib.cpp)
// It indicates if the actual wave sample can still be played. It is helpful for
// stopping speech as soon as a cancel command is applied.
//
// Returns 1 if yes, or 0 otherwise.
int fifo_is_command_enabled();
#endif
+370
View File
@@ -0,0 +1,370 @@
/***************************************************************************
* Copyright (C) 2005, 2006 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wx.h"
#include "wx/mdi.h"
#include "main.h"
#include "speech.h"
#include "voice.h"
#include "spect.h"
#include "wx/textctrl.h"
#include "wx/checkbox.h"
FormantDlg *formantdlg=NULL;
ByteGraph *pitchgraph=NULL;
BEGIN_EVENT_TABLE(FormantDlg, wxPanel)
EVT_BUTTON(T_ZOOMOUT,FormantDlg::OnCommand)
EVT_BUTTON(T_ZOOMIN,FormantDlg::OnCommand)
EVT_SPINCTRL(T_AMPLITUDE,FormantDlg::OnSpin)
EVT_SPINCTRL(T_TIMESEQ,FormantDlg::OnSpin)
EVT_SPINCTRL(T_AMPFRAME,FormantDlg::OnSpin)
EVT_SPINCTRL(T_TIMEFRAME,FormantDlg::OnSpin)
END_EVENT_TABLE()
void FormantDlg::OnCommand(wxCommandEvent& event)
{//=============================================
int id;
switch(id = event.GetId())
{
case T_ZOOMIN:
case T_ZOOMOUT:
currentcanvas->OnZoom(id);
break;
case T_AMPLITUDE:
currentcanvas->RefreshDialogValues(0);
break;
case T_TIMEFRAME:
case T_AMPFRAME:
currentcanvas->RefreshDialogValues(1);
break;
}
currentcanvas->SetFocus();
}
void FormantDlg::OnSpin(wxSpinEvent& event)
{//=============================================
int id;
switch(id = event.GetId())
{
case T_AMPLITUDE:
case T_TIMESEQ:
case T_TIMEFRAME:
currentcanvas->RefreshDialogValues(0);
break;
case T_AMPFRAME:
currentcanvas->RefreshDialogValues(1);
break;
}
currentcanvas->SetFocus();
}
FormantDlg::FormantDlg(wxWindow *parent) : wxPanel(parent,-1,wxDefaultPosition,wxSize(400,1000))
{//==========================================================================
int ix;
int y;
int xplace;
int id;
wxString string;
y = 24;
xplace = 28;
id = 201;
new wxStaticText(this,-1,_T("Formants"),wxPoint(4,5));
for(ix=0; ix<N_PEAKS; ix++)
{
string.Printf(_T("%d"),ix);
t_labpk[ix] = new wxStaticText(this,id++,string,
wxPoint(xplace-22,y+1+24*ix));
t_pkfreq[ix] = new wxTextCtrl(this,id++,_T(""),
wxPoint(xplace,y+24*ix),wxSize(48,20),
wxTE_CENTRE+wxTE_READONLY);
t_pkheight[ix] = new wxTextCtrl(this,id++,_T(""),
wxPoint(xplace+52,y+24*ix),wxSize(36,20),
wxTE_CENTRE+wxTE_READONLY);
t_pkwidth[ix] = new wxTextCtrl(this,id++,_T(""),
wxPoint(xplace+92,y+24*ix),wxSize(48,20),
wxTE_CENTRE+wxTE_READONLY);
#ifdef WIDTH2
t_pkright[ix] = new wxTextCtrl(this,id++,_T(""),
wxPoint(xplace+132,y+24*ix),wxSize(36,20),
wxTE_CENTRE+wxTE_READONLY);
#endif
if(ix>5) t_pkwidth[ix]->Hide();
}
t_ampframe = new wxSpinCtrl(this,T_AMPFRAME,_T(""),
wxPoint(6,244),wxSize(52,24),wxTE_CENTRE,0,500);
t_lab[3] = new wxStaticText(this,-1,_T("% amp - Frame"),wxPoint(61,248));
t_timeframe = new wxSpinCtrl(this,T_TIMEFRAME,_T(""),
wxPoint(6,272),wxSize(52,24),wxTE_CENTRE,0,500);
t_orig_frame = new wxStaticText(this,-1,_T("mS"),wxPoint(61,276));
t_zoomout = new wxButton(this,T_ZOOMOUT,_T("Zoom-"),wxPoint(16,310));
t_zoomin = new wxButton(this,T_ZOOMIN,_T("Zoom+"),wxPoint(106,310));
// usepitchenv = new wxCheckBox(this,T_USEPITCHENV,_T("???"),wxPoint(20,320));
t_amplitude = new wxSpinCtrl(this,T_AMPLITUDE,_T(""),
wxPoint(6,370),wxSize(52,24),wxTE_CENTRE,0,500);
t_lab[2] = new wxStaticText(this,-1,_T("% amp - Sequence"),wxPoint(61,374));
// t_timeseq = new wxSpinCtrl(this,T_TIMESEQ,_T(""),
// wxPoint(6,400),wxSize(52,24),wxTE_CENTRE,0,500);
t_orig_seq = new wxStaticText(this,-1,_T("mS"),wxPoint(61,404));
t_pitch = new wxStaticText(this,-1,_T(""),wxPoint(4,440),wxSize(192,24));
pitchgraph = new ByteGraph(this,wxPoint(0,460),wxSize(200,140));
pitchgraph->SetData(128,env_fall);
pitchgraph->ShowSpectrum(1);
pitchgraph->Show();
}
void FormantDlg::ShowFrame(SpectSeq *spectseq, int frame, int pk, int field)
{//==============================================================
int ix;
SpectFrame *sf;
wxString value;
int original_mS;
if(spectseq->frames == NULL)
return;
sf = spectseq->frames[frame];
if(field == 0xff)
{
// indicate the selected peak
// t_select_peak[pk]->SetValue(TRUE);
}
for(ix=0; ix<N_PEAKS; ix++)
{
if(field != 0xff && pk!=ix)
continue;
if(field == 0xff)
{
if(pk==ix)
value.Printf(_T("%d="),ix);
else
value.Printf(_T("%d"),ix);
t_labpk[ix]->SetLabel(value);
}
if(field & 1)
{
value.Printf(_T("%4d"),sf->peaks[ix].pkfreq);
t_pkfreq[ix]->SetValue(value);
}
if(field & 2)
{
value.Printf(_T("%3d"),sf->peaks[ix].pkheight >> 6);
t_pkheight[ix]->SetValue(value);
}
if(field & 4)
{
value.Printf(_T("%3d"),sf->peaks[ix].pkwidth);
t_pkwidth[ix]->SetValue(value);
#ifdef WIDTH2
value.Printf(_T("%3d"),sf->peaks[ix].pkright);
t_pkright[ix]->SetValue(value);
#endif
}
}
// find the time until the next keyframe
SetSpinCtrl(t_timeframe,int(spectseq->GetFrameLength(frame,1,&original_mS)+0.5)); // round to nearest integer
value.Printf(_T("%d mS"),original_mS);
t_orig_frame->SetLabel(value);
value.Printf(_T("%3d"),sf->amp_adjust);
t_ampframe->SetValue(value);
} // end of FormantDlg::ShowFrame
BEGIN_EVENT_TABLE(ByteGraph,wxScrolledWindow)
EVT_LEFT_DOWN(ByteGraph::OnMouse)
END_EVENT_TABLE()
static wxBrush BRUSH_FORMANT(wxColour(255,100,50),wxSOLID);
static wxPen PEN_KEYFRAME(wxColour(255,220,100),2,wxSOLID);
static wxPen PEN_KEYFORMANT(wxColour(0,0,0),2,wxSOLID);
ByteGraph::ByteGraph(wxWindow *parent, const wxPoint& pos, const wxSize &size):
wxScrolledWindow(parent,-1,pos,size,wxSUNKEN_BORDER)
{//============================================================
npoints = 0;
show_spectrum = 0;
} // end of ByteGraph::ByteGraph
void ByteGraph::SetData(int nx, unsigned char *data)
{//=================================================
npoints = nx;
graph = data;
Refresh();
} // end of ByteGraph::SetData
void ByteGraph::ShowSpectrum(int yes)
{//==================================
show_spectrum = yes;
}
void ByteGraph::DrawSpectrum(wxDC& dc)
{//===================================
SpectSeq *seq;
SpectFrame *sf;
int ix;
int x, y;
int pk;
double length;
int numframes;
int width, height;
double xscale, yscale;
spectrum_scale = 0;
if(currentcanvas==NULL) return;
if((seq = currentcanvas->spectseq) == NULL) return;
numframes = seq->numframes;
if(numframes == 0) return;
GetClientSize(&width,&height);
if(((length = seq->frames[numframes-1]->time)==0) && (numframes>1))
length = seq->frames[numframes-2]->time;
length = length - seq->frames[0]->time;
yscale = height/3000.0;
spectrum_scale = xscale = width/length;
dc.SetBrush(BRUSH_FORMANT);
dc.SetPen(*wxTRANSPARENT_PEN);
for(ix=0; ix<numframes; ix++)
{
sf = seq->frames[ix];
x = int(sf->time*xscale);
if(sf->keyframe)
{
dc.SetPen(PEN_KEYFRAME);
dc.DrawLine(x,0,x,height);
}
dc.SetPen(*wxTRANSPARENT_PEN);
dc.DrawCircle(x,int(height-(sf->formants[1].freq-200)*yscale),3);
dc.DrawCircle(x,int(height-(sf->formants[2].freq-200)*yscale),3);
dc.DrawCircle(x,int(height-(sf->formants[3].freq-200)*yscale),3);
if(sf->keyframe)
{
dc.SetPen(PEN_KEYFORMANT);
for(pk=1; pk<6; pk++)
{
y = height-int((sf->peaks[pk].pkfreq-200)*yscale);
dc.DrawLine(x-3,y,x+3,y);
}
}
}
}
void ByteGraph::OnDraw(wxDC &dc)
{//============================
int x, y, x2, y2, ix;
int width, height;
double xscale, yscale;
if(show_spectrum) DrawSpectrum(dc);
if(npoints==0) return;
GetClientSize(&width,&height);
xscale = double(width) / npoints;
yscale = double(height) / 256.0;
if(npoints > 8)
{
// draw pitch profile
dc.SetPen(*wxMEDIUM_GREY_PEN);
x = 0;
y = height - int(graph[0]*yscale);
for(ix=1; ix<npoints; ix++)
{
dc.DrawLine(x,y,x2=int(ix*xscale),y2=height - int(graph[ix]*yscale));
x = x2;
y = y2;
}
}
} // end of ByteGraph::OnDraw
void ByteGraph::OnMouse(wxMouseEvent& event)
{//========================================
int ix;
SpectSeq *seq;
double time;
double t0, t1;
double t2=0;
if(currentcanvas==NULL) return;
if((seq = currentcanvas->spectseq) == NULL) return;
wxClientDC dc(this);
PrepareDC(dc);
wxPoint pt(event.GetLogicalPosition(dc));
t0 = t2 = seq->frames[0]->time;
time = (double(pt.x) / spectrum_scale) + t0;
for(ix=1; ix<seq->numframes; ix++)
{
t1=t2;
t2 = seq->frames[ix]->time;
if(time <= t1 + (t2-t1)/2)
{
currentcanvas->SelectFrame(ix-1);
break;
}
}
} // end of ByteGraph::OnMouse
+1021
View File
File diff suppressed because it is too large Load Diff
Executable
+179
View File
@@ -0,0 +1,179 @@
/////////////////////////////////////////////////////////////////////////////
// Name: sashtest.h
// Purpose: Layout window/sash sample
// Author: Julian Smart
// Modified by:
// Created: 04/01/98
// RCS-ID: $Id: sashtest.h,v 1.4 2002/03/17 14:15:55 VZ Exp $
// Copyright: (c) Julian Smart
// Licence: wxWindows license
/////////////////////////////////////////////////////////////////////////////
#include "wx/toolbar.h"
#include "wx/laywin.h"
#include "wx/progdlg.h"
typedef unsigned short USHORT;
typedef unsigned char UCHAR;
extern wxMenuBar *MakeMenu(int type);
extern wxString path_phsource;
extern wxString path_speaktext;
extern wxString path_speech;
extern wxProgressDialog *progress;
extern int progress_max;
extern int gui_flag;
extern char *WavFileName();
class SpectDisplay;
// Define a new application
class MyApp: public wxApp
{
public:
bool OnInit(void);
int OnExit(void);
};
// Define a new frame
class MyFrame: public wxMDIParentFrame
{
public:
MyFrame(wxWindow *parent, const wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, const long style);
void OnSize(wxSizeEvent& event);
void OnAbout(wxCommandEvent& event);
void OnQuit(wxCommandEvent& event);
void OnOptions(wxCommandEvent& event);
void OnSpeak(wxCommandEvent& event);
void OnTools(wxCommandEvent& event);
void OnSashDrag(wxSashEvent& event);
void OnKey(wxKeyEvent& event);
void OnTimer(wxTimerEvent& event);
void LoadWavFile(void);
void OnNewWindow(wxCommandEvent& event);
void OnProsody(wxCommandEvent& event);
protected:
wxSashLayoutWindow* m_topWindow;
wxSashLayoutWindow* m_leftWindow1;
wxSashLayoutWindow* m_leftWindow2;
wxSashLayoutWindow* m_bottomWindow;
void SetVoiceTitle(char *voice_name);
wxTimer m_timer;
DECLARE_EVENT_TABLE()
};
class MyChild: public wxMDIChildFrame
{
public:
SpectDisplay *canvas;
MyChild(wxMDIParentFrame *parent, const wxString& title, const wxPoint& pos, const wxSize& size, const long style);
~MyChild(void);
void OnActivate(wxActivateEvent& event);
void OnQuit(wxCommandEvent& event);
void OnSave(wxCommandEvent& event);
void OnSaveAs(wxCommandEvent& event);
void OnSaveSelect(wxCommandEvent& event);
void OnSavePitchenv(wxCommandEvent& event);
void OnVoiceDlg(wxCommandEvent& event);
DECLARE_EVENT_TABLE()
};
class TranslDlg : public wxPanel
{//=============================
public:
TranslDlg(wxWindow *parent);
void OnKey(wxKeyEvent& event);
void SpeakFile(void);
void ReadVoice(void);
void OnCommand(wxCommandEvent& event);
private:
wxButton *t_translate;
wxButton *t_process;
wxTextCtrl* t_source;
wxTextCtrl* t_phonetic;
DECLARE_EVENT_TABLE()
};
extern MyFrame *myframe;
extern TranslDlg *transldlg;
extern wchar_t ipa1[256];
extern wxFont FONT_SMALL;
extern wxFont FONT_MEDIUM;
extern wxFont FONT_NORMAL;
enum {
MENU_QUIT = 1,
MENU_SPECTRUM,
MENU_SPECTRUM2,
MENU_CLOSE_ALL,
MENU_ABOUT,
MENU_OPTIONS,
MENU_PARAMS,
MENU_PATHS,
MENU_PATH1,
MENU_PATH2,
MENU_PATH3,
MENU_PROSODY,
MENU_COMPILE_PH,
MENU_COMPILE_DICT,
MENU_COMPILE_MBROLA,
MENU_SPEAK_TRANSLATE,
MENU_SPEAK_RULES,
MENU_SPEAK_TEXT,
MENU_SPEAK_FILE,
MENU_SPEAK_PAUSE,
MENU_SPEAK_STOP,
MENU_SPEAK_VOICE,
MENU_LOAD_WAV,
MENU_VOWELCHART,
MENU_VOWELCHART1,
MENU_VOWELCHART2,
MENU_VOWELCHART3,
MENU_LEXICON,
MENU_LEXICON_RU,
MENU_TO_UTF8,
MENU_COUNT_WORDS,
MENU_TEST,
MENU_TEST2,
SPECTSEQ_CLOSE,
SPECTSEQ_SAVE,
SPECTSEQ_SAVEAS,
SPECTSEQ_SAVESELECT,
SPECTSEQ_SAVEPITCH,
SPECTSEQ_VOICEDLG
};
#define ID_WINDOW_TOP 100
#define ID_WINDOW_LEFT1 101
#define ID_WINDOW_LEFT2 102
#define ID_WINDOW_BOTTOM 103
+205
View File
@@ -0,0 +1,205 @@
#ifndef MBROLIB_H
#define MBROLIB_H
/*
* mbrolib: mbrola wrapper.
*
* Copyright (C) 2007 Gilles Casse <gcasse@oralux.org>
*
* This 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.
*
* This 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 this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef __cplusplus
extern "C" {
#endif
/* < types */
/** Parameters */
typedef struct {
int ignore_error; /* 1=Ignore any fatal error or unknown diphone */
char comment_char; /* Comment character */
float volume_ratio; /* Volume ratio */
float frequency_ratio; /* Applied to pitch points */
float time_ratio; /* Applied to phone durations */
} mbrolib_parameter;
/** Returned errors */
typedef enum {
MBROLIB_OK=0,
MBROLIB_DATABASE_NOT_INSTALLED,
MBROLIB_INVAL,
MBROLIB_OUT_OF_MEMORY,
MBROLIB_OUT_OF_RANGE,
MBROLIB_READ_ERROR,
MBROLIB_WRITE_ERROR
} MBROLIB_ERROR;
/** Gender */
typedef enum {
MBROLIB_FEMALE,
MBROLIB_MALE
} MBROLIB_GENDER;
/** Voice descriptor */
typedef struct {
char *name; /* name (for example: "en1") */
char *filename; /* database pathname (for example: "/usr/share/mbrola/voices/en1) */
int rate; /* database sample rate */
MBROLIB_GENDER gender;
const char *language; /* Language and optional dialect qualifier in ascii (e.g. en, fr_ca). */
} mbrolib_voice;
/* > */
/** Initialization, returns a new handle.
First function.
@param the_sample_rate: output rate in Hz (for example 22050). If 0, keep the original database rate.
@return handle (or NULL if error).
*/
void* mbrolib_init( int sample_rate);
typedef void* (t_mbrolib_init)(int);
/** Returns the list of the installed mbrola databases.
The databases are searched according to the MBROLA_PATH environment variable if set,
or under a default path otherwise (see MBROLA_PATH in mbrolib.c).
An array of voices is returned. The last item is set to NULL.
The caller must not free the returned items or the array.
@param the_handle previously given by mbrolib_init.
@return An array of voices.
*/
const mbrolib_voice ** mbrolib_list_voices( void* the_handle);
typedef const mbrolib_voice ** (t_mbrolib_list_voices)(void*);
/** Set voice
@param the_handle.
@param the_database (for example, "en1").
@return error code (MBROLIB_OK, MBROLIB_DATABASE_NOT_INSTALLED, MBROLIB_INVAL).
*/
MBROLIB_ERROR mbrolib_set_voice( void* the_handle, const char* the_name);
typedef MBROLIB_ERROR (t_mbrolib_set_voice)( void*, const char*);
/** Get the current database parameters.
The caller supplies a pointer to an already allocated structure.
@param the_handle previously given by mbrolib_init.
@param the_parameters: pointer to the structure.
@return error code (MBROLIB_OK, MBROLIB_INVAL).
*/
MBROLIB_ERROR mbrolib_get_parameter(void* the_handle, mbrolib_parameter* the_parameter);
typedef MBROLIB_ERROR (t_mbrolib_get_parameter)(void*, mbrolib_parameter*);
/** Set the database parameters using the supplied data.
@param the_handle previously given by mbrolib_init.
@param the_parameters: pointer to the wished parameters.
@return error code (MBROLIB_OK, MBROLIB_INVAL).
*/
MBROLIB_ERROR mbrolib_set_parameter(void* the_handle, const mbrolib_parameter* the_parameter);
typedef MBROLIB_ERROR (t_mbrolib_set_parameter)(void*, const mbrolib_parameter*);
/** Write the mbrola phonemes in the internal buffer.
@param the_handle.
@param the_mbrola_phonemes.
@param the_size in bytes.
@return error code (MBROLIB_OK, MBROLIB_INVAL, MBROLIB_WRITE_ERROR, MBROLIB_READ_ERROR).
*/
MBROLIB_ERROR mbrolib_write(void* the_handle, const char* the_mbrola_phonemes, size_t the_size);
typedef MBROLIB_ERROR (t_mbrolib_write)(void*, const char*, size_t);
/** Read n bytes of the output samples.
@param the_handle.
@param the_samples (raw audio data, 16bits, mono).
@param the_size max number of int16 to read.
@param the_size number of int16 read.
@return error code (MBROLIB_OK, MBROLIB_INVAL, MBROLIB_READ_ERROR).
*/
MBROLIB_ERROR mbrolib_read(void* the_handle, short* the_samples, int the_max_size, int* the_read_size);
typedef MBROLIB_ERROR (t_mbrolib_read)(void*, short*, int, int*);
/** Flush
@param the_handle.
*/
void mbrolib_flush(void* the_handle);
typedef void (t_mbrolib_flush)(void*);
/** Release the handle
@param the_handle.
@return error code (MBROLIB_OK, MBROLIB_INVAL).
*/
MBROLIB_ERROR mbrolib_terminate(void* the_handle);
typedef MBROLIB_ERROR (t_mbrolib_terminate)(void*);
#ifdef __cplusplus
}
#endif
#endif
Executable
+137
View File
@@ -0,0 +1,137 @@
/***************************************************************************
* Copyright (C) 2005, 2006 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "wx/wx.h"
#include "main.h"
wxMenu *speak_menu;
wxMenuBar *MakeMenu(int type)
{//==========================
wxMenu *file_menu;
wxMenu *help_menu;
wxMenu *option_menu = NULL;
wxMenu *paths_menu;
wxMenu *data_menu = NULL;
wxMenu *tools_menu;
wxMenu *voice_menu;
wxMenu *vowelchart_menu;
wxMenu *lexicon_menu;
// FILE MENU
file_menu = new wxMenu;
file_menu->Append(MENU_SPECTRUM, _T("&Open"));
file_menu->Append(MENU_SPECTRUM2, _T("O&pen2"));
// file_menu->Append(MENU_PROSODY, _T("&Prosody window"));
if(type==0)
{
}
else
if(type==1)
{
file_menu->Append(SPECTSEQ_SAVE, _T("&Save"));
file_menu->Append(SPECTSEQ_SAVEAS, _T("Save &As"));
file_menu->Append(SPECTSEQ_SAVESELECT, _T("Save Selection"));
// file_menu->Append(SPECTSEQ_SAVEPITCH, _T("Save Pitch &Envelope"));
file_menu->Append(SPECTSEQ_CLOSE, _T("&Close"));
// file_menu->Append(MENU_CLOSE_ALL, _T("Close &All"));
}
else
if(type==2)
{
file_menu->Append(SPECTSEQ_SAVE, _T("&Save"));
file_menu->Append(SPECTSEQ_SAVEAS, _T("Save &As"));
// file_menu->Append(SPECTSEQ_SAVEPITCH, _T("Save Pitch &Envelope"));
file_menu->Append(SPECTSEQ_CLOSE, _T("&Close"));
// file_menu->Append(MENU_CLOSE_ALL, _T("Close &All"));
}
file_menu->Append(MENU_QUIT, _T("&Quit"));
// SPEAK_MENU
speak_menu = new wxMenu;
speak_menu->Append(MENU_SPEAK_TRANSLATE, _T("&Translate"));
speak_menu->Append(MENU_SPEAK_RULES, _T("Show &Rules"));
speak_menu->Append(MENU_SPEAK_TEXT, _T("&Speak"));
speak_menu->AppendSeparator();
speak_menu->Append(MENU_SPEAK_FILE, _T("Speak &file"));
speak_menu->Append(MENU_SPEAK_PAUSE, _T("&Pause"));
speak_menu->Append(MENU_SPEAK_STOP, _T("St&op"));
// VOICE MENU
voice_menu = new wxMenu;
voice_menu->Append(MENU_SPEAK_VOICE, _T("Select &Voice"));
// DATA MENU
data_menu = new wxMenu;
data_menu->Append(MENU_COMPILE_PH, _T("Compile &phoneme data"));
data_menu->Append(MENU_COMPILE_DICT, _T("Compile &dictionary"));
data_menu->Append(MENU_COMPILE_MBROLA, _T("Compile &mbrola phonemes list"));
// OPTIONS MENU
paths_menu = new wxMenu;
paths_menu->Append(MENU_PATH1, _T("Phoneme data source"));
paths_menu->Append(MENU_PATH2, _T("Dictionary data source"));
paths_menu->Append(MENU_PATH3, _T("Synthesized sound WAV file"));
option_menu = new wxMenu;
option_menu->Append(MENU_PATHS, _T("Set &paths"), paths_menu);
option_menu->Append(MENU_PARAMS, _T("Speed"));
// TOOLS
vowelchart_menu = new wxMenu;
vowelchart_menu->Append(MENU_VOWELCHART1, _T("From compiled phoneme data"));
vowelchart_menu->Append(MENU_VOWELCHART2, _T("From list of formants"));
vowelchart_menu->Append(MENU_VOWELCHART3, _T("From directory of vowel files"));
lexicon_menu = new wxMenu;
lexicon_menu->Append(MENU_LEXICON_RU, _T("Russian (lexicon.dict)"));
tools_menu = new wxMenu;
tools_menu->Append(MENU_VOWELCHART, _T("Make &Vowels Chart"), vowelchart_menu);
// tools_menu->Append(MENU_LOAD_WAV, _T("Analyse WAV file"));
tools_menu->Append(MENU_LEXICON, _T("Compare &Lexicon"), lexicon_menu);
tools_menu->Append(MENU_TO_UTF8, _T("Convert file to &UTF8"));
tools_menu->Append(MENU_COUNT_WORDS, _T("Count word &frequencies"));
tools_menu->Append(MENU_TEST , _T("Test (temporary)"));
// tools_menu->Append(MENU_TEST2 , _T("Test2"));
// HELP MENU
help_menu = new wxMenu;
help_menu->Append(MENU_ABOUT, _T("&About"));
wxMenuBar *menu_bar = new wxMenuBar;
menu_bar->Append(file_menu, _T("&File"));
menu_bar->Append(speak_menu, _T("&Speak"));
menu_bar->Append(voice_menu, _T("&Voice"));
menu_bar->Append(option_menu, _T("&Options"));
menu_bar->Append(tools_menu, _T("&Tools"));
menu_bar->Append(data_menu, _T("&Data"));
menu_bar->Append(help_menu, _T("&Help"));
return(menu_bar);
} // end of MakeMenu
+728
View File
@@ -0,0 +1,728 @@
/***************************************************************************
* Copyright (C) 2006 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; 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 <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <wctype.h>
#include <wchar.h>
#include "speak_lib.h"
#include "speech.h"
#include "phoneme.h"
#include "synthesize.h"
#include "translate.h"
int Translator::TranslateLetter(char *word, char *phonemes, int control)
{//=====================================================================
// get pronunciation for an isolated letter
// return number of bytes used by the letter
// control 2=say-as glyphs, 3-say-as chars
int n_bytes;
int letter;
int len;
unsigned char *p;
char ph_stress[2];
char capital[20];
char ph_buf[30];
char ph_buf2[50];
static char single_letter[8] = {0};
ph_buf[0] = 0;
capital[0] = 0;
n_bytes = utf8_in(&letter,word,0);
if(control > 2)
{
// include CAPITAL information
if(iswupper(letter))
{
Lookup("_cap",capital);
}
}
letter = towlower(letter);
len = utf8_out(letter,&single_letter[2]);
single_letter[2+len] = ' ';
memcpy(&single_letter[3+len],&word[n_bytes],3); // include the next letter, after a space, in case it affects the translation rules
single_letter[1] = '_';
if(Lookup(&single_letter[1],ph_buf) == 0)
{
single_letter[1] = ' ';
if(Lookup(&single_letter[2],ph_buf) == 0)
{
TranslateRules(&single_letter[2], ph_buf, sizeof(ph_buf), NULL,0,0);
}
}
if(ph_buf[0] == phonSWITCH)
{
strcpy(phonemes,ph_buf);
return(0);
}
if(ph_buf[0] == 0)
{
// character name not found
if(iswalpha(letter))
Lookup("_?A",ph_buf);
if((ph_buf[0]==0) && !iswspace(letter))
Lookup("_??",ph_buf);
}
// at a stress marker at the start of the letter name, unless one is already marked
ph_stress[0] = phonSTRESS_P;
ph_stress[1] = 0;
for(p=(unsigned char *)ph_buf; *p != 0; p++)
{
if(phoneme_tab[*p]->type == phSTRESS)
ph_stress[0] = 0; // stress is already marked
}
len = strlen(phonemes);
sprintf(ph_buf2,"%c%s%s%s",0xff,capital,ph_stress,ph_buf); // the 0xff marker will be removed or replaced in SetSpellingStress()
if((len + strlen(ph_buf2)) < N_WORD_PHONEMES)
{
strcpy(&phonemes[len],ph_buf2);
}
return(n_bytes);
} // end of TranslateLetter
void Translator::SetSpellingStress(char *phonemes, int control)
{//============================================================
// Individual letter names, reduce the stress of some.
int ix;
unsigned int c;
int n_stress=0;
int count;
unsigned char buf[N_WORD_PHONEMES];
for(ix=0; (c = phonemes[ix]) != 0; ix++)
{
if(c == phonSTRESS_P)
{
n_stress++;
}
buf[ix] = c;
}
buf[ix] = 0;
count = 0;
for(ix=0; (c = buf[ix]) != 0; ix++)
{
if(c == phonSTRESS_P)
{
count++;
if(langopts.spelling_stress == 1)
{
// stress on initial letter when spelling
if(count > 1)
c = phonSTRESS_3;
}
else
{
if((count != n_stress) && (control < 4))
{
if(((count % 3) != 0) || (count == n_stress-1))
c = phonSTRESS_3; // reduce to secondary stress
}
}
}
else
if(c == 0xff)
{
if(control < 2)
continue; // don't insert pauses
if(control == 4)
c = phonPAUSE; // pause after each character
if(((count % 3) == 0) || (control == 4))
c = phonPAUSE_NOLINK; // pause following a primary stress
else
continue; // remove marker
}
*phonemes++ = c;
}
if(control >= 2)
*phonemes++ = phonPAUSE_NOLINK;
*phonemes = 0;
} // end of SetSpellingStress
int Translator::LookupNum2(int value, int control, char *ph_out)
{//=============================================================
// Lookup a 2 digit number
// control bit 0: use special form of '1'
// control bit 2: use feminine form of '2'
int found;
int ix;
int units;
int used_and=0;
int next_phtype;
char string[12]; // for looking up entries in de_list
char ph_tens[50];
char ph_digits[50];
char ph_and[12];
if((value == 1) && (control & 1))
{
if(Lookup("_1a",ph_out) != 0)
return(0);
}
// is there a special pronunciation for this 2-digit number
found = 0;
if(control & 4)
{
sprintf(string,"_%df",value);
found = Lookup(string,ph_digits);
}
if(found == 0)
{
sprintf(string,"_%d",value);
found = Lookup(string,ph_digits);
}
// no, speak as tens+units
if((control & 2) && (value < 10))
{
// speak leading zero
Lookup("_0",ph_tens);
}
else
{
if(found)
{
strcpy(ph_out,ph_digits);
return(0);
}
if((value % 10) == 0)
{
sprintf(string,"_%d0",value / 10);
found = Lookup(string,ph_tens);
}
if(!found)
{
sprintf(string,"_%dX",value / 10);
Lookup(string,ph_tens);
}
if((value % 10) == 0)
{
strcpy(ph_out,ph_tens);
return(0);
}
found = 0;
units = (value % 10);
if(control & 4)
{
// is there a variant form of this number?
sprintf(string,"_%df",units);
found = Lookup(string,ph_digits);
}
if(found == 0)
{
sprintf(string,"_%d",units);
Lookup(string,ph_digits);
}
}
if(langopts.numbers & 0x30)
{
Lookup("_0and",ph_and);
if(langopts.numbers & 0x10)
sprintf(ph_out,"%s%s%s",ph_digits,ph_and,ph_tens);
else
sprintf(ph_out,"%s%s%s",ph_tens,ph_and,ph_digits);
used_and = 1;
}
else
{
if(langopts.numbers & 0x200)
{
// remove vowel from the end of tens if units starts with a vowel (LANG=Italian)
ix = strlen(ph_tens)-1;
if((next_phtype = phoneme_tab[(unsigned int)(ph_digits[0])]->type) == phSTRESS)
next_phtype = phoneme_tab[(unsigned int)(ph_digits[1])]->type;
if((phoneme_tab[(unsigned int)(ph_tens[ix])]->type == phVOWEL) && (next_phtype == phVOWEL))
ph_tens[ix] = 0;
}
sprintf(ph_out,"%s%s",ph_tens,ph_digits);
}
if(langopts.numbers & 0x100)
{
// only one primary stress
found = 0;
for(ix=strlen(ph_out)-1; ix>=0; ix--)
{
if(ph_out[ix] == phonSTRESS_P)
{
if(found)
ph_out[ix] = phonSTRESS_3;
else
found = 1;
}
}
}
return(used_and);
} // end of LookupNum2
int Translator::LookupNum3(int value, char *ph_out, int suppress_null, int thousandplex, int prev_thousands)
{//=========================================================================================================
// Translate a 3 digit number
int found;
int hundreds;
int x;
char string[12]; // for looking up entries in **_list
char buf1[100];
char buf2[100];
char ph_100[20];
char ph_10T[20];
char ph_digits[50];
char ph_thousands[50];
char ph_hundred_and[12];
hundreds = value / 100;
buf1[0] = 0;
if(hundreds > 0)
{
ph_thousands[0] = 0;
Lookup("_0C",ph_100);
if((hundreds >= 10) && ((langopts.numbers & 0x0800) || (hundreds != 19)))
{
ph_digits[0] = 0;
if(LookupThousands(hundreds / 10, thousandplex+1, ph_10T) == 0)
{
x = 0;
if(langopts.numbers2 & (1 << (thousandplex+1)))
x = 4;
LookupNum2(hundreds/10, x, ph_digits);
}
sprintf(ph_thousands,"%s%s%c",ph_digits,ph_10T,phonPAUSE_NOLINK);
hundreds %= 10;
if(hundreds == 0)
ph_100[0] = 0;
suppress_null = 1;
}
ph_digits[0] = 0;
if(hundreds > 0)
{
suppress_null = 1;
found = 0;
if((value % 1000) == 100)
{
// is there a special pronunciation for exactly 100 ?
found = Lookup("_1C0",ph_digits);
}
if(!found)
{
sprintf(string,"_%dC",hundreds);
found = Lookup(string,ph_digits); // is there a specific pronunciation for n-hundred ?
}
if(found)
{
ph_100[0] = 0;
}
else
{
if((hundreds > 1) || ((langopts.numbers & 0x400) == 0))
{
LookupNum2(hundreds,0,ph_digits);
}
}
}
sprintf(buf1,"%s%s%s",ph_thousands,ph_digits,ph_100);
}
ph_hundred_and[0] = 0;
if((langopts.numbers & 0x40) && ((value % 100) != 0))
{
if((value > 100) || (prev_thousands && (thousandplex==0)))
{
Lookup("_0and",ph_hundred_and);
}
}
buf2[0] = 0;
value = value % 100;
if(value == 0)
{
if(suppress_null == 0)
Lookup("_0",buf2);
}
else
{
x = 0;
if(thousandplex==0)
x = 1; // allow "eins" for 1 rather than "ein"
else
{
if(langopts.numbers2 & (1 << thousandplex))
x = 4; // use variant (feminine) for before thousands and millions
}
if(LookupNum2(value,x,buf2) != 0)
{
if(langopts.numbers & 0x80)
ph_hundred_and[0] = 0; // don't put 'and' after 'hundred' if there's 'and' between tens and units
}
}
sprintf(ph_out,"%s%s%s",buf1,ph_hundred_and,buf2);
return(0);
} // end of LookupNum3
static char *M_Variant(int value)
{//==============================
// returns M, or perhaps MA for some cases
// for Polish language - two forms of plural!
if ((translator->langopts.numbers & 0x80000) &&
((value % 10)>=2) &&
((value % 10)<=4) &&
((value % 100)>20 || (value % 100)<10))
{
return("MA");
}
return("M");
}
int Translator::LookupThousands(int value, int thousandplex, char *ph_out)
{//=======================================================================
int found;
char string[12];
char ph_of[12];
char ph_thousands[40];
ph_of[0] = 0;
// first look fora match with the exact value of thousands
sprintf(string,"_%dM%d",value,thousandplex);
if((found = Lookup(string,ph_thousands)) == 0)
{
if((value % 100) >= 20)
{
Lookup("_0of",ph_of);
}
sprintf(string,"_0%s%d",M_Variant(value),thousandplex);
if(Lookup(string,ph_thousands) == 0)
{
// repeat "thousand" if higher order names are not available
sprintf(string,"_%dM1",value);
if((found = Lookup(string,ph_thousands)) == 0)
Lookup("_0M1",ph_thousands);
}
}
sprintf(ph_out,"%s%s",ph_of,ph_thousands);
return(found);
}
int Translator::TranslateNumber_1(char *word, char *ph_out, unsigned int *flags, int wflags)
{//=========================================================================================
// Number translation with various options
// the "word" may be up to 4 digits
// "words" of 3 digits may be preceded by another number "word" for thousands or millions
int n_digits;
int value;
int ix;
unsigned char c;
int suppress_null = 0;
int decimal_point = 0;
int thousandplex = 0;
int thousands_inc = 0;
int prev_thousands = 0;
int this_value;
static int prev_value;
int decimal_count;
char string[12]; // for looking up entries in de_list
char buf1[100];
char ph_append[50];
char ph_buf[200];
char ph_buf2[50];
static const char str_pause[2] = {phonPAUSE_NOLINK,0};
for(ix=0; isdigit(word[ix]); ix++) ;
n_digits = ix;
value = this_value = atoi(word);
ph_append[0] = 0;
ph_buf2[0] = 0;
// is there a previous thousands part (as a previous "word") ?
if((n_digits == 3) && (word[-2] == langopts.thousands_sep) && isdigit(word[-3]))
{
prev_thousands = 1;
}
else
if((langopts.thousands_sep == ' ') || (langopts.numbers & 0x1000))
{
// thousands groups can be separated by spaces
if((n_digits == 3) && isdigit(word[-2]))
{
prev_thousands = 1;
}
}
if((word[0] == '0') && (prev_thousands == 0) && (word[1] != langopts.decimal_sep))
{
return(0); // number string with leading zero, speak as individual digits
}
if((langopts.numbers & 0x1000) && (word[n_digits] == ' '))
thousands_inc = 1;
else
if(word[n_digits] == langopts.thousands_sep)
thousands_inc = 2;
if(thousands_inc > 0)
{
// if the following "words" are three-digit groups, count them and add
// a "thousand"/"million" suffix to this one
ix = n_digits + thousands_inc;
while(isdigit(word[ix]) && isdigit(word[ix+1]) && isdigit(word[ix+2]))
{
thousandplex++;
if(word[ix+3] == langopts.thousands_sep)
ix += (3 + thousands_inc);
else
break;
}
}
if((value == 0) && prev_thousands)
{
suppress_null = 1;
}
if(word[n_digits] == langopts.decimal_sep)
{
// this "word" ends with a decimal point
Lookup("_dpt",ph_append);
decimal_point = 1;
}
else
if(suppress_null == 0)
{
if(thousands_inc > 0)
{
if((thousandplex > 0) && (value < 1000))
{
if(langopts.numbers & 0x40000)
{
if((thousandplex == 1) && (value >= 100))
{
// special word for 100,000's
char ph_buf3[20];
sprintf(string,"_%dL",value / 100);
if(Lookup(string,ph_buf2) == 0)
{
LookupNum2(value/100,0,ph_buf2);
Lookup("_0L",ph_buf3);
strcat(ph_buf2,ph_buf3);
}
value %= 100;
if(value == 0)
suppress_null = 1;
}
}
if((suppress_null == 0) && (LookupThousands(value,thousandplex,ph_append)))
{
// found an exact match for N thousand
value = 0;
suppress_null = 1;
}
}
}
}
else
if((thousandplex > 1) && prev_thousands && (prev_value > 0))
{
sprintf(string,"_0%s%d",M_Variant(value),thousandplex+1);
if(Lookup(string,buf1)==0)
{
// speak this thousandplex if there was no word for the previous thousandplex
sprintf(string,"_0M%d",thousandplex);
Lookup(string,ph_append);
}
}
if((ph_append[0] == 0) && (word[n_digits] == '.') && (thousandplex == 0))
{
Lookup("_.",ph_append);
}
LookupNum3(value, ph_buf, suppress_null, thousandplex, prev_thousands);
sprintf(ph_out,"%s%s%s",ph_buf2,ph_buf,ph_append);
while(decimal_point)
{
n_digits++;
decimal_count = 0;
while(isdigit(word[n_digits+decimal_count]))
decimal_count++;
if(decimal_count > 1)
{
switch(langopts.numbers & 0x6000)
{
case 0x4000:
if(decimal_count < 4)
{
// Polish decimal fraction
if ((word[n_digits] != '0'))
{
LookupNum3(atoi(&word[n_digits]),buf1,0,0,0);
strcat(ph_out,buf1);
n_digits += decimal_count;
}
else
if ((word[n_digits+1] != '0'))
{
Lookup("_0",buf1);
strcat(ph_out,buf1);
LookupNum3(atoi(&word[n_digits]),buf1,0,0,0);
strcat(ph_out,buf1);
n_digits += decimal_count;
}
}
break;
case 0x2000:
// Italian decimal fractions
if((decimal_count < 4) || ((decimal_count==4) && (word[n_digits] != '0')))
{
LookupNum3(atoi(&word[n_digits]),buf1,0,0,0);
strcat(ph_out,buf1);
if(word[n_digits]=='0')
{
// decimal part has leading zeros, so add a "hundredths" or "thousandths" suffix
sprintf(string,"_0Z%d",decimal_count);
Lookup(string,buf1);
strcat(ph_out,buf1);
}
n_digits += decimal_count;
}
break;
case 0x6000:
// Romanian decimal fractions
if((decimal_count <= 4) && (word[n_digits] != '0'))
{
LookupNum3(atoi(&word[n_digits]),buf1,0,0,0);
strcat(ph_out,buf1);
n_digits += decimal_count;
}
break;
}
}
while(isdigit(c = word[n_digits]) && (strlen(ph_out) < (N_WORD_PHONEMES - 10)))
{
value = word[n_digits++] - '0';
LookupNum2(value, 1, buf1);
strcat(ph_out,buf1);
}
// something after the decimal part ?
if(Lookup("_dpt2",buf1))
strcat(ph_out,buf1);
if(c == langopts.decimal_sep)
{
Lookup("_dpt",buf1);
strcat(ph_out,buf1);
}
else
{
decimal_point = 0;
}
}
if(ph_out[0] != 0)
{
int next_char;
utf8_in(&next_char,&word[n_digits+1],0);
if(!iswalpha(next_char))
strcat(ph_out,str_pause); // don't add pause for 100s, 6th, etc.
}
*flags = FLAG_FOUND;
prev_value = this_value;
return(1);
} // end of TranslateNumber_1
int Translator::TranslateNumber(char *word1, char *ph_out, unsigned int *flags, int wflags)
{//=======================================================================================
if(option_sayas == SAYAS_DIGITS1)
return(0); // speak digits individually
if((langopts.numbers & 0x3) == 1)
return(TranslateNumber_1(word1,ph_out,flags,wflags));
return(0);
} // end of TranslateNumber
+197
View File
@@ -0,0 +1,197 @@
/***************************************************************************
* Copyright (C) 2005,2006 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "wx/wx.h"
#include "wx/fileconf.h"
#include "sys/stat.h"
#include "speech.h"
#ifdef PLATFORM_WINDOWS
#include "wx/msw/registry.h"
#endif
#include "main.h"
#include "options.h"
extern void WavegenInit(int samplerate, int wavemult_fact);
extern void strncpy0(char *to,const char *from, int size);
extern int GetNumeric(wxTextCtrl *t);
extern void SetNumeric(wxTextCtrl *t, int value);
extern int samplerate;
extern char path_home[120]; // .speak directory in user's HOME
extern char voice_name[40];
wxString basedir;
wxString path_spectload;
wxString path_spectload2;
wxString path_pitches;
wxString path_wave;
wxString path_speech;
wxString path_voices;
wxString path_phsource;
wxString path_dictsource;
wxString path_speaktext;
int option_speed=160;
char path_dsource[80];
BEGIN_EVENT_TABLE(Options, wxDialog)
EVT_BUTTON(wxID_SAVE,Options::OnCommand)
EVT_BUTTON(wxID_CLOSE,Options::OnCommand)
END_EVENT_TABLE()
Options::Options(wxWindow *parent) : wxDialog(parent,-1,_T("Options"),wxDefaultPosition,wxDefaultSize)
{//===================================================================================================
m_lab[0] = new wxStaticText(this,-1,_T("Sample rate"),wxPoint(72,84));
m_samplerate = new wxTextCtrl(this,-1,_T(""),wxPoint(8,80),wxSize(60,24));
SetNumeric(m_samplerate,samplerate);
m_save = new wxButton(this,wxID_SAVE,_T("Save"),wxPoint(8,120));
m_close = new wxButton(this,wxID_CLOSE,_T("Cancel"),wxPoint(98,120));
Show();
}
Options::~Options()
{//================
int ix;
for(ix=0; ix < 1; ix++)
delete m_lab[ix];
delete m_samplerate;
delete m_save;
delete m_close;
}
void Options::OnCommand(wxCommandEvent& event)
{//===========================================
int id;
int value;
switch(id = event.GetId())
{
case wxID_SAVE:
value = GetNumeric(m_samplerate);
if(value > 0) WavegenInit(value,0);
Destroy();
break;
case wxID_CLOSE:
Destroy();
break;
}
}
void ConfigSetPaths()
{//==================
// set paths from wxStrings
strncpy0(path_source,path_phsource.mb_str(wxConvLocal),sizeof(path_source)-1);
strcat(path_source,"/");
strncpy0(path_dsource,path_dictsource.mb_str(wxConvLocal),sizeof(path_source)-1);
strcat(path_dsource,"/");
}
void ConfigInit()
{//==============
long value;
wxString string;
const char *path_base;
#ifdef PLATFORM_WINDOWS
char buf[200];
wxRegKey *pRegKey = new wxRegKey(_T("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Speech\\Voices\\Tokens\\eSpeak"));
if(pRegKey->Exists() )
{
wxString RegVal;
pRegKey->QueryValue(_T("Path"),RegVal);
strcpy(buf,RegVal.mb_str(wxConvLocal));
path_base = buf;
}
else
{
path_base = "C:\\Program Files\\eSpeak";
}
sprintf(path_home,"%s\\espeak-data",path_base);
#else
snprintf(path_home,sizeof(path_home),"%s/espeak-data",getenv("HOME"));
path_base = path_home;
#endif
mkdir(path_home,S_IRWXU); // create if it doesn't already exist
wxFileConfig *pConfig = new wxFileConfig(_T("espeakedit"));
wxFileConfig::Set(pConfig);
pConfig->Read(_T("/samplerate"),&value,22050);
#ifdef PLATFORM_WINDOWS
value = 22050;
#endif
WavegenInit(value,0);
pConfig->Read(_T("/basedir"),&basedir,wxString(path_base,wxConvLocal));
pConfig->Read(_T("/spectload"),&path_spectload,basedir+_T("/phsource"));
pConfig->Read(_T("/spectload2"),&path_spectload2,basedir+_T("/phsource"));
pConfig->Read(_T("/pitchpath"),&path_pitches,basedir+_T("/pitch"));
pConfig->Read(_T("/wavepath"),&path_wave,wxEmptyString);
pConfig->Read(_T("/speechpath"),&path_speech,wxEmptyString);
pConfig->Read(_T("/voicepath"),&path_voices,basedir+_T("/voices"));
pConfig->Read(_T("/voicename"),&string,wxEmptyString);
strcpy(voice_name,string.mb_str(wxConvLocal));
pConfig->Read(_T("/phsource"),&path_phsource,basedir+_T("/phsource"));
pConfig->Read(_T("/dictsource"),&path_dictsource,basedir+_T("/dictsource"));
pConfig->Read(_T("/speaktext"),&path_speaktext,wxEmptyString);
option_speed = pConfig->Read(_T("/speed"),160);
ConfigSetPaths();
} // end of ConfigInit
void ConfigSave(int exit)
{//======================
wxFileConfig *pConfig = (wxFileConfig *)(wxConfigBase::Get());
#ifndef PLATFORM_WINDOWS
pConfig->Write(_T("/samplerate"),samplerate);
#endif
pConfig->Write(_T("/basedir"),basedir);
pConfig->Write(_T("/spectload"),path_spectload);
pConfig->Write(_T("/spectload2"),path_spectload2);
pConfig->Write(_T("/pitchpath"),path_pitches);
pConfig->Write(_T("/wavepath"),path_wave);
pConfig->Write(_T("/speechpath"),path_speech);
pConfig->Write(_T("/voicepath"),path_voices);
pConfig->Write(_T("/voicename"),wxString(voice_name,wxConvLocal));
pConfig->Write(_T("/phsource"),path_phsource);
pConfig->Write(_T("/dictsource"),path_dictsource);
pConfig->Write(_T("/speaktext"),path_speaktext);
pConfig->Write(_T("/speed"),option_speed);
if(exit)
delete wxFileConfig::Set((wxFileConfig *)NULL);
}
Executable
+62
View File
@@ -0,0 +1,62 @@
/***************************************************************************
options.h - description
-------------------
begin : Sat Mar 13 2004
copyright : (C) 2004 by Jonathan Duddington
email : jsd@jsd.clara.co.uk
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef OPTIONS_H
#define OPTIONS_H
#include "wx/dialog.h"
#include "wx/spinctrl.h"
/**
*@author Jonathan Duddington
*/
extern void ConfigInit();
extern void ConfigSave(int end);
extern void ConfigSetPaths();
extern wxString path_spectload;
extern wxString path_spectload2;
extern wxString path_pitches;
extern wxString path_voices;
extern wxString path_phsource;
extern wxString path_dictsource;
extern char path_source[80];
extern char path_dsource[80];
extern int option_speed;
// not currently used
class Options : public wxDialog {
public:
Options(wxWindow *parent);
~Options();
private:
void OnCommand(wxCommandEvent& event);
wxButton *m_save;
wxButton *m_close;
wxStaticText *m_lab[10];
wxTextCtrl *m_samplerate;
DECLARE_EVENT_TABLE()
};
#endif
Executable
+147
View File
@@ -0,0 +1,147 @@
/***************************************************************************
* Copyright (C) 2005,2006 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
// phoneme types
#define phPAUSE 0
#define phSTRESS 1
#define phVOWEL 2
#define phLIQUID 3
#define phSTOP 4
#define phVSTOP 5
#define phFRICATIVE 6
#define phVFRICATIVE 7
#define phNASAL 8
#define phVIRTUAL 9
#define phINVALID 15
// phoneme properties
// bits 16-19 give place of articulation (not currently used)
#define phWAVE 0x01
#define phUNSTRESSED 0x02
#define phFORTIS 0x08
#define phVOICED 0x10
#define phSIBILANT 0x20
#define phNOLINK 0x40
#define phTRILL 0x80
#define phVOWEL2 0x100 // liquid that is considered a vowel
#define phPALATAL 0x200
#define phLONG 0x1000
#define phAPPENDPH 0x2000 // always insert another phoneme (link_out) after this one
#define phALTERNATIVE 0x0c00 // bits 10,11 specifying use of alternative_ph
#define phBEFOREVOWEL 0x0000
#define phBEFOREVOWELPAUSE 0x0400
#define phBEFORENOTVOWEL 0x0c00
#define phSWITCHVOICING 0x0800
// fixed phoneme code numbers, these can be used from the program code
#define phonCONTROL 1
#define phonSTRESS_U 2
#define phonSTRESS_D 3
#define phonSTRESS_2 4
#define phonSTRESS_3 5
#define phonSTRESS_P 6
#define phonSTRESS_TONIC 7
#define phonSTRESS_PREV 8
#define phonPAUSE 9
#define phonPAUSE_SHORT 10
#define phonPAUSE_NOLINK 11
#define phonLENGTHEN 12
#define phonSCHWA 13
#define phonSCHWA_SHORT 14
#define phonEND_WORD 15
#define phonSONORANT 16
#define phonDEFAULTTONE 17
#define phonCAPITAL 18
#define phonGLOTTALSTOP 19
#define phonSYLLABIC 20
#define phonSWITCH 21
#define phonX1 22 // a language specific action
#define phonPAUSE_VSHORT 23
// place of articulation
#define phPLACE 0xf0000
#define phPLACE_pla 0x60000
#define N_PHONEME_TABS 50 // number of phoneme tables
#define N_PHONEME_TAB 256 // max phonemes in a phoneme table
#define N_PHONEME_TAB_NAME 32 // must be multiple of 4
// main table of phonemes, index by phoneme number (1-254)
typedef struct {
unsigned int mnemonic; // 1st char is in the l.s.byte
unsigned int phflags; // bits 28-30 reduce_to level, bits 16-19 place of articulation
// bits 10-11 alternative ph control
unsigned short std_length; // for vowels, in mS; for phSTRESS, the stress/tone type
unsigned short spect;
unsigned short before;
unsigned short after;
unsigned char code; // the phoneme number
unsigned char type; // phVOWEL, phPAUSE, phSTOP etc
unsigned char start_type;
unsigned char end_type;
unsigned char length_mod; // a length_mod group number, used to access length_mod_tab
unsigned char reduce_to; // change to this phoneme if unstressed
unsigned char alternative_ph; // change to this phoneme if a vowel follows/doesn't follow
unsigned char link_out; // insert linking phoneme if a vowel follows
} PHONEME_TAB;
// Several phoneme tables may be loaded into memory. phoneme_tab points to
// one for the current voice
extern int n_phoneme_tab;
extern PHONEME_TAB *phoneme_tab[N_PHONEME_TAB];
extern unsigned char phoneme_tab_flags[N_PHONEME_TAB]; // bit 0: not inherited
typedef struct {
char name[N_PHONEME_TAB_NAME];
PHONEME_TAB *phoneme_tab_ptr;
int n_phonemes;
int includes; // also include the phonemes from this other phoneme table
} PHONEME_TAB_LIST;
// table of phonemes to be replaced with different phonemes, for the current voice
#define N_REPLACE_PHONEMES 60
typedef struct {
unsigned char old_ph;
unsigned char new_ph;
char type; // 0=always replace, 1=only at end of word
} REPLACE_PHONEMES;
extern int n_replace_phonemes;
extern REPLACE_PHONEMES replace_phonemes[N_REPLACE_PHONEMES];
char *EncodePhonemes(char *p, char *outptr, unsigned char *bad_phoneme);
void DecodePhonemes(const char *inptr, char *outptr);
const char *PhonemeTabName(void);
int LookupPh(const char *string);
extern const char *WordToString(unsigned int word);
+575
View File
@@ -0,0 +1,575 @@
/***************************************************************************
* Copyright (C) 2007 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; 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 "speak_lib.h"
#include "speech.h"
#include "phoneme.h"
#include "synthesize.h"
#include "translate.h"
int Translator::ChangePhonemes(PHONEME_LIST2 *phlist, int n_ph, int index, PHONEME_TAB *ph, CHANGEPH *ch)
{//======================================================================================================
// Called for each phoneme in the phoneme list, to allow a language to make changes
// flags: bit 0=1 last phoneme in a word
// bit 1=1 this is the highest stressed vowel in the current word
// bit 2=1 after the highest stressed vowel in the current word
// bit 3=1 the phonemes were specified explicitly, or found from an entry in the xx_list dictionary
// ph The current phoneme
return(0);
}
int Translator::SubstitutePhonemes(PHONEME_LIST2 *plist_out)
{//=========================================================
// Copy the phonemes list and perform any substitutions that are required for the
// current voice
int ix;
int k;
int replace_flags;
int n_plist_out = 0;
int word_end;
int max_stress = -1;
int switched_language = 0;
int max_stress_posn=0;
int n_syllables = 0;
int syllable = 0;
int syllable_stressed = 0;
PHONEME_LIST2 *plist2;
PHONEME_LIST2 *pl;
PHONEME_TAB *next=NULL;
for(ix=0; (ix < n_ph_list2) && (n_plist_out < N_PHONEME_LIST); ix++)
{
plist2 = &ph_list2[ix];
if(plist2->phcode == phonSWITCH)
switched_language ^= 1;
// don't do any substitution if the language has been temporarily changed
if(switched_language == 0)
{
if(ix < (n_ph_list2 -1))
next = phoneme_tab[ph_list2[ix+1].phcode];
word_end = 0;
if((plist2+1)->sourceix || ((next != 0) && (next->type == phPAUSE)))
word_end = 1; // this phoneme is the end of a word
if(langopts.phoneme_change != 0)
{
// this language does changes to phonemes after translation
if(plist2->sourceix)
{
// start of a word, find the stressed vowel
syllable = 0;
syllable_stressed = 0;
n_syllables = 0;
max_stress = -1;
max_stress_posn = ix;
for(k=ix; k < n_ph_list2; k++)
{
if(((pl = &ph_list2[k])->sourceix != 0) && (k > ix))
break;
pl->stress &= 0xf;
if(phoneme_tab[pl->phcode]->type == phVOWEL)
{
n_syllables++;
if(pl->stress > max_stress)
{
syllable_stressed = n_syllables;
max_stress = pl->stress;
max_stress_posn = k;
}
}
}
}
if(phoneme_tab[plist2->phcode]->type == phVOWEL)
{
syllable++;
}
// make any language specific changes
int flags;
CHANGEPH ch;
flags = 0;
if(ix == max_stress_posn)
flags |= 2;
if(ix > max_stress_posn)
flags |= 4;
if(ph_list2[ix].synthflags & SFLAG_DICTIONARY)
flags |= 8;
ch.flags = flags | word_end;
ch.stress = plist2->stress;
ch.stress_highest = max_stress;
ch.n_vowels = n_syllables;
ch.vowel_this = syllable;
ch.vowel_stressed = syllable_stressed;
ChangePhonemes(ph_list2, n_ph_list2, ix, phoneme_tab[ph_list2[ix].phcode], &ch);
}
// check whether a Voice has specified that we should replace this phoneme
for(k=0; k<n_replace_phonemes; k++)
{
if(plist2->phcode == replace_phonemes[k].old_ph)
{
replace_flags = replace_phonemes[k].type;
if((replace_flags & 1) && (word_end == 0))
continue; // this replacement only occurs at the end of a word
if((replace_flags & 2) && ((plist2->stress & 0x7) > 3))
continue; // this replacement doesn't occur in stressed syllables
// substitute the replacement phoneme
plist2->phcode = replace_phonemes[k].new_ph;
break;
}
}
if(plist2->phcode == 0)
{
continue; // phoneme has been replaced by NULL, so don't copy it
}
}
// copy phoneme into the output list
memcpy(&plist_out[n_plist_out++],plist2,sizeof(PHONEME_LIST2));
}
return(n_plist_out);
} // end of SubstitutePhonemes
void Translator::MakePhonemeList(int post_pause, int start_sentence)
{//============================================================================================
int ix=0;
int j;
int insert_ph = 0;
int insert_synthflags = 0;
PHONEME_LIST *phlist;
PHONEME_TAB *ph;
PHONEME_TAB *prev, *next, *next2;
int unstress_count = 0;
int word_has_stress = 0;
int switched_language = 0;
int max_stress;
int voicing;
int regression;
int end_sourceix;
int alternative;
PHONEME_LIST2 ph_list3[N_PHONEME_LIST];
static PHONEME_LIST2 ph_list2_null = {0,0,0,0,0};
PHONEME_LIST2 *plist2 = &ph_list2_null;
phlist = phoneme_list;
end_sourceix = ph_list2[n_ph_list2-1].sourceix;
// is the last word of the clause unstressed ?
max_stress = 0;
for(j=n_ph_list2-3; j>=0; j--)
{
// start with the last phoneme (before the terminating pauses) and move forwards
if((ph_list2[j].stress & 0x7f) > max_stress)
max_stress = ph_list2[j].stress & 0x7f;
if(ph_list2[j].sourceix != 0)
break;
}
if(max_stress < 4)
{
// the last word is unstressed, look for a previous word that can be stressed
while(--j >= 0)
{
if(ph_list2[j].stress & 0x80) // dictionary flags indicated that this stress can be promoted
{
ph_list2[j].stress = 4; // promote to stressed
break;
}
if((ph_list2[j].stress & 0x7f) >= 4)
{
// found a stressed syllable, so stop looking
break;
}
}
}
if((regression = langopts.param[LOPT_REGRESSIVE_VOICING]) != 0)
{
// set consonant clusters to all voiced or all unvoiced
// Regressive
int type;
voicing = 0;
for(j=n_ph_list2-1; j>=0; j--)
{
ph = phoneme_tab[ph_list2[j].phcode];
if(ph == NULL)
continue;
if(ph->code == phonSWITCH)
switched_language ^= 1;
if(switched_language)
continue;
type = ph->type;
if(regression & 0x2)
{
// LANG=Russian, [v] amd [v;] don't cause regression
if((ph->mnemonic == 'v') || (ph->mnemonic == ((';'<<8)+'v')))
type = phLIQUID;
}
if((type==phSTOP) || type==(phFRICATIVE))
{
if(voicing==0)
{
voicing = 1;
}
else
if((voicing==2) && ((ph->phflags & phALTERNATIVE)==phSWITCHVOICING))
{
ph_list2[j].phcode = ph->alternative_ph; // change to voiced equivalent
}
}
else
if((type==phVSTOP) || type==(phVFRICATIVE))
{
if(voicing==0)
{
voicing = 2;
}
else
if((voicing==1) && ((ph->phflags & phALTERNATIVE)==phSWITCHVOICING))
{
ph_list2[j].phcode = ph->alternative_ph; // change to unvoiced equivalent
}
}
else
{
if(regression & 0x8)
{
// LANG=Polish, propagate through liquids and nasals
if((type == phPAUSE) || (type == phVOWEL))
voicing = 0;
}
else
{
voicing = 0;
}
}
if((regression & 0x4) && (ph_list2[j].sourceix))
{
// stop propagation at a word boundary
voicing = 0;
}
}
}
n_ph_list2 = SubstitutePhonemes(ph_list3) - 2;
// transfer all the phonemes of the clause into phoneme_list
ph = phoneme_tab[phonPAUSE];
switched_language = 0;
for(j=0; insert_ph || ((j<n_ph_list2) && (ix < N_PHONEME_LIST-3)); j++)
{
prev = ph;
plist2 = &ph_list3[j];
if(insert_ph != 0)
{
// we have a (linking) phoneme which we need to insert here
j--;
next = phoneme_tab[plist2->phcode]; // this phoneme, i.e. after the insert
plist2 = &ph_list2_null;
ph = phoneme_tab[insert_ph];
plist2->synthflags = insert_synthflags;
insert_ph = 0;
insert_synthflags = 0;
}
else
{
// otherwise get the next phoneme from the list
ph = phoneme_tab[plist2->phcode];
if(plist2->phcode == phonSWITCH)
{
// change phoneme table
SelectPhonemeTable(plist2->tone_number);
switched_language ^= SFLAG_SWITCHED_LANG;
}
next = phoneme_tab[(plist2+1)->phcode]; // the phoneme after this one
}
if(plist2->sourceix)
word_has_stress = 0; // start of a word
if(ph == NULL) continue;
if(ph->type == phVOWEL)
{
// check for consecutive unstressed syllables
if(plist2->stress == 0)
{
// an unstressed vowel
unstress_count++;
if((unstress_count > 1) && ((unstress_count & 1)==0))
{
// in a sequence of unstressed syllables, reduce alternate syllables to 'diminished'
// stress. But not for the last phoneme of a stressed word
if((langopts.stress_flags & 0x2) || (word_has_stress && ((plist2+1)->sourceix!=0)))
{
// An unstressed final vowel of a stressed word
unstress_count=1; // try again for next syllable
}
else
{
plist2->stress = 1; // change stress to 'diminished'
}
}
}
else
{
unstress_count = 0;
if(plist2->stress > 3)
word_has_stress = 1; // word has a primary or a secondary stress
}
}
if(ph->alternative_ph > 0)
{
alternative = 0;
switch(ph->phflags & phALTERNATIVE)
{
// This phoneme changes if vowel follows, or doesn't follow, depending on its phNOTFOLLOWS flag
case phBEFORENOTVOWEL:
if(next->type != phVOWEL)
alternative = ph->alternative_ph;
break;
case phBEFOREVOWELPAUSE:
if((next->type == phVOWEL) || (next->type == phPAUSE))
alternative = ph->alternative_ph;
break;
case phBEFOREVOWEL:
if(next->type == phVOWEL)
alternative = ph->alternative_ph;
break;
}
if(alternative == 1)
continue; // NULL phoneme, discard
if(alternative > 1)
{
ph = phoneme_tab[alternative];
if(ph->type == phVOWEL)
plist2->synthflags |= SFLAG_SYLLABLE;
else
plist2->synthflags &= ~SFLAG_SYLLABLE;
}
}
#ifdef deleted
// experimenting with [t] glottaling
if((ph->mnemonic == 't') && ((prev->type == phVOWEL) || (prev->mnemonic == 'n')))
{
if(((plist2+1)->sourceix != 0) || ((plist2-1)->stress == 4) && (next->type == phVOWEL))
{
ph = phoneme_tab[phonGLOTTALSTOP];
}
}
#endif
if((plist2+1)->synthflags & SFLAG_LENGTHEN)
{
static char types_double[] = {phFRICATIVE,phVFRICATIVE,phNASAL,phLIQUID,0};
if(strchr(types_double,next->type))
{
// lengthen this consonant by doubling it
insert_ph = next->code;
(plist2+1)->synthflags ^= SFLAG_LENGTHEN;
}
}
if((plist2+1)->sourceix != 0)
{
int x;
if(langopts.word_gap & 1)
{
insert_ph = phonPAUSE_VSHORT;
}
if(langopts.vowel_pause && (ph->type != phPAUSE) && (next->type == phVOWEL))
{
if(langopts.vowel_pause & 0x04)
{
// break before a word which starts with a vowel
insert_ph = phonPAUSE_VSHORT;
}
if((ph->type == phVOWEL) && ((x = langopts.vowel_pause & 0x03) != 0))
{
// adjacent vowels over a word boundary
if(x == 2)
insert_ph = phonPAUSE_SHORT;
else
insert_ph = phonPAUSE_VSHORT;
}
if(((plist2+1)->stress >= 4) && (langopts.vowel_pause & 0x08))
{
// pause before a words which starts with a stressed vowel
insert_ph = phonPAUSE_SHORT;
}
}
}
next2 = phoneme_tab[(plist2+2)->phcode];
#ifdef deleted
if((ph->type != phVOWEL) && (ph->type != phPAUSE) //&& ((ph->phflags & phVOICED)==0)
&& (next->type == phLIQUID) && (next->mnemonic != ';') && (next2->type != phVOWEL))
{
// semi-vowel surrounded by consonants. precede by a short schwa
insert_ph = phonSCHWA_SHORT;
insert_synthflags = SFLAG_SYLLABLE;
}
if((ph->type == phLIQUID) && (prev->type != phVOWEL) && (next->type != phVOWEL))
{
// semi-vowel surrounded by consonants. precede by a short schwa
insert_ph = phonSCHWA_SHORT;
insert_synthflags = SFLAG_SYLLABLE;
}
#endif
if((insert_ph == 0) && (ph->link_out != 0) && (((plist2+1)->synthflags & SFLAG_EMBEDDED)==0))
{
if(ph->phflags & phAPPENDPH)
{
// always append the specified phoneme, unless it already is the next phoneme
if((ph->link_out != (plist2+1)->phcode) && (next->type == phVOWEL))
// if(ph->link_out != (plist2+1)->phcode)
{
insert_ph = ph->link_out;
}
}
else
if(((langopts.word_gap & 2)==0) || ((plist2+1)->sourceix == 0))
{
// This phoneme can be linked to a following vowel by inserting a linking phoneme
if(next->type == phVOWEL)
insert_ph = ph->link_out;
else
if(next->code == phonPAUSE_SHORT)
{
// Pause followed by Vowel, replace the Short Pause with the linking phoneme,
if(next2->type == phVOWEL)
(plist2+1)->phcode = ph->link_out; // replace pause by linking phoneme
}
}
}
if(ph->phflags & phVOICED)
{
// check that a voiced consonant is preceded or followed by a vowel or liquid
// and if not, add a short schwa
// not yet implemented
}
phlist[ix].ph = ph;
phlist[ix].type = ph->type;
phlist[ix].env = PITCHfall; // default, can be changed in the "intonation" module
phlist[ix].synthflags = plist2->synthflags | switched_language;
phlist[ix].tone = plist2->stress & 0xf;
phlist[ix].tone_ph = plist2->tone_number;
phlist[ix].sourceix = 0;
if(plist2->sourceix != 0)
{
phlist[ix].sourceix = plist2->sourceix;
phlist[ix].newword = 1; // this phoneme is the start of a word
if(start_sentence)
{
phlist[ix].newword = 5; // start of sentence + start of word
start_sentence = 0;
}
}
else
{
phlist[ix].newword = 0;
}
phlist[ix].length = ph->std_length;
if(ph->type==phVOWEL || ph->type==phLIQUID || ph->type==phNASAL || ph->type==phVSTOP || ph->type==phVFRICATIVE)
{
phlist[ix].length = 128; // length_mod
phlist[ix].env = PITCHfall;
}
phlist[ix].prepause = 0;
phlist[ix].amp = 20; // default, will be changed later
phlist[ix].pitch1 = 0x400;
phlist[ix].pitch2 = 0x400;
ix++;
}
phlist[ix].newword = 2; // end of clause
phlist[ix].type = phPAUSE; // terminate with 2 Pause phonemes
phlist[ix].length = post_pause; // length of the pause, depends on the punctuation
phlist[ix].sourceix = end_sourceix;
phlist[ix].synthflags = 0;
phlist[ix++].ph = phoneme_tab[phonPAUSE];
phlist[ix].type = phPAUSE;
phlist[ix].length = 0;
phlist[ix].sourceix=0;
phlist[ix].synthflags = 0;
phlist[ix++].ph = phoneme_tab[phonPAUSE_VSHORT];
n_phoneme_list = ix;
} // end of MakePhonemeList
+466
View File
@@ -0,0 +1,466 @@
// NOTE: Copy this file to portaudio.h in order to compile with V18 portaudio
#ifndef PORT_AUDIO_H
#define PORT_AUDIO_H
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
/*
* $Id: portaudio.h,v 1.5 2002/03/26 18:04:22 philburk Exp $
* PortAudio Portable Real-Time Audio Library
* PortAudio API Header File
* Latest version available at: http://www.audiomulch.com/portaudio/
*
* Copyright (c) 1999-2000 Ross Bencina and Phil Burk
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* Any person wishing to distribute modifications to the Software is
* requested to send the modifications to the original developer so that
* they can be incorporated into the canonical version.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
typedef int PaError;
typedef enum {
paNoError = 0,
paHostError = -10000,
paInvalidChannelCount,
paInvalidSampleRate,
paInvalidDeviceId,
paInvalidFlag,
paSampleFormatNotSupported,
paBadIODeviceCombination,
paInsufficientMemory,
paBufferTooBig,
paBufferTooSmall,
paNullCallback,
paBadStreamPtr,
paTimedOut,
paInternalError,
paDeviceUnavailable
} PaErrorNum;
/*
Pa_Initialize() is the library initialisation function - call this before
using the library.
*/
PaError Pa_Initialize( void );
/*
Pa_Terminate() is the library termination function - call this after
using the library.
*/
PaError Pa_Terminate( void );
/*
Pa_GetHostError() returns a host specific error code.
This can be called after receiving a PortAudio error code of paHostError.
*/
long Pa_GetHostError( void );
/*
Pa_GetErrorText() translates the supplied PortAudio error number
into a human readable message.
*/
const char *Pa_GetErrorText( PaError errnum );
/*
Sample formats
These are formats used to pass sound data between the callback and the
stream. Each device has a "native" format which may be used when optimum
efficiency or control over conversion is required.
Formats marked "always available" are supported (emulated) by all
PortAudio implementations.
The floating point representation (paFloat32) uses +1.0 and -1.0 as the
maximum and minimum respectively.
paUInt8 is an unsigned 8 bit format where 128 is considered "ground"
*/
typedef unsigned long PaSampleFormat;
#define paFloat32 ((PaSampleFormat) (1<<0)) /*always available*/
#define paInt16 ((PaSampleFormat) (1<<1)) /*always available*/
#define paInt32 ((PaSampleFormat) (1<<2)) /*always available*/
#define paInt24 ((PaSampleFormat) (1<<3))
#define paPackedInt24 ((PaSampleFormat) (1<<4))
#define paInt8 ((PaSampleFormat) (1<<5))
#define paUInt8 ((PaSampleFormat) (1<<6))
#define paCustomFormat ((PaSampleFormat) (1<<16))
/*
Device enumeration mechanism.
Device ids range from 0 to Pa_CountDevices()-1.
Devices may support input, output or both.
*/
typedef int PaDeviceID;
#define paNoDevice -1
int Pa_CountDevices( void );
typedef struct
{
int structVersion;
const char *name;
int maxInputChannels;
int maxOutputChannels;
/* Number of discrete rates, or -1 if range supported. */
int numSampleRates;
/* Array of supported sample rates, or {min,max} if range supported. */
const double *sampleRates;
PaSampleFormat nativeSampleFormats;
}
PaDeviceInfo;
/*
Pa_GetDefaultInputDeviceID(), Pa_GetDefaultOutputDeviceID() return the
default device ids for input and output respectively, or paNoDevice if
no device is available.
The result can be passed to Pa_OpenStream().
On the PC, the user can specify a default device by
setting an environment variable. For example, to use device #1.
set PA_RECOMMENDED_OUTPUT_DEVICE=1
The user should first determine the available device ids by using
the supplied application "pa_devs".
*/
PaDeviceID Pa_GetDefaultInputDeviceID( void );
PaDeviceID Pa_GetDefaultOutputDeviceID( void );
/*
Pa_GetDeviceInfo() returns a pointer to an immutable PaDeviceInfo structure
for the device specified.
If the device parameter is out of range the function returns NULL.
PortAudio manages the memory referenced by the returned pointer, the client
must not manipulate or free the memory. The pointer is only guaranteed to be
valid between calls to Pa_Initialize() and Pa_Terminate().
*/
const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID device );
/*
PaTimestamp is used to represent a continuous sample clock with arbitrary
start time that can be used for syncronization. The type is used for the
outTime argument to the PortAudioCallback and as the result of Pa_StreamTime()
*/
typedef double PaTimestamp;
/*
PortAudioCallback is implemented by PortAudio clients.
inputBuffer and outputBuffer are arrays of interleaved samples,
the format, packing and number of channels used by the buffers are
determined by parameters to Pa_OpenStream() (see below).
framesPerBuffer is the number of sample frames to be processed by the callback.
outTime is the time in samples when the buffer(s) processed by
this callback will begin being played at the audio output.
See also Pa_StreamTime()
userData is the value of a user supplied pointer passed to Pa_OpenStream()
intended for storing synthesis data etc.
return value:
The callback can return a non-zero value to stop the stream. This may be
useful in applications such as soundfile players where a specific duration
of output is required. However, it is not necessary to utilise this mechanism
as StopStream() will also terminate the stream. A callback returning a
non-zero value must fill the entire outputBuffer.
NOTE: None of the other stream functions may be called from within the
callback function except for Pa_GetCPULoad().
*/
typedef int (PortAudioCallback)(
void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData );
/*
Stream flags
These flags may be supplied (ored together) in the streamFlags argument to
the Pa_OpenStream() function.
*/
#define paNoFlag (0)
#define paClipOff (1<<0) /* disable default clipping of out of range samples */
#define paDitherOff (1<<1) /* disable default dithering */
#define paPlatformSpecificFlags (0x00010000)
typedef unsigned long PaStreamFlags;
/*
A single PortAudioStream provides multiple channels of real-time
input and output audio streaming to a client application.
Pointers to PortAudioStream objects are passed between PortAudio functions.
*/
typedef void PortAudioStream;
#define PaStream PortAudioStream
/*
Pa_OpenStream() opens a stream for either input, output or both.
stream is the address of a PortAudioStream pointer which will receive
a pointer to the newly opened stream.
inputDevice is the id of the device used for input (see PaDeviceID above.)
inputDevice may be paNoDevice to indicate that an input device is not required.
numInputChannels is the number of channels of sound to be delivered to the
callback. It can range from 1 to the value of maxInputChannels in the
PaDeviceInfo record for the device specified by the inputDevice parameter.
If inputDevice is paNoDevice numInputChannels is ignored.
inputSampleFormat is the sample format of inputBuffer provided to the callback
function. inputSampleFormat may be any of the formats described by the
PaSampleFormat enumeration (see above). PortAudio guarantees support for
the device's native formats (nativeSampleFormats in the device info record)
and additionally 16 and 32 bit integer and 32 bit floating point formats.
Support for other formats is implementation defined.
inputDriverInfo is a pointer to an optional driver specific data structure
containing additional information for device setup or stream processing.
inputDriverInfo is never required for correct operation. If not used
inputDriverInfo should be NULL.
outputDevice is the id of the device used for output (see PaDeviceID above.)
outputDevice may be paNoDevice to indicate that an output device is not required.
numOutputChannels is the number of channels of sound to be supplied by the
callback. See the definition of numInputChannels above for more details.
outputSampleFormat is the sample format of the outputBuffer filled by the
callback function. See the definition of inputSampleFormat above for more
details.
outputDriverInfo is a pointer to an optional driver specific data structure
containing additional information for device setup or stream processing.
outputDriverInfo is never required for correct operation. If not used
outputDriverInfo should be NULL.
sampleRate is the desired sampleRate. For full-duplex streams it is the
sample rate for both input and output
framesPerBuffer is the length in sample frames of all internal sample buffers
used for communication with platform specific audio routines. Wherever
possible this corresponds to the framesPerBuffer parameter passed to the
callback function.
numberOfBuffers is the number of buffers used for multibuffered communication
with the platform specific audio routines. If you pass zero, then an optimum
value will be chosen for you internally. This parameter is provided only
as a guide - and does not imply that an implementation must use multibuffered
i/o when reliable double buffering is available (such as SndPlayDoubleBuffer()
on the Macintosh.)
streamFlags may contain a combination of flags ORed together.
These flags modify the behaviour of the streaming process. Some flags may only
be relevant to certain buffer formats.
callback is a pointer to a client supplied function that is responsible
for processing and filling input and output buffers (see above for details.)
userData is a client supplied pointer which is passed to the callback
function. It could for example, contain a pointer to instance data necessary
for processing the audio buffers.
return value:
Upon success Pa_OpenStream() returns PaNoError and places a pointer to a
valid PortAudioStream in the stream argument. The stream is inactive (stopped).
If a call to Pa_OpenStream() fails a non-zero error code is returned (see
PaError above) and the value of stream is invalid.
*/
PaError Pa_OpenStream( PortAudioStream** stream,
PaDeviceID inputDevice,
int numInputChannels,
PaSampleFormat inputSampleFormat,
void *inputDriverInfo,
PaDeviceID outputDevice,
int numOutputChannels,
PaSampleFormat outputSampleFormat,
void *outputDriverInfo,
double sampleRate,
unsigned long framesPerBuffer,
unsigned long numberOfBuffers,
PaStreamFlags streamFlags,
PortAudioCallback *callback,
void *userData );
/*
Pa_OpenDefaultStream() is a simplified version of Pa_OpenStream() that opens
the default input and/or output devices. Most parameters have identical meaning
to their Pa_OpenStream() counterparts, with the following exceptions:
If either numInputChannels or numOutputChannels is 0 the respective device
is not opened. This has the same effect as passing paNoDevice in the device
arguments to Pa_OpenStream().
sampleFormat applies to both the input and output buffers.
*/
PaError Pa_OpenDefaultStream( PortAudioStream** stream,
int numInputChannels,
int numOutputChannels,
PaSampleFormat sampleFormat,
double sampleRate,
unsigned long framesPerBuffer,
unsigned long numberOfBuffers,
PortAudioCallback *callback,
void *userData );
/*
Pa_CloseStream() closes an audio stream, flushing any pending buffers.
*/
PaError Pa_CloseStream( PortAudioStream* );
/*
Pa_StartStream() and Pa_StopStream() begin and terminate audio processing.
Pa_StopStream() waits until all pending audio buffers have been played.
Pa_AbortStream() stops playing immediately without waiting for pending
buffers to complete.
*/
PaError Pa_StartStream( PortAudioStream *stream );
PaError Pa_StopStream( PortAudioStream *stream );
PaError Pa_AbortStream( PortAudioStream *stream );
/*
Pa_StreamActive() returns one (1) when the stream is active (ie playing
or recording audio), zero (0) when not playing, or a negative error number
if the stream is invalid.
The stream is active between calls to Pa_StartStream() and Pa_StopStream(),
but may also become inactive if the callback returns a non-zero value.
In the latter case, the stream is considered inactive after the last
buffer has finished playing.
*/
PaError Pa_StreamActive( PortAudioStream *stream );
/*
Pa_StreamTime() returns the current output time in samples for the stream.
This time may be used as a time reference (for example synchronizing audio to
MIDI).
*/
PaTimestamp Pa_StreamTime( PortAudioStream *stream );
/*
Pa_GetCPULoad() returns the CPU Load for the stream.
The "CPU Load" is a fraction of total CPU time consumed by the stream's
audio processing routines including, but not limited to the client supplied
callback.
A value of 0.5 would imply that PortAudio and the sound generating
callback was consuming roughly 50% of the available CPU time.
This function may be called from the callback function or the application.
*/
double Pa_GetCPULoad( PortAudioStream* stream );
/*
Pa_GetMinNumBuffers() returns the minimum number of buffers required by
the current host based on minimum latency.
On the PC, for the DirectSound implementation, latency can be optionally set
by user by setting an environment variable.
For example, to set latency to 200 msec, put:
set PA_MIN_LATENCY_MSEC=200
in the AUTOEXEC.BAT file and reboot.
If the environment variable is not set, then the latency will be determined
based on the OS. Windows NT has higher latency than Win95.
*/
int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate );
/*
Pa_Sleep() puts the caller to sleep for at least 'msec' milliseconds.
You may sleep longer than the requested time so don't rely on this for
accurate musical timing.
Pa_Sleep() is provided as a convenience for authors of portable code (such as
the tests and examples in the PortAudio distribution.)
*/
void Pa_Sleep( long msec );
/*
Pa_GetSampleSize() returns the size in bytes of a single sample in the
supplied PaSampleFormat, or paSampleFormatNotSupported if the format is
no supported.
*/
PaError Pa_GetSampleSize( PaSampleFormat format );
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PORT_AUDIO_H */
+466
View File
@@ -0,0 +1,466 @@
// NOTE: Copy this file to portaudio.h in order to compile with V18 portaudio
#ifndef PORT_AUDIO_H
#define PORT_AUDIO_H
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
/*
* $Id: portaudio.h,v 1.5 2002/03/26 18:04:22 philburk Exp $
* PortAudio Portable Real-Time Audio Library
* PortAudio API Header File
* Latest version available at: http://www.audiomulch.com/portaudio/
*
* Copyright (c) 1999-2000 Ross Bencina and Phil Burk
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* Any person wishing to distribute modifications to the Software is
* requested to send the modifications to the original developer so that
* they can be incorporated into the canonical version.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
typedef int PaError;
typedef enum {
paNoError = 0,
paHostError = -10000,
paInvalidChannelCount,
paInvalidSampleRate,
paInvalidDeviceId,
paInvalidFlag,
paSampleFormatNotSupported,
paBadIODeviceCombination,
paInsufficientMemory,
paBufferTooBig,
paBufferTooSmall,
paNullCallback,
paBadStreamPtr,
paTimedOut,
paInternalError,
paDeviceUnavailable
} PaErrorNum;
/*
Pa_Initialize() is the library initialisation function - call this before
using the library.
*/
PaError Pa_Initialize( void );
/*
Pa_Terminate() is the library termination function - call this after
using the library.
*/
PaError Pa_Terminate( void );
/*
Pa_GetHostError() returns a host specific error code.
This can be called after receiving a PortAudio error code of paHostError.
*/
long Pa_GetHostError( void );
/*
Pa_GetErrorText() translates the supplied PortAudio error number
into a human readable message.
*/
const char *Pa_GetErrorText( PaError errnum );
/*
Sample formats
These are formats used to pass sound data between the callback and the
stream. Each device has a "native" format which may be used when optimum
efficiency or control over conversion is required.
Formats marked "always available" are supported (emulated) by all
PortAudio implementations.
The floating point representation (paFloat32) uses +1.0 and -1.0 as the
maximum and minimum respectively.
paUInt8 is an unsigned 8 bit format where 128 is considered "ground"
*/
typedef unsigned long PaSampleFormat;
#define paFloat32 ((PaSampleFormat) (1<<0)) /*always available*/
#define paInt16 ((PaSampleFormat) (1<<1)) /*always available*/
#define paInt32 ((PaSampleFormat) (1<<2)) /*always available*/
#define paInt24 ((PaSampleFormat) (1<<3))
#define paPackedInt24 ((PaSampleFormat) (1<<4))
#define paInt8 ((PaSampleFormat) (1<<5))
#define paUInt8 ((PaSampleFormat) (1<<6))
#define paCustomFormat ((PaSampleFormat) (1<<16))
/*
Device enumeration mechanism.
Device ids range from 0 to Pa_CountDevices()-1.
Devices may support input, output or both.
*/
typedef int PaDeviceID;
#define paNoDevice -1
int Pa_CountDevices( void );
typedef struct
{
int structVersion;
const char *name;
int maxInputChannels;
int maxOutputChannels;
/* Number of discrete rates, or -1 if range supported. */
int numSampleRates;
/* Array of supported sample rates, or {min,max} if range supported. */
const double *sampleRates;
PaSampleFormat nativeSampleFormats;
}
PaDeviceInfo;
/*
Pa_GetDefaultInputDeviceID(), Pa_GetDefaultOutputDeviceID() return the
default device ids for input and output respectively, or paNoDevice if
no device is available.
The result can be passed to Pa_OpenStream().
On the PC, the user can specify a default device by
setting an environment variable. For example, to use device #1.
set PA_RECOMMENDED_OUTPUT_DEVICE=1
The user should first determine the available device ids by using
the supplied application "pa_devs".
*/
PaDeviceID Pa_GetDefaultInputDeviceID( void );
PaDeviceID Pa_GetDefaultOutputDeviceID( void );
/*
Pa_GetDeviceInfo() returns a pointer to an immutable PaDeviceInfo structure
for the device specified.
If the device parameter is out of range the function returns NULL.
PortAudio manages the memory referenced by the returned pointer, the client
must not manipulate or free the memory. The pointer is only guaranteed to be
valid between calls to Pa_Initialize() and Pa_Terminate().
*/
const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID device );
/*
PaTimestamp is used to represent a continuous sample clock with arbitrary
start time that can be used for syncronization. The type is used for the
outTime argument to the PortAudioCallback and as the result of Pa_StreamTime()
*/
typedef double PaTimestamp;
/*
PortAudioCallback is implemented by PortAudio clients.
inputBuffer and outputBuffer are arrays of interleaved samples,
the format, packing and number of channels used by the buffers are
determined by parameters to Pa_OpenStream() (see below).
framesPerBuffer is the number of sample frames to be processed by the callback.
outTime is the time in samples when the buffer(s) processed by
this callback will begin being played at the audio output.
See also Pa_StreamTime()
userData is the value of a user supplied pointer passed to Pa_OpenStream()
intended for storing synthesis data etc.
return value:
The callback can return a non-zero value to stop the stream. This may be
useful in applications such as soundfile players where a specific duration
of output is required. However, it is not necessary to utilise this mechanism
as StopStream() will also terminate the stream. A callback returning a
non-zero value must fill the entire outputBuffer.
NOTE: None of the other stream functions may be called from within the
callback function except for Pa_GetCPULoad().
*/
typedef int (PortAudioCallback)(
void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData );
/*
Stream flags
These flags may be supplied (ored together) in the streamFlags argument to
the Pa_OpenStream() function.
*/
#define paNoFlag (0)
#define paClipOff (1<<0) /* disable default clipping of out of range samples */
#define paDitherOff (1<<1) /* disable default dithering */
#define paPlatformSpecificFlags (0x00010000)
typedef unsigned long PaStreamFlags;
/*
A single PortAudioStream provides multiple channels of real-time
input and output audio streaming to a client application.
Pointers to PortAudioStream objects are passed between PortAudio functions.
*/
typedef void PortAudioStream;
#define PaStream PortAudioStream
/*
Pa_OpenStream() opens a stream for either input, output or both.
stream is the address of a PortAudioStream pointer which will receive
a pointer to the newly opened stream.
inputDevice is the id of the device used for input (see PaDeviceID above.)
inputDevice may be paNoDevice to indicate that an input device is not required.
numInputChannels is the number of channels of sound to be delivered to the
callback. It can range from 1 to the value of maxInputChannels in the
PaDeviceInfo record for the device specified by the inputDevice parameter.
If inputDevice is paNoDevice numInputChannels is ignored.
inputSampleFormat is the sample format of inputBuffer provided to the callback
function. inputSampleFormat may be any of the formats described by the
PaSampleFormat enumeration (see above). PortAudio guarantees support for
the device's native formats (nativeSampleFormats in the device info record)
and additionally 16 and 32 bit integer and 32 bit floating point formats.
Support for other formats is implementation defined.
inputDriverInfo is a pointer to an optional driver specific data structure
containing additional information for device setup or stream processing.
inputDriverInfo is never required for correct operation. If not used
inputDriverInfo should be NULL.
outputDevice is the id of the device used for output (see PaDeviceID above.)
outputDevice may be paNoDevice to indicate that an output device is not required.
numOutputChannels is the number of channels of sound to be supplied by the
callback. See the definition of numInputChannels above for more details.
outputSampleFormat is the sample format of the outputBuffer filled by the
callback function. See the definition of inputSampleFormat above for more
details.
outputDriverInfo is a pointer to an optional driver specific data structure
containing additional information for device setup or stream processing.
outputDriverInfo is never required for correct operation. If not used
outputDriverInfo should be NULL.
sampleRate is the desired sampleRate. For full-duplex streams it is the
sample rate for both input and output
framesPerBuffer is the length in sample frames of all internal sample buffers
used for communication with platform specific audio routines. Wherever
possible this corresponds to the framesPerBuffer parameter passed to the
callback function.
numberOfBuffers is the number of buffers used for multibuffered communication
with the platform specific audio routines. If you pass zero, then an optimum
value will be chosen for you internally. This parameter is provided only
as a guide - and does not imply that an implementation must use multibuffered
i/o when reliable double buffering is available (such as SndPlayDoubleBuffer()
on the Macintosh.)
streamFlags may contain a combination of flags ORed together.
These flags modify the behaviour of the streaming process. Some flags may only
be relevant to certain buffer formats.
callback is a pointer to a client supplied function that is responsible
for processing and filling input and output buffers (see above for details.)
userData is a client supplied pointer which is passed to the callback
function. It could for example, contain a pointer to instance data necessary
for processing the audio buffers.
return value:
Upon success Pa_OpenStream() returns PaNoError and places a pointer to a
valid PortAudioStream in the stream argument. The stream is inactive (stopped).
If a call to Pa_OpenStream() fails a non-zero error code is returned (see
PaError above) and the value of stream is invalid.
*/
PaError Pa_OpenStream( PortAudioStream** stream,
PaDeviceID inputDevice,
int numInputChannels,
PaSampleFormat inputSampleFormat,
void *inputDriverInfo,
PaDeviceID outputDevice,
int numOutputChannels,
PaSampleFormat outputSampleFormat,
void *outputDriverInfo,
double sampleRate,
unsigned long framesPerBuffer,
unsigned long numberOfBuffers,
PaStreamFlags streamFlags,
PortAudioCallback *callback,
void *userData );
/*
Pa_OpenDefaultStream() is a simplified version of Pa_OpenStream() that opens
the default input and/or output devices. Most parameters have identical meaning
to their Pa_OpenStream() counterparts, with the following exceptions:
If either numInputChannels or numOutputChannels is 0 the respective device
is not opened. This has the same effect as passing paNoDevice in the device
arguments to Pa_OpenStream().
sampleFormat applies to both the input and output buffers.
*/
PaError Pa_OpenDefaultStream( PortAudioStream** stream,
int numInputChannels,
int numOutputChannels,
PaSampleFormat sampleFormat,
double sampleRate,
unsigned long framesPerBuffer,
unsigned long numberOfBuffers,
PortAudioCallback *callback,
void *userData );
/*
Pa_CloseStream() closes an audio stream, flushing any pending buffers.
*/
PaError Pa_CloseStream( PortAudioStream* );
/*
Pa_StartStream() and Pa_StopStream() begin and terminate audio processing.
Pa_StopStream() waits until all pending audio buffers have been played.
Pa_AbortStream() stops playing immediately without waiting for pending
buffers to complete.
*/
PaError Pa_StartStream( PortAudioStream *stream );
PaError Pa_StopStream( PortAudioStream *stream );
PaError Pa_AbortStream( PortAudioStream *stream );
/*
Pa_StreamActive() returns one (1) when the stream is active (ie playing
or recording audio), zero (0) when not playing, or a negative error number
if the stream is invalid.
The stream is active between calls to Pa_StartStream() and Pa_StopStream(),
but may also become inactive if the callback returns a non-zero value.
In the latter case, the stream is considered inactive after the last
buffer has finished playing.
*/
PaError Pa_StreamActive( PortAudioStream *stream );
/*
Pa_StreamTime() returns the current output time in samples for the stream.
This time may be used as a time reference (for example synchronizing audio to
MIDI).
*/
PaTimestamp Pa_StreamTime( PortAudioStream *stream );
/*
Pa_GetCPULoad() returns the CPU Load for the stream.
The "CPU Load" is a fraction of total CPU time consumed by the stream's
audio processing routines including, but not limited to the client supplied
callback.
A value of 0.5 would imply that PortAudio and the sound generating
callback was consuming roughly 50% of the available CPU time.
This function may be called from the callback function or the application.
*/
double Pa_GetCPULoad( PortAudioStream* stream );
/*
Pa_GetMinNumBuffers() returns the minimum number of buffers required by
the current host based on minimum latency.
On the PC, for the DirectSound implementation, latency can be optionally set
by user by setting an environment variable.
For example, to set latency to 200 msec, put:
set PA_MIN_LATENCY_MSEC=200
in the AUTOEXEC.BAT file and reboot.
If the environment variable is not set, then the latency will be determined
based on the OS. Windows NT has higher latency than Win95.
*/
int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate );
/*
Pa_Sleep() puts the caller to sleep for at least 'msec' milliseconds.
You may sleep longer than the requested time so don't rely on this for
accurate musical timing.
Pa_Sleep() is provided as a convenience for authors of portable code (such as
the tests and examples in the PortAudio distribution.)
*/
void Pa_Sleep( long msec );
/*
Pa_GetSampleSize() returns the size in bytes of a single sample in the
supplied PaSampleFormat, or paSampleFormatNotSupported if the format is
no supported.
*/
PaError Pa_GetSampleSize( PaSampleFormat format );
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PORT_AUDIO_H */
+1127
View File
File diff suppressed because it is too large Load Diff
+607
View File
@@ -0,0 +1,607 @@
/***************************************************************************
* Copyright (C) 2005, 2006 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "wx/wx.h"
#include "wx/numdlg.h"
#include "speak_lib.h"
#include "main.h"
#include "speech.h"
#include "phoneme.h"
#include "synthesize.h"
#include "prosodydisplay.h"
extern MyFrame *myframe;
extern ChildFrProsody *prosodyframe;
extern ProsodyDisplay *prosodycanvas;
wxMenu *menu_prosody;
BEGIN_EVENT_TABLE(ProsodyDisplay, wxScrolledWindow)
EVT_LEFT_DOWN(ProsodyDisplay::OnMouse)
EVT_RIGHT_DOWN(ProsodyDisplay::OnMouse)
EVT_CHAR(ProsodyDisplay::OnKey)
EVT_MENU(-1, ProsodyDisplay::OnMenu)
END_EVENT_TABLE()
static wxPen PEN_PITCHENV(wxColour(0,0,255),1,wxSOLID);
static wxPen PEN_SAMPLED(wxColour(0,200,200),2,wxSOLID);
static wxPen PEN_PHSELECTED(wxColour(255,0,0),2,wxSOLID);
ProsodyDisplay::ProsodyDisplay(wxWindow *parent, const wxPoint& pos, const wxSize& size)
: wxScrolledWindow(parent, -1, pos, size,
wxSUNKEN_BORDER | wxNO_FULL_REPAINT_ON_RESIZE)
{//=====================================================================
linewidth = size.GetWidth();
scalex = 0.5;
scaley = double(LINESEP*6)/80.0;
selected_ph = -1;
// SetBackgroundColour(* wxWHITE);
LayoutData(phoneme_list,n_phoneme_list);
} // end of ProsodyDisplay::ProsodyDisplay
ProsodyDisplay::~ProsodyDisplay()
{//==========================
prosodycanvas = NULL;
}
void InitProsodyDisplay()
{//======================
wxMenu *menu_envelopes;
menu_envelopes = new wxMenu;
// entries match those in envelope_data[] in intonation.cpp
menu_envelopes->Append(0x100,_T("Fall"));
menu_envelopes->Append(0x101,_T("Rise"));
menu_envelopes->Append(0x102,_T("Fall-rise"));
menu_envelopes->Append(0x103,_T("Fall-rise (R)"));
menu_envelopes->Append(0x104,_T("Fall-2"));
menu_envelopes->Append(0x105,_T("Fall-3"));
menu_prosody = new wxMenu;
menu_prosody->Append(1,_T("Pitch envelope"),menu_envelopes);
menu_prosody->Append(2,_T("Amplitude"));
menu_prosody->Append(3,_T("Length"));
menu_prosody->Append(4,_T("Play F2"));
}
void ProsodyDisplay::RefreshLine(int line)
{//=====================================
int x,y;
CalcScrolledPosition(0,line*FRAMEHEIGHT,&x,&y);
RefreshRect(wxRect(0,y,linewidth,FRAMEHEIGHT));
}
int ProsodyDisplay::GetWidth(PHONEME_LIST *p)
{//========================================
int w;
if(p->ph == NULL)
return(0);
w = p->ph->std_length;
if(w == 0) w = 60;
if(p->length != 0)
w = (w * p->length) / 256;
return(int((w + p->prepause)* scalex) + 1);
}
void ProsodyDisplay::SelectPh(int index)
{//=====================================
// A phoneme has been selected
PHONEME_LIST *p;
char buf[120];
if(index < 0) return;
p = &phlist[index];
if(p == NULL) return;
sprintf(buf,"Stress %d Amp %2d LenMod %2d Pitch %3d %3d [env=%d] Flags %.2x ",
p->tone,p->amp,p->length,p->pitch1,p->pitch2,p->env,p->ph->phflags);
wxLogStatus(wxString(buf,wxConvLocal));
}
void ProsodyDisplay::ChangePh(int pitch1, int pitch2)
{//================================================
PHONEME_LIST *p;
int sign1;
int sign2;
if(selected_ph < 0)
return;
p = &phlist[selected_ph];
sign1 = p->pitch1 - p->pitch2;
p->pitch1 += pitch1;
p->pitch2 += (pitch1 + pitch2);
sign2 = p->pitch1 - p->pitch2;
if((sign1 != 0) && ((sign1 * sign2) <= 0))
{
// change of sign, change rise to fall
if(p->env == 1)
p->env = 0;
else
if(p->env == 0)
p->env = 1;
}
}
void ProsodyDisplay::OnMenu(wxCommandEvent& event)
{//===============================================
int id;
int value;
PHONEME_LIST *p;
id = event.GetId();
p = &phlist[selected_ph];
if((id & 0xf00) == 0x100)
{
// selected a pitch envelope
p->env = id - 0x100;
}
switch(id)
{
case 2:
value = wxGetNumberFromUser(_T(""),_T("Amplitude"),_T(""),p->amp,0,40);
if(value >= 0)
p->amp = value;
break;
case 3:
value = wxGetNumberFromUser(_T(""),_T("Length"),_T(""),p->length,1,500);
if(value >= 0)
p->length = value;
break;
case 4:
MakeWave2(phlist,numph);
break;
}
SelectPh(selected_ph);
Refresh();
}
void ProsodyDisplay::OnMouse(wxMouseEvent& event)
{//============================================
int line;
int ix;
int xpos=0;
wxClientDC dc(this);
PrepareDC(dc);
wxPoint pt(event.GetLogicalPosition(dc));
if(selected_ph >= 0)
{
// find line for previously selected phoneme
for(line=0; line<num_lines; line++)
if(linetab[line+1] > selected_ph) break;
RefreshLine(line);
selected_ph = -1;
}
line = pt.y / FRAMEHEIGHT;
// find which phoneme is selected on this line
for(ix=linetab[line]; (ix<linetab[line+1]) && (ix<numph); ix++)
{
xpos += GetWidth(&phlist[ix]);
if(xpos > pt.x)
{
selected_ph = ix;
SelectPh(selected_ph);
break;
}
}
RefreshLine(line);
if(event.RightDown())
{
PopupMenu(menu_prosody);
}
} // end of ProsodyDisplay::OnMouse
void ProsodyDisplay::OnKey(wxKeyEvent& event)
{//========================================
PHONEME_LIST *p;
int display=1;
if(selected_ph < 0)
selected_ph = 0;
p = &phlist[selected_ph];
switch(event.GetKeyCode())
{
case WXK_F2:
// make and play from this clause
MakeWave2(phlist,numph);
break;
case WXK_LEFT:
if(selected_ph > 1)
selected_ph--;
break;
case WXK_RIGHT:
if(selected_ph < (numph-2))
selected_ph++;
break;
case WXK_UP:
if(event.ControlDown())
ChangePh(-1,2);
else
ChangePh(1,0);
display = 1;
break;
case WXK_DOWN:
if(event.ControlDown())
ChangePh(1,-2);
else
ChangePh(-1,0);
break;
case ',':
case '<':
if(p->length > 0)
p->length--;
break;
case '.':
case '>':
p->length++;
break;
default:
event.Skip();
break;
}
if(display)
{
Refresh();
SelectPh(selected_ph);
}
} // end of ProsodyDisplay::OnKey
void ProsodyDisplay::DrawEnv(wxDC& dc, int x1, int y1, int width, PHONEME_LIST *ph)
{//==============================================================================
int pitchr;
int pitch;
int p1;
int ix;
int x,y;
int y2=0;
unsigned char *env;
if(width <= 0) return;
if((pitchr = ph->pitch2 - ph->pitch1) < 0)
{
pitchr = -pitchr;
p1 = ph->pitch2;
}
else
{
p1 = ph->pitch1;
}
dc.SetPen(PEN_PITCHENV);
env = envelope_data[ph->env];
if((ph->type == phVOWEL) && (ph->tone_ph != 0))
{
// the envelope is given by a Tone phoneme acting on this vowel
env = LookupEnvelope(phoneme_tab[ph->tone_ph]->spect);
}
for(ix=0; ix<=width; ix+=4)
{
x = int((ix * 127.9)/width);
pitch = ph->pitch1 + (pitchr * env[x])/256;
y = y1-int(pitch * scaley);
if(ix > 0)
dc.DrawLine(x1+ix-4,y2,x1+ix,y);
y2 = y;
}
} // end of DrawEnv
static void GetPhonemeName(PHONEME_TAB *ph, wxString& string)
{//==========================================================
string = wxString(WordToString(ph->mnemonic),wxConvLocal);
}
void ProsodyDisplay::DrawPitchline(wxDC& dc, int line, int x1, int x2)
{//=================================================================
int ix;
int endix;
int y;
int offy;
int xpos;
int width; // total width, including pre-pause
int width2; // width without pre-pause
int width_env;
int textwidth, textheight;
wxString string;
PHONEME_LIST *p;
if(linetab[line] >= numph) return;
offy = (line+1) * FRAMEHEIGHT - 1;
y = LINEBASE+LINESEP;
dc.SetPen(*wxLIGHT_GREY_PEN);
for(ix=0; ix<5; ix++)
{
dc.DrawLine(0,offy-y,linewidth,offy-y);
y += LINESEP;
}
endix = linetab[line+1];
xpos = 0;
for(ix=linetab[line]; ix<endix; ix++)
{
if(ix < 0 || ix > numph-2) break;
if(xpos > x2) break; // past the redraw region
p = &phlist[ix];
width = GetWidth(p);
width2 = width - int(p->prepause * scalex) - 1;
if(xpos+width < x1)
{
xpos += width;
continue; // before the redraw region
}
// is this phoneme selected ?
if(ix == selected_ph)
{
dc.SetPen(PEN_PHSELECTED);
dc.DrawLine(xpos,offy-LINEBASE,xpos+width,offy-LINEBASE);
}
// draw separator bar
if(p->newword)
dc.SetPen(*wxBLACK_PEN); // word boundary
else
dc.SetPen(*wxLIGHT_GREY_PEN);
dc.DrawLine(xpos,offy-LINEBASE,xpos,offy-LINEBASE-LINESEP*6);
// draw pitch envelope
if(((p->ph->phflags & phWAVE) == 0) && (p->ph->type != phPAUSE))
{
if(!(p->synthflags & SFLAG_SEQCONTINUE))
{
width_env = width2;
if(phlist[ix+1].synthflags & SFLAG_SEQCONTINUE)
width_env += GetWidth(&phlist[ix+1]);
DrawEnv(dc,xpos+1+(width-width2),offy-LINEBASE,width_env,p);
}
}
else
if(p->type != phPAUSE)
{
// sampled sound, draw a flat line
dc.SetPen(PEN_SAMPLED);
dc.DrawLine(xpos+1+(width-width2),offy-LINEBASE-LINESEP,
xpos+1+width,offy-LINEBASE-LINESEP);
}
// show phoneme name from the PHONEME_TAB
GetPhonemeName(p->ph,string);
dc.GetTextExtent(string,&textwidth,&textheight);
dc.DrawText(string,xpos+(width-textwidth/2)/2, offy-LINEBASE);
xpos += width;
}
// draw the separator bar at the end of the line
if(ix==endix && ix<numph && phlist[ix].newword)
dc.SetPen(*wxLIGHT_GREY_PEN);
else
dc.SetPen(*wxBLACK_PEN); // word boundary or end of list
dc.DrawLine(xpos,offy-LINEBASE,xpos,offy-LINEBASE-LINESEP*6);
} // end of ProsodyDisplay::DrawPitchline
void ProsodyDisplay::OnDraw(wxDC& dc)
{//================================
int x1,y1;
int vX,vY,vW,vH; // Dimensions of client area in pixels
int line, start, end;
wxRegionIterator upd(GetUpdateRegion()); // get the update rect list
while (upd)
{
vX = upd.GetX();
vY = upd.GetY();
vW = upd.GetW();
vH = upd.GetH();
CalcUnscrolledPosition(vX,vY,&x1,&y1);
// Repaint this rectangle, find which lines to redraw
start = y1/FRAMEHEIGHT;
end = (y1+vH)/FRAMEHEIGHT;
for(line=start; line<=end && line<num_lines; line++)
DrawPitchline(dc,line,x1,x1+vW);
upd ++ ;
}
} // end of ProsodyDisplay::OnDraw
void ProsodyDisplay::LayoutData(PHONEME_LIST *ph_list, int n_ph)
{//===========================================================
// divide the phoneme list into lines for display
int xpos;
int w;
int ix;
PHONEME_LIST *p;
numph = n_ph;
phlist = ph_list;
num_lines = 0;
linetab[0] = 1;
xpos = linewidth;
// could improve this to do 'wordwrap' - only split on word boundary
// or unvoiced phonemes
for(ix=1; ix<numph-2; ix++)
{
p = &phlist[ix];
w = GetWidth(p);
if(w + xpos >= linewidth)
{
linetab[num_lines++] = ix;
xpos = 0;
}
xpos += w;
}
linetab[num_lines]=numph-2;
SetScrollbars(SCROLLUNITS,SCROLLUNITS,linewidth/SCROLLUNITS,
(num_lines*FRAMEHEIGHT)/SCROLLUNITS+1);
Refresh();
} // end of ProsodyDisplay::LayoutData
void MyFrame::OnProsody(wxCommandEvent& WXUNUSED(event))
{//=====================================================
// Open the Prosody display window
// Make another frame, containing a canvas
if(prosodyframe != NULL)
{
// The Prosody window is already open
prosodyframe->Activate();
return;
}
prosodyframe = new ChildFrProsody(myframe, _T(""),
wxPoint(10, 200), wxSize(1000, 300),
wxDEFAULT_FRAME_STYLE |
wxNO_FULL_REPAINT_ON_RESIZE);
prosodyframe->SetTitle(_T("Prosody"));
// Give it a status line
prosodyframe->CreateStatusBar();
int width, height;
wxMDIClientWindow *clientwin = this->GetClientWindow();
clientwin->GetClientSize(&width, &height);
#ifdef deleted
wxPanel *panel = new wxPanel(prosodyframe,-1,wxPoint(0,0), wxSize(width,50));
ProsodyDisplay *canvas = new ProsodyDisplay(prosodyframe, wxPoint(0, 50), wxSize(width-2, height-50));
#else
ProsodyDisplay *canvas = new ProsodyDisplay(prosodyframe, wxPoint(0, 50), wxSize(width-10, height));
#endif
prosodycanvas = canvas;
// Associate the menu bar with the frame
prosodyframe->SetMenuBar(MakeMenu(2));
prosodyframe->prosodycanvas = canvas;
prosodyframe->Show(TRUE);
}
BEGIN_EVENT_TABLE(ChildFrProsody, wxMDIChildFrame)
EVT_MENU(SPECTSEQ_CLOSE, ChildFrProsody::OnQuit)
// EVT_ACTIVATE(ChildFrProsody::OnActivate)
END_EVENT_TABLE()
extern wxList my_children;
ChildFrProsody::ChildFrProsody(wxMDIParentFrame *parent, const wxString& title, const wxPoint& pos, const wxSize& size,
const long style):
wxMDIChildFrame(parent, -1, title, pos, size, style)
{
my_children.Append(this);
}
ChildFrProsody::~ChildFrProsody(void)
{
wxWindow *w;
my_children.DeleteObject(this);
prosodycanvas = NULL;
prosodyframe = NULL;
#ifndef PLATFORM_WINDOWS
// bug in wxMDIChildFrame, we need to explicitly remove the ChildFrame from the ClientWindow
w = myframe->GetClientWindow();
w->RemoveChild(this);
#endif
}
void ChildFrProsody::OnQuit(wxCommandEvent& WXUNUSED(event))
{
Close(TRUE);
}
+64
View File
@@ -0,0 +1,64 @@
#define FRAMEHEIGHT 120
#define LINEBASE 20
#define LINESEP 16
#define SCROLLUNITS 20
class ProsodyDisplay: public wxScrolledWindow
{//========================================
public:
ProsodyDisplay(wxWindow *parent, const wxPoint& pos, const wxSize& size);
~ProsodyDisplay();
virtual void OnDraw(wxDC& dc);
void OnMouse(wxMouseEvent& event);
void OnKey(wxKeyEvent& event);
void OnMenu(wxCommandEvent& event);
void Save(const wxString &path=_T(""));
void LayoutData(PHONEME_LIST *phlist, int n_ph);
private:
void Redraw(wxDC& dc, int x1, int y1, int x2, int y2);
void DrawEnv(wxDC& dc, int x1, int y1, int width, PHONEME_LIST *ph);
void DrawPitchline(wxDC& dc, int line, int x1, int x2);
void RefreshLine(int line);
int GetWidth(PHONEME_LIST *p);
void ChangePh(int pitch1, int pitch2);
void SelectPh(int index);
int linewidth; // height for drawing pitch
double scalex; // scale length values
double scaley; // scale pitch values
int selected_ph;
int numph;
PHONEME_LIST *phlist;
int num_lines;
short linetab[N_PHONEME_LIST/2];
DECLARE_EVENT_TABLE()
}; // end class ProsodyDisplay
class ChildFrProsody: public wxMDIChildFrame
{
public:
ProsodyDisplay *prosodycanvas;
ChildFrProsody(wxMDIParentFrame *parent, const wxString& title, const wxPoint& pos, const wxSize& size, const long style);
~ChildFrProsody(void);
void OnActivate(wxActivateEvent& event);
void OnQuit(wxCommandEvent& event);
void OnSave(wxCommandEvent& event);
void OnSaveAs(wxCommandEvent& event);
DECLARE_EVENT_TABLE()
};
+1856
View File
File diff suppressed because it is too large Load Diff
+583
View File
@@ -0,0 +1,583 @@
/***************************************************************************
* Copyright (C) 2005,2006 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "StdAfx.h"
#include <stdlib.h>
#include <stdio.h>
#include <wctype.h>
#include "speak_lib.h"
#include "speech.h"
#include "voice.h"
#include "phoneme.h"
#include "synthesize.h"
#include "translate.h"
extern int GetAmplitude(void);
// convert from words-per-minute to internal speed factor
static unsigned char speed_lookup[290] = {
250, 246, 243, 239, 236, // 80
233, 229, 226, 223, 220, // 85
217, 214, 211, 208, 205, // 90
202, 197, 194, 192, 190, // 95
187, 185, 183, 180, 178, // 100
176, 174, 172, 170, 168, // 105
166, 164, 161, 159, 158, // 110
156, 154, 152, 150, 148, // 115
146, 145, 143, 141, 137, // 120
136, 135, 133, 132, 131, // 125
129, 128, 127, 126, 125, // 130
124, 122, 121, 120, 119, // 135
117, 116, 115, 114, 113, // 140
112, 111, 110, 108, 107, // 145
106, 105, 104, 103, 102, // 150
101, 100, 99, 98, 97, // 155
96, 95, 93, 92, 92, // 160
91, 90, 89, 89, 88, // 165
87, 87, 86, 85, 85, // 170
84, 83, 83, 82, 81, // 175
80, 80, 79, 78, 78, // 180
77, 76, 76, 75, 73, // 185
72, 72, 71, 71, 70, // 190
70, 69, 69, 68, 67, // 195
67, 66, 66, 65, 65, // 200
64, 64, 63, 63, 62, // 205
62, 61, 60, 60, 59, // 210
59, 58, 58, 57, 57, // 215
56, 56, 55, 55, 55, // 220
54, 54, 53, 53, 52, // 225
52, 51, 51, 50, 50, // 230
49, 49, 49, 48, 48, // 235
47, 47, 46, 46, 46, // 240
45, 45, 44, 44, 43, // 245
43, 43, 42, 42, 41, // 250
41, 41, 40, 40, 39, // 255
39, 39, 38, 38, 38, // 260
37, 37, 37, 36, 36, // 265
35, 35, 35, 34, 34, // 270
34, 33, 33, 33, 32, // 275
32, 32, 32, 31, 31, // 280
31, 30, 30, 30, 29, // 285
29, 29, 29, 28, 28, // 290
28, 28, 27, 27, 27, // 295
26, 26, 26, 26, 25, // 300
25, 25, 22, 22, 22, // 305
22, 22, 22, 22, 22, // 310
21, 21, 21, 21, 21, // 315
21, 20, 20, 20, 20, // 320
20, 15, 15, 15, 15, // 325
15, 15, 15, 15, 16, // 330
16, 16, 16, 15, 15, // 335
15, 15, 15, 15, 15, // 340
15, 17, 17, 16, 16, // 345
15, 15, 14, 14, 13, // 350
13, 12, 12, 11, 11, // 355
10, 10, 9, 8, 8, // 360
7, 7, 6, 6, 5, // 365
};
static int speed1 = 130;
static int speed2 = 121;
static int speed3 = 118;
void SetSpeed(int control)
{//=======================
int x;
int s1;
int wpm;
wpm = embedded_value[EMBED_S];
if(wpm > 369) wpm = 369;
if(wpm < 80) wpm = 80;
x = speed_lookup[wpm-80];
if(control & 1)
{
// set speed factors for different syllable positions within a word
// these are used in CalcLengths()
speed1 = (x * voice->speedf1)/256;
speed2 = (x * voice->speedf2)/256;
speed3 = (x * voice->speedf3)/256;
}
if(control & 2)
{
// these are used in synthesis file
s1 = (x * voice->speedf1)/256;
speed_factor1 = (256 * s1)/115; // full speed adjustment, used for pause length
if(speed_factor1 < 16)
speed_factor1 = 16;
if(wpm >= 170)
// speed_factor2 = 100 + (166*s1)/128; // reduced speed adjustment, used for playing recorded sounds
speed_factor2 = 110 + (151*s1)/128; // reduced speed adjustment, used for playing recorded sounds
else
speed_factor2 = 128 + (128*s1)/130; // = 215 at 170 wpm
}
} // end of SetSpeed
#ifdef deleted
void SetAmplitude(int amp)
{//=======================
static unsigned char amplitude_factor[] = {0,5,6,7,9,11,14,17,21,26, 32, 38,44,50,56,63,70,77,84,91,100 };
if((amp >= 0) && (amp <= 20))
{
option_amplitude = (amplitude_factor[amp] * 480)/256;
}
}
#endif
void SetParameter(int parameter, int value, int relative)
{//======================================================
// parameter: reset-all, amp, pitch, speed, linelength, expression, capitals, number grouping
// relative 0=absolute 1=relative
int new_value = value;
int default_value;
if(relative)
{
if(parameter < 5)
{
default_value = param_defaults[parameter];
new_value = default_value + (default_value * value)/100;
}
}
param_stack[0].parameter[parameter] = new_value;
switch(parameter)
{
case espeakRATE:
embedded_value[EMBED_S] = new_value;
embedded_value[EMBED_S2] = new_value;
SetSpeed(3);
break;
case espeakVOLUME:
embedded_value[EMBED_A] = new_value;
GetAmplitude();
break;
case espeakPITCH:
if(new_value > 99) new_value = 99;
embedded_value[EMBED_P] = new_value;
break;
case espeakRANGE:
if(new_value > 99) new_value = 99;
embedded_value[EMBED_R] = new_value;
break;
case espeakPUNCTUATION:
break;
case espeakCAPITALS:
break;
case espeakLINELENGTH:
option_linelength = new_value;
break;
default:
break;
}
} // end of SetParameter
static void DoEmbedded2(int &embix)
{//================================
// There were embedded commands in the text at this point
unsigned int word;
do {
word = embedded_list[embix++];
if((word & 0x1f) == EMBED_S)
{
// speed
SetEmbedded(word & 0x7f, word >> 8); // adjusts embedded_value[EMBED_S]
SetSpeed(1);
}
} while((word & 0x80) == 0);
}
void Translator::CalcLengths()
{//===========================
int ix;
int ix2;
PHONEME_LIST *prev;
PHONEME_LIST *next;
PHONEME_LIST *next2;
PHONEME_LIST *next3;
PHONEME_LIST *p;
PHONEME_LIST *p2;
int stress;
int type;
static int more_syllables=0;
int pre_sonorant=0;
int pre_voiced=0;
int last_pitch = 0;
int pitch_start;
int length_mod;
int len;
int env2;
int end_of_clause;
int embedded_ix = 0;
int min_drop;
unsigned char *pitch_env=NULL;
for(ix=1; ix<n_phoneme_list; ix++)
{
prev = &phoneme_list[ix-1];
p = &phoneme_list[ix];
stress = p->tone & 0xf;
next = &phoneme_list[ix+1];
if(p->synthflags & SFLAG_EMBEDDED)
{
DoEmbedded2(embedded_ix);
}
type = p->type;
if(p->synthflags & SFLAG_SYLLABLE)
type = phVOWEL;
switch(type)
{
case phPAUSE:
last_pitch = 0;
break;
case phSTOP:
last_pitch = 0;
if(prev->type == phFRICATIVE)
p->prepause = 20;
else
if((more_syllables > 0) || (stress < 4))
p->prepause = 40;
else
p->prepause = 60;
if((langopts.word_gap & 0x10) && (p->newword))
p->prepause = 60;
if(p->synthflags & SFLAG_LENGTHEN)
p->prepause += langopts.long_stop;
break;
case phVFRICATIVE:
if(next->type==phVOWEL)
{
pre_voiced = 1;
}
case phFRICATIVE:
if(p->newword)
p->prepause = 15;
if(next->type==phPAUSE && prev->type==phNASAL && !(p->ph->phflags&phFORTIS))
p->prepause = 25;
if((p->ph->phflags & phSIBILANT) && next->type==phSTOP && !next->newword)
{
if(prev->type == phVOWEL)
p->length = 200; // ?? should do this if it's from a prefix
else
p->length = 150;
}
else
p->length = 256;
if((langopts.word_gap & 0x10) && (p->newword))
p->prepause = 30;
break;
case phVSTOP:
if(prev->type==phVFRICATIVE || prev->type==phFRICATIVE || (prev->ph->phflags & phSIBILANT) || (prev->type == phLIQUID))
p->prepause = 30;
if(next->type==phVOWEL || next->type==phLIQUID)
{
if((next->type==phVOWEL) || !next->newword)
pre_voiced = 1;
p->prepause = 40;
if((prev->type == phPAUSE) || (prev->type == phVOWEL)) // || (prev->ph->mnemonic == ('/'*256+'r')))
p->prepause = 0;
else
if(p->newword==0)
{
if(prev->type==phLIQUID)
p->prepause = 20;
if(prev->type==phNASAL)
p->prepause = 12;
if(prev->type==phSTOP && !(prev->ph->phflags & phFORTIS))
p->prepause = 0;
}
}
if((langopts.word_gap & 0x10) && (p->newword) && (p->prepause < 20))
p->prepause = 20;
break;
case phLIQUID:
case phNASAL:
p->amp = stress_amps[1]; // unless changed later
p->length = 256; // TEMPORARY
min_drop = 0;
if(p->newword)
{
if(prev->type==phLIQUID)
p->prepause = 25;
if(prev->type==phVOWEL)
p->prepause = 12;
}
if(next->type==phVOWEL)
{
pre_sonorant = 1;
}
else
if((prev->type==phVOWEL) || (prev->type == phLIQUID))
{
p->length = prev->length;
p->pitch2 = last_pitch;
if(p->pitch2 < 7)
p->pitch2 = 7;
p->pitch1 = p->pitch2 - 8;
p->env = PITCHfall;
pre_voiced = 0;
if(p->type == phLIQUID)
{
p->length = speed1;
p->pitch1 = p->pitch2 - 20; // post vocalic [r/]
}
if(next->type == phVSTOP)
{
p->length = (p->length * 160)/100;
}
if(next->type == phVFRICATIVE)
{
p->length = (p->length * 120)/100;
}
}
else
{
p->pitch2 = last_pitch;
for(ix2=ix; ix2<n_phoneme_list; ix2++)
{
if(phoneme_list[ix2].type == phVOWEL)
{
p->pitch2 = phoneme_list[ix2].pitch2;
break;
}
}
p->pitch1 = p->pitch2-8;
p->env = PITCHfall;
pre_voiced = 0;
}
break;
case phVOWEL:
min_drop = 0;
next2 = &phoneme_list[ix+2];
next3 = &phoneme_list[ix+3];
if(stress > 7) stress = 7;
if(pre_sonorant)
p->amp = stress_amps[stress]-1;
else
p->amp = stress_amps[stress];
if(ix >= (n_phoneme_list-3))
{
// last phoneme of a clause, limit its amplitude
if(p->amp > langopts.param[LOPT_MAXAMP_EOC])
p->amp = langopts.param[LOPT_MAXAMP_EOC];
}
// is the last syllable of a word ?
more_syllables=0;
end_of_clause = 0;
for(p2 = p+1; p2->newword== 0; p2++)
{
if(p2->type == phVOWEL)
more_syllables++;
}
if((p2->newword & 2) && (more_syllables==0))
{
end_of_clause = 2;
}
// calc length modifier
if(more_syllables==0)
{
len = langopts.length_mods0[next2->ph->length_mod *10+ next->ph->length_mod];
if((next->newword) && (langopts.word_gap & 0x4))
{
// consider as a pause + first phoneme of the next word
length_mod = (len + langopts.length_mods0[next->ph->length_mod *10+ 1])/2;
}
else
length_mod = len;
}
else
{
length_mod = langopts.length_mods[next2->ph->length_mod *10+ next->ph->length_mod];
if((next->type == phNASAL) && (next2->type == phSTOP || next2->type == phVSTOP) && (next3->ph->phflags & phFORTIS))
length_mod -= 15;
}
if(more_syllables==0)
length_mod *= speed1;
else
if(more_syllables==1)
length_mod *= speed2;
else
length_mod *= speed3;
length_mod = length_mod / 128;
// if(length_mod < 24)
// length_mod = 24; // restrict how much lengths can be reduced
if(length_mod < 9)
length_mod = 9; // restrict how much lengths can be reduced
if(stress >= 7)
{
// tonic syllable, include a constant component so it doesn't decrease directly with speed
length_mod += 22;
}
length_mod = (length_mod * stress_lengths[stress])/128;
if(end_of_clause == 2)
{
// this is the last syllable in the clause, lengthen it - more for short vowels
length_mod = length_mod * (256 + (280 - p->ph->std_length)/3)/256;
}
if(p->type != phVOWEL)
{
length_mod = 256; // syllabic consonant
min_drop = 8;
}
p->length = length_mod;
// pre-vocalic part
// set last-pitch
env2 = p->env;
if(env2 > 1) env2++; // version for use with preceding semi-vowel
if(p->tone_ph != 0)
{
pitch_env = LookupEnvelope(phoneme_tab[p->tone_ph]->spect);
}
else
{
pitch_env = envelope_data[env2];
}
pitch_start = p->pitch1 + ((p->pitch2-p->pitch1)*pitch_env[0])/256;
if(pre_sonorant || pre_voiced)
{
// set pitch for pre-vocalic part
if(pitch_start - last_pitch > 9)
last_pitch = pitch_start - 9;
prev->pitch1 = last_pitch;
prev->pitch2 = pitch_start;
if(last_pitch < pitch_start)
{
prev->env = PITCHrise;
p->env = env2;
}
else
{
prev->env = PITCHfall;
}
prev->length = length_mod;
prev->amp = p->amp;
if((prev->type != phLIQUID) && (prev->amp > 18))
prev->amp = 18;
}
// vowel & post-vocalic part
next->synthflags &= ~SFLAG_SEQCONTINUE;
if(next->type == phNASAL && next2->type != phVOWEL)
next->synthflags |= SFLAG_SEQCONTINUE;
if(next->type == phLIQUID)
{
next->synthflags |= SFLAG_SEQCONTINUE;
if(next2->type == phVOWEL)
{
next->synthflags &= ~SFLAG_SEQCONTINUE;
}
if(next2->type != phVOWEL)
{
if(next->ph->mnemonic == ('/'*256+'r'))
{
next->synthflags &= ~SFLAG_SEQCONTINUE;
// min_drop = 15;
}
}
}
if((min_drop > 0) && ((p->pitch2 - p->pitch1) < min_drop))
{
p->pitch1 = p->pitch2 - min_drop;
if(p->pitch1 < 0)
p->pitch1 = 0;
}
last_pitch = p->pitch1 + ((p->pitch2-p->pitch1)*envelope_data[p->env][127])/256;
pre_sonorant = 0;
pre_voiced = 0;
break;
}
}
} // end of CalcLengths
Executable
+258
View File
@@ -0,0 +1,258 @@
short int sin_tab[2048] = {
0, -25, -50, -75, -100, -125, -150, -175,
-201, -226, -251, -276, -301, -326, -351, -376,
-401, -427, -452, -477, -502, -527, -552, -577,
-602, -627, -652, -677, -702, -727, -752, -777,
-802, -827, -852, -877, -902, -927, -952, -977,
-1002, -1027, -1052, -1077, -1102, -1127, -1152, -1177,
-1201, -1226, -1251, -1276, -1301, -1326, -1350, -1375,
-1400, -1425, -1449, -1474, -1499, -1523, -1548, -1573,
-1597, -1622, -1647, -1671, -1696, -1721, -1745, -1770,
-1794, -1819, -1843, -1868, -1892, -1917, -1941, -1965,
-1990, -2014, -2038, -2063, -2087, -2111, -2136, -2160,
-2184, -2208, -2233, -2257, -2281, -2305, -2329, -2353,
-2377, -2401, -2425, -2449, -2473, -2497, -2521, -2545,
-2569, -2593, -2617, -2640, -2664, -2688, -2712, -2735,
-2759, -2783, -2806, -2830, -2853, -2877, -2900, -2924,
-2947, -2971, -2994, -3018, -3041, -3064, -3088, -3111,
-3134, -3157, -3180, -3204, -3227, -3250, -3273, -3296,
-3319, -3342, -3365, -3388, -3410, -3433, -3456, -3479,
-3502, -3524, -3547, -3570, -3592, -3615, -3637, -3660,
-3682, -3705, -3727, -3749, -3772, -3794, -3816, -3839,
-3861, -3883, -3905, -3927, -3949, -3971, -3993, -4015,
-4037, -4059, -4080, -4102, -4124, -4146, -4167, -4189,
-4211, -4232, -4254, -4275, -4296, -4318, -4339, -4360,
-4382, -4403, -4424, -4445, -4466, -4487, -4508, -4529,
-4550, -4571, -4592, -4613, -4633, -4654, -4675, -4695,
-4716, -4736, -4757, -4777, -4798, -4818, -4838, -4859,
-4879, -4899, -4919, -4939, -4959, -4979, -4999, -5019,
-5039, -5059, -5078, -5098, -5118, -5137, -5157, -5176,
-5196, -5215, -5235, -5254, -5273, -5292, -5311, -5331,
-5350, -5369, -5388, -5406, -5425, -5444, -5463, -5482,
-5500, -5519, -5537, -5556, -5574, -5593, -5611, -5629,
-5648, -5666, -5684, -5702, -5720, -5738, -5756, -5774,
-5791, -5809, -5827, -5844, -5862, -5880, -5897, -5914,
-5932, -5949, -5966, -5984, -6001, -6018, -6035, -6052,
-6069, -6085, -6102, -6119, -6136, -6152, -6169, -6185,
-6202, -6218, -6235, -6251, -6267, -6283, -6299, -6315,
-6331, -6347, -6363, -6379, -6395, -6410, -6426, -6441,
-6457, -6472, -6488, -6503, -6518, -6533, -6549, -6564,
-6579, -6594, -6608, -6623, -6638, -6653, -6667, -6682,
-6696, -6711, -6725, -6739, -6754, -6768, -6782, -6796,
-6810, -6824, -6838, -6852, -6865, -6879, -6893, -6906,
-6920, -6933, -6946, -6960, -6973, -6986, -6999, -7012,
-7025, -7038, -7051, -7064, -7076, -7089, -7101, -7114,
-7126, -7139, -7151, -7163, -7175, -7187, -7199, -7211,
-7223, -7235, -7247, -7259, -7270, -7282, -7293, -7305,
-7316, -7327, -7338, -7349, -7361, -7372, -7382, -7393,
-7404, -7415, -7425, -7436, -7446, -7457, -7467, -7478,
-7488, -7498, -7508, -7518, -7528, -7538, -7548, -7557,
-7567, -7577, -7586, -7596, -7605, -7614, -7623, -7633,
-7642, -7651, -7660, -7668, -7677, -7686, -7695, -7703,
-7712, -7720, -7728, -7737, -7745, -7753, -7761, -7769,
-7777, -7785, -7793, -7800, -7808, -7816, -7823, -7830,
-7838, -7845, -7852, -7859, -7866, -7873, -7880, -7887,
-7894, -7900, -7907, -7914, -7920, -7926, -7933, -7939,
-7945, -7951, -7957, -7963, -7969, -7975, -7980, -7986,
-7991, -7997, -8002, -8008, -8013, -8018, -8023, -8028,
-8033, -8038, -8043, -8047, -8052, -8057, -8061, -8066,
-8070, -8074, -8078, -8082, -8086, -8090, -8094, -8098,
-8102, -8105, -8109, -8113, -8116, -8119, -8123, -8126,
-8129, -8132, -8135, -8138, -8141, -8143, -8146, -8149,
-8151, -8153, -8156, -8158, -8160, -8162, -8164, -8166,
-8168, -8170, -8172, -8174, -8175, -8177, -8178, -8179,
-8181, -8182, -8183, -8184, -8185, -8186, -8187, -8187,
-8188, -8189, -8189, -8190, -8190, -8190, -8190, -8190,
-8191, -8190, -8190, -8190, -8190, -8190, -8189, -8189,
-8188, -8187, -8187, -8186, -8185, -8184, -8183, -8182,
-8181, -8179, -8178, -8177, -8175, -8174, -8172, -8170,
-8168, -8166, -8164, -8162, -8160, -8158, -8156, -8153,
-8151, -8149, -8146, -8143, -8141, -8138, -8135, -8132,
-8129, -8126, -8123, -8119, -8116, -8113, -8109, -8105,
-8102, -8098, -8094, -8090, -8086, -8082, -8078, -8074,
-8070, -8066, -8061, -8057, -8052, -8047, -8043, -8038,
-8033, -8028, -8023, -8018, -8013, -8008, -8002, -7997,
-7991, -7986, -7980, -7975, -7969, -7963, -7957, -7951,
-7945, -7939, -7933, -7926, -7920, -7914, -7907, -7900,
-7894, -7887, -7880, -7873, -7866, -7859, -7852, -7845,
-7838, -7830, -7823, -7816, -7808, -7800, -7793, -7785,
-7777, -7769, -7761, -7753, -7745, -7737, -7728, -7720,
-7712, -7703, -7695, -7686, -7677, -7668, -7660, -7651,
-7642, -7633, -7623, -7614, -7605, -7596, -7586, -7577,
-7567, -7557, -7548, -7538, -7528, -7518, -7508, -7498,
-7488, -7478, -7467, -7457, -7446, -7436, -7425, -7415,
-7404, -7393, -7382, -7372, -7361, -7349, -7338, -7327,
-7316, -7305, -7293, -7282, -7270, -7259, -7247, -7235,
-7223, -7211, -7199, -7187, -7175, -7163, -7151, -7139,
-7126, -7114, -7101, -7089, -7076, -7064, -7051, -7038,
-7025, -7012, -6999, -6986, -6973, -6960, -6946, -6933,
-6920, -6906, -6893, -6879, -6865, -6852, -6838, -6824,
-6810, -6796, -6782, -6768, -6754, -6739, -6725, -6711,
-6696, -6682, -6667, -6653, -6638, -6623, -6608, -6594,
-6579, -6564, -6549, -6533, -6518, -6503, -6488, -6472,
-6457, -6441, -6426, -6410, -6395, -6379, -6363, -6347,
-6331, -6315, -6299, -6283, -6267, -6251, -6235, -6218,
-6202, -6185, -6169, -6152, -6136, -6119, -6102, -6085,
-6069, -6052, -6035, -6018, -6001, -5984, -5966, -5949,
-5932, -5914, -5897, -5880, -5862, -5844, -5827, -5809,
-5791, -5774, -5756, -5738, -5720, -5702, -5684, -5666,
-5648, -5629, -5611, -5593, -5574, -5556, -5537, -5519,
-5500, -5482, -5463, -5444, -5425, -5406, -5388, -5369,
-5350, -5331, -5311, -5292, -5273, -5254, -5235, -5215,
-5196, -5176, -5157, -5137, -5118, -5098, -5078, -5059,
-5039, -5019, -4999, -4979, -4959, -4939, -4919, -4899,
-4879, -4859, -4838, -4818, -4798, -4777, -4757, -4736,
-4716, -4695, -4675, -4654, -4633, -4613, -4592, -4571,
-4550, -4529, -4508, -4487, -4466, -4445, -4424, -4403,
-4382, -4360, -4339, -4318, -4296, -4275, -4254, -4232,
-4211, -4189, -4167, -4146, -4124, -4102, -4080, -4059,
-4037, -4015, -3993, -3971, -3949, -3927, -3905, -3883,
-3861, -3839, -3816, -3794, -3772, -3749, -3727, -3705,
-3682, -3660, -3637, -3615, -3592, -3570, -3547, -3524,
-3502, -3479, -3456, -3433, -3410, -3388, -3365, -3342,
-3319, -3296, -3273, -3250, -3227, -3204, -3180, -3157,
-3134, -3111, -3088, -3064, -3041, -3018, -2994, -2971,
-2947, -2924, -2900, -2877, -2853, -2830, -2806, -2783,
-2759, -2735, -2712, -2688, -2664, -2640, -2617, -2593,
-2569, -2545, -2521, -2497, -2473, -2449, -2425, -2401,
-2377, -2353, -2329, -2305, -2281, -2257, -2233, -2208,
-2184, -2160, -2136, -2111, -2087, -2063, -2038, -2014,
-1990, -1965, -1941, -1917, -1892, -1868, -1843, -1819,
-1794, -1770, -1745, -1721, -1696, -1671, -1647, -1622,
-1597, -1573, -1548, -1523, -1499, -1474, -1449, -1425,
-1400, -1375, -1350, -1326, -1301, -1276, -1251, -1226,
-1201, -1177, -1152, -1127, -1102, -1077, -1052, -1027,
-1002, -977, -952, -927, -902, -877, -852, -827,
-802, -777, -752, -727, -702, -677, -652, -627,
-602, -577, -552, -527, -502, -477, -452, -427,
-401, -376, -351, -326, -301, -276, -251, -226,
-201, -175, -150, -125, -100, -75, -50, -25,
0, 25, 50, 75, 100, 125, 150, 175,
201, 226, 251, 276, 301, 326, 351, 376,
401, 427, 452, 477, 502, 527, 552, 577,
602, 627, 652, 677, 702, 727, 752, 777,
802, 827, 852, 877, 902, 927, 952, 977,
1002, 1027, 1052, 1077, 1102, 1127, 1152, 1177,
1201, 1226, 1251, 1276, 1301, 1326, 1350, 1375,
1400, 1425, 1449, 1474, 1499, 1523, 1548, 1573,
1597, 1622, 1647, 1671, 1696, 1721, 1745, 1770,
1794, 1819, 1843, 1868, 1892, 1917, 1941, 1965,
1990, 2014, 2038, 2063, 2087, 2111, 2136, 2160,
2184, 2208, 2233, 2257, 2281, 2305, 2329, 2353,
2377, 2401, 2425, 2449, 2473, 2497, 2521, 2545,
2569, 2593, 2617, 2640, 2664, 2688, 2712, 2735,
2759, 2783, 2806, 2830, 2853, 2877, 2900, 2924,
2947, 2971, 2994, 3018, 3041, 3064, 3088, 3111,
3134, 3157, 3180, 3204, 3227, 3250, 3273, 3296,
3319, 3342, 3365, 3388, 3410, 3433, 3456, 3479,
3502, 3524, 3547, 3570, 3592, 3615, 3637, 3660,
3682, 3705, 3727, 3749, 3772, 3794, 3816, 3839,
3861, 3883, 3905, 3927, 3949, 3971, 3993, 4015,
4037, 4059, 4080, 4102, 4124, 4146, 4167, 4189,
4211, 4232, 4254, 4275, 4296, 4318, 4339, 4360,
4382, 4403, 4424, 4445, 4466, 4487, 4508, 4529,
4550, 4571, 4592, 4613, 4633, 4654, 4675, 4695,
4716, 4736, 4757, 4777, 4798, 4818, 4838, 4859,
4879, 4899, 4919, 4939, 4959, 4979, 4999, 5019,
5039, 5059, 5078, 5098, 5118, 5137, 5157, 5176,
5196, 5215, 5235, 5254, 5273, 5292, 5311, 5331,
5350, 5369, 5388, 5406, 5425, 5444, 5463, 5482,
5500, 5519, 5537, 5556, 5574, 5593, 5611, 5629,
5648, 5666, 5684, 5702, 5720, 5738, 5756, 5774,
5791, 5809, 5827, 5844, 5862, 5880, 5897, 5914,
5932, 5949, 5966, 5984, 6001, 6018, 6035, 6052,
6069, 6085, 6102, 6119, 6136, 6152, 6169, 6185,
6202, 6218, 6235, 6251, 6267, 6283, 6299, 6315,
6331, 6347, 6363, 6379, 6395, 6410, 6426, 6441,
6457, 6472, 6488, 6503, 6518, 6533, 6549, 6564,
6579, 6594, 6608, 6623, 6638, 6653, 6667, 6682,
6696, 6711, 6725, 6739, 6754, 6768, 6782, 6796,
6810, 6824, 6838, 6852, 6865, 6879, 6893, 6906,
6920, 6933, 6946, 6960, 6973, 6986, 6999, 7012,
7025, 7038, 7051, 7064, 7076, 7089, 7101, 7114,
7126, 7139, 7151, 7163, 7175, 7187, 7199, 7211,
7223, 7235, 7247, 7259, 7270, 7282, 7293, 7305,
7316, 7327, 7338, 7349, 7361, 7372, 7382, 7393,
7404, 7415, 7425, 7436, 7446, 7457, 7467, 7478,
7488, 7498, 7508, 7518, 7528, 7538, 7548, 7557,
7567, 7577, 7586, 7596, 7605, 7614, 7623, 7633,
7642, 7651, 7660, 7668, 7677, 7686, 7695, 7703,
7712, 7720, 7728, 7737, 7745, 7753, 7761, 7769,
7777, 7785, 7793, 7800, 7808, 7816, 7823, 7830,
7838, 7845, 7852, 7859, 7866, 7873, 7880, 7887,
7894, 7900, 7907, 7914, 7920, 7926, 7933, 7939,
7945, 7951, 7957, 7963, 7969, 7975, 7980, 7986,
7991, 7997, 8002, 8008, 8013, 8018, 8023, 8028,
8033, 8038, 8043, 8047, 8052, 8057, 8061, 8066,
8070, 8074, 8078, 8082, 8086, 8090, 8094, 8098,
8102, 8105, 8109, 8113, 8116, 8119, 8123, 8126,
8129, 8132, 8135, 8138, 8141, 8143, 8146, 8149,
8151, 8153, 8156, 8158, 8160, 8162, 8164, 8166,
8168, 8170, 8172, 8174, 8175, 8177, 8178, 8179,
8181, 8182, 8183, 8184, 8185, 8186, 8187, 8187,
8188, 8189, 8189, 8190, 8190, 8190, 8190, 8190,
8191, 8190, 8190, 8190, 8190, 8190, 8189, 8189,
8188, 8187, 8187, 8186, 8185, 8184, 8183, 8182,
8181, 8179, 8178, 8177, 8175, 8174, 8172, 8170,
8168, 8166, 8164, 8162, 8160, 8158, 8156, 8153,
8151, 8149, 8146, 8143, 8141, 8138, 8135, 8132,
8129, 8126, 8123, 8119, 8116, 8113, 8109, 8105,
8102, 8098, 8094, 8090, 8086, 8082, 8078, 8074,
8070, 8066, 8061, 8057, 8052, 8047, 8043, 8038,
8033, 8028, 8023, 8018, 8013, 8008, 8002, 7997,
7991, 7986, 7980, 7975, 7969, 7963, 7957, 7951,
7945, 7939, 7933, 7926, 7920, 7914, 7907, 7900,
7894, 7887, 7880, 7873, 7866, 7859, 7852, 7845,
7838, 7830, 7823, 7816, 7808, 7800, 7793, 7785,
7777, 7769, 7761, 7753, 7745, 7737, 7728, 7720,
7712, 7703, 7695, 7686, 7677, 7668, 7660, 7651,
7642, 7633, 7623, 7614, 7605, 7596, 7586, 7577,
7567, 7557, 7548, 7538, 7528, 7518, 7508, 7498,
7488, 7478, 7467, 7457, 7446, 7436, 7425, 7415,
7404, 7393, 7382, 7372, 7361, 7349, 7338, 7327,
7316, 7305, 7293, 7282, 7270, 7259, 7247, 7235,
7223, 7211, 7199, 7187, 7175, 7163, 7151, 7139,
7126, 7114, 7101, 7089, 7076, 7064, 7051, 7038,
7025, 7012, 6999, 6986, 6973, 6960, 6946, 6933,
6920, 6906, 6893, 6879, 6865, 6852, 6838, 6824,
6810, 6796, 6782, 6768, 6754, 6739, 6725, 6711,
6696, 6682, 6667, 6653, 6638, 6623, 6608, 6594,
6579, 6564, 6549, 6533, 6518, 6503, 6488, 6472,
6457, 6441, 6426, 6410, 6395, 6379, 6363, 6347,
6331, 6315, 6299, 6283, 6267, 6251, 6235, 6218,
6202, 6185, 6169, 6152, 6136, 6119, 6102, 6085,
6069, 6052, 6035, 6018, 6001, 5984, 5966, 5949,
5932, 5914, 5897, 5880, 5862, 5844, 5827, 5809,
5791, 5774, 5756, 5738, 5720, 5702, 5684, 5666,
5648, 5629, 5611, 5593, 5574, 5556, 5537, 5519,
5500, 5482, 5463, 5444, 5425, 5406, 5388, 5369,
5350, 5331, 5311, 5292, 5273, 5254, 5235, 5215,
5196, 5176, 5157, 5137, 5118, 5098, 5078, 5059,
5039, 5019, 4999, 4979, 4959, 4939, 4919, 4899,
4879, 4859, 4838, 4818, 4798, 4777, 4757, 4736,
4716, 4695, 4675, 4654, 4633, 4613, 4592, 4571,
4550, 4529, 4508, 4487, 4466, 4445, 4424, 4403,
4382, 4360, 4339, 4318, 4296, 4275, 4254, 4232,
4211, 4189, 4167, 4146, 4124, 4102, 4080, 4059,
4037, 4015, 3993, 3971, 3949, 3927, 3905, 3883,
3861, 3839, 3816, 3794, 3772, 3749, 3727, 3705,
3682, 3660, 3637, 3615, 3592, 3570, 3547, 3524,
3502, 3479, 3456, 3433, 3410, 3388, 3365, 3342,
3319, 3296, 3273, 3250, 3227, 3204, 3180, 3157,
3134, 3111, 3088, 3064, 3041, 3018, 2994, 2971,
2947, 2924, 2900, 2877, 2853, 2830, 2806, 2783,
2759, 2735, 2712, 2688, 2664, 2640, 2617, 2593,
2569, 2545, 2521, 2497, 2473, 2449, 2425, 2401,
2377, 2353, 2329, 2305, 2281, 2257, 2233, 2208,
2184, 2160, 2136, 2111, 2087, 2063, 2038, 2014,
1990, 1965, 1941, 1917, 1892, 1868, 1843, 1819,
1794, 1770, 1745, 1721, 1696, 1671, 1647, 1622,
1597, 1573, 1548, 1523, 1499, 1474, 1449, 1425,
1400, 1375, 1350, 1326, 1301, 1276, 1251, 1226,
1201, 1177, 1152, 1127, 1102, 1077, 1052, 1027,
1002, 977, 952, 927, 902, 877, 852, 827,
802, 777, 752, 727, 702, 677, 652, 627,
602, 577, 552, 527, 502, 477, 452, 427,
401, 376, 351, 326, 301, 276, 251, 226,
201, 175, 150, 125, 100, 75, 50, 25,
};
Executable
+663
View File
@@ -0,0 +1,663 @@
/***************************************************************************
* Copyright (C) 2005,2006 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "StdAfx.h"
#include "speech.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef PLATFORM_WINDOWS
#include <windows.h>
#include <winreg.h>
#else
#include <unistd.h>
#endif
#ifndef NEED_GETOPT
#include <getopt.h>
#endif
#include <time.h>
#include <signal.h>
#include <locale.h>
#include "sys/stat.h"
#include "speak_lib.h"
#include "voice.h"
#include "phoneme.h"
#include "synthesize.h"
#include "translate.h"
char path_home[120];
char wavefile[120];
int (* uri_callback)(int, const char *, const char *) = NULL;
static const char *help_text =
"\nspeak [options] [\"<words>\"]\n\n"
"-f <text file> Text file to speak\n"
"--stdin Read text input from stdin instead of a file\n\n"
"If neither -f nor --stdin, <words> are spoken, or if none then text is\n"
"spoken from stdin, each line separately.\n\n"
"-a <integer>\n"
"\t Amplitude, 0 to 200, default is 100\n"
"-l <integer>\n"
"\t Line length. If not zero (which is the default), consider\n"
"\t lines less than this length as end-of-clause\n"
"-p <integer>\n"
"\t Pitch adjustment, 0 to 99, default is 50\n"
"-s <integer>\n"
"\t Speed in words per minute 80 to 370, default is 170\n"
"-v <voice name>\n"
"\t Use voice file of this name from espeak-data/voices\n"
"-w <wave file name>\n"
"\t Write output to this WAV file, rather than speaking it directly\n"
"-b\t Input text is 8-bit encoding\n"
"-m\t Interpret SSML markup, and ignore other < > tags\n"
"-q\t Quiet, don't produce any speech (may be useful with -x)\n"
"-x\t Write phoneme mnemonics to stdout\n"
"-X\t Write phonemes mnemonics and translation trace to stdout\n"
"--stdout Write speech output to stdout\n"
"--compile=<voice name>\n"
"\t Compile the pronunciation rules and dictionary in the current\n"
"\t directory. =<voice name> is optional and specifies which language\n"
"--punct=\"<characters>\"\n"
"\t Speak the names of punctuation characters during speaking. If\n"
"\t =<characters> is omitted, all punctuation is spoken.\n"
"--voices=<langauge>\n"
"\t List the available voices for the specified language.\n"
"\t If <language> is omitted, then list all voices.\n";
#ifdef deleted
"-k <integer>\n"
"\t Indicate capital letters with: 1=sound, 2=the word \"capitals\",\n"
"\t higher values = a pitch increase (try -k20).\n";
#endif
void DisplayVoices(FILE *f_out, char *language);
voice_t voice_data;
USHORT voice_pcnt[N_PEAKS+1][3];
voice_t *voice;
int GetFileLength(const char *filename)
{//====================================
struct stat statbuf;
if(stat(filename,&statbuf) != 0)
return(0);
if((statbuf.st_mode & S_IFMT) == S_IFDIR)
// if(S_ISDIR(statbuf.st_mode))
return(-2); // a directory
return(statbuf.st_size);
} // end of GetFileLength
char *Alloc(int size)
{//==================
char *p;
if((p = (char *)malloc(size)) == NULL)
fprintf(stderr,"Can't allocate memory\n");
return(p);
}
void Free(void *ptr)
{//=================
if(ptr != NULL)
free(ptr);
}
void DisplayVoices(FILE *f_out, char *language)
{//============================================
int ix;
char *p;
int len;
int count;
int scores = 0;
const espeak_VOICE *v;
char *lang_name;
char age_buf[12];
const espeak_VOICE **voices;
espeak_VOICE voice_select;
static char genders[4] = {' ','M','F',' '};
if((language != NULL) && (language[0] != 0))
{
// display only voices for the specified language, in order of priority
voice_select.languages = language;
voice_select.age = 0;
voice_select.gender = 0;
voice_select.name = NULL;
voices = espeak_ListVoices(&voice_select);
scores = 1;
}
else
{
voices = espeak_ListVoices(NULL);
}
fprintf(f_out,"Pty Language Age/Gender VoiceName File Other Langs\n");
for(ix=0; (v = voices[ix]) != NULL; ix++)
{
count = 0;
p = v->languages;
while(*p != 0)
{
len = strlen(p+1);
lang_name = p+1;
if(v->age == 0)
strcpy(age_buf," ");
else
sprintf(age_buf,"%3d",v->age);
if(count==0)
{
fprintf(f_out,"%2d %-12s%s%c %-17s %-11s ",
p[0],lang_name,age_buf,genders[v->gender],v->name,v->identifier);
}
else
{
fprintf(f_out,"(%s %d)",lang_name,p[0]);
}
count++;
p += len+2;
}
// if(scores)
// fprintf(f_out,"%3d ",v->score);
fputc('\n',f_out);
}
} // end of DisplayVoices
static void PitchAdjust(int pitch_adjustment)
{//==========================================
int ix, factor;
extern unsigned char pitch_adjust_tab[100];
voice->pitch_base = (voice->pitch_base * pitch_adjust_tab[pitch_adjustment])/128;
// adjust formants to give better results for a different voice pitch
factor = 256 + (25 * (pitch_adjustment - 50))/50;
for(ix=0; ix<=5; ix++)
{
voice->freq[ix] = (voice->freq2[ix] * factor)/256;
}
} // end of PitchAdjustment
void MarkerEvent(int type, unsigned int char_position, int value, unsigned char *out_ptr)
{//======================================================================================
// Do nothing in the command-line version.
} // end of MarkerEvent
static void init_path(void)
{//========================
#ifdef PLATFORM_WINDOWS
HKEY RegKey;
unsigned long size;
unsigned long var_type;
unsigned char buf[100];
buf[0] = 0;
RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Speech\\Voices\\Tokens\\eSpeak", 0, KEY_READ, &RegKey);
size = sizeof(buf);
var_type = REG_SZ;
RegQueryValueEx(RegKey, "path", 0, &var_type, buf, &size);
sprintf(path_home,"%s\\espeak-data",buf);
#else
snprintf(path_home,sizeof(path_home),"%s/espeak-data",getenv("HOME"));
if(access(path_home,R_OK) != 0)
{
strcpy(path_home,PATH_ESPEAK_DATA);
}
#endif
}
static int initialise(void)
{//========================
int param;
int result;
// It seems that the wctype functions don't work until the locale has been set
// to something other than the default "C". Then, not only Latin1 but also the
// other characters give the correct results with iswalpha() etc.
#ifdef PLATFORM_RISCOS
setlocale(LC_CTYPE,"ISO8859-1");
#else
if(setlocale(LC_CTYPE,"en_US.UTF-8") == NULL)
setlocale(LC_CTYPE,"");
#endif
WavegenInit(22050,0); // 22050
if((result = LoadPhData()) != 1)
{
if(result == -1)
fprintf(stderr,"Failed to load espeak-data\n");
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
SynthesizeInit();
for(param=0; param<N_SPEECH_PARAM; param++)
param_stack[0].parameter[param] = param_defaults[param];
return(0);
}
static void StopSpeak(int unused)
{//==============================
signal(SIGINT,SIG_IGN);
// DEBUG
// printf("\n*** Interrupting speech output (use Ctrl-D to actually quit).\n");
fflush(stdout);
SpeakNextClause(NULL,NULL,5);
signal(SIGINT,StopSpeak);
} // end of StopSpeak()
#ifdef NEED_GETOPT
struct option {
char *name;
int has_arg;
int *flag;
int val;
};
static int optind;
static int optional_argument;
static const char *arg_opts = "afklpsvw"; // which options have arguments
static char *opt_string="";
#define no_argument 0
#define optional_argument 2
#endif
int main (int argc, char **argv)
//==============================
{
static struct option long_options[] =
{
/* These options set a flag. */
// {"verbose", no_argument, &verbose_flag, 1},
// {"brief", no_argument, &verbose_flag, 0},
/* These options don't set a flag.
We distinguish them by their indices. */
{"help", no_argument, 0, 'h'},
{"stdin", no_argument, 0, 0x100},
{"stdout", no_argument, 0, 0x101},
{"compile", optional_argument, 0, 0x102},
{"punct", optional_argument, 0, 0x103},
{"voices", optional_argument, 0, 0x104},
{0, 0, 0, 0}
};
static const char *err_load = "Failed to read ";
FILE *f_text=NULL;
const char *p_text=NULL;
int option_index = 0;
int c;
int value;
int speed=170;
int ix;
char *optarg2;
int amp = 100; // default
int speaking = 0;
int quiet = 0;
int flag_stdin = 0;
int flag_compile = 0;
int pitch_adjustment = 50;
char filename[120];
char voicename[40];
char dictname[40];
voicename[0] = 0;
mbrola_name[0] = 0;
dictname[0] = 0;
wavefile[0] = 0;
filename[0] = 0;
option_linelength = 0;
option_phonemes = 0;
option_waveout = 0;
option_multibyte = espeakCHARS_AUTO; // auto
f_trans = stdout;
init_path();
#ifdef NEED_GETOPT
optind = 1;
while(optind < argc)
{
int len;
char *p;
if((c = *opt_string) == 0)
{
opt_string = argv[optind];
if(opt_string[0] != '-')
break;
optind++;
opt_string++;
c = *opt_string;
}
opt_string++;
p = optarg2 = opt_string;
if(c == '-')
{
opt_string="";
for(ix=0; ;ix++)
{
if(long_options[ix].name == 0)
break;
len = strlen(long_options[ix].name);
if(memcmp(long_options[ix].name,p,len)==0)
{
c = long_options[ix].val;
optarg2 = NULL;
if((long_options[ix].has_arg != 0) && (p[len]=='='))
{
optarg2 = &p[len+1];
}
break;
}
}
}
else
if(strchr(arg_opts,c) != NULL)
{
opt_string="";
if(optarg2[0]==0)
{
// the option's value is in the next argument
optarg2 = argv[optind++];
}
}
#else
while(true)
{
c = getopt_long (argc, argv, "a:bf:hk:l:p:qs:v:w:xXm",
long_options, &option_index);
/* Detect the end of the options. */
if (c == -1)
break;
optarg2 = optarg;
#endif
switch (c)
{
case 'b':
option_multibyte = espeakCHARS_8BIT;
break;
case 'h':
printf("\nspeak text-to-speech: %s\n%s",version_string,help_text);
exit(0);
break;
case 'k':
option_capitals = atoi(optarg2);
break;
case 'x':
option_phonemes = 1;
break;
case 'X':
option_phonemes = 2;
break;
case 'm':
option_ssml = 1;
break;
case 'p':
pitch_adjustment = atoi(optarg2);
if(pitch_adjustment > 99) pitch_adjustment = 99;
break;
case 'q':
quiet = 1;
break;
case 'f':
strncpy0(filename,optarg2,sizeof(filename));
break;
case 'l':
value = 0;
value = atoi(optarg2);
option_linelength = value;
break;
case 'a':
amp = atoi(optarg2);
break;
case 's':
speed = atoi(optarg2);
break;
case 'v':
strncpy0(voicename,optarg2,sizeof(voicename));
break;
case 'w':
option_waveout = 1;
strncpy0(wavefile,optarg2,sizeof(wavefile));
break;
case 0x100: // --stdin
flag_stdin = 1;
break;
case 0x101: // --stdout
option_waveout = 1;
strcpy(wavefile,"stdout");
break;
case 0x102: // --compile
if(optarg2 != NULL)
strncpy0(voicename,optarg2,sizeof(voicename));
flag_compile = 1;
break;
case 0x103: // --punct
option_punctuation = 1;
if(optarg2 != NULL)
{
ix = 0;
while((ix < N_PUNCTLIST) && ((option_punctlist[ix] = optarg2[ix]) != 0)) ix++;
option_punctlist[N_PUNCTLIST-1] = 0;
option_punctuation = 2;
}
break;
case 0x104: // --voices
DisplayVoices(stdout,optarg2);
exit(0);
default:
exit(0);
}
}
initialise();
if(voicename[0] == 0)
strcpy(voicename,"default");
if(SetVoiceByName(voicename) != EE_OK)
{
fprintf(stderr,"%svoice '%s'\n",err_load,voicename);
exit(2);
}
if(flag_compile)
{
#ifdef PLATFORM_WINDOWS
CompileDictionary("C:\\Program Files\\eSpeak\\dictsource\\",dictionary_name,NULL,NULL);
#else
CompileDictionary(NULL,dictionary_name,NULL,NULL);
#endif
exit(0);
}
SetParameter(espeakRATE,speed,0);
SetParameter(espeakVOLUME,amp,0);
SetParameter(espeakCAPITALS,option_capitals,0);
SetParameter(espeakPUNCTUATION,option_punctuation,0);
if(pitch_adjustment != 50)
{
PitchAdjust(pitch_adjustment);
}
WavegenSetVoice(voice);
if(filename[0]==0)
{
if((optind < argc) && (flag_stdin == 0))
{
// there's a non-option parameter, and no -f or --stdin
// use it as text
p_text = argv[optind];
}
else
{
f_text = stdin;
if(flag_stdin == 0)
option_linelength = -1; // single input lines on stdin
}
}
else
{
f_text = fopen(filename,"r");
}
if((f_text == NULL) && (p_text == NULL))
{
fprintf(stderr,"%sfile '%s'\n",err_load,filename);
exit(1);
}
if(option_waveout || quiet)
{
if(quiet)
{
// no sound output
OpenWaveFile(NULL,samplerate);
option_waveout = 1;
}
else
{
// write sound output to a WAV file
if(OpenWaveFile(wavefile,samplerate) != 0)
{
fprintf(stderr,"Can't write to output file '%s'\n'",wavefile);
exit(3);
}
}
InitText(0);
SpeakNextClause(f_text,p_text,0);
for(;;)
{
if(WavegenFile() != 0)
{
break; // finished, wavegen command queue is empty
}
if(Generate(phoneme_list,&n_phoneme_list,1)==0)
SpeakNextClause(NULL,NULL,1);
}
CloseWaveFile(samplerate);
}
else
{
// Silence on ^C or SIGINT
// signal(SIGINT,StopSpeak);
// output sound using portaudio
WavegenInitSound();
InitText(0);
SpeakNextClause(f_text,p_text,0);
if(option_quiet)
{
while(SpeakNextClause(NULL,NULL,1) != 0);
return(0);
}
#ifdef USE_PORTAUDIO
speaking = 1;
while(speaking)
{
// NOTE: if nanosleep() isn't recognised on your system, try replacing
// this by sleep(1);
#ifdef PLATFORM_WINDOWS
Sleep(300); // 0.3s
#else
#ifdef USE_NANOSLEEP
struct timespec period;
struct timespec remaining;
period.tv_sec = 0;
period.tv_nsec = 300000000; // 0.3 sec
nanosleep(&period,&remaining);
#else
sleep(1);
#endif
#endif
if(SynthOnTimer() != 0)
speaking = 0;
}
#else
fprintf(stderr,"-w option must be used because the program was built without a sound interface\n");
#endif // USE_PORTAUDIO
}
return(0);
}
+1066
View File
File diff suppressed because it is too large Load Diff
+571
View File
@@ -0,0 +1,571 @@
#ifndef SPEAK_LIB_H
#define SPEAK_LIB_H
/***************************************************************************
* Copyright (C) 2006 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/*************************************************************/
/* This is the header file for the library version of espeak */
/* */
/*************************************************************/
#include <stdio.h>
/********************/
/* Initialization */
/********************/
typedef enum {
espeakEVENT_LIST_TERMINATED = 0, // Retrieval mode: terminates the event list.
espeakEVENT_WORD = 1, // Start of word
espeakEVENT_SENTENCE, // Start of sentence
espeakEVENT_MARK, // Mark
espeakEVENT_PLAY, // Audio element
espeakEVENT_END, // End of sentence
espeakEVENT_MSG_TERMINATED // End of message
} espeak_EVENT_TYPE;
typedef struct {
espeak_EVENT_TYPE type;
unsigned int unique_identifier; // message identifier (or 0 for key or character)
int text_position; // the number of characters from the start of the text
int length; // word length, in characters (for espeakEVENT_WORD)
int audio_position; // the time in mS within the generated speech output data
int sample; // sample id (internal use)
void* user_data; // pointer supplied by the calling program
union {
int number; // used for WORD and SENTENCE events
const char *name; // used for MARK and PLAY events. UTF8 string
} id;
} espeak_EVENT;
/*
When a message is supplied to espeak_synth, the request is buffered and espeak_synth returns. When the message is really processed, the callback function will be repetedly called.
In RETRIEVAL mode, the callback function supplies to the calling program the audio data and an event list terminated by 0 (LIST_TERMINATED).
In PLAYBACK mode, the callback function is called as soon as an event happens.
For example suppose that the following message is supplied to espeak_Synth:
"hello, hello."
* Once processed in RETRIEVAL mode, it could lead to 3 calls of the callback function :
** Block 1:
<audio data> +
List of events: SENTENCE + WORD + LIST_TERMINATED
** Block 2:
<audio data> +
List of events: WORD + END + LIST_TERMINATED
** Block 3:
no audio data
List of events: MSG_TERMINATED + LIST_TERMINATED
* Once processed in PLAYBACK mode, it could lead to 5 calls of the callback function:
** SENTENCE
** WORD (call when the sounds are actually played)
** WORD
** END (call when the end of sentence is actually played.)
** MSG_TERMINATED
The MSG_TERMINATED event is the last event. It can inform the calling program to clear the user data related to the message.
So if the synthesis must be stopped, the callback function is called for each pending message with the MSG_TERMINATED event.
A MARK event indicates a <mark> element in the text.
A PLAY event indicates an <audio> element in the text, for which the calling program should play the named sound file.
*/
typedef enum {
POS_CHARACTER = 1,
POS_WORD,
POS_SENTENCE
} espeak_POSITION_TYPE;
typedef enum {
/* PLAYBACK mode: plays the audio data, supplies events to the calling program*/
AUDIO_OUTPUT_PLAYBACK,
/* RETRIEVAL mode: supplies audio data and events to the calling program */
AUDIO_OUTPUT_RETRIEVAL,
/* SYNCHRONOUS mode: as RETRIEVAL but doesn't return until synthesis is completed */
AUDIO_OUTPUT_SYNCHRONOUS,
/* Synchronous playback */
AUDIO_OUTPUT_SYNCH_PLAYBACK
} espeak_AUDIO_OUTPUT;
typedef enum {
EE_OK=0,
EE_INTERNAL_ERROR=-1,
EE_BUFFER_FULL=1,
EE_NOT_FOUND=2
} espeak_ERROR;
#ifdef __cplusplus
extern "C"
#endif
int espeak_Initialize(espeak_AUDIO_OUTPUT output, int buflength, const char *path);
/* Must be called before any synthesis functions are called.
output: the audio data can either be played by eSpeak or passed back by the SynthCallback function.
buflength: The length in mS of sound buffers passed to the SynthCallback function.
path: The directory which contains the espeak-data directory, or NULL for the default location.
Returns: sample rate in Hz
*/
typedef int (t_espeak_callback)(short*, int, espeak_EVENT*);
#ifdef __cplusplus
extern "C"
#endif
void espeak_SetSynthCallback(t_espeak_callback* SynthCallback);
/* Must be called before any synthesis functions are called.
This specifies a function in the calling program which is called when a buffer of
speech sound data has been produced.
The callback function is of the form:
int SynthCallback(short *wav, int numsamples, espeak_EVENT *events);
wav: is the speech sound data which has been produced.
NULL indicates that the synthesis has been completed.
numsamples: is the number of entries in wav. This number may vary, may be less than
the value implied by the buflength parameter given in espeak_Initialize, and may
sometimes be zero (which does NOT indicate end of synthesis).
events: an array of espeak_EVENT items which indicate word and sentence events, and
also the occurance if <mark> and <audio> elements within the text.
Callback returns: 0=continue synthesis, 1=abort synthesis.
*/
#ifdef __cplusplus
extern "C"
#endif
void espeak_SetUriCallback(int (*UriCallback)(int, const char*, const char*));
/* This function must be called before synthesis functions are used, in order to deal with
<audio> tags. It specifies a callback function which is called when an <audio> element is
encountered and allows the calling program to indicate whether the sound file which
is specified in the <audio> element is available and is to be played.
The callback function is of the form:
int UriCallback(int type, const char *uri, const char *base);
type: type of callback event. Currently only 1= <audio> element
uri: the "src" attribute from the <audio> element
base: the "xml:base" attribute (if any) from the <speak> element
Return: 1=don't play the sound, but speak the text alternative.
0=place a PLAY event in the event list at the point where the <audio> element
occurs. The calling program can then play the sound at that point.
*/
/********************/
/* Synthesis */
/********************/
#define espeakCHARS_AUTO 0
#define espeakCHARS_UTF8 1
#define espeakCHARS_8BIT 2
#define espeakCHARS_WCHAR 3
#define espeakSSML 0x10
#define espeakPHONEMES 0x100
#define espeakENDPAUSE 0x1000
#define espeakKEEP_NAMEDATA 0x2000
#ifdef __cplusplus
extern "C"
#endif
espeak_ERROR espeak_Synth(const void *text,
size_t size,
unsigned int position,
espeak_POSITION_TYPE position_type,
unsigned int end_position,
unsigned int flags,
unsigned int* unique_identifier,
void* user_data);
/* Synthesize speech for the specified text. The speech sound data is passed to the calling
program in buffers by means of the callback function specified by espeak_SetSynthCallback(). The command is asynchronous: it is internally buffered and returns as soon as possible. If espeak_Initialize was previously called with AUDIO_OUTPUT_PLAYBACK as argument, the sound data are played by eSpeak.
text: The text to be spoken, terminated by a zero character. It may be either 8-bit characters,
wide characters (wchar_t), or UTF8 encoding. Which of these is determined by the "flags"
parameter.
size: Equal to (or greatrer than) the size of the text data, in bytes. This is used in order
to allocate internal storage space for the text. This value is not used for
AUDIO_OUTPUT_SYNCHRONOUS mode.
position: The position in the text where speaking starts. Zero indicates speak from the
start of the text.
position_type: Determines whether "position" is a number of characters, words, or sentences.
Values:
end_position: If set, this gives a character position at which speaking will stop. A value
of zero indicates no end position.
flags: These may be OR'd together:
Type of character codes, one of:
espeakCHARS_UTF8 UTF8 encoding
espeakCHARS_8BIT The 8 bit ISO-8859 character set for the particular language.
espeakCHARS_AUTO 8 bit or UTF8 (this is the default)
espeakCHARS_WCHAR Wide characters (wchar_t)
espeakSSML Elements within < > are treated as SSML elements, or if not recognised are ignored.
espeakPHONEMES Text within [[ ]] is treated as phonemes codes (in espeak's Hirschenbaum encoding).
espeakENDPAUSE If set then a sentence pause is added at the end of the text. If not set then
this pause is suppressed.
unique_identifier: message identifier; helpful for identifying later
data supplied to the callback.
user_data: pointer which will be passed to the callback function.
Return: EE_OK: operation achieved
EE_BUFFER_FULL: the command can not be buffered;
you may try after a while to call the function again.
EE_INTERNAL_ERROR.
*/
#ifdef __cplusplus
extern "C"
#endif
espeak_ERROR espeak_Synth_Mark(const void *text,
size_t size,
const char *index_mark,
unsigned int end_position,
unsigned int flags,
unsigned int* unique_identifier,
void* user_data);
/* Synthesize speech for the specified text. Similar to espeak_Synth() but the start position is
specified by the name of a <mark> element in the text.
index_mark: The "name" attribute of a <mark> element within the text which specified the
point at which synthesis starts. UTF8 string.
For the other parameters, see espeak_Synth()
Return: EE_OK: operation achieved
EE_BUFFER_FULL: the command can not be buffered;
you may try after a while to call the function again.
EE_INTERNAL_ERROR.
*/
#ifdef __cplusplus
extern "C"
#endif
espeak_ERROR espeak_Key(const char *key_name);
/* Speak the name of a keyboard key.
Currently this just speaks the "key_name" as given
Return: EE_OK: operation achieved
EE_BUFFER_FULL: the command can not be buffered;
you may try after a while to call the function again.
EE_INTERNAL_ERROR.
*/
#ifdef __cplusplus
extern "C"
#endif
espeak_ERROR espeak_Char(wchar_t character);
/* Speak the name of the given character
Return: EE_OK: operation achieved
EE_BUFFER_FULL: the command can not be buffered;
you may try after a while to call the function again.
EE_INTERNAL_ERROR.
*/
/* Note, there is no function to play a sound icon. This would be done by the calling program */
/***********************/
/* Speech Parameters */
/***********************/
typedef enum {
espeakSILENCE=0, /* internal use */
espeakRATE,
espeakVOLUME,
espeakPITCH,
espeakRANGE,
espeakPUNCTUATION,
espeakCAPITALS,
espeakEMPHASIS, /* internal use */
espeakLINELENGTH, /* internal use */
espeakVOICETYPE, // internal, 1=mbrola
N_SPEECH_PARAM /* last enum */
} espeak_PARAMETER;
typedef enum {
espeakPUNCT_NONE=0,
espeakPUNCT_ALL=1,
espeakPUNCT_SOME=2
} espeak_PUNCT_TYPE;
#ifdef __cplusplus
extern "C"
#endif
espeak_ERROR espeak_SetParameter(espeak_PARAMETER parameter, int value, int relative);
/* Sets the value of the specified parameter.
relative=0 Sets the absolute value of the parameter.
relative=1 Sets a relative value of the parameter.
parameter:
espeakRATE: speaking speed in word per minute.
espeakVOLUME: volume in range 0-100 0=silence
espeakPITCH: base pitch, range 0-100. 50=normal
espeakRANGE: pitch range, range 0-100. 0-monotone, 50=normal
espeakPUNCTUATION: which punctuation characters to announce:
value in espeak_PUNCT_TYPE (none, all, some),
see espeak_GetParameter() to specify which characters are announced.
espeakCAPITALS: announce capital letters by:
0=none,
1=sound icon,
2=spelling,
3 or higher, by raising pitch. This values gives the amount in Hz by which the pitch
of a word raised to indicate it has a capital letter.
Return: EE_OK: operation achieved
EE_BUFFER_FULL: the command can not be buffered;
you may try after a while to call the function again.
EE_INTERNAL_ERROR.
*/
#ifdef __cplusplus
extern "C"
#endif
int espeak_GetParameter(espeak_PARAMETER parameter, int current);
/* current=0 Returns the default value of the specified parameter.
current=1 Returns the current value of the specified parameter, as set by SetParameter()
*/
#ifdef __cplusplus
extern "C"
#endif
espeak_ERROR espeak_SetPunctuationList(const wchar_t *punctlist);
/* Specified a list of punctuation characters whose names are to be spoken when the
value of the Punctuation parameter is set to "some".
punctlist: A list of character codes, terminated by a zero character.
Return: EE_OK: operation achieved
EE_BUFFER_FULL: the command can not be buffered;
you may try after a while to call the function again.
EE_INTERNAL_ERROR.
*/
#ifdef __cplusplus
extern "C"
#endif
void espeak_SetPhonemeTrace(int value, FILE *stream);
/* Controls the output of phoneme symbols for the text
value=0 No phoneme output (default)
value=1 Output the translated phoneme symbols for the text
value=2 as (1), but also output a trace of how the translation was done (matching rules and list entries)
stream output stream for the phoneme symbols (and trace). If stream=NULL then it uses stdout.
*/
#ifdef __cplusplus
extern "C"
#endif
void espeak_CompileDictionary(const char *path, FILE *log);
/* Compile pronunciation dictionary for a language which corresponds to the currently
selected voice. The required voice should be selected before calling this function.
path: The directory which contains the language's '_rules' and '_list' files.
'path' should end with a path separator character ('/').
log: Stream for error reports and statistics information. If log=NULL then stderr will be used.
*/
/***********************/
/* Voice Selection */
/***********************/
// voice table
typedef struct {
char *name; // a given name for this voice. UTF8 string.
char *languages; // list of pairs of (byte) priority + (string) language (and dialect qualifier)
char *identifier; // the filename for this voice within espeak-data/voices
unsigned char gender; // 0=none 1=male, 2=female,
unsigned char age; // 0=not specified, or age in years
unsigned char variant; // only used when passed as a parameter to espeak_SetVoiceByProperties
unsigned char xx1; // for internal use
int score; // for internal use
void *spare; // for internal use
} espeak_VOICE;
/* Note: The espeak_VOICE structure is used for two purposes:
1. To return the details of the available voices.
2. As a parameter to espeak_SetVoiceByProperties() in order to specify selection criteria.
In (1), the "languages" field consists of a list of (UTF8) language names for which this voice
may be used, each language name in the list is terminated by a zero byte and is also preceded by
a single byte which gives a "priority" number. The list of languages is terminated by an
additional zero byte.
A language name consists of a language code, optionally followed by one or more qualifier (dialect)
names separated by hyphens (eg. "en-uk"). A voice might, for example, have languages "en-uk" and
"en". Even without "en" listed, voice would still be selected for the "en" language (because
"en-uk" is related) but at a lower priority.
The priority byte indicates how the voice is preferred for the language. A low number indicates a
more preferred voice, a higher number indicates a less preferred voice.
In (2), the "languages" field consists simply of a single (UTF8) language name, with no preceding
priority byte.
*/
#ifdef __cplusplus
extern "C"
#endif
const espeak_VOICE **espeak_ListVoices(espeak_VOICE *voice_spec);
/* Reads the voice files from espeak-data/voices and creates an array of espeak_VOICE pointers.
The list is terminated by a NULL pointer
If voice_spec is NULL then all voices are listed.
If voice spec is give, then only the voices which are compatible with the voice_spec
are listed, and they are listed in preference order.
*/
#ifdef __cplusplus
extern "C"
#endif
espeak_ERROR espeak_SetVoiceByName(const char *name);
/* Searches for a voice with a matching "name" field. Language is not considered.
"name" is a UTF8 string.
Return: EE_OK: operation achieved
EE_BUFFER_FULL: the command can not be buffered;
you may try after a while to call the function again.
EE_INTERNAL_ERROR.
*/
#ifdef __cplusplus
extern "C"
#endif
espeak_ERROR espeak_SetVoiceByProperties(espeak_VOICE *voice_spec);
/* An espeak_VOICE structure is used to pass criteria to select a voice. Any of the following
fields may be set:
name NULL, or a voice name
languages NULL, or a single language string (with optional dialect), eg. "en-uk", or "en"
gender 0=not specified, 1=male, 2=female
age 0=not specified, or an age in years
variant After a list of candidates is produced, scored and sorted, "variant" is used to index
that list and choose a voice.
variant=0 takes the top voice (i.e. best match). variant=1 takes the next voice, etc
*/
#ifdef __cplusplus
extern "C"
#endif
espeak_VOICE *espeak_GetCurrentVoice(void);
/* Returns the espeak_VOICE data for the currently selected voice.
This is not affected by temporary voice changes caused by SSML elements such as <voice> and <s>
*/
#ifdef __cplusplus
extern "C"
#endif
espeak_ERROR espeak_Cancel(void);
/* Stop immediately synthesis and audio output of the current text. When this
function returns, the audio output is fully stopped and the synthesizer is ready to
synthesize a new message.
Return: EE_OK: operation achieved
EE_INTERNAL_ERROR.
*/
#ifdef __cplusplus
extern "C"
#endif
int espeak_IsPlaying(void);
/* Returns 1 if audio is played, 0 otherwise.
*/
#ifdef __cplusplus
extern "C"
#endif
espeak_ERROR espeak_Synchronize(void);
/* This function returns when all data have been spoken.
Return: EE_OK: operation achieved
EE_INTERNAL_ERROR.
*/
#ifdef __cplusplus
extern "C"
#endif
espeak_ERROR espeak_Terminate(void);
/* last function to be called.
Return: EE_OK: operation achieved
EE_INTERNAL_ERROR.
*/
#ifdef __cplusplus
extern "C"
#endif
const char *espeak_Info(void* ptr);
/* Returns the version number string.
The parameter is for future use, and should be set to NULL
*/
#endif
Executable
+606
View File
@@ -0,0 +1,606 @@
/***************************************************************************
* Copyright (C) 2005,2006 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "wx/wx.h"
#include "speak_lib.h"
#include "speech.h"
#include "main.h"
#include "voice.h"
#include "phoneme.h"
#include "synthesize.h"
#include "spect.h"
#include "wx/txtstrm.h"
#include "wx/brush.h"
#include "wx/datstrm.h"
extern int PeaksToHarmspect(wavegen_peaks_t *peaks, int pitch, int *htab, int control);
extern unsigned char pk_shape1[];
extern int pk_select;
wxPen BLUE_PEN(wxColour(0,0,255),2,wxSOLID);
wxBrush BRUSH_SELECTED_PEAK(wxColour(255,180,180),wxSOLID);
wxBrush BRUSH_MARKER[N_MARKERS] = {
wxBrush(wxColour(200,0,255),wxSOLID),
wxBrush(wxColour(255,0,0),wxSOLID),
wxBrush(wxColour(255,200,0),wxSOLID),
wxBrush(wxColour(0,255,0),wxSOLID),
wxBrush(wxColour(0,255,255),wxSOLID),
wxBrush(wxColour(200,0,255),wxSOLID),
wxBrush(wxColour(200,0,255),wxSOLID),
wxBrush(wxColour(255,0,200),wxSOLID) };
#define DRAWPEAKWIDTH 2000
#define PEAKSHAPEW 256
#include <math.h>
static int default_freq[N_PEAKS] =
{200,500,1200,3000,3500,4000,6900,7800,9000};
static int default_width[N_PEAKS] =
{750,500,550,550,600,700,700,700,700};
float SpectTilt(int value, int freq)
{//=================================
float x;
float y;
if((currentcanvas == NULL) || (currentcanvas->spectseq->bass_reduction == 0))
return(float(value));
y = value*value*2;
if(freq < 600)
{
return(sqrt(y/2.5));
}
else
if(freq < 1050)
{
x = 1.0 + ((1050.0-freq)* 1.5)/450.0;
return(sqrt(y/x));
}
else
{
return(sqrt(y));
}
}
SpectFrame::SpectFrame(SpectFrame *copy)
{//=====================================
int ix;
selected = 0;
keyframe = 0;
spect = NULL;
markers = 0;
pitch = 0;
nx = 0;
time = 0;
length = 0;
amp_adjust = 100;
length_adjust = 0;
for(ix=0; ix<N_PEAKS; ix++)
{
formants[ix].freq = 0;
peaks[ix].pkfreq = default_freq[ix];
peaks[ix].pkheight = 0;
peaks[ix].pkwidth = default_width[ix];
peaks[ix].pkright = default_width[ix];
}
if(copy != NULL)
{
*this = *copy;
spect = new USHORT[nx];
memcpy(spect,copy->spect,sizeof(USHORT)*nx);
}
}
SpectFrame::~SpectFrame()
{//=======================
if(spect != NULL)
delete spect;
}
int SpectFrame::Import(wxInputStream& stream1)
{//==========================================
int ix;
double x;
unsigned short *spect_data;
wxTextInputStream stream(stream1);
stream >> time;
stream >> pitch;
stream >> nx;
stream >> dx;
if(stream1.Eof())
return(1);
for(ix=0; ix<N_PEAKS; ix++)
{
peaks[ix].pkfreq = default_freq[ix];
peaks[ix].pkheight = 0;
peaks[ix].pkwidth = default_width[ix];
peaks[ix].pkright = default_width[ix];
}
for(ix=1; ix<=5; ix++)
{
stream >> x;
formants[ix].freq = (int)x;
if(x > 0)
peaks[ix].pkfreq = (int)x;
stream >> x;
formants[ix].bandw = (int)x;
}
spect_data = new USHORT[nx];
if(spect_data == NULL)
{
wxLogError(_T("Failed to allocate memory"));
return(1);
}
max_y = 0;
for(ix=0; ix<nx; ix++)
{
stream >> x;
spect_data[ix] = (int)(sqrt(x) * 16386);
if(spect_data[ix] > max_y)
max_y = spect_data[ix];
}
spect = spect_data;
return(0);
} // End of SpectFrame::Import
int SpectFrame::ImportSPC2(wxInputStream& stream, float &time_acc)
{//===============================================================
int ix;
int size;
unsigned short *spect_data;
CYCLE cy;
CYCLE *p;
float len;
static char peak_factor[8] = {4,5,11,20,20,25,32,32};
stream.Read(&cy,44);
size = SPC2_size_cycle(&cy);
p = (CYCLE *)malloc(size);
if(p == NULL)
{
return(1);
}
stream.SeekI(-44,wxFromCurrent);
stream.Read(p,size);
time = time_acc;
len = cy.length / 15625.0;
time_acc += len;
pitch = float(cy.pitch) / 16.0;
nx = cy.n_harm;
dx = pitch;
for(ix=0; ix<7; ix++)
{
peaks[ix].pkfreq = cy.peak_data[ix].freq * peak_factor[ix];
if(peaks[ix].pkfreq == 0)
peaks[ix].pkfreq = default_freq[ix];
peaks[ix].pkheight = cy.peak_data[ix].height * 40;
peaks[ix].pkwidth = cy.peak_data[ix].width_l * 12;
peaks[ix].pkright = cy.peak_data[ix].width_r * 12;
}
for(ix=7; ix<=8; ix++)
{
peaks[ix].pkfreq = default_freq[ix]; // default
peaks[ix].pkheight = 0;
peaks[ix].pkwidth = peaks[ix].pkright = default_width[ix];
}
if(((cy.flags & 0x80)==0) && (peaks[1].pkheight > 0))
keyframe = 1;
if(cy.flags & 0x08)
markers |= 4;
if(cy.flags & 0x10)
markers |= 2;
if(cy.flags & 0x04)
markers |= 8;
spect_data = new USHORT[nx];
if(spect_data == NULL)
{
wxLogError(_T("Failed to allocate memory"));
return(1);
}
max_y = 0;
for(ix=0; ix<nx; ix++)
{
spect_data[ix] = p->data[ix];
if(spect_data[ix] > max_y)
max_y = spect_data[ix];
}
if(nx==0)
{
nx = int(8000/dx);
spect_data = new USHORT[nx];
for(ix=0; ix<nx; ix++)
spect_data[ix] = 1;
max_y = 1;
}
spect = spect_data;
free(p);
return(0);
} // end of ImportSPC2
int SpectFrame::Load(wxInputStream& stream)
{//========================================
int ix;
int x;
unsigned short *spect_data;
wxDataInputStream s(stream);
time = s.ReadDouble();
pitch = s.ReadDouble();
length = s.ReadDouble();
dx = s.ReadDouble();
nx = s.Read16();
markers = s.Read16();
amp_adjust = s.Read16();
for(ix=0; ix<N_PEAKS; ix++)
{
formants[ix].freq = s.Read16();
formants[ix].bandw = s.Read16();
peaks[ix].pkfreq = s.Read16();
if((peaks[ix].pkheight = s.Read16()) > 0)
keyframe = 1;
peaks[ix].pkwidth = s.Read16();
peaks[ix].pkright = s.Read16();
}
spect_data = new USHORT[nx];
if(spect_data == NULL)
{
wxLogError(_T("Failed to allocate memory"));
return(1);
}
max_y = 0;
for(ix=0; ix<nx; ix++)
{
x = spect_data[ix] = s.Read16();
if(x > max_y) max_y = x;
}
spect = spect_data;
return(0);
} // End of SpectFrame::Load
int SpectFrame::Save(wxOutputStream& stream)
{//=========================================
int ix;
wxDataOutputStream s(stream);
s.WriteDouble(time);
s.WriteDouble(pitch);
s.WriteDouble(length);
s.WriteDouble(dx);
s.Write16(nx);
s.Write16(markers);
s.Write16(amp_adjust);
for(ix=0; ix<N_PEAKS; ix++)
{
s.Write16(formants[ix].freq);
s.Write16(formants[ix].bandw);
s.Write16(peaks[ix].pkfreq);
s.Write16(keyframe ? peaks[ix].pkheight : 0);
s.Write16(peaks[ix].pkwidth);
s.Write16(peaks[ix].pkright);
}
for(ix=0; ix<nx; ix++)
{
s.Write16(spect[ix]);
}
return(0);
} // end of SpectFrame::Save
void SpectFrame::ZeroPeaks()
{//=========================
int pk;
for(pk=0; pk<N_PEAKS; pk++)
peaks[pk].pkheight = 0;
}
void SpectFrame::CopyPeaks(SpectFrame *sf)
{//=======================================
memcpy(peaks,sf->peaks,sizeof(peaks));
keyframe = sf->keyframe;
}
void SpectFrame::ToggleMarker(int n)
{//=================================
markers ^= 1<<n;
}
double SpectFrame::GetRms(int seq_amplitude)
{//=========================================
int h;
float total=0;
int maxh;
int height;
int htab[400];
wavegen_peaks_t wpeaks[9];
for(h=0; h<9; h++)
{
height = (peaks[h].pkheight * seq_amplitude * amp_adjust)/10000;
wpeaks[h].height = height << 8;
wpeaks[h].freq = peaks[h].pkfreq << 16;
wpeaks[h].left = peaks[h].pkwidth << 16;
wpeaks[h].right = peaks[h].pkright << 16;
}
maxh = PeaksToHarmspect(wpeaks,90<<16,htab,0);
for(h=1; h<maxh; h++)
{
total += ((htab[h] * htab[h]) >> 10);
}
rms = sqrt(total) / 14.5;
// DrawPeaks(NULL,0,0,amp);
return(rms);
}
void SpectFrame::DrawPeaks(wxDC *dc, int offy, int frame_width, int seq_amplitude, double scale_x)
{//==============================================================================================
// dc==NULL means don't draw, just calculate RMS
int peak;
peak_t *pk;
int x1,x2,x3,width,ix;
int y1, y2;
double yy;
int max_ix;
int buf[DRAWPEAKWIDTH*2];
max_ix = int(9000 * scale_x);
memset(buf,0,sizeof(buf));
for(peak=0; peak<N_PEAKS; peak++)
{
pk = &peaks[peak];
if((pk->pkfreq == 0) || (pk->pkheight==0)) continue;
x1 = (int)(pk->pkfreq*scale_x);
x2 = (int)((pk->pkfreq + pk->pkright)*scale_x);
x3 = (int)((pk->pkfreq - pk->pkwidth)*scale_x);
if(x3 >= DRAWPEAKWIDTH)
continue; // whole peak is off the scale
if((width = x2-x1) <= 0) continue;
for(ix=0; ix<width; ix++)
{
buf[x1+ix] += pk->pkheight * pk_shape1[(ix*PEAKSHAPEW)/width];
}
if((width = x1-x3) <= 0) continue;
for(ix=1; ix<width; ix++)
{
if(x3+ix >= 0)
{
buf[x3+ix] += pk->pkheight * pk_shape1[((width-ix)*PEAKSHAPEW)/width];
}
}
}
rms = buf[0]>>12;
rms = rms*rms*23;
rms = rms*rms;
if(dc != NULL) dc->SetPen(*wxGREEN_PEN);
x1 = 0;
y1 = offy - ((buf[0] * FRAME_HEIGHT) >> 21);
for(ix=1; ix<max_ix; ix++)
{
yy = buf[ix]>>12;
yy = yy*yy*23;
rms += (yy*yy);
x2 = ix;
y2 = offy - ((buf[ix] * FRAME_HEIGHT) >> 21);
if(dc != NULL) dc->DrawLine(x1,y1,x2,y2);
x1 = x2;
y1 = y2;
}
rms = sqrt(rms)/200000.0;
// apply adjustment from spectseq amplitude
rms = rms * seq_amplitude * amp_adjust / 10000.0;
rms = GetRms(seq_amplitude);
} // end of SpectFrame::DrawPeaks
void SpectFrame::Draw(wxDC& dc, int offy, int frame_width, double scalex, double scaley)
{//=====================================================================================
int pt;
int peak;
peak_t *pk;
int ix;
double x0, x1;
int y0, y1;
int x, x2, x3;
double xinc;
double yf;
int font_height;
wxString text;
if(currentcanvas == NULL)
return;
dc.SetFont(*wxSWISS_FONT);
xinc = dx * scalex;
x0 = xinc;
x1 = nx * xinc;
if(selected) // this frame is selected
{
// highlight selected peak by drawing a red triangle
pk = &peaks[pk_select];
x2 = int(pk->pkright * scalex * 0.44);
x3 = int(pk->pkwidth * scalex * 0.44);
x = int((pk->pkfreq) * scalex);
y1 = (pk->pkheight * FRAME_HEIGHT) >> 14;
if(y1 < 5) y1 = 5;
wxPoint triangle[3];
dc.SetBrush(BRUSH_SELECTED_PEAK);
dc.SetPen(*wxTRANSPARENT_PEN);
triangle[0] = wxPoint(0,-y1);
triangle[1] = wxPoint(x2,0);
triangle[2] = wxPoint(-x3,0);
dc.DrawPolygon(3,triangle,x,offy);
}
// draw the measured formants
dc.SetPen(BLUE_PEN);
for(peak=1; peak<=5; peak++)
{
if(formants[peak].freq != 0)
{
// set height from linear interpolation of the adjacent
// points in the spectrum
pt = (int)(formants[peak].freq / dx);
y0 = spect[pt-1];
y1 = spect[pt];
yf = (y1-y0) * (formants[peak].freq - pt*dx)/dx;
y1 = offy - (int)((y0+yf) * scaley);
x1 = formants[peak].freq * scalex;
dc.DrawLine((int)x1,offy,(int)x1,y1);
}
}
// draw the spectrum outline
if(keyframe)
dc.SetPen(*wxBLACK_PEN);
else
dc.SetPen(*wxMEDIUM_GREY_PEN);
if(spect != NULL)
{
y0 = offy - (int)(spect[0] * scaley);
for(pt=1; pt<nx; pt++)
{
x1 = x0 + xinc;
y1 = offy - (int)(SpectTilt(spect[pt],int(pt*dx)) * scaley);
dc.DrawLine((int)x0,y0,(int)x1,y1);
x0 = x1;
y0 = y1;
}
}
if(currentcanvas->zoom < 2)
dc.SetFont(FONT_SMALL);
else
dc.SetFont(FONT_MEDIUM);
// Markers
x = frame_width - 120 - 32;
for(ix=0; ix<N_MARKERS; ix++)
{
if(markers & 1<<ix)
{
dc.SetBrush(BRUSH_MARKER[ix]);
y0 = offy-FRAME_HEIGHT+22;
dc.DrawRectangle(x,y0,22,22);
if(currentcanvas->zoom > 2)
{
text.Printf(_T("%d"),ix);
dc.DrawText(text,x+2,y0);
}
x -= 26;
}
}
DrawPeaks(&dc,offy,frame_width,currentcanvas->spectseq->amplitude,scalex);
font_height = int(15 / currentcanvas->zoomy);
text.Printf(_T("%3dmS %.1fHz"),int(time*1000),pitch);
dc.DrawText(text,frame_width-130,offy-FRAME_HEIGHT+20+font_height);
if(keyframe || rms > 0)
{
text.Printf(_T("%3d"),(int)rms);
dc.DrawText(text,frame_width-130,offy-FRAME_HEIGHT+20+font_height*2);
}
dc.SetPen(*wxBLACK_PEN);
dc.DrawLine(0,offy,frame_width,offy); // base line
} // end of SpectFrame::Draw
Executable
+341
View File
@@ -0,0 +1,341 @@
#include "wx/spinctrl.h"
#include "wx/notebook.h"
#define PROGRAM_NAME _T("Voice Editor")
#define FRAME_WIDTH 1000 // max width for 8000kHz frame
#define MAX_DISPLAY_FREQ 9500
#define FRAME_HEIGHT 240
#define T_ZOOMOUT 301
#define T_ZOOMIN 302
#define T_USEPITCHENV 303
#define T_SAMPRATE 304
#define T_PITCH1 305
#define T_PITCH2 306
#define T_DURATION 307
#define T_AMPLITUDE 308
#define T_AMPFRAME 309
#define T_TIMEFRAME 310
#define T_TIMESEQ 311
#define FILEID1_SPECTSEQ 0x43455053
#define FILEID2_SPECTSEQ 0x51455354
#define FILEID1_SPC2 0x32435053 // an old format for spectrum files
#define FILEID1_PITCHENV 0x43544950
#define FILEID2_PITCHENV 0x564e4548
#define FILEID1_PRAATSEQ 0x41415250
#define FILEID2_PRAATSEQ 0x51455354
class MyFrame;
typedef struct {
unsigned short pitch1;
unsigned short pitch2;
unsigned char env[128];
} PitchEnvelope;
typedef struct {
short freq;
short bandw;
} formant_t;
//===============================================================================================
// Old "SPC2" format
typedef struct {
unsigned char freq; /* *factor for f0-f7 of [4,5,11,20,20,25,32,32] */
unsigned char height;
unsigned char width_l; /* * 8 Hz */
unsigned char width_r; /* * 8 Hz */
} PEAKS7;
/* flags: bit 0 label is present in data[]
bit 1 mark cycle - blue background
bit 2 mark cycle - green square
bit 3 mark cycle - 'lengthen' indicator
bit 4 mark cycle - 'standard'
bit 7 hide peaks
*/
typedef struct {
short pitch; /* pitch of fundamental, 1/16 Hz */
short length; /* length in 1/15625 sec */
unsigned char n_harm; /* number of harmonic data, h1 ... hn */
unsigned char flags; /* bits 0 */
unsigned char aspiration;
unsigned char hf_voicing;
unsigned char spare1;
unsigned char spare2;
unsigned char spare3;
unsigned char spare4;
PEAKS7 peak_data[7];
unsigned char data[1]; /* firstly harmonic data, then f3,f4,f5 data */
} CYCLE; /* VARIABLE LENGTH Structure */
extern int SPC2_size_cycle(CYCLE *cy);
//==============================================================================================
class SpectFrame
{//=============
public:
SpectFrame(SpectFrame *copy=NULL);
~SpectFrame();
int Import(wxInputStream &stream);
int ImportSPC2(wxInputStream &stream, float &time_acc);
int Load(wxInputStream &stream);
int Save(wxOutputStream &stream);
void Draw(wxDC &dc, int offy, int frame_width, double scalex, double scaley);
void ZeroPeaks();
void CopyPeaks(SpectFrame *sf);
void ToggleMarker(int n);
void MakeWave(int peaks, PitchEnvelope &pitch, int amplitude, int duration);
void MakeHtab(int numh, int *htab, int pitch);
double GetRms(int amp);
int selected;
int keyframe;
int amp_adjust;
float length_adjust;
double rms;
float time;
float pitch;
float length;
float dx;
int nx;
int markers;
int max_y;
USHORT *spect; // sqrt of harmonic amplitudes, 1-nx at 'pitch'
formant_t formants[N_PEAKS]; // this is just the estimate given by Praat
peak_t peaks[N_PEAKS];
private:
void DrawPeaks(wxDC *dc, int offy, int frame_width, int seq_amplitude, double scalex);
};
class SpectSeq
{//===========
public:
SpectSeq(int nframes=0);
~SpectSeq();
int Import(wxInputStream& stream);
int ImportSPC2(wxInputStream& stream);
int Save(wxOutputStream& stream, int selection);
int Load(wxInputStream& stream);
void Draw(wxDC &dc, int start_y, int end_y);
void MakePitchenv(PitchEnvelope &pitch);
void MakeWave(int start, int end, PitchEnvelope &pitch);
void InterpolatePeaks(int on);
void InterpolateAdjacent(void);
void CopyDown(int frame, int direction);
void SelectAll(int yes);
int CountSelected();
void DeleteSelected();
void ClipboardCopy();
int ClipboardInsert(int insert_at);
float GetFrameLength(int frame, int plus, int *original_mS);
float GetKeyedLength();
void SetFrameLengths();
void ConstructVowel(void);
int numframes;
int amplitude;
int spare;
wxString name;
SpectFrame **frames;
PitchEnvelope pitchenv;
int pitch1;
int pitch2;
int duration;
int grid;
int bass_reduction;
int max_x;
int max_y;
private:
void Load2(wxInputStream& stream, int import, int n);
void InterpolatePeak(int peak);
void ApplyAmp_adjust(SpectFrame *sp, peak_t *peaks);
double scalex;
double scaley;
};
class SpectDisplay: public wxScrolledWindow
{//========================================
public:
SpectDisplay(wxWindow *parent, const wxPoint& pos, const wxSize& size, SpectSeq *spect);
~SpectDisplay();
virtual void OnDraw(wxDC& dc);
void OnMouse(wxMouseEvent& event);
void OnKey(wxKeyEvent& event);
void OnActivate(int active);
void OnMenu(wxCommandEvent& event);
void Save(const wxString &path=_T(""),int selection=0);
void SavePitchenv(PitchEnvelope &pitch);
void OnZoom(int command);
SpectSeq *spectseq;
float zoomx;
float zoomy;
int zoom;
wxString savepath;
void ReadDialogValues();
void WriteDialogValues();
void WriteDialogLength();
void RefreshDialogValues(int type);
int ScrollToFrame(int frame, int centre);
void SelectFrame(int frame);
private:
void RefreshFrame(int frame);
void SetKeyframe(SpectFrame *sf, int yes);
void PlayChild(int number, PitchEnvelope pitchenv);
void SetExtent();
int sframe;
int pk_num;
DECLARE_EVENT_TABLE()
};
class ByteGraph: public wxScrolledWindow
{//=====================================
public:
ByteGraph(wxWindow *parent, const wxPoint& pos, const wxSize &size);
virtual void OnDraw(wxDC &dc);
void SetData(int nx, unsigned char *data);
void ShowSpectrum(int yes);
private:
int npoints;
unsigned char *graph;
int show_spectrum;
double spectrum_scale;
void DrawSpectrum(wxDC &dc);
void OnMouse(wxMouseEvent& event);
DECLARE_EVENT_TABLE()
};
class FormantDlg : public wxPanel
{//==============================
public:
FormantDlg(wxWindow *parent);
void ShowFrame(SpectSeq *spectseq, int frame, int pk, int field);
void OnCommand(wxCommandEvent& event);
void OnSpin(wxSpinEvent& event);
wxCheckBox *usepitchenv;
wxSpinCtrl *t_amplitude;
wxSpinCtrl *t_ampframe;
wxSpinCtrl *t_timeframe;
wxSpinCtrl *t_timeseq;
wxStaticText *t_pitch;
wxStaticText *t_orig_frame;
wxStaticText *t_orig_seq;
private:
wxStaticText *t_lab[10];
wxStaticText *t_labpk[N_PEAKS];
wxTextCtrl *t_pkfreq[N_PEAKS];
wxTextCtrl *t_pkheight[N_PEAKS];
wxTextCtrl *t_pkwidth[N_PEAKS];
wxTextCtrl *t_pkright[N_PEAKS];
wxRadioButton *t_select_peak[N_PEAKS];
wxButton *t_zoomout;
wxButton *t_zoomin;
DECLARE_EVENT_TABLE()
};
class VoiceDlg : public wxPanel
{//=============================
public:
VoiceDlg(wxWindow *parent);
void ReadParams();
void WriteParams();
void Save();
void Load();
void SetFromSpect(SpectSeq *spect);
voice_t *voice_p;
PitchEnvelope pitchenv;
wxSpinCtrl* vd_pitch1;
wxSpinCtrl* vd_pitch2;
wxTextCtrl* vd_duration;
private:
wxButton *vd_defaults;
wxButton *vd_load;
wxButton *vd_save;
wxStaticText* vd_labpk[N_PEAKS+1];
wxSpinCtrl* vd[N_PEAKS+1][3];
wxCheckBox* vd_usepitch;
wxStaticText* vd_lab[3];
ByteGraph *vd_pitchgraph;
wxComboBox *vd_pitchchoice;
int pitch1;
int pitch2;
void OnCommand(wxCommandEvent& event);
void OnSpin(wxSpinEvent& event);
DECLARE_EVENT_TABLE()
};
extern SpectSeq *clipboard_spect;
extern int pk_select;
extern int samplerate;
extern unsigned char env_fall[];
extern FormantDlg* formantdlg;
extern VoiceDlg* voicedlg;
extern wxNotebook* notebook;
extern ByteGraph* pitchgraph;
extern SpectDisplay *currentcanvas;
extern float polint(float xa[],float ya[],int n,float x);
extern void WavegenInit(int samplerate, int wavemult_fact);
extern void WavegenInitPkData(int); // initialise envelope data
extern void SetPitch(int length, unsigned char *env, int pitch1, int pitch2);
extern void SetSynthHtab(int length_mS, USHORT *ht1, int nh1, float pitch1, USHORT *ht2, int nh2, float pitch2);
extern void MakeWaveFile();
extern void MakeWaveBuf(int length, UCHAR *buf);
extern int OpenWaveFile(const char *path, int samplerate);
extern int OpenWaveFile2(const char *path);
extern void CloseWaveFile(int rate);
extern void PlayWavFile(const char *fname);
extern void SetSpinCtrl(wxSpinCtrl *t, int value);
extern int GetNumeric(wxTextCtrl *t);
extern void SetNumeric(wxTextCtrl *t, int value);
+1009
View File
File diff suppressed because it is too large Load Diff
+1085
View File
File diff suppressed because it is too large Load Diff
Executable
+95
View File
@@ -0,0 +1,95 @@
/***************************************************************************
* Copyright (C) 2005,2006 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <sys/types.h>
// conditional compilation options
#if defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN
#define ARCH_BIG
#endif
#define PLATFORM_POSIX
#define PATHSEP '/'
#define USE_PORTAUDIO
#define USE_NANOSLEEP
#define __cdecl
#define ESPEAK_API extern "C"
#ifdef LIBRARY
#define USE_ASYNC
//#define USE_MBROLA_LIB
#endif
#ifdef _ESPEAKEDIT
#define USE_ASYNC
#define LOG_FRAMES // write keyframe info to log-espeakedit
#endif
// will look for espeak_data directory here, and also in user's home directory
#ifndef PATH_ESPEAK_DATA
#define PATH_ESPEAK_DATA "/usr/share/espeak-data"
#endif
typedef unsigned short USHORT;
typedef unsigned char UCHAR;
typedef double DOUBLEX;
#define N_PEAKS 9
#define N_MARKERS 8
typedef struct {
const char *mnem;
int value;
} MNEM_TAB;
int LookupMnem(MNEM_TAB *table, char *string);
typedef struct {
short pkfreq;
short pkheight;
short pkwidth;
short pkright;
} peak_t;
typedef struct {
short frflags;
unsigned char length;
unsigned char rms;
short ffreq[9];
unsigned char fheight[9];
unsigned char fwidth[6]; // width/4
unsigned char fright[6]; // width/4
} frame_t;
int GetFileLength(const char *filename);
char *Alloc(int size);
void Free(void *ptr);
extern void strncpy0(char *to,const char *from, int size);
extern char path_home[];
extern const char *version_string;
extern const int version_phdata;
+684
View File
@@ -0,0 +1,684 @@
/***************************************************************************
* Copyright (C) 2007 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; 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 <ctype.h>
#include <wctype.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "speak_lib.h"
#include "speech.h"
#include "phoneme.h"
#include "synthesize.h"
#include "translate.h"
#include "voice.h"
#ifdef USE_MBROLA_LIB
extern unsigned char *outbuf;
#ifndef PLATFORM_WINDOWS
#include "mbrolib.h"
void * mb_handle;
#else
#include <windows.h>
typedef void (WINAPI *PROCVV)(void);
typedef void (WINAPI *PROCVI)(int);
typedef void (WINAPI *PROCVF)(float);
typedef int (WINAPI *PROCIV)();
typedef int (WINAPI *PROCIC) (char *);
typedef int (WINAPI *PROCISI)(short *,int);
typedef char* (WINAPI *PROCVCI)(char *,int);
PROCIC init_MBR;
PROCIC write_MBR;
PROCIV flush_MBR;
PROCISI read_MBR;
PROCVV close_MBR;
PROCVV reset_MBR;
PROCIV lastError_MBR;
PROCVCI lastErrorStr_MBR;
PROCVI setNoError_MBR;
PROCVI setFreq_MBR;
PROCVF setVolumeRatio_MBR;
HINSTANCE hinstDllMBR = NULL;
BOOL load_MBR()
{
if(hinstDllMBR != NULL)
return TRUE; // already loaded
if (!(hinstDllMBR=LoadLibrary("mbrola.dll")))
return FALSE;
init_MBR =(PROCIC) GetProcAddress(hinstDllMBR,"init_MBR");
write_MBR =(PROCIC) GetProcAddress(hinstDllMBR,"write_MBR");
flush_MBR =(PROCIV) GetProcAddress(hinstDllMBR,"flush_MBR");
read_MBR =(PROCISI) GetProcAddress(hinstDllMBR,"read_MBR");
close_MBR =(PROCVV) GetProcAddress(hinstDllMBR,"close_MBR");
reset_MBR =(PROCVV) GetProcAddress(hinstDllMBR,"reset_MBR");
lastError_MBR =(PROCIV) GetProcAddress(hinstDllMBR,"lastError_MBR");
lastErrorStr_MBR =(PROCVCI) GetProcAddress(hinstDllMBR,"lastErrorStr_MBR");
setNoError_MBR =(PROCVI) GetProcAddress(hinstDllMBR,"setNoError_MBR");
setVolumeRatio_MBR =(PROCVF) GetProcAddress(hinstDllMBR,"setVolumeRatio_MBR");
return TRUE;
}
void unload_MBR()
{
if (hinstDllMBR)
{
FreeLibrary (hinstDllMBR);
hinstDllMBR=NULL;
}
}
#endif // windows
#endif // USE_MBROLA_LIB
MBROLA_TAB *mbrola_tab = NULL;
int mbrola_control = 0;
espeak_ERROR LoadMbrolaTable(const char *mbrola_voice, const char *phtrans)
{//========================================================================
// Load a phoneme name translation table from espeak-data/mbrola
int size;
FILE *f_in;
char path[150];
mbrola_name[0] = 0;
if(mbrola_voice == NULL)
{
samplerate = samplerate_native;
SetParameter(espeakVOICETYPE,0,0);
return(EE_OK);
}
sprintf(path,"%s/mbrola/%s",path_home,mbrola_voice);
#ifdef USE_MBROLA_LIB
#ifdef PLATFORM_WINDOWS
if(load_MBR() == FALSE) // load mbrola.dll
return(EE_INTERNAL_ERROR);
if(init_MBR(path) != 0) // initialise the required mbrola voice
return(EE_NOT_FOUND);
setNoError_MBR(1); // don't stop on phoneme errors
#else
mb_handle = mbrolib_init( 22050 );
mbrolib_parameter m_parameters;
if(mb_handle == NULL)
return(EE_INTERNAL_ERROR);
MBROLIB_ERROR a_status = mbrolib_set_voice(mb_handle, mbrola_voice);
if(a_status != MBROLIB_OK)
return(EE_NOT_FOUND);
#endif // not windows
#endif // USE_MBROLA_LIB
// read eSpeak's mbrola phoneme translation data, eg. en1_phtrans
sprintf(path,"%s/mbrola_ph/%s",path_home,phtrans);
size = GetFileLength(path);
if((f_in = fopen(path,"r")) == NULL)
return(EE_NOT_FOUND);
if((mbrola_tab = (MBROLA_TAB *)realloc(mbrola_tab,size)) == NULL)
{
fclose(f_in);
return(EE_INTERNAL_ERROR);
}
fread(&mbrola_control,4,1,f_in);
fread(mbrola_tab,size,1,f_in);
fclose(f_in);
#ifdef USE_MBROLA_LIB
#ifdef PLATFORM_WINDOWS
setVolumeRatio_MBR((float)(mbrola_control & 0xff) /16.0);
#else
mbrolib_get_parameter(mb_handle,&m_parameters);
m_parameters.ignore_error = 1;
m_parameters.volume_ratio = (float)(mbrola_control & 0xff) /16.0;
mbrolib_set_parameter(mb_handle,&m_parameters);
#endif // not windows
#endif // USE_MBROLA_LIB
option_quiet = 1;
samplerate = 16000;
strcpy(mbrola_name,mbrola_voice);
SetParameter(espeakVOICETYPE,1,0);
return(EE_OK);
}
int GetMbrName(PHONEME_LIST *plist, PHONEME_TAB *ph, PHONEME_TAB *ph_prev, PHONEME_TAB *ph_next, int *name2, int *split, int *control)
{//==============================================================================================================
// Look up a phoneme in the mbrola phoneme name translation table
// It may give none, 1, or 2 mbrola phonemes
int mnem = ph->mnemonic;
MBROLA_TAB *pr;
PHONEME_TAB *other_ph;
int found = 0;
// control
// bit 0 skip the next phoneme
// bit 1 match this and Previous phoneme
// bit 2 only at the start of a word
pr = mbrola_tab;
while(pr->name != 0)
{
if(mnem == pr->name)
{
if(pr->next_phoneme == 0)
found = 1;
else
{
if(pr->control & 2)
other_ph = ph_prev;
else
other_ph = ph_next;
if((pr->next_phoneme == other_ph->mnemonic) ||
((pr->next_phoneme == 2) && (other_ph->type == phVOWEL)) ||
((pr->next_phoneme == '_') && (other_ph->type == phPAUSE)))
{
found = 1;
}
}
if((pr->control & 4) && (plist->newword == 0)) // only at start of word
found = 0;
if(found)
{
*name2 = pr->mbr_name2;
*split = pr->percent;
*control = pr->control;
return(pr->mbr_name);
}
}
pr++;
}
*name2=0;
*split=0;
*control=0;
return(mnem);
}
static char *WritePitch(int env, int pitch1, int pitch2, int split)
{//================================================================
int x;
int ix;
int pitch_base;
int pitch_range;
int p1,p2,p_end;
unsigned char *pitch_env;
int max = -1;
int min = 999;
int y_max=0;
int y_min=0;
int y2;
int y[4];
int env_split;
char buf[40];
static char output[40];
output[0] = 0;
pitch_env = envelope_data[env];
if(pitch1 > pitch2)
{
x = pitch1; // swap values
pitch1 = pitch2;
pitch2 = x;
}
pitch_base = voice->pitch_base + (pitch1 * voice->pitch_range);
pitch_range = voice->pitch_base + (pitch2 * voice->pitch_range) - pitch_base;
env_split = (split * 128)/100;
if(env_split < 0)
env_split = 0-env_split;
// find max and min in the pitch envelope
for(x=0; x<128; x++)
{
if(pitch_env[x] > max)
{
max = pitch_env[x];
y_max = x;
}
if(pitch_env[x] < min)
{
min = pitch_env[x];
y_min = x;
}
}
// set an additional pitch point half way through the phoneme.
// but look for a maximum or a minimum and use that instead
y[2] = 64;
if((y_max > 0) && (y_max < 127))
{
y[2] = y_max;
}
if((y_min > 0) && (y_min < 127))
{
y[2] = y_min;
}
y[1] = y[2] / 2;
y[3] = y[2] + (127 - y[2])/2;
// set initial pitch
p1 = ((pitch_env[0]*pitch_range)>>8) + pitch_base; // Hz << 12
p_end = ((pitch_env[127]*pitch_range)>>8) + pitch_base;
if(split >= 0)
{
sprintf(buf," 0 %d",p1/4096);
strcat(output,buf);
}
// don't use intermediate pitch points for linear rise and fall
if(env > 1)
{
for(ix=1; ix<4; ix++)
{
p2 = ((pitch_env[y[ix]]*pitch_range)>>8) + pitch_base;
if(split > 0)
{
y2 = (y[ix] * 100)/env_split;
}
else
if(split < 0)
{
y2 = ((y[ix]-env_split) * 100)/env_split;
}
else
{
y2 = (y[ix] * 100)/128;
}
if((y2 > 0) && (y2 <= 100))
{
sprintf(buf," %d %d",y2,p2/4096);
strcat(output,buf);
}
}
}
if(split <= 0)
{
sprintf(buf," 100 %d",p_end/4096);
strcat(output,buf);
}
strcat(output,"\n");
return(output);
} // end of SetPitch
#ifdef USE_MBROLA_LIB
static void MbrolaMarker(int type, int char_posn, int length, int value)
{//=====================================================================
MarkerEvent(type,(char_posn & 0xffffff) | (length << 24),value,outbuf);
}
static void MbrolaEmbedded(int &embix, int sourceix)
{//=================================================
// There were embedded commands in the text at this point
unsigned int word; // bit 7=last command for this word, bits 5,6 sign, bits 0-4 command
unsigned int value;
int command;
do {
word = embedded_list[embix++];
value = word >> 8;
command = word & 0x7f;
switch(command & 0x1f)
{
case EMBED_M: // named marker
MbrolaMarker(espeakEVENT_MARK, (sourceix & 0x7ff) + clause_start_char, 0, value);
break;
}
} while ((word & 0x80) == 0);
}
#ifdef PLATFORM_WINDOWS
int MbrolaSynth(char *p_mbrola)
{//============================
// p_mbrola is a string of mbrola pho lines - Windows
int len;
int finished;
int result=0;
if(synth_callback == NULL)
return(1);
if(p_mbrola == NULL)
flush_MBR();
else
result = write_MBR(p_mbrola);
finished = 0;
while(!finished && ((len = read_MBR((short *)outbuf, outbuf_size/2)) > 0))
{
out_ptr = outbuf + len*2;
if(event_list)
event_list[event_list_ix].type = espeakEVENT_LIST_TERMINATED; // indicates end of event list
count_samples += len;
finished = synth_callback((short *)outbuf, len, event_list);
event_list_ix=0;
}
if(finished)
{
// cancelled by user, discard any unused mbrola speech
flush_MBR();
while((len = read_MBR((short *)outbuf, outbuf_size/2)) > 0);
}
return(finished);
} // end of SynthMbrola
#else
int MbrolaSynth(char *p_mbrola)
{//============================
// p_mbrola is a string of mbrola pho lines - Linux
// This is wrong
// It must be called from WavegenFill()
int len;
int finished;
int result=0;
if(synth_callback == NULL)
return(1);
if(p_mbrola == NULL)
mbrolib_flush(mb_handle);
else
result = mbrolib_write(mb_handle,p_mbrola,strlen(p_mbrola));
finished = 0;
while(!finished && (mbrolib_read(mb_handle, (short *)out_ptr, (out_end - out_ptr)/2, &len) == MBROLIB_OK))
{
if(len == 0)
break;
out_ptr += (len*2);
if(event_list)
event_list[event_list_ix].type = espeakEVENT_LIST_TERMINATED; // indicates end of event list
count_samples += len;
finished = synth_callback((short *)outbuf, len, event_list);
event_list_ix=0;
}
if(finished)
{
// cancelled by user, discard any unused mbrola speech
mbrolib_flush(mb_handle);
while(mbrolib_read(mb_handle, (short *)outbuf, outbuf_size/2, &len) == MBROLIB_OK)
{
if(len == 0)
break;
}
}
return(finished);
} // end of SynthMbrola
#endif // not windows
#endif // USE_MBROLA_LIB
void MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, FILE *f_mbrola)
{//======================================================================
// Generate a mbrola pho file
int name;
int phix;
int len;
int len1;
PHONEME_TAB *ph;
PHONEME_TAB *ph_next;
PHONEME_TAB *ph_prev;
PHONEME_LIST *p;
PHONEME_LIST *next;
PHONEME_LIST *prev;
int pause = 0;
int released;
int name2;
int control;
int done;
int len_percent;
char buf[80];
char mbr_buf[120];
#ifdef USE_MBROLA_LIB
int embedded_ix=0;
int word_count=0;
event_list_ix = 0;
out_ptr = outbuf;
#ifdef PLATFORM_WINDOWS
setNoError_MBR(1); // don't stop on phoneme errors
#endif
#else
// fprintf(f_mbrola,";; v=%.2f\n",(float)(mbrola_control & 0xff)/16.0); // ;; v= has no effect on mbrola
#endif
for(phix=1; phix < n_phonemes; phix++)
{
mbr_buf[0] = 0;
p = &plist[phix];
next = &plist[phix+1];
prev = &plist[phix-1];
ph = p->ph;
ph_prev = plist[phix-1].ph;
ph_next = plist[phix+1].ph;
#ifdef USE_MBROLA_LIB
if(p->synthflags & SFLAG_EMBEDDED)
{
MbrolaEmbedded(embedded_ix, p->sourceix);
}
if(p->newword & 1)
MbrolaMarker(espeakEVENT_WORD, (p->sourceix & 0x7ff) + clause_start_char, p->sourceix >> 11, clause_start_word + word_count++);
#endif
name = GetMbrName(p,ph,ph_prev,ph_next,&name2,&len_percent,&control);
if(control & 1)
phix++;
if(name == 0)
continue; // ignore this phoneme
if(ph->type == phPAUSE)
{
name = '_';
len = (p->length * speed_factor1)/256;
if(len == 0) continue;
}
else
len = (70 * speed_factor2)/256;
sprintf(buf,"%s\t",WordToString(name));
strcat(mbr_buf,buf);
if(name2 == '_')
{
// add a pause after this phoneme
pause = PauseLength(len_percent);
name2 = 0;
}
done = 0;
switch(ph->type)
{
case phVOWEL:
len = ph->std_length;
if(p->synthflags & SFLAG_LENGTHEN)
len += phoneme_tab[phonLENGTHEN]->std_length; // phoneme was followed by an extra : symbol
if(ph_next->type == phPAUSE)
len += 50; // lengthen vowels before a pause
len = (len * p->length)/256;
if(name2 == 0)
{
sprintf(buf,"%d\t%s", len, WritePitch(p->env,p->pitch1,p->pitch2,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));
strcat(mbr_buf,buf);
sprintf(buf,"%s\t%d\t%s", WordToString(name2), len-len1, WritePitch(p->env,p->pitch1,p->pitch2,-len_percent));
strcat(mbr_buf,buf);
}
done = 1;
break;
case phSTOP:
released = 0;
if(next->type==phVOWEL) released = 1;
if(next->type==phLIQUID && !next->newword) released = 1;
if(released)
len = DoSample(p->ph,next->ph,2,0,-1);
else
len = DoSample(p->ph,phoneme_tab[phonPAUSE],2,0,-1);
len = (len * 1000)/samplerate; // convert to mS
len += PauseLength(p->prepause);
break;
case phVSTOP:
len = (80 * speed_factor2)/256;
break;
case phFRICATIVE:
len = 0;
if(p->synthflags & SFLAG_LENGTHEN)
len = DoSample(ph,ph_next,2,p->length,-1); // play it twice for [s:] etc.
len += DoSample(ph,ph_next,2,p->length,-1);
len = (len * 1000)/samplerate; // convert to mS
break;
case phNASAL:
if(next->type != phVOWEL)
{
len = DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE],2,p,-1);
len = (len * 1000)/samplerate;
}
break;
}
if(!done)
{
if(name2 != 0)
{
len1 = (len * len_percent)/100;
sprintf(buf,"%d\n%s\t",len1,WordToString(name2));
strcat(mbr_buf,buf);
len -= len1;
}
sprintf(buf,"%d\n",len);
strcat(mbr_buf,buf);
}
if(pause)
{
sprintf(buf,"_ \t%d\n",PauseLength(pause));
strcat(mbr_buf,buf);
pause = 0;
}
if(f_mbrola)
{
fwrite(mbr_buf,1,strlen(mbr_buf),f_mbrola); // write .pho to a file
}
else
{
#ifdef USE_MBROLA_LIB
if(MbrolaSynth(mbr_buf) != 0)
return;
#endif
}
}
#ifdef USE_MBROLA_LIB
MbrolaSynth(NULL);
#endif
} // end of SynthMbrola
#ifdef TEST_MBROLA
PHONEME_LIST mbrola_phlist;
int mbrola_n_ph;
int mbrola_phix;
int MbrolaFill(int fill_zeros)
{//===========================
}
int MbrolaGenerate(PHONEME_LIST *phoneme_list, int *n_ph, int resume)
{//==================================================================
if(resume == 0)
{
mbrola_phlist = phoneme_list;
mbrola_n_ph = n_ph;
mbrola_phix = 0;
}
resume(0); // finished phoneme list
}
#endif
+643
View File
@@ -0,0 +1,643 @@
/***************************************************************************
* Copyright (C) 2005,2006 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; 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 <ctype.h>
#include <wctype.h>
#include <string.h>
#include "speak_lib.h"
#include "speech.h"
#include "voice.h"
#include "phoneme.h"
#include "synthesize.h"
#include "translate.h"
#include "wave.h"
const char *version_string = "1.24.02 04.May.07";
const int version_phdata = 0x012201;
int option_device_number = -1;
// copy the current phoneme table into here
int n_phoneme_tab;
PHONEME_TAB *phoneme_tab[N_PHONEME_TAB];
unsigned char phoneme_tab_flags[N_PHONEME_TAB]; // bit 0: not inherited
unsigned int *phoneme_index=NULL;
char *spects_data=NULL;
unsigned char *wavefile_data=NULL;
static unsigned char *phoneme_tab_data = NULL;
int n_phoneme_tables;
PHONEME_TAB_LIST phoneme_tab_list[N_PHONEME_TABS];
static int phoneme_tab_number = 0;
int wavefile_ix; // a wavefile to play along with the synthesis
int wavefile_amp;
int wavefile_ix2;
int wavefile_amp2;
int seq_len_adjust;
int vowel_transition[4];
int vowel_transition0;
int vowel_transition1;
void FormantTransitions(frameref_t *seq, int &n_frames, PHONEME_TAB *this_ph, PHONEME_TAB *other_ph, int which);
int FormantTransition2(frameref_t *seq, int &n_frames, unsigned int data1, unsigned int data2, PHONEME_TAB *other_ph, int which);
const char *PhonemeTabName(void)
{//=============================
return(phoneme_tab_list[phoneme_tab_number].name);
}
static int ReadPhFile(char **ptr, const char *fname)
{//=================================================
FILE *f_in;
char *p;
unsigned int length;
char buf[200];
sprintf(buf,"%s%c%s",path_home,PATHSEP,fname);
length = GetFileLength(buf);
if((f_in = fopen(buf,"rb")) == NULL)
{
fprintf(stderr,"Can't read data file: '%s'\n",buf);
return(1);
}
if(*ptr != NULL)
Free(*ptr);
if((p = Alloc(length)) == NULL)
{
fclose(f_in);
return(-1);
}
if(fread(p,1,length,f_in) != length)
{
fclose(f_in);
return(-1);
}
*ptr = p;
fclose(f_in);
return(0);
} // end of ReadPhFile
int LoadPhData()
{//=============
int ix;
int n_phonemes;
int version;
int result = 1;
unsigned char *p;
if(ReadPhFile((char **)(&phoneme_tab_data),"phontab") != 0)
return(-1);
if(ReadPhFile((char **)(&phoneme_index),"phonindex") != 0)
return(-1);
if(ReadPhFile((char **)(&spects_data),"phondata") != 0)
return(-1);
wavefile_data = (unsigned char *)spects_data;
version = *((unsigned int *)spects_data);
if(version != version_phdata)
{
result = version;
}
// set up phoneme tables
p = phoneme_tab_data;
n_phoneme_tables = p[0];
p+=4;
for(ix=0; ix<n_phoneme_tables; ix++)
{
n_phonemes = p[0];
phoneme_tab_list[ix].n_phonemes = p[0];
phoneme_tab_list[ix].includes = p[1];
p += 4;
memcpy(phoneme_tab_list[ix].name,p,N_PHONEME_TAB_NAME);
p += N_PHONEME_TAB_NAME;
phoneme_tab_list[ix].phoneme_tab_ptr = (PHONEME_TAB *)p;
p += (n_phonemes * sizeof(PHONEME_TAB));
}
if(phoneme_tab_number >= n_phoneme_tables)
phoneme_tab_number = 0;
return(result);
} // end of LoadPhData
void FreePhData(void)
{//==================
Free(phoneme_tab_data);
Free(phoneme_index);
Free(spects_data);
}
int LookupPh(const char *string)
{//=============================
int ix;
unsigned char c;
unsigned int mnem;
// Pack up to 4 characters into a word
mnem = 0;
for(ix=0; ix<4; ix++)
{
if(string[ix]==0) break;
c = string[ix];
mnem |= (c << (ix*8));
}
for(ix=0; ix<n_phoneme_tab; ix++)
{
if(phoneme_tab[ix] == NULL)
continue;
if(phoneme_tab[ix]->mnemonic == mnem)
return(ix);
}
return(0);
}
static unsigned int LookupSound2(int index, unsigned int other_phcode, int control)
{//================================================================================
// control=1 get formant transition data only
int code;
unsigned int value, value2;
while((value = phoneme_index[index++]) != 0)
{
if((code = (value & 0xff)) == other_phcode)
{
while(((value2 = phoneme_index[index]) != 0) && ((value2 & 0xff) < 8))
{
switch(value2 & 0xff)
{
case 0:
// next entry is a wavefile to be played along with the synthesis
if(control==0)
{
wavefile_ix = value2 >> 8;
}
break;
case 1:
if(control==0)
seq_len_adjust = value2 >> 8;
break;
case 2:
if(control==0)
seq_len_adjust = -(value2 >> 8);
break;
case 3:
if(control==0)
{
wavefile_amp = value2 >> 8;
}
break;
case 4:
// formant transition data, 2 words
vowel_transition[0] = value2 >> 8;
vowel_transition[1] = phoneme_index[index++ + 1];
break;
case 5:
// formant transition data, 2 words
vowel_transition[2] = value2 >> 8;
vowel_transition[3] = phoneme_index[index++ + 1];
break;
}
index++;
}
return(value >> 8);
}
else
if((code == 4) || (code == 5))
{
// formant transition data, ignore next word of data
index++;
}
}
return(3); // not found
} // end of LookupSound2
unsigned int LookupSound(PHONEME_TAB *this_ph, PHONEME_TAB *other_ph, int which, int *match_level, int control)
{//============================================================================================================
// follows, 1 other_ph preceeds this_ph, 2 other_ph follows this_ph
// control: 1= get formant transition data only
int spect_list;
int spect_list2;
int s_list;
unsigned char virtual_ph;
int result;
int level=0;
unsigned int other_code;
unsigned int other_virtual;
if(control==0)
{
wavefile_ix = 0;
wavefile_amp = 32;
seq_len_adjust = 0;
}
memset(vowel_transition,0,sizeof(vowel_transition));
other_code = other_ph->code;
if(phoneme_tab[other_code]->type == phPAUSE)
other_code = phonPAUSE_SHORT; // use this version of Pause for matching
if(which==1)
{
spect_list = this_ph->after;
virtual_ph = this_ph->start_type;
spect_list2 = phoneme_tab[virtual_ph]->after;
other_virtual = other_ph->end_type;
}
else
{
spect_list = this_ph->before;
virtual_ph = this_ph->end_type;
spect_list2 = phoneme_tab[virtual_ph]->before;
other_virtual = other_ph->start_type;
}
result = 3;
// look for ph1-ph2 combination
if((s_list = spect_list) != 0)
{
if((result = LookupSound2(s_list,other_code,control)) != 3)
{
level = 2;
}
else
if(other_virtual != 0)
{
if((result = LookupSound2(spect_list,other_virtual,control)) != 3)
{
level = 1;
}
}
}
// not found, look in a virtual phoneme if one is given for this phoneme
if((result==3) && (virtual_ph != 0) && ((s_list = spect_list2) != 0))
{
if((result = LookupSound2(s_list,other_code,control)) != 3)
{
level = 1;
}
else
if(other_virtual != 0)
{
if((result = LookupSound2(spect_list2,other_virtual,control)) != 3)
{
level = 1;
}
}
}
if(match_level != NULL)
*match_level = level;
if(result==0)
return(0); // NULL was given in the phoneme source
// note: values = 1 indicates use the default for this phoneme, even though we found a match
// which set a secondary reference
if(result >= 4)
{
// values 1-3 can be used for special codes
// 1 = DFT from the phoneme source file
return(result);
}
// no match found for other_ph, return the default
return(LookupSound2(this_ph->spect,phonPAUSE,control));
} // end of LookupSound
frameref_t *LookupSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB *next_ph,
int which, int *match_level, int *n_frames, PHONEME_LIST *plist)
{//=========================================================================================================
int ix;
int nf;
int nf1;
int seq_break;
frameref_t *frames;
int length1;
int length_std;
int length_factor;
SPECT_SEQ *seq;
SPECT_SEQ *seq2;
PHONEME_TAB *next2_ph;
static frameref_t frames_buf[N_SEQ_FRAMES];
PHONEME_TAB *other_ph;
if(which == 1)
other_ph = prev_ph;
else
other_ph = next_ph;
if((ix = LookupSound(this_ph,other_ph,which,match_level,0)) < 4)
return(NULL);
seq = (SPECT_SEQ *)(&spects_data[ix]);
nf = seq->n_frames;
if(nf >= N_SEQ_FRAMES)
nf = N_SEQ_FRAMES - 1;
seq_break = 0;
length1 = 0;
for(ix=0; ix<nf; ix++)
{
frames_buf[ix].frame = &seq->frame[ix];
frames_buf[ix].frflags = seq->frame[ix].frflags;
frames_buf[ix].length = seq->frame[ix].length;
if(seq->frame[ix].frflags & FRFLAG_VOWEL_CENTRE)
seq_break = ix;
}
frames = &frames_buf[0];
if(seq_break > 0)
{
if(which==1)
{
nf = seq_break + 1;
}
else
{
frames = &frames_buf[seq_break]; // body of vowel, skip past initial frames
nf -= seq_break;
}
}
// do we need to modify a frame for blending with a consonant?
if(this_ph->type == phVOWEL)
{
if((which==2) && ((frames[nf-1].frflags & FRFLAG_BREAK) == 0))
{
// lookup formant transition for the following phoneme
if(*match_level == 0)
{
LookupSound(next_ph,this_ph,1,NULL,1);
seq_len_adjust += FormantTransition2(frames,nf,vowel_transition[2],vowel_transition[3],next_ph,which);
}
else
if(next_ph->phflags == phVOWEL2)
{
// not really a consonant, rather a coloured vowel
if(LookupSound(next_ph,this_ph,1,NULL,1) == 0)
{
next2_ph = plist[2].ph;
LookupSound(next2_ph,next_ph,1,NULL,1);
seq_len_adjust += FormantTransition2(frames,nf,vowel_transition[2],vowel_transition[3],next2_ph,which);
}
}
}
else
{
if(*match_level == 0)
seq_len_adjust = FormantTransition2(frames,nf,vowel_transition0,vowel_transition1,prev_ph,which);
}
// FormantTransitions(frames,nf,this_ph,other_ph,which);
}
nf1 = nf - 1;
for(ix=0; ix<nf1; ix++)
length1 += frames[ix].length;
if((wavefile_ix != 0) && ((wavefile_ix & 0x800000)==0))
{
// a secondary reference has been returned, which is not a wavefile
// add these spectra to the main sequence
seq2 = (SPECT_SEQ *)(&spects_data[wavefile_ix]);
// first frame of the addition just sets the length of the last frame of the main seq
nf--;
for(ix=0; ix<seq2->n_frames; ix++)
{
frames[nf].length = seq2->frame[ix].length;
if(ix > 0)
frames[nf].frame = &seq2->frame[ix];
nf++;
}
wavefile_ix = 0;
}
if((this_ph->type == phVOWEL) && (length1 > 0))
{
if(which==2)
{
// adjust the length of the main part to match the standard length specified for the vowel
// less the front part of the vowel and any added suffix
length_std = this_ph->std_length + seq_len_adjust - 45;
if(length_std < 10)
length_std = 10;
if(plist->synthflags & SFLAG_LENGTHEN)
length_std += phoneme_tab[phonLENGTHEN]->std_length; // phoneme was followed by an extra : symbol
// can adjust vowel length for stressed syllables here
length_factor = (length_std * 256)/ length1;
for(ix=0; ix<nf1; ix++)
{
frames[ix].length = (frames[ix].length * length_factor)/256;
}
}
else
{
// front of a vowel
if(*match_level == 0)
{
// allow very short vowels to have shorter front parts
if(this_ph->std_length < 130)
frames[0].length = (frames[0].length * this_ph->std_length)/130;
}
if(seq_len_adjust != 0)
{
length_std = 0;
for(ix=0; ix<nf1; ix++)
{
length_std += frames[ix].length;
}
length_factor = ((length_std + seq_len_adjust) * 256)/length_std;
for(ix=0; ix<nf1; ix++)
{
frames[ix].length = (frames[ix].length * length_factor)/256;
}
}
}
}
*n_frames = nf;
return(frames);
} // end of LookupSpect
unsigned char *LookupEnvelope(int ix)
{//================================
if(ix==0)
return(NULL);
return((unsigned char *)&spects_data[phoneme_index[ix]]);
}
static void SetUpPhonemeTable(int number, int recursing)
{//=====================================================
int ix;
int includes;
int ph_code;
PHONEME_TAB *phtab;
if(recursing==0)
{
memset(phoneme_tab_flags,0,sizeof(phoneme_tab_flags));
}
if((includes = phoneme_tab_list[number].includes) > 0)
{
// recursively include base phoneme tables
SetUpPhonemeTable(includes-1,1);
}
// now add the phonemes from this table
phtab = phoneme_tab_list[number].phoneme_tab_ptr;
for(ix=0; ix<phoneme_tab_list[number].n_phonemes; ix++)
{
ph_code = phtab[ix].code;
phoneme_tab[ph_code] = &phtab[ix];
if(ph_code > n_phoneme_tab)
n_phoneme_tab = ph_code;
if(recursing == 0)
phoneme_tab_flags[ph_code] |= 1; // not inherited
}
} // end of SetUpPhonemeTable
void SelectPhonemeTable(int number)
{//================================
n_phoneme_tab = 0;
SetUpPhonemeTable(number,0); // recursively for included phoneme tables
n_phoneme_tab++;
} // end of SelectPhonemeTable
int LookupPhonemeTable(const char *name)
{//=====================================
int ix;
for(ix=0; ix<n_phoneme_tables; ix++)
{
if(strcmp(name,phoneme_tab_list[ix].name)==0)
{
phoneme_tab_number = ix;
break;
}
}
if(ix == n_phoneme_tables)
return(-1);
return(ix);
}
int SelectPhonemeTableName(const char *name)
{//=========================================
// Look up a phoneme set by name, and select it if it exists
// Returns the phoneme table number
int ix;
if((ix = LookupPhonemeTable(name)) == -1)
return(-1);
SelectPhonemeTable(ix);
return(ix);
} // end of DelectPhonemeTableName
void LoadConfig(void)
{//==================
// Load configuration file, if one exists
char buf[130];
FILE *f;
int ix;
char c1;
char *p;
char string[120];
sprintf(buf,"%s%c%s",path_home,PATHSEP,"config");
if((f = fopen(buf,"r"))==NULL)
{
return;
}
while(fgets(buf,sizeof(buf),f)!=NULL)
{
if(memcmp(buf,"tone",4)==0)
{
ReadTonePoints(&buf[5],tone_points);
}
else
if(memcmp(buf,"pa_device",9)==0)
{
sscanf(&buf[7],"%d",&option_device_number);
}
else
if(memcmp(buf,"soundicon",9)==0)
{
ix = sscanf(&buf[10],"_%c %s",&c1,string);
if(ix==2)
{
soundicon_tab[n_soundicon_tab].name = c1;
p = Alloc(strlen(string+1));
strcpy(p,string);
soundicon_tab[n_soundicon_tab].filename = p;
soundicon_tab[n_soundicon_tab++].length = 0;
}
}
}
} // end of LoadConfig
+1509
View File
File diff suppressed because it is too large Load Diff
+241
View File
@@ -0,0 +1,241 @@
/***************************************************************************
* Copyright (C) 2005,2006 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#define N_PHONEME_LIST 700 // enough for source[] full of text, else it will truncate
#define MAX_HARMONIC 400 // 400 * 50Hz = 20 kHz, more than enough
#define N_SEQ_FRAMES 25 // max frames in a spectrum sequence (real max is ablut 8)
#define PITCHfall 0
#define PITCHrise 1
// flags set for frames within a spectrum sequence
#define FRFLAG_VOWEL_CENTRE 0x02 // centre point of vowel
#define FRFLAG_LEN_MOD 0x04 // reduce effect of length adjustment
#define FRFLAG_BREAK_LF 0x08 // but keep f3 upwards
#define FRFLAG_BREAK 0x10 // don't merge with next frame
#define FRFLAG_FORMANT_RATE 0x20 // Flag5 allow increased rate of change of formant freq
#define FRFLAG_MODULATE 0x40 // Flag6 modulate amplitude of some cycles to give trill
#define FRFLAG_DEFER_WAV 0x80 // Flag7 defer mixing WAV until the next frame
#define FRFLAG_COPIED 0x8000 // This frame has been copied into temporary rw memory
#define SFLAG_SEQCONTINUE 0x01 // a liquid or nasal after a vowel, but not followed by a vowel
#define SFLAG_EMBEDDED 0x02 // there are embedded commands before this phoneme
#define SFLAG_SYLLABLE 0x04 // vowel or syllabic consonant
#define SFLAG_LENGTHEN 0x08 // lengthen symbol : included after this phoneme
#define SFLAG_DICTIONARY 0x10 // the pronunciation of this word was listed in the xx_list dictionary
#define SFLAG_SWITCHED_LANG 0x20 // this word uses phonemes from a different language
// embedded command numbers
#define EMBED_P 1 // pitch
#define EMBED_S 2 // speed (used in setlengths)
#define EMBED_A 3 // amplitude/volume
#define EMBED_R 4 // pitch range/expression
#define EMBED_H 5 // echo/reverberation
#define EMBED_T 6 // different tone
#define EMBED_I 7 // sound icon
#define EMBED_S2 8 // speed (used in synthesize)
#define EMBED_Y 9 // say-as commands
#define EMBED_M 10 // mark name
#define EMBED_U 11 // audio uri
#define EMBED_B 12 // break
#define EMBED_F 13 // emphasis
#define N_EMBEDDED_VALUES 14
extern int embedded_value[N_EMBEDDED_VALUES];
extern int embedded_default[N_EMBEDDED_VALUES];
// formant data used by wavegen
typedef struct {
int freq; // Hz<<16
int height; // height<<15
int left; // Hz<<16
int right; // Hz<<16
DOUBLEX freq1; // floating point versions of the above
DOUBLEX height1;
DOUBLEX left1;
DOUBLEX right1;
DOUBLEX freq_inc; // increment by this every 64 samples
DOUBLEX height_inc;
DOUBLEX left_inc;
DOUBLEX right_inc;
} wavegen_peaks_t;
typedef struct {
short length;
unsigned char n_frames;
unsigned char flags;
frame_t frame[N_SEQ_FRAMES]; // max. frames in a spectrum sequence
} SPECT_SEQ;
typedef struct {
short length;
short frflags;
frame_t *frame;
} frameref_t;
typedef struct {
PHONEME_TAB *ph;
unsigned char env; // pitch envelope number
unsigned char tone;
unsigned char type;
unsigned char prepause;
unsigned char amp;
unsigned char tone_ph; // tone phoneme to use with this vowel
unsigned char newword; // bit 0=start of word, bit 1=end of clause, bit 2=start of sentence
unsigned char synthflags;
short length; // length_mod
short pitch1; // pitch, 0-4095 within the Voice's pitch range
short pitch2;
unsigned short sourceix; // ix into the original source text string, only set at the start of a word
} PHONEME_LIST;
typedef struct {
int name;
int length;
char *data;
char *filename;
} SOUND_ICON;
typedef struct {
int name;
unsigned int next_phoneme;
int mbr_name;
int mbr_name2;
int percent; // percentage length of first component
int control;
} MBROLA_TAB;
// phoneme table
extern PHONEME_TAB *phoneme_tab[N_PHONEME_TAB];
// list of phonemes in a clause
extern int n_phoneme_list;
extern PHONEME_LIST phoneme_list[N_PHONEME_LIST];
extern unsigned int embedded_list[];
extern unsigned char env_fall[128];
extern unsigned char env_rise[128];
extern unsigned char env_frise[128];
// queue of commands for wavegen
#define WCMD_AMPLITUDE 1
#define WCMD_PITCH 2
#define WCMD_SPECT 3
#define WCMD_SPECT2 4
#define WCMD_PAUSE 5
#define WCMD_WAVE 6
#define WCMD_WAVE2 7
#define WCMD_MARKER 8
#define WCMD_VOICE 9
#define WCMD_EMBEDDED 10
#define N_WCMDQ 160
#define MIN_WCMDQ 20 // need this many free entries before adding new phoneme
extern long wcmdq[N_WCMDQ][4];
extern int wcmdq_head;
extern int wcmdq_tail;
// from Wavegen file
int WcmdqFree();
void WcmdqStop();
int WcmdqUsed();
void WcmdqInc();
int WavegenOpenSound();
int WavegenCloseSound();
int WavegenInitSound();
void WavegenInit(int rate, int wavemult_fact);
int OpenWaveFile(const char *path, int rate);
void CloseWaveFile(int rate);
float polint(float xa[],float ya[],int n,float x);
int WavegenFile(void);
int WavegenFill(int fill_zeros);
void MarkerEvent(int type, unsigned int char_position, int value, unsigned char *out_ptr);
extern unsigned char *wavefile_data;
extern int samplerate;
extern int samplerate_native;
extern int wavefile_ix;
extern int wavefile_amp;
extern int wavefile_ix2;
extern int wavefile_amp2;
extern int vowel_transition[4];
extern int vowel_transition0, vowel_transition1;
extern char mbrola_name[20];
// from synthdata file
unsigned int LookupSound(PHONEME_TAB *ph1, PHONEME_TAB *ph2, int which, int *match_level, int control);
frameref_t *LookupSpect(PHONEME_TAB *ph1, PHONEME_TAB *prev_ph, PHONEME_TAB *next_ph, int which, int *match_level, int *n_frames, PHONEME_LIST *plist);
unsigned char *LookupEnvelope(int ix);
int LoadPhData();
void SynthesizeInit(void);
int Generate(PHONEME_LIST *phoneme_list, int *n_ph, int resume);
void MakeWave2(PHONEME_LIST *p, int n_ph);
int SynthOnTimer(void);
int SpeakNextClause(FILE *f_text, const void *text_in, int control);
int SynthStatus(void);
void SetSpeed(int control);
void SetEmbedded(int control, int value);
void SelectPhonemeTable(int number);
int SelectPhonemeTableName(const char *name);
extern unsigned char *envelope_data[16];
extern int formant_rate[]; // max rate of change of each formant
extern int speed_factor1;
extern int speed_factor2;
extern long count_samples;
extern int outbuf_size;
extern unsigned char *out_ptr;
extern unsigned char *out_start;
extern unsigned char *out_end;
extern int event_list_ix;
extern espeak_EVENT *event_list;
extern t_espeak_callback* synth_callback;
#define N_SOUNDICON_TAB 100
extern int n_soundicon_tab;
extern SOUND_ICON soundicon_tab[N_SOUNDICON_TAB];
espeak_ERROR SetVoiceByName(const char *name);
espeak_ERROR SetVoiceByProperties(espeak_VOICE *voice_selector);
espeak_ERROR LoadMbrolaTable(const char *mbrola_voice, const char *phtrans);
void SetParameter(int parameter, int value, int relative);
void MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, FILE *f_mbrola);
int MbrolaSynth(char *p_mbrola);
int DoSample(PHONEME_TAB *ph1, PHONEME_TAB *ph2, int which, int length_mod, int amp);
int DoSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB *next_ph,
int which, PHONEME_LIST *plist, int modulation);
int PauseLength(int pause);
+132
View File
@@ -0,0 +1,132 @@
/***************************************************************************
* Copyright (C) 2005,2006 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; 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 <ctype.h>
#include <wctype.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include "speak_lib.h"
#include "speech.h"
#include "phoneme.h"
#include "synthesize.h"
#include "translate.h"
#include "tr_languages.h"
Translator_English::Translator_English() : Translator()
{//===================================
// static int stress_lengths2[8] = {182,140, 220,220, 220,240, 248,270};
static int stress_lengths2[8] = {182,140, 220,220, 0,0, 248,275};
memcpy(stress_lengths,stress_lengths2,sizeof(stress_lengths));
langopts.vowel_pause = 0;
langopts.stress_rule = 0;
langopts.word_gap = 0;
langopts.numbers = 0x41;
}
static unsigned char initials_bitmap[86] = {
0x00, 0x00, 0x00, 0x00, 0x22, 0x08, 0x00, 0x88, // 0
0x20, 0x24, 0x20, 0x80, 0x10, 0x00, 0x00, 0x00,
0x00, 0x28, 0x08, 0x00, 0x88, 0x22, 0x04, 0x00, // 16
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x88, 0x22, 0x04, 0x00, 0x02, 0x00, 0x00, // 32
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x28, 0x8a, 0x03, 0x00, 0x00, 0x40, 0x00, // 48
0x02, 0x00, 0x41, 0xca, 0x9b, 0x06, 0x20, 0x80,
0x91, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x00, // 64
0x08, 0x20, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x22, 0x00, 0x01, 0x00, };
int Translator_English::Unpronouncable(char *word)
{//===============================================
/* Determines whether a word in 'unpronouncable', i.e. whether it should
be spoken as individual letters.
This function is language specific.
*/
unsigned char c;
int vowel_posn=9;
int index;
int ix;
int apostrophe=0;
// words which we pass through to the dictionary, even though they look unpronouncable
static const char *exceptions[] = {
"'s ", "st ","nd ","rd ","th ",NULL };
if((*word == ' ') || (*word == 0))
return(0);
for(ix=0; exceptions[ix] != NULL; ix++)
{
// Seemingly uncpronouncable words, but to be looked in the dictionary rules instead
if(memcmp(word,exceptions[ix],3)==0)
return(0);
}
index=0;
while(((c = word[index++]) != 0) && !isspace(c))
{
if(IsVowel(c) || (c == 'y'))
{
vowel_posn = index;
break;
}
if(c == '\'')
apostrophe = 1;
else
if((c < 'a') || (c > 'z'))
return(0); // letter (not vowel) outside a-z range or apostrophe, abort test
}
if((vowel_posn > 5) || ((word[0]!='s') && (vowel_posn > 4)))
return(1); // no vowel, or no vowel in first four letters
/* there is at least one vowel, is the initial letter combination valid ? */
if(vowel_posn < 3)
return(0); /* vowel in first two letters, OK */
if(apostrophe)
return(0); // first two letters not a-z, abort test
index = (word[0]-'a') * 26 + (word[1]-'a');
if(initials_bitmap[index >> 3] & (1L << (index & 7)))
return(0);
else
return(1); /****/
} /* end of Unpronounceable */
+854
View File
@@ -0,0 +1,854 @@
/***************************************************************************
* Copyright (C) 2005,2006 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; 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 <ctype.h>
#include <wctype.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <wctype.h>
#include "speak_lib.h"
#include "speech.h"
#include "phoneme.h"
#include "synthesize.h"
#include "translate.h"
#include "tr_languages.h"
#define L_qa 0x716100
#define OFFSET_GREEK 0x380
#define OFFSET_CYRILLIC 0x420
#define OFFSET_DEVANAGARI 0x900
void SetupTranslator(Translator *tr, int *lengths, int *amps)
{//==========================================================
if(lengths != NULL)
memcpy(tr->stress_lengths,lengths,sizeof(tr->stress_lengths));
if(amps != NULL)
memcpy(tr->stress_amps,amps,sizeof(tr->stress_amps));
}
Translator *SelectTranslator(const char *name)
{//===========================================
int name2 = 0;
Translator *tr;
// convert name string into a word of up to 4 characters, for the switch()
while(*name != 0)
name2 = (name2 << 8) + *name++;
switch(name2)
{
case L('a','f'):
tr = new Translator_Afrikaans();
break;
case L('c','y'): // Welsh
{
static int stress_lengths_cy[8] = {170,230, 190,190, 0, 0, 250,270};
static int stress_amps_cy[8] = {16,15, 18,18, 0,0, 24,22 }; // 'diminished' is used to mark a quieter, final unstressed syllable
tr = new Translator();
SetupTranslator(tr,stress_lengths_cy,stress_amps_cy);
tr->charset_a0 = charsets[14]; // ISO-8859-14
// tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
tr->langopts.stress_rule = 2;
// 'diminished' is an unstressed final syllable
tr->langopts.stress_flags = 0x6 | 0x10;
tr->langopts.word_gap = 0;
tr->langopts.vowel_pause = 0;
tr->langopts.unstressed_wd1 = 0;
tr->langopts.unstressed_wd2 = 2;
tr->langopts.param[LOPT_SONORANT_MIN] = 120; // limit the shortening of sonorants before short vowels
tr->langopts.numbers = 0x401;
SetLetterVowel(tr,'w'); // add letter to vowels and remove from consonants
SetLetterVowel(tr,'y');
}
break;
case L('d','e'):
{
static const int stress_lengths_de[8] = {150,130, 190,190, 0, 0, 260,275};
tr = new Translator();
tr->langopts.stress_rule = 0;
tr->langopts.word_gap = 0x2; // don't use linking phonemes
tr->langopts.vowel_pause = 0x30;
tr->langopts.param[LOPT_PREFIXES] = 1;
memcpy(tr->stress_lengths,stress_lengths_de,sizeof(tr->stress_lengths));
tr->langopts.numbers = 0x11c19;
SetLetterVowel(tr,'y');
}
break;
case L('e','n'):
tr = new Translator_English();
break;
case L('e','l'): // Greek
{
static int stress_lengths_el[8] = {155, 180, 210, 210, 0, 0, 270, 300};
static int stress_amps_el[8] = {15,12, 20,20, 20,24, 24,22 }; // 'diminished' is used to mark a quieter, final unstressed syllable
// character codes offset by 0x380
static const char el_vowels[] = {0x10,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x35,0x37,0x39,0x3f,0x45,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0};
static const char el_voiceless[]= {0x38,0x3a,0x3f,0x40,0x42,0x43,0x44,0x46,0x47,0};
static const char el_consonants[]={0x32,0x33,0x34,0x36,0x38,0x3a,0x3b,0x3c,0x3d,0x3e,0x40,0x41,0x42,0x43,0x44,0x46,0x47,0x48,0};
static const wchar_t el_char_apostrophe[] = {0x3c3,0}; // σ
tr = new Translator();
SetupTranslator(tr,stress_lengths_el,stress_amps_el);
tr->charset_a0 = charsets[7]; // ISO-8859-7
tr->char_plus_apostrophe = el_char_apostrophe;
tr->letter_bits_offset = OFFSET_GREEK;
memset(tr->letter_bits,0,sizeof(tr->letter_bits));
SetLetterBits(tr,LETTERGP_A,el_vowels);
SetLetterBits(tr,LETTERGP_B,el_voiceless);
SetLetterBits(tr,LETTERGP_C,el_consonants);
tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
tr->langopts.stress_rule = 2;
tr->langopts.stress_flags = 0x6; // mark unstressed final syllables as diminished
tr->langopts.word_gap = 0;
tr->langopts.vowel_pause = 0;
tr->langopts.unstressed_wd1 = 0;
tr->langopts.unstressed_wd2 = 2;
tr->langopts.param[LOPT_SONORANT_MIN] = 130; // limit the shortening of sonorants before short vowels
tr->langopts.numbers = 0xb09;
tr->langopts.numbers2 = 0x2; // variant form of numbers before thousands
}
break;
case L('e','o'):
{
static int stress_lengths_eo[8] = {145, 180, 200, 190, 0, 0, 300, 320};
static int stress_amps_eo[] = {16,14, 20,20, 20,24, 24,22 };
static const wchar_t eo_char_apostrophe[2] = {'l',0};
tr = new Translator();
SetupTranslator(tr,stress_lengths_eo,stress_amps_eo);
tr->charset_a0 = charsets[3]; // ISO-8859-3
tr->char_plus_apostrophe = eo_char_apostrophe;
tr->langopts.vowel_pause = 1;
tr->langopts.stress_rule = 2;
tr->langopts.stress_flags = 0x6 | 0x10;
// tr->langopts.stress_flags = 0x1; // don't give full stress to monosyllables
tr->langopts.unstressed_wd1 = 1;
tr->langopts.unstressed_wd2 = 2;
tr->langopts.numbers = 0x1409;
}
break;
case L('e','s'): // Spanish
{
static int stress_lengths_es[8] = {170, 200, 180, 180, 0, 0, 220, 250};
static int stress_amps_es[8] = {16,12, 18,18, 20,22, 22,22 }; // 'diminished' is used to mark a quieter, final unstressed syllable
tr = new Translator();
SetupTranslator(tr,stress_lengths_es,stress_amps_es);
tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
tr->langopts.stress_rule = 2;
// stress last syllable if it doesn't end in vowel or "s" or "n"
// 'diminished' is an unstressed final syllable
tr->langopts.stress_flags = 0x200 | 0x6 | 0x10;
tr->langopts.word_gap = 0;
tr->langopts.vowel_pause = 0;
tr->langopts.unstressed_wd1 = 0;
tr->langopts.unstressed_wd2 = 2;
tr->langopts.param[LOPT_SONORANT_MIN] = 120; // limit the shortening of sonorants before short vowels
tr->langopts.numbers = 0x529;
}
break;
case L('f','i'): // Finnish
{
static int stress_amps_fi[8] = {18,16, 22,22, 20,22, 22,22 };
static int stress_lengths_fi[8] = {150,170, 200,200, 0,0, 210,250};
tr = new Translator();
SetupTranslator(tr,stress_lengths_fi,stress_amps_fi);
tr->langopts.stress_rule = 0;
tr->langopts.stress_flags = 0x56; // move secondary stress from light to a following heavy syllable
tr->langopts.param[LOPT_IT_DOUBLING] = 1;
tr->langopts.long_stop = 140;
tr->langopts.numbers = 0x1009;
SetLetterVowel(tr,'y');
tr->langopts.max_initial_consonants = 2;
tr->langopts.spelling_stress = 1;
tr->punct_to_tone[0][1] = 0; // don't raise pitch for comma
// tr->punct_to_tone[0][2] = 0; // don't raise pitch for question
}
break;
case L('f','r'): // french
{
static int stress_lengths_fr[8] = {180, 180, 180, 180, 0, 0, 220, 220};
static int stress_amps_fr[8] = {16,14, 20,20, 20,24, 24,22 };
tr = new Translator();
SetupTranslator(tr,stress_lengths_fr,stress_amps_fr);
tr->langopts.stress_rule = 3; // stress on final syllable
tr->langopts.stress_flags = 0x20; // don't use secondary stress
tr->langopts.param[LOPT_IT_LENGTHEN] = 1; // remove lengthen indicator from unstressed syllables
tr->langopts.numbers = 0x1509;
SetLetterVowel(tr,'y');
}
break;
case L('h','i'):
{
static const char dev_consonants2[] = {0x02,0x03,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f};
static wchar_t replace_chars_hi[11] = {0x966,0x967,0x968,0x969,0x96a,0x96b,0x96c,0x96d,0x96e,0x96f,0}; // digits 0-9
static wchar_t replacement_chars_hi[11] = {0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0};
static int stress_lengths_hi[8] = {190, 190, 210, 210, 0, 0, 230, 250};
static int stress_amps_hi[8] = {17,14, 20,20, 20,24, 24,22 };
tr = new Translator();
SetupTranslator(tr,stress_lengths_hi,stress_amps_hi);
tr->charset_a0 = charsets[19]; // ISCII
tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
tr->langopts.stress_rule = 6; // stress on last heaviest syllable
tr->langopts.stress_flags = 0x4; // use 'diminished' for unstressed final syllable
tr->langopts.numbers = 0x811 + 0x40000;
tr->letter_bits_offset = OFFSET_DEVANAGARI;
tr->langopts.replace_chars = replace_chars_hi;
tr->langopts.replacement_chars = replacement_chars_hi;
SetLetterBitsRange(tr,LETTERGP_A,0x06,0x14); // vowel letters
SetLetterBitsRange(tr,LETTERGP_B,0x3e,0x4d); // vowel signs + virama
SetLetterBitsRange(tr,LETTERGP_C,0x15,0x39); // the main consonant range
SetLetterBits(tr,LETTERGP_C,dev_consonants2); // additional consonants
tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words
}
break;
case L('h','u'): // Hungarian
{
static int stress_amps_hu[8] = {16,16, 20,20, 20,24, 24,22 };
static int stress_lengths_hu[8] = {180,150, 200,180, 0,0, 230,270};
static wchar_t replace_chars_hu[] = {0xd4,0xf4,0xdb,0xfb,0};
static wchar_t replacement_chars_hu[] = {0x150,0x151,0x170,0x171,0}; // allow o,u-circumflex for o,u-double-acute
tr = new Translator();
SetupTranslator(tr,stress_lengths_hu,stress_amps_hu);
tr->charset_a0 = charsets[2]; // ISO-8859-2
tr->langopts.replace_chars = replace_chars_hu;
tr->langopts.replacement_chars = replacement_chars_hu;
tr->langopts.stress_rule = 0;
tr->langopts.stress_flags = 0x56; // move secondary stress from light to a following heavy syllable
tr->langopts.param[LOPT_REGRESSIVE_VOICING] = 0x4;
tr->langopts.param[LOPT_IT_DOUBLING] = 1;
tr->langopts.long_stop = 130;
tr->langopts.numbers = 0x1809;
SetLetterVowel(tr,'y');
tr->langopts.spelling_stress = 1;
}
break;
case L('i','t'): // Italian
{
static int stress_lengths_it[8] = {150, 140, 180, 180, 0, 0, 270, 320};
static int stress_amps_it[8] = {15,14, 19,19, 20,24, 24,22 };
tr = new Translator();
SetupTranslator(tr,stress_lengths_it,stress_amps_it);
tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
tr->langopts.stress_rule = 2;
tr->langopts.word_gap = 0;
tr->langopts.vowel_pause = 1;
tr->langopts.unstressed_wd1 = 2;
tr->langopts.unstressed_wd2 = 2;
tr->langopts.param[LOPT_IT_LENGTHEN] = 2; // remove lengthen indicator from unstressed or non-penultimate syllables
tr->langopts.param[LOPT_IT_DOUBLING] = 2;
tr->langopts.param[LOPT_SONORANT_MIN] = 130; // limit the shortening of sonorants before short vowels
tr->langopts.numbers = 0x2709;
}
break;
case L('n','l'):
{
static const int stress_lengths_nl[8] = {160,135, 210,210, 0, 0, 260,280};
tr = new Translator();
tr->langopts.stress_rule = 0;
tr->langopts.word_gap = 0;
tr->langopts.vowel_pause = 1;
tr->langopts.param[LOPT_DIERESES] = 1;
tr->langopts.param[LOPT_PREFIXES] = 1;
SetLetterVowel(tr,'y');
tr->langopts.numbers = 0x11419;
memcpy(tr->stress_lengths,stress_lengths_nl,sizeof(tr->stress_lengths));
}
break;
case L('n','o'): // Norwegian
{
static int stress_amps_no[] = {16,16, 20,20, 20,24, 24,22 };
static int stress_lengths_no[8] = {160,140, 200,200, 0,0, 250,270};
tr = new Translator();
SetupTranslator(tr,stress_lengths_no,stress_amps_no);
tr->langopts.stress_rule = 0;
SetLetterVowel(tr,'y');
}
break;
case L('p','l'): // Polish
{
static int stress_lengths_pl[8] = {160, 180, 175, 175, 0, 0, 260, 290};
static int stress_amps_pl[8] = {17,14, 19,19, 20,24, 24,22 }; // 'diminished' is used to mark a quieter, final unstressed syllable
tr = new Translator();
SetupTranslator(tr,stress_lengths_pl,stress_amps_pl);
tr->charset_a0 = charsets[2]; // ISO-8859-2
tr->langopts.stress_rule = 2;
tr->langopts.stress_flags = 0x6; // mark unstressed final syllables as diminished
tr->langopts.param[LOPT_REGRESSIVE_VOICING] = 0x8;
tr->langopts.max_initial_consonants = 7; // for example: wchrzczony :)
tr->langopts.numbers=0x81809 + 0x4000;
tr->langopts.param[LOPT_COMBINE_WORDS] = 2 + 0x100; // combine 'nie' (marked with $alt2) with some 1-syllable words (marked with $alt)
SetLetterVowel(tr,'y');
}
break;
case L('p','t'): // Portuguese
{
static int stress_lengths_pt[8] = {170, 120, 210, 210, 0, 0, 270, 295};
static int stress_amps_pt[8] = {16,13, 19,19, 20,24, 24,22 }; // 'diminished' is used to mark a quieter, final unstressed syllable
tr = new Translator();
SetupTranslator(tr,stress_lengths_pt,stress_amps_pt);
tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
// tr->langopts.vowel_pause = 1;
tr->langopts.stress_rule = 3; // stress on final syllable
tr->langopts.stress_flags = 0x6 | 0x10;
tr->langopts.numbers = 0xa69 + 0x2000;
}
break;
case L('r','o'): // Romanian
{
static int stress_lengths_ro[8] = {170, 170, 180, 180, 0, 0, 240, 260};
static int stress_amps_ro[8] = {15,13, 18,18, 20,22, 22,22 };
static wchar_t replace_chars_ro[5] = {0x218,0x219,0x21a,0x21b,0};
static wchar_t replacement_chars_ro[5] = {0x15e,0x15f,0x162,0x163,0}; // replace s-comma, t-comma by s-cedilla, t-cedilla
tr = new Translator();
SetupTranslator(tr,stress_lengths_ro,stress_amps_ro);
tr->langopts.stress_rule = 2;
tr->langopts.stress_flags = 0x100 + 0x6;
tr->charset_a0 = charsets[2]; // ISO-8859-2
tr->langopts.replace_chars = replace_chars_ro;
tr->langopts.replacement_chars = replacement_chars_ro;
tr->langopts.numbers = 0x1829+0x6000;
tr->langopts.numbers2 = 0x1e; // variant numbers before all thousandplex
}
break;
case L('r','u'): // Russian
tr = new Translator_Russian();
break;
case L('s','k'): // Slovak
case L('c','s'): // Czech
case L('h','r'): // Croatian
{
static int stress_amps_sk[8] = {16,16, 20,20, 20,24, 24,22 };
static int stress_lengths_sk[8] = {180,180, 200,180, 0,0, 220,230};
static char *sk_voiced = "bdgjlmnrvwzaeiouy";
tr = new Translator();
SetupTranslator(tr,stress_lengths_sk,stress_amps_sk);
tr->charset_a0 = charsets[2]; // ISO-8859-2
tr->langopts.stress_rule = 0;
tr->langopts.stress_flags = 0x16;
tr->langopts.param[LOPT_REGRESSIVE_VOICING] = 0x3;
tr->langopts.max_initial_consonants = 5;
tr->langopts.spelling_stress = 1;
tr->langopts.param[LOPT_COMBINE_WORDS] = 4; // combine some prepositions with the following word
tr->langopts.numbers = 0x1c0d + 0x84000;
if(name2 == L('h','r'))
tr->langopts.numbers2 = 0xa; // variant numbers before thousands,milliards
if(name2 == L('c','s'))
tr->langopts.numbers2 = 0x8; // variant numbers before milliards
SetLetterVowel(tr,'y');
SetLetterVowel(tr,'r');
SetLetterBits(tr,5,sk_voiced);
}
break;
case L('s','v'): // Swedish
{
static int stress_amps_sv[] = {16,16, 20,20, 20,24, 24,22 };
static int stress_lengths_sv[8] = {160,135, 220,220, 0,0, 250,280};
tr = new Translator();
SetupTranslator(tr,stress_lengths_sv,stress_amps_sv);
tr->langopts.stress_rule = 0;
SetLetterVowel(tr,'y');
// SetLetterBits(tr,6,"eiyäö"); // soft vowels NOTE accented letters don't work in SetLetterBits
tr->langopts.numbers = 0x1109;
}
break;
case L('s','w'): // Swahili
{
static int stress_lengths_sw[8] = {160, 170, 200, 200, 0, 0, 320, 340};
static int stress_amps_sw[] = {16,12, 19,19, 20,24, 24,22 };
tr = new Translator();
SetupTranslator(tr,stress_lengths_sw,stress_amps_sw);
tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
tr->langopts.vowel_pause = 1;
tr->langopts.stress_rule = 2;
tr->langopts.stress_flags = 0x6 | 0x10;
tr->langopts.numbers = 0x408e1;
}
break;
case L('t','r'): // Turkish
{
static int stress_amps_tr[8] = {16,16, 20,20, 20,24, 24,22 };
static int stress_lengths_tr[8] = {180,150, 200,180, 0,0, 230,270};
tr = new Translator();
SetupTranslator(tr,stress_lengths_tr,stress_amps_tr);
tr->charset_a0 = charsets[9]; // ISO-8859-9 - Latin5
tr->langopts.stress_rule = 3; // stress on the last syllable
tr->langopts.numbers = 0x1009;
tr->langopts.max_initial_consonants = 2;
}
break;
case L('v','i'): // Vietnamese
{
static int stress_lengths_vi[8] = {150, 150, 180, 180, 210, 220, 220, 280};
static int stress_amps_vi[] = {16,16, 16,16, 24,24, 24,22 };
static wchar_t vowels_vi[] = {
0x61, 0xe0, 0xe1, 0x1ea3, 0xe3, 0x1ea1, // a
0x103, 0x1eb1, 0x1eaf, 0x1eb3, 0x1eb5, 0x1eb7, // ă
0xe2, 0x1ea7, 0x1ea5, 0x1ea9, 0x1eab, 0x1ead, // â
0x65, 0xe8, 0xe9, 0x1ebb, 0x1ebd, 0x1eb9, // e
0xea, 0x1ec1, 0x1ebf, 0x1ec3, 0x1ec5, 0x1ec7, // i
0x69, 0xec, 0xed, 0x1ec9, 0x129, 0x1ecb, // i
0x6f, 0xf2, 0xf3, 0x1ecf, 0xf5, 0x1ecd, // o
0xf4, 0x1ed3, 0x1ed1, 0x1ed5, 0x1ed7, 0x1ed9, // ô
0x1a1, 0x1edd, 0x1edb, 0x1edf, 0x1ee1, 0x1ee3, // ơ
0x75, 0xf9, 0xfa, 0x1ee7, 0x169, 0x1ee5, // u
0x1b0, 0x1eeb, 0x1ee9, 0x1eed, 0x1eef, 0x1ef1, // ư
0x79, 0x1ef3, 0xfd, 0x1ef7, 0x1ef9, 0x1e, 0 }; // y
tr = new Translator();
SetupTranslator(tr,stress_lengths_vi,stress_amps_vi);
tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
tr->langopts.stress_rule = 0;
tr->langopts.word_gap = 0x4; // length of a final vowel is less dependent on the next consonant
tr->langopts.vowel_pause = 4;
tr->letter_groups[0] = vowels_vi;
// tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words
tr->langopts.intonation = 1; // Tone language, use CalcPitches_Tone() rather than CalcPitches()
tr->langopts.unstressed_wd1 = 2;
}
break;
case L('z','h'):
case L_qa + 'a': // Test qaa
{
static int stress_lengths_qaa[8] = {200,200, 248,248, 248,0, 248,250};
static int stress_amps_qaa[] = {16,16, 20,20, 24,24, 24,22 };
tr = new Translator();
SetupTranslator(tr,stress_lengths_qaa,stress_amps_qaa);
tr->langopts.stress_rule = 0;
tr->langopts.vowel_pause = 0;
tr->langopts.intonation = 1; // Tone language, use CalcPitches_Tone() rather than CalcPitches()
tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
tr->langopts.tone_numbers = 9;
tr->langopts.ideographs = 1;
tr->langopts.word_gap = 0x4; // length of a final vowel is less dependent on the next consonant
}
break;
default:
tr = new Translator();
break;
}
tr->translator_name = name2;
if(tr->langopts.numbers & 0x8)
{
// use . and ; for thousands and decimal separators
tr->langopts.thousands_sep = '.';
tr->langopts.decimal_sep = ',';
}
if(tr->langopts.numbers & 0x4)
{
tr->langopts.thousands_sep = 0; // don't allow thousands separator, except space
}
return(tr);
} // end of SelectTranslator
//**********************************************************************************************************
Translator_Russian::Translator_Russian() : Translator()
{//===================================
static int stress_amps_ru[] = {16,16, 18,18, 20,24, 24,22 };
static int stress_lengths_ru[8] = {150,140, 220,220, 0,0, 260,280};
// character codes offset by 0x420
static const char ru_vowels[] = {0x10,0x15,0x31,0x18,0x1e,0x23,0x2b,0x2d,0x2e,0x2f,0};
static const char ru_consonants[] = {0x11,0x12,0x13,0x14,0x16,0x17,0x19,0x1a,0x1b,0x1c,0x1d,0x1f,0x20,0x21,0x22,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2c,0};
static const char ru_soft[] = {0x2c,0x19,0x27,0x29,0}; // letter group B [k ts; s;]
static const char ru_hard[] = {0x2a,0x16,0x26,0x28,0}; // letter group H [S Z ts]
static const char ru_nothard[] = {0x11,0x12,0x13,0x14,0x17,0x19,0x1a,0x1b,0x1c,0x1d,0x1f,0x20,0x21,0x22,0x24,0x25,0x27,0x29,0x2c,0};
static const char ru_voiced[] = {0x11,0x12,0x13,0x14,0x16,0x17,0}; // letter group G (voiced obstruents)
static const char ru_ivowels[] = {0x2c,0x15,0x31,0x18,0x2e,0x2f,0}; // letter group Y (iotated vowels & soft-sign)
// these are unicode character codes
static const wchar_t ru_L08[] = {0x43a,0x43f,0x441,0x442,0x444,0x445,0}; // кпстфх
static const wchar_t ru_L09[] = {0x431,0x433,0x434,0x436,0x43b,0x43c,0x43d,0x440,0}; // бгджзлмнр
SetupTranslator(this,stress_lengths_ru,stress_amps_ru);
charset_a0 = charsets[18]; // KOI8-R
transpose_offset = 0x42f; // convert cyrillic from unicode into range 0x01 to 0x22
transpose_min = 0x430;
transpose_max = 0x451;
letter_bits_offset = OFFSET_CYRILLIC;
memset(letter_bits,0,sizeof(letter_bits));
SetLetterBits(this,0,ru_vowels);
SetLetterBits(this,1,ru_soft);
SetLetterBits(this,2,ru_consonants);
SetLetterBits(this,3,ru_hard);
SetLetterBits(this,4,ru_nothard);
SetLetterBits(this,5,ru_voiced);
SetLetterBits(this,6,ru_ivowels);
SetLetterBits(this,7,ru_vowels);
letter_groups[8] = ru_L08; // This is L08 in ru_rules
letter_groups[9] = ru_L09; // This is L09 in ru_rules
langopts.param[LOPT_UNPRONOUNCABLE] = 0x432; // [v] don't count this character at start of word
langopts.param[LOPT_REGRESSIVE_VOICING] = 1;
langopts.param[LOPT_KEEP_UNSTR_VOWEL] = 1;
langopts.stress_rule = 5;
langopts.stress_flags = 0x1020;
langopts.numbers = 0x409;
langopts.phoneme_change = 1;
langopts.testing = 2;
} // end of Translator_Russian
#define PH(c1,c2) (c2<<8)+c1 // combine two characters into an integer for phoneme name
#define PY(c1,c2,c3) (c3<<16)+(c2<<8)+c1
/*
typedef struct {
int flags;
unsigned char stress; // stress level of this vowel
unsigned char stress_highest; // the highest stress level of a vowel in this word
unsigned char n_vowels; // number of vowels in the word
unsigned char vowel_this; // syllable number of this vowel (counting from 1)
unsigned char vowel_stressed; // syllable number of the highest stressed vowel
} CHANGEPH;
*/
#define RUSSIAN2
#ifdef RUSSIAN2
int Translator_Russian::ChangePhonemes(PHONEME_LIST2 *phlist, int n_ph, int index, PHONEME_TAB *ph, CHANGEPH *ch)
{//==============================================================================================================
// Called for each phoneme in the phoneme list, to allow a language to make changes
// flags: bit 0=1 last phoneme in a word
// bit 1=1 this is the highest stressed vowel in the current word
// bit 2=1 after the highest stressed vowel in the current word
// bit 3=1 the phonemes were specified explicitly, or found from an entry in the xx_list dictionary
// bits 8-15 syllable number, 1=first syllable, etc.
// ph The current phoneme
int variant;
int vowelix;
PHONEME_TAB *prev, *next;
if(ch->flags & 8)
return(0); // full phoneme translation has already been given
// Russian vowel softening and reduction rules
if(ph->type == phVOWEL)
{
int prestressed = ch->vowel_stressed==ch->vowel_this+1; // the next vowel after this has the main stress
#define N_VOWELS_RU 11
static unsigned int vowels_ru[N_VOWELS_RU] = {'a','V','O','I',PH('I','#'),PH('E','#'),PH('E','2'),
PH('V','#'),PH('I','3'),PH('I','2'),PH('E','3')};
static unsigned int vowel_replace[N_VOWELS_RU][6] = {
// stressed, soft, soft-stressed, j+stressed, j+soft, j+soft-stressed
/*0*/ {'A', 'I', PH('j','a'), 'a', 'a', 'a'}, // a Uses 3,4,5 columns.
/*1*/ {'A', 'V', PH('j','a'), 'a', 'V', 'a'}, // V Uses 3,4,5 columns.
/*2*/ {'o', '8', '8', 'o', '8', '8'}, // O
/*3*/ {'i', 'I', 'i', 'a', 'I', 'a'}, // I Uses 3,4,5 columns.
/*4*/ {'i', PH('I','#'), 'i', 'i', PH('I','#'), 'i'}, // I#
/*5*/ {'E', PH('E','#'), 'E', 'e', PH('E','#'), 'e'}, // E#
/*6*/ {'E', PH('E','2'), 'E', 'e', PH('E','2'), 'e'}, // E2 Uses 3,4,5 columns.
/*7*/ {'i', 'V', 'i', 'A', 'V', 'A'}, // V#
/*8*/ {PH('j','a'), 'I', PH('j','a'), 'e', 'I', 'e'}, // I3 Uses 3,4,5 columns.
/*9*/ {'e', 'I', 'e', 'e', 'I', 'e'}, // I2
/*10*/ {'e', PH('E', '2'), 'e', 'e', PH('E','2'), 'e'} // E3
};
prev = phoneme_tab[phlist[index-1].phcode];
next = phoneme_tab[phlist[index+1].phcode];
// lookup the vowel name to get an index into the vowel_replace[] table
for(vowelix=0; vowelix<N_VOWELS_RU; vowelix++)
{
if(vowels_ru[vowelix] == ph->mnemonic)
break;
}
if(vowelix == N_VOWELS_RU)
return(0);
if(prestressed)
{
if((vowelix==6)&&(prev->mnemonic=='j'))
vowelix=8;
if(vowelix==1)
vowelix=0;
if(vowelix==4)
vowelix=3;
if(vowelix==6)
vowelix=5;
if(vowelix==7)
vowelix=8;
if(vowelix==10)
vowelix=9;
}
// do we need a variant of this vowel, depending on the stress and adjacent phonemes ?
variant = -1;
int stressed = ch->flags & 2;
int soft=prev->phflags & phPALATAL;
if (soft && stressed)
variant = 2; else
if (stressed)
variant = 0; else
if (soft)
variant = 1;
if(variant >= 0)
{
if(prev->mnemonic == 'j')
variant += 3;
phlist[index].phcode = PhonemeCode(vowel_replace[vowelix][variant]);
}
else
{
phlist[index].phcode = PhonemeCode(vowels_ru[vowelix]);
}
}
return(0);
}
#else
int Translator_Russian::ChangePhonemes(PHONEME_LIST2 *phlist, int n_ph, int index, PHONEME_TAB *ph, CHANGEPH *ch)
{//===============================================================================================================
// Called for each phoneme in the phoneme list, to allow a language to make changes
// flags: bit 0=1 last phoneme in a word
// bit 1=1 this is the highest stressed vowel in the current word
// bit 2=1 after the highest stressed vowel in the current word
// bit 3=1 the phonemes were specified explicitly, or found from an entry in the xx_list dictionary
// ph The current phoneme
int variant;
int vowelix;
PHONEME_TAB *prev, *next;
if(ch->flags & 8)
return(0); // full phoneme translation has already been given
// Russian vowel softening and reduction rules
if(ph->type == phVOWEL)
{
#define N_VOWELS_RU 7
static unsigned char vowels_ru[N_VOWELS_RU] = {'a','A','o','E','i','u','y'};
// each line gives: soft, reduced, soft-reduced, post-tonic
static unsigned short vowel_replace[N_VOWELS_RU][4] = {
{'&', 'V', 'I', 'V'}, // a
{'&', 'V', 'I', 'V'}, // A
{'8', 'V', 'I', 'V'}, // o
{'e', 'I', 'I', 'I'}, // E
{'i', 'I', 'I', 'I'}, // i
{'u'+('"'<<8), 'U', 'U', 'U'}, // u
{'y', 'Y', 'Y', 'Y'}}; // y
prev = phoneme_tab[phlist[index-1].phcode];
next = phoneme_tab[phlist[index+1].phcode];
if(prev->mnemonic == 'j')
return(0);
// lookup the vowel name to get an index into the vowel_replace[] table
for(vowelix=0; vowelix<N_VOWELS_RU; vowelix++)
{
if(vowels_ru[vowelix] == ph->mnemonic)
break;
}
if(vowelix == N_VOWELS_RU)
return(0);
// do we need a variant of this vowel, depending on the stress and adjacent phonemes ?
variant = -1;
if(ch->flags & 2)
{
// a stressed vowel
if((prev->phflags & phPALATAL) && ((next->phflags & phPALATAL) || phoneme_tab[phlist[index+2].phcode]->mnemonic == ';'))
{
// between two palatal consonants, use the soft variant
variant = 0;
}
}
else
{
// an unstressed vowel
if(prev->phflags & phPALATAL)
{
variant = 2; // unstressed soft
}
else
if((ph->mnemonic == 'o') && ((prev->phflags & phPLACE) == phPLACE_pla))
{
variant = 2; // unstressed soft ([o] vowel following: ш ж
}
else
if(ch->flags & 4)
{
variant = 3; // post tonic
}
else
{
variant = 1; // unstressed
}
}
if(variant >= 0)
{
phlist[index].phcode = PhonemeCode(vowel_replace[vowelix][variant]);
}
}
return(0);
}
#endif
//**********************************************************************************************************
Translator_Afrikaans::Translator_Afrikaans() : Translator()
{//=========================================
// Initialise options for this language
static const int stress_lengths2[8] = {170,140, 220,220, 0, 0, 250,270};
langopts.stress_rule = 0;
langopts.word_gap = 0;
langopts.vowel_pause = 0x30;
langopts.param[LOPT_DIERESES] = 1;
langopts.param[LOPT_PREFIXES] = 1;
SetLetterVowel(this,'y'); // add 'y' to vowels
langopts.numbers = 0x0d1;
memcpy(stress_lengths,stress_lengths2,sizeof(stress_lengths));
}
int Translator_Afrikaans::TranslateChar(char *ptr, int prev_in, int c, int next_in)
{//===============================================================================
// look for 'n and replace by a special character (unicode: schwa)
if(!iswalpha(prev_in))
{
if((c == '\'') && (next_in == 'n'))
{
// n preceded by either apostrophe or U2019 "right single quotation mark"
ptr[0] = ' '; // delete the n
return(0x0259); // replace ' by unicode schwa character
}
}
return(c);
}
+58
View File
@@ -0,0 +1,58 @@
/***************************************************************************
* Copyright (C) 2005,2006 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
class Translator_English: public Translator
{//=======================================
public:
Translator_English();
int Unpronouncable(char *word);
}; // end of class Translator_English
class Translator_Russian: public Translator
{//=======================================
public:
Translator_Russian();
private:
int ChangePhonemes(PHONEME_LIST2 *phlist, int n_ph, int index, PHONEME_TAB *ph, CHANGEPH *ch);
}; // end of class Translator_Russian
class Translator_Afrikaans: public Translator
{//==========================================
public:
Translator_Afrikaans();
private:
int TranslateChar(char *ptr, int prev_in, int c, int next_in);
}; // end of class Translator_Afrikaans
+2150
View File
File diff suppressed because it is too large Load Diff
+509
View File
@@ -0,0 +1,509 @@
/***************************************************************************
* Copyright (C) 2005,2006 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#define L(c1,c2) (c1<<8)+c2 // combine two characters into an integer for translator name
#define CTRL_EMBEDDED 0x01 // control character at the start of an embedded command
#define REPLACED_E 'E' // 'e' replaced by silent e
#define N_WORD_PHONEMES 160 // max phonemes in a word
#define N_WORD_BYTES 160 // max bytes for the UTF8 characters in a word
#define N_CLAUSE_WORDS 256 // max words in a clause
#define N_RULE_GROUP2 120 // max num of two-letter rule chains
#define N_HASH_DICT 1024
#define N_CHARSETS 20
/* flags from word dictionary */
// bits 0-3 stressed syllable, 7=unstressed
#define FLAG_SKIPWORDS 0xe0 /* bits 5,6,7 number of words to skip */
#define FLAG_SKIPWORDS_1 0x20
#define FLAG_PREPAUSE 0x100
#define FLAG_ONLY 0x200
#define BITNUM_FLAG_ONLY 9 // bit 9 is set
#define BITNUM_FLAG_ONLY_S 10 // bit 10 is set
#define FLAG_ONLY_S 0x400
#define FLAG_STRESS_END 0x800 /* full stress if at end of clause */
#define FLAG_STRESS_END2 0x1000 /* full stress if at end of clause, or only followed by unstressed */
#define FLAG_UNSTRESS_END 0x2000 /* reduce stress at end of clause */
#define FLAG_ATEND 0x4000 /* use this pronunciation if at end of clause */
#define FLAG_CAPITAL 0x8000 /* pronunciation if initial letter is upper case */
#define FLAG_DOT 0x10000 /* ignore '.' after word (abbreviation) */
#define FLAG_ABBREV 0x20000 /* use this pronunciation rather than split into letters */
#define FLAG_STEM 0x40000 // must have a suffix
#define FLAG_DOUBLING 0x80000 // doubles the following consonant
#define FLAG_ALT_TRANS 0x100000 // language specific
#define FLAG_ALT2_TRANS 0x200000 // language specific
#define FLAG_VERBF 0x400000 /* verb follows */
#define FLAG_VERBSF 0x800000 /* verb follows, may have -s suffix */
#define FLAG_NOUNF 0x1000000 /* noun follows */
#define FLAG_VERB 0x2000000 /* pronunciation for verb */
#define FLAG_PAST 0x4000000 /* pronunciation for past tense */
#define FLAG_PASTF 0x8000000 /* past tense follows */
#define FLAG_VERB_EXT 0x10000000 /* extend the 'verb follows' */
#define FLAG_PAUSE1 0x40000000 // shorter prepause
#define FLAG_FOUND 0x80000000 /* pronunciation was found in the dictionary list */
// wordflags, flags in source word
#define FLAG_ALL_UPPER 0x1 /* no lower case letters in the word */
#define FLAG_FIRST_UPPER 0x2 /* first letter is upper case */
#define FLAG_HAS_PLURAL 0x4 /* upper-case word with s or 's lower-case ending */
#define FLAG_PHONEMES 0x8 /* word is phonemes */
#define FLAG_LAST_WORD 0x10 /* last word in clause */
#define FLAG_STRESSED_WORD 0x20 /* this word has explicit stress */
#define FLAG_EMBEDDED 0x40 /* word is preceded by embedded commands */
#define FLAG_HYPHEN 0x80
#define FLAG_DONT_SWITCH_TRANSLATOR 0x1000
#define FLAG_SUFFIX_REMOVED 0x2000
#define FLAG_HYPHEN_AFTER 0x4000
// prefix/suffix flags (bits 8 to 14, bits 16 to 22) don't use 0x8000, 0x800000
#define SUFX_E 0x0100 // e may have been added
#define SUFX_I 0x0200 // y may have been changed to i
#define SUFX_P 0x0400 // prefix
#define SUFX_V 0x0800 // suffix means use the verb form pronunciation
#define SUFX_D 0x1000 // previous letter may have been doubles
#define SUFX_F 0x2000 // verb follows
#define SUFX_Q 0x4000 // don't retranslate
#define SUFX_T 0x10000 // don't affect the stress position in the stem
#define FLAG_SUFX 0x04
#define FLAG_SUFX_S 0x08
#define FLAG_SUFX_E_ADDED 0x10
// codes in dictionary rules
#define RULE_PRE 1
#define RULE_POST 2
#define RULE_PHONEMES 3
#define RULE_PH_COMMON 4 // At start of rule. Its phoneme string is used by subsequent rules
#define RULE_CONDITION 5 // followed by condition number (byte)
#define RULE_GROUP_START 6
#define RULE_GROUP_END 7
#define RULE_SPACE 32 // ascii space
#define RULE_SYLLABLE 9
#define RULE_STRESSED 10
#define RULE_DOUBLE 11
#define RULE_INC_SCORE 12
#define RULE_DEL_FWD 13
#define RULE_ENDING 14
#define RULE_DIGIT 15 // D digit
#define RULE_NONALPHA 16 // Z non-alpha
#define RULE_LETTER_GROUPS 17 // 17 to 23
#define RULE_LETTER1 17 // A vowels
#define RULE_LETTER2 18 // B 'hard' consonants
#define RULE_LETTER3 19 // C all consonants
#define RULE_LETTER4 20 // H letter group
#define RULE_LETTER5 21 // F letter group
#define RULE_LETTER6 22 // G letter group
#define RULE_LETTER7 23 // Y letter group
#define RULE_NO_SUFFIX 24 // N
#define RULE_NOTVOWEL 25 // K
#define RULE_IFVERB 26 // V
#define RULE_LETTERGP 27 // L + letter group number
#define RULE_ALT1 28 // word has $alt attribute
#define RULE_LAST_RULE 28
#define LETTERGP_A 0
#define LETTERGP_B 1
#define LETTERGP_C 2
#define LETTERGP_H 3
#define LETTERGP_F 4
#define LETTERGP_G 5
#define LETTERGP_Y 6
#define LETTERGP_VOWEL2 7
// Punctuation types returned by ReadClause()
// bits 0-7 pause x 10mS, bits 8-10 intonation type,
// bit 15=sentence, bit 14=clause, bits 13=voice change
// bit 12 used to distinguish otherwise identical types
#define CLAUSE_BIT_SENTENCE 0x8000
#define CLAUSE_BIT_VOICE 0x2000
#define CLAUSE_NONE 0 + 0x0400
#define CLAUSE_PARAGRAPH 70 + 0x8000
#define CLAUSE_EOF 35 + 0x9000
#define CLAUSE_VOICE 0 + 0x2400
#define CLAUSE_PERIOD 35 + 0x8000
#define CLAUSE_COMMA 20 + 0x4100
#define CLAUSE_SHORTCOMMA 15 + 0x4100
#define CLAUSE_QUESTION 35 + 0x8200
#define CLAUSE_EXCLAMATION 40 + 0x8300
#define CLAUSE_COLON 30 + 0x4000
#ifdef PLATFORM_RISCOS
#define CLAUSE_SEMICOLON 30 + 0x4000
#else
#define CLAUSE_SEMICOLON 30 + 0x4100
#endif
#define SAYAS_CHARS 0x12
#define SAYAS_GLYPHS 0x13
#define SAYAS_SINGLE_CHARS 0x14
#define SAYAS_KEY 0x20
#define SAYAS_DIGITS1 0x21
#define SAYAS_DIGITS 0x30 // + number of digits
// Rule:
// [4] [match] [1 pre] [2 post] [3 phonemes] 0
// match 1 pre 2 post 0 - use common phoneme string
// match 1 pre 2 post 3 0 - empty phoneme string
typedef struct {
int points;
const char *phonemes;
int end_type;
char *del_fwd;
} MatchRecord;
// used to mark words with the source[] buffer
typedef struct{
unsigned short start;
unsigned short sourceix;
unsigned short flags;
unsigned char pre_pause;
unsigned char wmark;
unsigned char length;
} WORD_TAB;
// a clause translated into phoneme codes (first stage)
typedef struct {
unsigned char phcode;
unsigned char stress;
unsigned char tone_number;
unsigned char synthflags;
unsigned short sourceix;
} PHONEME_LIST2;
typedef struct {
int type;
int parameter[N_SPEECH_PARAM];
} PARAM_STACK;
extern PARAM_STACK param_stack[];
extern const int param_defaults[N_SPEECH_PARAM];
#define N_LOPTS 12
#define LOPT_DIERESES 1
// 1=remove [:] from unstressed syllables, 2= remove from unstressed or non-penultimate syllables
#define LOPT_IT_LENGTHEN 2
// 1=german
#define LOPT_PREFIXES 3
// non-zero, change voiced/unoiced to match last consonant in a cluster
// bit 1=LANG=ru, don't propagate over [v]
// bit 2=don't propagate acress word boundaries
// bit 3=LANG=pl, propagate over liquids and nasals
#define LOPT_REGRESSIVE_VOICING 4
// 0=default, 1=no check, other allow this character as an extra initial letter (default is 's')
#define LOPT_UNPRONOUNCABLE 5
// select length_mods tables, (length_mod_tab) + (length_mod_tab0 * 100)
#define LOPT_LENGTH_MODS 6
// increase this to prevent sonorants being shortened before shortened (eg. unstressed) vowels
#define LOPT_SONORANT_MIN 7
// Italian "syntactic doubling"
#define LOPT_IT_DOUBLING 8
// max. amplitude for vowel at the end of a clause
#define LOPT_MAXAMP_EOC 9
// don't reduce the strongest vowel in a word which is marked 'unstressed'
#define LOPT_KEEP_UNSTR_VOWEL 10
// LANG=cs,sk combine some prepositions with the following word, if the combination has N or fewer syllables
#define LOPT_COMBINE_WORDS 11
typedef struct {
// bit0=don't link consonants with the next word
// bit1=don't add linking phonemes
// bit2=length of a final vowel doesn't depend on the next phoneme
// bit4=longer pause before STOP, VSTOP,FRIC
// bit8-11=separate words with adjacent stressed syllables with (1=pause, 2=long pausem 3=[?] phoneme)
int word_gap;
int vowel_pause;
int stress_rule; // 1=first syllable, 2=penultimate, 3=last
// bit0=don't stress monosyllables,
// bit1=don't set diminished stress,
// bit2=mark unstressed final syllables as diminished
// 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
// 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 2-syllable words, give the first syllable a secondary stress
int stress_flags;
int unstressed_wd1; // stress for $u word of 1 syllable
int unstressed_wd2; // stress for $u word of >1 syllable
int param[N_LOPTS];
unsigned char *length_mods;
unsigned char *length_mods0;
// bits0-1=which numbers routine to use.
// bit2= thousands separator must be space
// bit3= , decimal separator, not .
// bit4=use three-and-twenty rather than twenty-three
// bit5='and' between tens and units
// bit6=add "and" after hundred and thousand
// bit7=don't have "and" both after hundreds and also between tens and units
// bit8=only one primary stress in tens+units
// bit9=only one vowel betwen tens and units
// bit10=omit "one" before "hundred"
// bit11=don't say 19** as nineteen hundred
// bit12=allow space as thousands separator (in addition to langopts.thousands_sep)
// bits13-15 post-decimal-digits 0=single digits, 1=(LANG=it) 2=(LANG=pl) 3=(LANG=ro)
// bit16=dot after number indicates ordinal
// bit18=special word for 100,000s LANG=sw
// bit19=(LANG=pl)
int numbers;
// bits 1-4 use variant form of numbers before thousands,millions,etc.
int numbers2;
int thousands_sep;
int decimal_sep;
int intonation; // 1=tone language
int long_stop; // extra mS pause for a lengthened stop
int phoneme_change; // TEST, change phonemes, after translation
char max_initial_consonants;
char spelling_stress; // 0=default, 1=stress first letter
char tone_numbers;
char ideographs; // treat as separate words
int testing; // testing options: bit 1= specify stressed syllable in the form: "outdoor/2"
const wchar_t *replace_chars; // characters to be substitutes
const wchar_t *replacement_chars; // substitutes for replace_chars
} LANGUAGE_OPTIONS;
// a parameter of ChangePhonemes()
typedef struct {
int flags;
unsigned char stress; // stress level of this vowel
unsigned char stress_highest; // the highest stress level of a vowel in this word
unsigned char n_vowels; // number of vowels in the word
unsigned char vowel_this; // syllable number of this vowel (counting from 1)
unsigned char vowel_stressed; // syllable number of the highest stressed vowel
} CHANGEPH;
#define NUM_SEP_DOT 0x0008 // . , for thousands and decimal separator
#define NUM_SEP_SPACE 0x1000 // allow space as thousands separator (in addition to langopts.thousands_sep)
#define NUM_DEC_IT 0x2000 // (LANG=it) speak post-decimal-point digits as a combined number not as single digits
class Translator
{//=============
public:
Translator();
virtual ~Translator();
void *TranslateClause(FILE *f_text, const void *vp_input, int *tone, char **voice_change);
int TranslateWord(char *word, int next_pause, WORD_TAB *wtab);
int LoadDictionary(const char *name, int no_error);
virtual void CalcLengths();
virtual void CalcPitches(int clause_tone);
LANGUAGE_OPTIONS langopts;
int translator_name;
int transpose_offset;
int transpose_max;
int transpose_min;
char phon_out[300];
char word_phonemes[N_WORD_PHONEMES]; // a word translated into phoneme codes
int stress_amps[8];
int stress_amps_r[8];
int stress_lengths[8];
int dict_condition; // conditional apply some pronunciation rules and dict.lookups
const unsigned short *charset_a0; // unicodes for characters 0xa0 to oxff
const wchar_t *char_plus_apostrophe; // single chars + apostrophe treated as words
// holds properties of characters: vowel, consonant, etc for pronunciation rules
unsigned char letter_bits[256];
int letter_bits_offset;
#define N_LETTER_TYPES 20
const wchar_t *letter_groups[N_LETTER_TYPES];
/* index1=option, index2 by 0=. 1=, 2=?, 3=! 4=none */
unsigned char punct_to_tone[4][5];
char *data_dictrules; // language_1 translation rules file
char *data_dictlist; // language_2 dictionary lookup file
char *dict_hashtab[N_HASH_DICT]; // hash table to index dictionary lookup file
private:
int TranslateWord2(char *word, WORD_TAB *wtab, int pre_pause, int next_pause);
int TranslateLetter(char *letter, char *phonemes, int control);
void SetSpellingStress(char *phonemes, int control);
void GetTranslatedPhonemeString(char *phon_out, int n_phon_out);
void WriteMnemonic(int *ix, int mnem);
void MakePhonemeList(int post_pause, int new_sentence);
int SubstitutePhonemes(PHONEME_LIST2 *plist_out);
int ReadClause(FILE *f_in, char *buf, unsigned short *charix, int n_buf);
int AnnouncePunctuation(int c1, int c2, char *buf, int ix);
int LookupDict2(char *word, char *word2, char *phonetic, unsigned int *flags, int end_flags);
const char *LookupSpecial(char *string);
const char *LookupCharName(int c);
int LookupNum2(int value, int control, char *ph_out);
int LookupNum3(int value, char *ph_out, int suppress_null, int thousandplex, int prev_thousands);
int LookupThousands(int value, int thousandplex, char *ph_out);
int TranslateNumber_1(char *word1, char *ph_out, unsigned int *flags, int wflags);
void InitGroups(void);
void AppendPhonemes(char *string, int size, const char *ph);
char *DecodeRule(const char *group, char *rule);
void MatchRule(char *word[], const char *group, char *rule, MatchRecord *match_out, int end_flags, int dict_flags);
int TranslateRules(char *p, char *phonemes, int size, char *end_phonemes, int end_flags, int dict_flags);
void ApplySpecialAttribute(char *phonemes, int dict_flags);
int IsLetter(int letter, int group);
void CalcPitches_Tone(int clause_tone);
protected:
virtual int Unpronouncable(char *word);
virtual void SetWordStress(char *output, unsigned int dictionary_flags, int tonic, int prev_stress);
virtual int RemoveEnding(char *word, int end_type, char *word_copy);
virtual int TranslateChar(char *ptr, int prev_in, int c, int next_in);
virtual int TranslateNumber(char *word1, char *ph_out, unsigned int *flags, int wflags);
virtual int ChangePhonemes(PHONEME_LIST2 *phlist, int n_ph, int index, PHONEME_TAB *ph, CHANGEPH *ch);
int IsVowel(int letter);
int LookupDictList(char *word1, char *ph_out, unsigned int *flags, int end_flags);
int Lookup(char *word, char *ph_out);
// groups1 and groups2 are indexes into data_dictrules, set up by InitGroups()
// the two-letter rules for each letter must be consecutive in the language_rules source
char *groups1[256]; // translation rule lists, index by single letter
char *groups2[N_RULE_GROUP2]; // translation rule lists, indexed by two-letter pairs
unsigned int groups2_name[N_RULE_GROUP2]; // the two letter pairs for groups2[]
int n_groups2; // number of groups2[] entries used
unsigned char groups2_count[256]; // number of 2 letter groups for this initial letter
unsigned char groups2_start[256]; // index into groups2
int n_ph_list2;
PHONEME_LIST2 ph_list2[N_PHONEME_LIST]; // first stage of text->phonemes
int expect_verb;
int expect_past; // expect past tense
int expect_verb_s;
int word_flags; // word is all upper case
int prev_last_stress;
int prepause_timeout;
int end_stressed_vowel; // word ends with stressed vowel
int prev_dict_flags; // dictionary flags from previous word
char *clause_end;
int word_vowel_count; // number of vowels so far
int word_stressed_count; // number of vowels so far which could be stressed
int clause_upper_count; // number of upper case letters in the clause
int clause_lower_count; // number of lower case letters in the clause
}; // end of class Translator
extern int option_tone1;
extern int option_tone2;
extern int option_waveout;
extern int option_quiet;
extern int option_phonemes;
extern int option_linelength; // treat lines shorter than this as end-of-clause
extern int option_harmonic1;
extern int option_multibyte;
extern int option_capitals;
extern int option_punctuation;
extern int option_endpause;
extern int option_ssml;
extern int option_phoneme_input; // allow [[phonemes]] in input text
extern int option_phoneme_variants;
extern int option_sayas;
extern int count_characters;
extern int count_words;
extern int count_sentences;
extern int skip_characters;
extern int skip_words;
extern int skip_sentences;
extern int skipping_text;
extern int end_character_position;
extern int clause_start_char;
extern int clause_start_word;
extern char *namedata;
#define N_MARKER_LENGTH 50 // max.length of a mark name
extern char skip_marker[N_MARKER_LENGTH];
#define N_PUNCTLIST 60
extern wchar_t option_punctlist[N_PUNCTLIST];
extern int speech_parameters[];
extern Translator *translator;
extern const unsigned short *charsets[N_CHARSETS];
extern char dictionary_name[40];
extern char ctrl_embedded; // to allow an alternative CTRL for embedded commands
extern char *p_textinput;
extern wchar_t *p_wchar_input;
extern int ungot_char;
extern int (* uri_callback)(int, const char *, const char *);
extern void SetLengthMods(Translator *tr, int value);
Translator *SelectTranslator(const char *name);
int CompileDictionary(const char *dsource, const char *dict_name, FILE *log, char *err_name);
void LoadConfig(void);
int PhonemeCode(unsigned int mnem);
void ChangeWordStress(char *word, int new_stress);
int TransposeAlphabet(char *text, int offset, int min, int max);
int utf8_in(int *c, char *buf, int backwards);
int utf8_out(unsigned int c, char *buf);
int lookupwchar(const unsigned short *list,int c);
int Eof(void);
char *strchr_w(const char *s, int c);
int IsBracket(int c);
void SetLetterBits(Translator *tr, int group, const char *string);
void SetLetterBitsRange(Translator *tr, int group, int first, int last);
void SetLetterVowel(Translator *tr, int c);
void InitNamedata(void);
void InitText(int flags);
void InitText2(void);
int IsDigit(unsigned int c);
int IsAlpha(unsigned int c);
int isspace2(unsigned int c);
extern FILE *f_trans; // for logging
+355
View File
@@ -0,0 +1,355 @@
/***************************************************************************
* Copyright (C) 2005,2006 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "wx/wx.h"
#include "speak_lib.h"
#include "speech.h"
#include "phoneme.h"
#include "voice.h"
#include "main.h"
#include "synthesize.h"
#include "translate.h"
#include "prosodydisplay.h"
#include "wx/font.h"
#include "wx/textctrl.h"
#include "wx/button.h"
#include "wx/checkbox.h"
#include "wx/filename.h"
#include "wx/sound.h"
#define T_SOURCE 501
#define T_PHONETIC 502
#define T_TRANSLATE 503
#define T_PROCESS 504
#define T_RULES 505
TranslDlg *transldlg = NULL;
extern char *speech_to_phonemes(char *data, char *phout);
extern ProsodyDisplay *prosodycanvas;
BEGIN_EVENT_TABLE(TranslDlg, wxPanel)
EVT_BUTTON(T_TRANSLATE,TranslDlg::OnCommand)
EVT_BUTTON(T_PROCESS,TranslDlg::OnCommand)
EVT_BUTTON(T_RULES,TranslDlg::OnCommand)
END_EVENT_TABLE()
class IPATextCtrl : public wxTextCtrl
{
public:
void OnKey(wxKeyEvent& event);
IPATextCtrl(wxWindow *parent,wxWindowID id,const wxPoint& pos,const wxSize& size);
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(IPATextCtrl, wxTextCtrl)
EVT_CHAR(IPATextCtrl::OnKey)
END_EVENT_TABLE()
// using Kirschenbaum scheme to enter IPA from the keyboard
wchar_t ipa1[256] = {
0,0x250,32,32,32,32,32,32,32,32,0xa,0xb,0xc,0x26f,0x272,0x275,
32,32,0x25a,32,32,32,32,0x28d,32,32,32,32,32,32,32,32,
0x20,0x21,0x22,0x23,0x24,0x25,0x0e6,0x27,0x28,0x27e,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
0x252,0x31,0x32,0x25c,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x294,
0x259,0x251,0x3b2,0xe7,0xf0,0x25b,0x46,0x262,0x127,0x26a,0x25f,0x4b,0x26c,0x271,0x14b,0x254,
0x3a6,0x263,0x280,0x283,0x3b8,0x28a,0x28c,0x153,0x3c7,0xf8,0x292,0x32a,0x5c,0x5d,0x5e,0x5f,
0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x303,0x7f,
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
};
IPATextCtrl::IPATextCtrl(wxWindow *parent,wxWindowID id,const wxPoint& pos,const wxSize& size) :
wxTextCtrl(parent,id,_T(""),pos,size,wxTE_MULTILINE)
{
wxTextAttr attr;
wxFont font = wxFont(12,wxFONTFAMILY_ROMAN,wxFONTSTYLE_NORMAL,wxFONTWEIGHT_LIGHT);
attr.SetFont(font);
SetDefaultStyle(attr);
}
void IPATextCtrl::OnKey(wxKeyEvent& event)
{
long key;
wchar_t wbuf[3];
key = event.m_keyCode;
key = event.GetKeyCode();
if(event.ControlDown())
key = key & 0x1f;
if(key==0xd && !event.ControlDown())
event.Skip(); // ENTER
else
if(key != 8 && key < 0x7f)
{
wbuf[0] = ipa1[key];
wbuf[1] = 0;
WriteText(wxString(wbuf));
}
else
event.Skip();
}
void PlayWavFile(const char *fname)
{//================================
char command[120];
#ifdef PLATFORM_WINDOWS
wxSound(wxString(fname,wxConvLocal)).Play(wxSOUND_SYNC);
#else
sprintf(command,"play %s",fname);
if(system(command) == -1)
{
wxLogError(_T("Failed to run system command:\n\n"+wxString(command,wxConvLocal)));
}
#endif
}
char *WavFileName(void)
{//====================
static char f_speech[120];
if(!wxDirExists(wxPathOnly(path_speech)))
{
path_speech = wxFileSelector(_T("Speech output file"),
path_phsource,_T("speech.wav"),_T("*"),_T("*"),wxSAVE);
}
strcpy(f_speech,path_speech.mb_str(wxConvLocal));
return(f_speech);
}
int OpenWaveFile2(const char *fname)
{//=================================
int result;
if((result = OpenWaveFile(fname, samplerate)) != 0)
{
wxLogStatus(_T("Can't write to WAV file: '"+wxString(fname,wxConvLocal))+_T("'"));
}
return(result);
}
void MakeWave2(PHONEME_LIST *p, int n_phonemes)
{//============================================
int result;
char *fname_speech;
int n_ph;
n_ph = n_phonemes;
fname_speech = WavFileName();
OpenWaveFile2(fname_speech);
Generate(p,&n_ph,0);
for(;;)
{
result = WavegenFile();
if(result != 0)
break;
Generate(p,&n_ph,1);
}
CloseWaveFile(samplerate);
PlayWavFile(fname_speech);
} // end of MakeWave2
TranslDlg::TranslDlg(wxWindow *parent) : wxPanel(parent)
{//=====================================================
t_source = new wxTextCtrl(this,T_SOURCE,_T(""),wxPoint(0,4),
wxSize(200,250),wxTE_MULTILINE);
t_phonetic = new wxTextCtrl(this,T_PHONETIC,_T(""),wxPoint(0,262),
wxSize(200,350),wxTE_MULTILINE);
t_translate = new wxButton(this,T_TRANSLATE,_T("Translate"),wxPoint(4,620));
t_translate = new wxButton(this,T_RULES,_T("Show Rules"),wxPoint(4,652));
t_process = new wxButton(this,T_PROCESS,_T("Speak"),wxPoint(100,620));
t_source->SetFocus();
} // end of TransDlg::TransDlg
void TranslDlg::SpeakFile(void)
{//============================
wxString file;
wxFileName fname;
FILE *f_text;
char buf[200];
fname = wxFileName(path_speaktext);
file = wxFileSelector(_T("Text file to speak"),fname.GetPath(),fname.GetName(),_T(""),_T("*"),wxOPEN);
if(file == wxEmptyString) return;
strcpy(buf,file.mb_str(wxConvLocal));
f_text = fopen(buf,"r");
if(f_text == NULL)
{
wxLogError(_T("Failed to read: ")+file);
return;
}
path_speaktext = file;
InitText(0);
SpeakNextClause(f_text,NULL,0);
return;
} // end of SpeakFile
void TranslDlg::ReadVoice()
{//========================
wxString path;
wxString filename;
char fname[130];
sprintf(fname,"%s/voices",path_home);
path = wxFileSelector(_T("Load voice"),wxString(fname,wxConvLocal),_T(""),_T(""),_T("*"),wxOPEN);
if(path.IsEmpty())
return;
filename = path.Mid(strlen(fname)+1);
strcpy(fname,filename.mb_str(wxConvLocal));
if(SetVoiceByName(fname) != EE_OK)
{
wxLogError(_T("Failed to load voice data"));
}
WavegenSetVoice(voice);
}
void TranslDlg::OnCommand(wxCommandEvent& event)
{//=============================================
#define N_PH_LIST N_PHONEME_LIST
void *vp;
char buf[500];
char phon_out[N_PH_LIST*2];
int clause_tone;
int clause_count;
FILE *f;
char fname_temp[100];
static int n_ph_list;
static PHONEME_LIST ph_list[N_PH_LIST+1];
option_phonemes = 0;
switch(event.GetId())
{
case T_RULES:
case MENU_SPEAK_RULES:
option_phonemes = 2;
strcpy(fname_temp,tmpnam(NULL));
if((f = fopen(fname_temp,"w+")) != NULL)
{
f_trans = f; // write translation rule trace to a temp file
}
case T_TRANSLATE:
case MENU_SPEAK_TRANSLATE:
SpeakNextClause(NULL,NULL,2); // stop speaking file
strncpy0(buf,t_source->GetValue().mb_str(wxConvLocal),sizeof(buf));
phon_out[0] = 0;
n_ph_list = 0;
clause_count = 0;
vp = buf;
InitText(0);
while((vp != NULL) && (n_ph_list < N_PH_LIST))
{
vp = translator->TranslateClause(NULL,vp,&clause_tone,NULL);
if(clause_count++ > 0)
strcat(phon_out," ||");
strcat(phon_out,translator->phon_out);
t_phonetic->SetValue(wxString(translator->phon_out,wxConvLocal));
translator->CalcPitches(clause_tone);
translator->CalcLengths();
if((n_ph_list + n_phoneme_list) >= N_PH_LIST)
{
n_phoneme_list = N_PH_LIST - n_ph_list - n_phoneme_list;
}
memcpy(&ph_list[n_ph_list],phoneme_list,sizeof(PHONEME_LIST)*n_phoneme_list);
n_ph_list += n_phoneme_list;
}
t_phonetic->Clear();
if(option_phonemes == 2)
{
option_phonemes=0;
rewind(f_trans);
while(fgets(buf,sizeof(buf),f_trans) != NULL)
{
t_phonetic->AppendText(wxString(buf,wxConvLocal));
}
t_phonetic->AppendText(_T("---\n"));
fclose(f_trans);
remove(fname_temp);
}
t_phonetic->AppendText(wxString(phon_out,wxConvLocal));
// MbrolaTranslate(ph_list,n_ph_list,stdout); // for Testing
break;
case T_PROCESS:
case MENU_SPEAK_TEXT:
if(prosodycanvas != NULL)
{
prosodycanvas->LayoutData(ph_list,n_ph_list);
}
else
{
myframe->OnProsody(event);
}
MakeWave2(ph_list,n_ph_list);
break;
}
} // end of TranslDlg::OnCommand
+69
View File
@@ -0,0 +1,69 @@
/***************************************************************************
* Copyright (C) 2005 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
typedef struct {
char name[40];
int phoneme_tab_ix; // phoneme table number
int pitch_base; // Hz<<12
int pitch_range; // standard = 0x1000
int speedf1;
int speedf2;
int speedf3;
int flutter;
int roughness;
int echo_delay;
int echo_amp;
int n_harmonic_peaks; // highest formant which is formed from adding harmonics
int peak_shape; // alternative shape for formant peaks (0=standard 1=squarer)
// parameters used by Wavegen
int freq[N_PEAKS]; // 100% = 256
int height[N_PEAKS]; // 100% = 256
int width[N_PEAKS]; // 100% = 256
// copies without temporary adjustments from embedded commands
int freq2[N_PEAKS]; // 100% = 256
int height2[N_PEAKS]; // 100% = 256
int width2[N_PEAKS]; // 100% = 256
// This table provides the opportunity for tone control.
// Adjustment of harmonic amplitudes, steps of 8Hz
// value of 128 means no change
#define N_TONE_ADJUST 1000
unsigned char tone_adjust[N_TONE_ADJUST]; // 8Hz steps * 1000 = 8kHz
} voice_t;
// percentages shown to user, ix=N_PEAKS means ALL peaks
extern USHORT voice_pcnt[N_PEAKS+1][3];
extern voice_t *voice;
extern int tone_points[10];
voice_t *LoadVoice(char *voice_name, int control);
voice_t *LoadVoiceVariant(const char *voice_name, int variant);
void WavegenSetVoice(voice_t *v);
void ReadTonePoints(char *string, int *tone_pts);
+363
View File
@@ -0,0 +1,363 @@
/***************************************************************************
* Copyright (C) 2005, 2006 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "wx/wx.h"
#include "main.h"
#include "speech.h"
#include "voice.h"
#include "spect.h"
#include "options.h"
#include "wx/dialog.h"
#include "wx/textctrl.h"
#include "wx/checkbox.h"
#include "wx/txtstrm.h"
#include "wx/wfstream.h"
#include "wx/filename.h"
VoiceDlg *voicedlg = NULL;
voice_t voice_data;
USHORT voice_pcnt[N_PEAKS+1][3];
static void VoiceSetup()
{//=====================
int pk;
voice_t *v = &voice_data;
for(pk=0; pk<N_PEAKS; pk++)
{
v->freq[pk] = int(voice_pcnt[pk][0] * 2.56001 * voice_pcnt[N_PEAKS][0] / 100.0);
v->height[pk] = int(voice_pcnt[pk][1] * 2.56001 * voice_pcnt[N_PEAKS][1] / 100.0);
v->width[pk] = int(voice_pcnt[pk][2] * 2.56001 * voice_pcnt[N_PEAKS][2] / 100.0);
}
}
static void VoiceInit()
{//====================
int pk, j;
voice_t *v;
v = &voice_data;
v->pitch_base = 71 << 12;
v->pitch_range = 0x1000; // default = 0x1000
for(pk=0; pk<N_PEAKS+1; pk++)
{
for(j=0; j<3; j++)
voice_pcnt[pk][j] = 100;
}
VoiceSetup();
voice = v;
} // end of voice_init
#define VD_DEFAULTS 401
#define VD_LOAD 402
#define VD_SAVE 403
#define VD_PITCHCHOICE 404
#define VD_PITCH1 405
#define VD_PITCH2 406
#define VD_USEPITCH 407
wxString pitchchoices[3] = {_T("From spectrum"),_T("Standard"),_T("Monotone")};
BEGIN_EVENT_TABLE(VoiceDlg, wxPanel)
EVT_BUTTON(VD_DEFAULTS,VoiceDlg::OnCommand)
EVT_BUTTON(VD_LOAD,VoiceDlg::OnCommand)
EVT_BUTTON(VD_SAVE,VoiceDlg::OnCommand)
EVT_SPINCTRL(VD_PITCH1,VoiceDlg::OnSpin)
EVT_SPINCTRL(VD_PITCH2,VoiceDlg::OnSpin)
EVT_CHECKBOX(VD_USEPITCH,VoiceDlg::OnCommand)
EVT_COMBOBOX(VD_PITCHCHOICE,VoiceDlg::OnCommand)
END_EVENT_TABLE()
VoiceDlg::VoiceDlg(wxWindow *parent) : wxPanel(parent)
{//=========================================================
int ix, j;
int y;
wxString string;
voice_t *v;
wxString captions[3] = {_T("Freq"),_T("Height"),_T("Width")};
voice_p = v = &voice_data;
VoiceInit();
pitch1 = 80;
pitch2 = 120;
for(j=0; j<3; j++)
new wxStaticText(this,-1,captions[j],wxPoint(34+j*56,6));
y = 26;
for(ix=0; ix<N_PEAKS+1; ix++)
{
if(ix==N_PEAKS)
{
string = _T("All");
y += 8;
}
else
string.Printf(_T("%2d"),ix);
vd_labpk[ix] = new wxStaticText(this,-1,string,wxPoint(8,y+2));
for(j=0; j<3; j++)
{
string.Printf(_T("%d"),voice_pcnt[ix][j]);
vd[ix][j] = new wxSpinCtrl(this,-1,string,
wxPoint(32+j*56,y),wxSize(52,22),wxSP_ARROW_KEYS,0,300);
}
y += 26;
}
for(ix=6; ix<N_PEAKS; ix++)
vd[ix][2]->Hide();
vd_defaults = new wxButton(this,VD_DEFAULTS,_T("Defaults"),wxPoint(32,293));
y = 324;
vd_pitch1 = new wxSpinCtrl(this,VD_PITCH1,_T("80"),
wxPoint(32,y),wxSize(52,24),wxTE_CENTRE,60,500);
vd_pitch2 = new wxSpinCtrl(this,VD_PITCH2,_T("120"),
wxPoint(88,y),wxSize(52,24),wxTE_CENTRE,60,500);
vd_lab[1] = new wxStaticText(this,-1,_T("Hz"),wxPoint(142,y+2));
vd_usepitch = new wxCheckBox(this,VD_USEPITCH,_T("Pitch from spectrum"),
wxPoint(4,y+24));
// vd_load = new wxButton(this,VD_LOAD,_T("Load"),wxPoint(16,y+56));
// vd_save = new wxButton(this,VD_SAVE,_T("Save"),wxPoint(106,y+56));
y = 422;
vd_duration = new wxTextCtrl(this,-1,_T(""),
wxPoint(4,y),wxSize(52,24),wxTE_CENTRE);
vd_lab[2] = new wxStaticText(this,-1,_T("Duration"),wxPoint(60,y+2));
y = 422;
vd_pitchchoice = new wxComboBox(this,VD_PITCHCHOICE,_T("Standard"),
wxPoint(4,y+30),wxSize(192,24),3,pitchchoices);
vd_pitchgraph = new ByteGraph(this,wxPoint(0,y+60),wxSize(200,100));
vd_pitchgraph->SetData(128,env_fall);
vd_pitchgraph->Show();
} // end of VoiceDlg::VoiceDlg
void VoiceDlg::OnCommand(wxCommandEvent& event)
{//============================================
// JSD EVT_COMBOBOX causes GDB lockup if a breakpoint is set
int id;
int ix, j;
switch(id = event.GetId())
{
case VD_DEFAULTS:
for(ix=0; ix<N_PEAKS+1; ix++)
{
for(j=0; j<3; j++)
{
voice_pcnt[ix][j] = 100;
vd[ix][j]->SetValue(_T("100"));
}
}
VoiceSetup();
break;
case VD_LOAD:
Load();
break;
case VD_SAVE:
Save();
break;
case VD_USEPITCH:
case VD_PITCHCHOICE:
if(currentcanvas != NULL)
SetFromSpect(currentcanvas->spectseq);
break;
}
if(currentcanvas != NULL) currentcanvas->SetFocus();
} // end of VoiceDlg::OnCommand
void VoiceDlg::ReadParams()
{//========================
int ix, j;
pitchenv.pitch1 = vd_pitch1->GetValue();
pitchenv.pitch2 = vd_pitch2->GetValue();
voice_data.pitch_base = (pitchenv.pitch1 - 9) << 12;
voice_data.pitch_range = (pitchenv.pitch2 - pitchenv.pitch1) * 108;
for(ix=0; ix<N_PEAKS+1; ix++)
{
for(j=0; j<3; j++)
{
voice_pcnt[ix][j] = vd[ix][j]->GetValue();
}
}
VoiceSetup();
} // end of VoiceDlg::ReadParams
void VoiceDlg::WriteParams()
{//========================
int ix;
voice_t *v = voice;
int val1, val2;
val1 = (v->pitch_base >> 12) + 9;
val2 = v->pitch_range/108 + val1;
vd_pitch1->SetValue(val1);
vd_pitch2->SetValue(val2);
for(ix=0; ix<N_PEAKS; ix++)
{
vd[ix][0]->SetValue(int(float(v->freq[ix]) / 2.56001 + 0.5));
vd[ix][1]->SetValue(int(float(v->height[ix]) / 2.56001 + 0.5));
vd[ix][2]->SetValue(int(float(v->width[ix]) / 2.56001 + 0.5));
}
vd[ix][0]->SetValue(100);
vd[ix][1]->SetValue(100);
vd[ix][2]->SetValue(100);
} // end of VoiceDlg::WriteParams
void VoiceDlg::OnSpin(wxSpinEvent& event)
{//======================================
ReadParams();
}
void VoiceDlg::Save()
{//==================
int pk;
wxString filename;
FILE *f;
filename = wxFileSelector(_T("Save voice"),path_voices,_T(""),_T(""),_T("*"),wxSAVE);
if(filename.IsEmpty())
return;
f = fopen(filename.mb_str(wxConvLocal),"w");
if(f == NULL)
{
wxLogError(_T("Failed to open '%s'"),filename.c_str());
return;
}
wxFileName fn(filename);
path_voices = fn.GetPath();
ReadParams();
fprintf(f,"%3d %3d\n",vd_pitch1->GetValue(),vd_pitch2->GetValue());
for(pk=0; pk<N_PEAKS+1; pk++)
{
fprintf(f,"%3d %3d %3d\n",voice_pcnt[pk][0],voice_pcnt[pk][1],voice_pcnt[pk][2]);
}
fclose(f);
} // end of VoiceDlg::Save
void VoiceDlg::Load()
{//===============================
wxString filename;
int pk, j;
filename = wxFileSelector(_T("Load voice"),path_voices,_T(""),_T(""),_T("*"),wxOPEN);
if(filename.IsEmpty()) return;
wxFileInputStream stream(filename);
if(stream.Ok() == FALSE)
{
wxLogError(_T("Failed to open '%s'"),filename.c_str());
return;
}
wxFileName fn(filename);
path_voices = fn.GetPath();
wxTextInputStream s(stream);
s >> pitch1;
s >> pitch2;
for(pk=0; pk<N_PEAKS+1; pk++)
{
for(j=0; j<3; j++)
{
s >> voice_pcnt[pk][j];
SetSpinCtrl(vd[pk][j],voice_pcnt[pk][j]);
}
}
SetSpinCtrl(voicedlg->vd_pitch1,pitch1);
SetSpinCtrl(voicedlg->vd_pitch2,pitch2);
ReadParams();
} // end of VoiceDlg::Load
void VoiceDlg::SetFromSpect(SpectSeq* spect)
{//=========================================
if(spect==NULL) return;
if(vd_pitchchoice->GetValue() == _T("From spectrum"))
{
pitchenv = spect->pitchenv;
}
else
if(vd_pitchchoice->GetValue() == _T("Monotone"))
{
memset(pitchenv.env,127,128);
}
else
if(vd_pitchchoice->GetValue() == _T("Standard"))
{
memcpy(pitchenv.env,env_fall,128);
}
vd_pitchgraph->SetData(128,pitchenv.env);
vd_pitchgraph->Show();
if(vd_usepitch->GetValue())
{
SetSpinCtrl(vd_pitch1,spect->pitch1);
SetSpinCtrl(vd_pitch2,spect->pitch2);
}
else
{
SetSpinCtrl(vd_pitch1,pitch1);
SetSpinCtrl(vd_pitch2,pitch2);
}
} // end of VoiceDlg::SetFromSpect
Executable
+1420
View File
File diff suppressed because it is too large Load Diff
+493
View File
@@ -0,0 +1,493 @@
/***************************************************************************
* Copyright (C) 2005,2006 by Jonathan Duddington *
* jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <math.h>
#include "wx/wx.h"
#include <wx/dcmemory.h>
#include <wx/dc.h>
#include <wx/bitmap.h>
#include <wx/dirdlg.h>
#include "wx/filename.h"
#include "wx/wfstream.h"
#include "speak_lib.h"
#include "main.h"
#include "speech.h"
#include "voice.h"
#include "spect.h"
#include "phoneme.h"
#include "synthesize.h"
#include "translate.h"
/* Read a file of vowel symbols and f1,f2 formants, and produce a vowel diagram
*/
extern wxString path_phsource;
extern char path_source[];
extern char voice_name[];
extern char *spects_data;
extern int n_phoneme_tables;
extern PHONEME_TAB_LIST phoneme_tab_list[N_PHONEME_TABS];
// size of the vowelchart png
#define WIDTH 1200
#define HEIGHT 800
#define ROUND(x) ((int) ((x) + 0.5))
static int HslValue (double n1, double n2, double hue)
{//===================================================
double value;
if (hue > 255)
hue -= 255;
else if (hue < 0)
hue += 255;
if (hue < 42.5)
value = n1 + (n2 - n1) * (hue / 42.5);
else if (hue < 127.5)
value = n2;
else if (hue < 170)
value = n1 + (n2 - n1) * ((170 - hue) / 42.5);
else
value = n1;
return ROUND (value * 255.0);
}
/**
* @hue: Hue channel, returns Red channel
* @saturation: Saturation channel, returns Green channel
* @lightness: Lightness channel, returns Blue channel
*
* The arguments are pointers to int, with the values pointed to in the
* following ranges: H [0, 360], L [0, 255], S [0, 255].
*
* The function changes the arguments to point to the RGB value
* corresponding, with the returned values all in the range [0, 255].
**/
void HslToRgb (int *hue, int *saturation, int *lightness)
{//======================================================
double h, s, l;
h = (*hue * 256)/360;
s = *saturation;
l = *lightness;
if (s == 0)
{
/* achromatic case */
*hue = (int)l;
*lightness = (int)l;
*saturation = (int)l;
}
else
{
double m1, m2;
if (l < 128)
m2 = (l * (255 + s)) / 65025.0;
else
m2 = (l + s - (l * s) / 255.0) / 255.0;
m1 = (l / 127.5) - m2;
/* chromatic case */
*hue = HslValue (m1, m2, h + 85);
*saturation = HslValue (m1, m2, h);
*lightness = HslValue (m1, m2, h - 85);
}
}
static int vowel_posn[N_PHONEME_TAB];
static int vowel_posn_ix;
static double log2a(double x)
{//========================
// log2(x) = log(x) / log(2)
return(log(x) / 0.693147);
}
static int VowelX(int f2)
{//======================
return(WIDTH - int((log2a(f2) - 9.49)*WIDTH/1.8));
// return(1024 - int((log2a(f2) - 9.50)*1020/1.8));
}
static int VowelY(int f1)
{//======================
return(int((log2a(f1) - 7.85)*HEIGHT/2.15));
}
static int VowelZ(int f3)
{//======================
int z;
// range 2000-3000Hz, log2= 10.96 to 11.55
z = int((log2a(f3) - 11.05)*256/0.50);
if(z < 0) z = 0;
if(z > 255) z = 255;
return(z);
}
static void DrawVowel(wxDC *dc, wxString name, int f1, int f2, int f3, int g1, int g2)
{//==================================================================================
int ix;
int posn;
int collisions;
int x,y,z,x2,y2;
int r,g,b;
wxBrush brush;
y = VowelY(f1);
x = VowelX(f2);
z = VowelZ(f3);
if(y < 0) y = 0;
if(y > (HEIGHT-4)) y= (HEIGHT-4);
if(x < 0) x = 0;
if(x > (WIDTH-8)) x = (WIDTH-8);
r = z;
g = 255;
b = 100 + z/2;
HslToRgb(&r,&g,&b);
brush.SetColour(r,g,b);
dc->SetBrush(brush);
dc->DrawCircle(x,y,4);
// check for a label already at this position
collisions = 0;
posn = (x/8)*WIDTH + (y/8);
for(ix=0; ix<vowel_posn_ix; ix++)
{
if(posn == vowel_posn[ix])
collisions++;
}
vowel_posn[vowel_posn_ix++] = posn;
dc->DrawText(name,x+4,y+(collisions*10));
if(g2 != 0xffff)
{
y2 = VowelY(g1);
x2 = VowelX(g2);
dc->DrawLine(x,y,x2,y2);
}
}
static int VowelChartDir(wxDC *dc, wxBitmap *bitmap)
{//=================================================
int ix;
int nf;
int count = 0;
SpectSeq *spectseq;
SpectFrame *frame1;
SpectFrame *frame2=NULL;
wxFileName filename;
wxString dir = wxDirSelector(_T("Directory of vowel files"),path_phsource);
if(dir.IsEmpty()) return(0);
wxString path = wxFindFirstFile(dir+_T("/*"),wxFILE);
while (!path.empty())
{
if((spectseq = new SpectSeq) == NULL) break;
filename = wxFileName(path);
wxFileInputStream stream(path);
if(stream.Ok() == FALSE)
{
path = wxFindNextFile();
continue;
}
spectseq->Load(stream);
nf = 0;
frame1 = NULL;
if(spectseq->numframes > 0)
{
frame2 = spectseq->frames[0];
}
for(ix=0; ix<spectseq->numframes; ix++)
{
if(spectseq->frames[ix]->keyframe)
{
nf++;
frame2 = spectseq->frames[ix];
if(frame2->markers & FRFLAG_VOWEL_CENTRE)
frame1 = frame2;
}
}
if((nf >= 3) && (frame1 != NULL))
{
DrawVowel(dc,wxString(filename.GetName()),
frame1->peaks[1].pkfreq, frame1->peaks[2].pkfreq, frame1->peaks[3].pkfreq,
frame2->peaks[1].pkfreq, frame2->peaks[2].pkfreq);
count++;
}
delete spectseq;
path = wxFindNextFile();
}
filename.SetPath(dir);
filename.SetFullName(_T("vowelchart.png"));
bitmap->SaveFile(filename.GetFullPath(),wxBITMAP_TYPE_PNG);
return(count);
}
static int VowelChartList(wxDC *dc, wxBitmap *bitmap, char *fname)
{//===============================================================
// Plot a graph of vowel formants.
// y-axis is decreasing f1 (closeness)
// x-axis is decreasing f2 (backness)
FILE *f_in;
int ix;
int f1,f2,f3,g1,g2;
int count=0;
wxFileName filename;
char name[40];
char buf[120];
wxString path;
if(fname != NULL)
{
path = wxString(fname,wxConvLocal);
}
else
{
path = wxFileSelector(_T("Read file of vowel formants"),path_phsource,
_T(""),_T(""),_T("*"),wxOPEN);
}
if(path.IsEmpty())
{
return(0);
}
filename = wxFileName(path);
strcpy(buf,path.mb_str(wxConvLocal));
f_in = fopen(buf,"r");
if(f_in == NULL)
{
wxLogError(_T("Can't read file: %s"),buf);
return(0);
}
while(fgets(buf,sizeof(buf),f_in) != NULL)
{
g2 = 0xffff;
ix = sscanf(buf,"%s %d %d %d %d %d",name,&f1,&f2,&f3,&g1,&g2);
if(ix >= 3)
{
DrawVowel(dc,wxString(name,wxConvLocal),
f1,f2,f3,g1,g2);
count++;
}
}
filename.SetExt(_T("png"));
bitmap->SaveFile(filename.GetFullPath(),wxBITMAP_TYPE_PNG);
return(count);
}
void VowelChart(int control, char *fname)
{//======================================
// Plot a graph of vowel formants.
// y-axis is decreasing f1 (closeness)
// x-axis is decreasing f2 (backness)
// control=1 from directory of lists
// control=2 from single list
// control=3 from directory of phoneme source data files
int ix;
int x,y;
int count;
wxFileName filename;
wxBitmap bitmap(WIDTH,HEIGHT);
// Create a memory DC
wxMemoryDC dc;
dc.SelectObject(bitmap);
dc.SetBrush(*wxWHITE_BRUSH);
dc.SetFont(*wxSWISS_FONT);
dc.Clear();
// draw grid
dc.SetPen(*wxLIGHT_GREY_PEN);
for(ix=200; ix<=1000; ix+=50)
{
y = VowelY(ix);
dc.DrawLine(0,y,WIDTH,y);
if((ix % 100) == 0)
dc.DrawText(wxString::Format(_T("%d"),ix),1,y);
}
for(ix=800; ix<=2400; ix+=100)
{
x = VowelX(ix);
dc.DrawLine(x,0,x,HEIGHT);
if((ix % 200)==0)
dc.DrawText(wxString::Format(_T("%d"),ix),x+1,0);
}
dc.SetPen(*wxBLACK_PEN);
vowel_posn_ix = 0;
if(control==3)
count = VowelChartDir(&dc, &bitmap);
else
count = VowelChartList(&dc, &bitmap, fname);
if(control != 1)
wxLogStatus(_T("Plotted %d vowels"),count);
}
static void FindPhonemesUsed(void)
{//===============================
int hash;
char *p;
char *next;
unsigned char c;
int count = 0;
// look through all the phoneme strings in the **_rules data
// and mark these phoneme codes as used.
p = translator->data_dictrules;
while(*p != 0)
{
if(*p == RULE_GROUP_END)
p++;
if(*p == RULE_GROUP_START)
p += (strlen(p)+1);
while((((c = *p) != RULE_PHONEMES)) && (c != 0)) p++;
count++;
if(c == RULE_PHONEMES)
{
p++;
while(*p != 0)
{
phoneme_tab_flags[*p & 0xff] |= 2;
p++;
}
}
p++;
}
for(hash=0; hash<N_HASH_DICT; hash++)
{
p = translator->dict_hashtab[hash];
if(p == NULL)
continue;
while(*p != 0)
{
next = p + p[0];
if((p[1] & 0x80) == 0)
{
p += ((p[1] & 0x3f) + 2);
while(*p != 0)
{
phoneme_tab_flags[*p & 0xff] |= 2;
p++;
}
}
p = next;
}
}
} // end of FindPhonemesUsed
void MakeVowelLists(void)
{//======================
// For each phoneme table, make a list of its vowels and their
// formant frequencies (f1,f2,f3) for use by VowelChart()
int table;
int ix;
int phcode;
PHONEME_TAB *ph;
FILE *f;
SPECT_SEQ *seq;
frame_t *frame;
int match_level;
char dirname[100];
char fname[120];
progress = new wxProgressDialog(_T("Vowel charts"),_T(""),n_phoneme_tables);
sprintf(dirname,"%s%s",path_source,"vowelcharts");
mkdir(dirname,S_IRWXU | S_IRGRP | S_IROTH);
for(table=0; table<n_phoneme_tables; table++)
{
sprintf(fname,"%s/%s",dirname,phoneme_tab_list[table].name);
if((f = fopen(fname,"w"))==NULL) continue;
progress->Update(table);
// select the phoneme table by name
// if(SetVoiceByName(phoneme_tab_list[table].name) != 0) continue;
if(SelectPhonemeTableName(phoneme_tab_list[table].name) < 0) continue;
FindPhonemesUsed();
// phoneme table is terminated by a phoneme with no name (=0)
for(phcode=1; phcode < n_phoneme_tab; phcode++)
{
//if((phoneme_tab_flags[phcode] & 3) == 0)
// continue; // inherited, and not used
ph = phoneme_tab[phcode];
if(ph->type != phVOWEL)
continue;
if((ix = LookupSound(ph, phoneme_tab[phonPAUSE], 1, &match_level, 0)) == 0)
continue;
seq = (SPECT_SEQ *)(&spects_data[ix]);
frame = &seq->frame[1];
fprintf(f,"%s\t %3d %4d %4d",WordToString(ph->mnemonic),
frame->ffreq[1],frame->ffreq[2],frame->ffreq[3]);
frame = &seq->frame[seq->n_frames-1];
fprintf(f," %3d %4d %4d\n",frame->ffreq[1],frame->ffreq[2],frame->ffreq[3]);
}
fclose(f);
VowelChart(1,fname); // draw the vowel chart
}
LoadVoice(voice_name,0); // reset the original phoneme table
delete progress;
}
Executable
+1074
View File
File diff suppressed because it is too large Load Diff
+41
View File
@@ -0,0 +1,41 @@
#ifndef WAVE_H
#define WAVE_H
#include "stdint.h"
extern int option_device_number;
extern void wave_init();
// TBD: the arg could be "alsa", "oss",...
extern void* wave_open(char* the_api);
extern size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSize);
extern int wave_close(void* theHandler);
extern void wave_flush(void* theHandler);
extern int wave_is_busy(void* theHandler);
extern void wave_terminate();
extern uint32_t wave_get_read_position(void* theHandler);
extern uint32_t wave_get_write_position(void* theHandler);
// Supply the remaining time in ms before the sample is played
// (or 0 if the event has been already played).
// sample: sample identifier
// time: supplied value in ms
//
// return 0 if ok or -1 otherwise (stream not opened).
extern int wave_get_remaining_time(uint32_t sample, uint32_t* time);
// set the callback which informs if the output is still enabled.
// Helpful if a new sample is waiting for free space whereas sound must be stopped.
typedef int (t_wave_callback)(void);
extern void wave_set_callback_is_output_enabled(t_wave_callback* cb);
// general functions
extern void clock_gettime2(struct timespec *ts);
extern void add_time_in_ms(struct timespec *ts, int time_in_ms);
// for tests
extern void *wave_test_get_write_buffer();
#endif
+1767
View File
File diff suppressed because it is too large Load Diff