mirror of
https://github.com/rhasspy/espeak-ng.git
synced 2026-11-13 09:14:12 +00:00
git-svn-id: https://espeak.svn.sourceforge.net/svnroot/espeak/trunk@1 d46cf337-b52f-0410-862d-fd96e6ae7743
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
// This is a dummy file.
|
||||
// A file of this name is needed on Windows
|
||||
|
||||
Executable
+2138
File diff suppressed because it is too large
Load Diff
Executable
+1287
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
|
||||
Executable
+2805
File diff suppressed because it is too large
Load Diff
+573
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
//>
|
||||
@@ -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
|
||||
Executable
+707
@@ -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());
|
||||
|
||||
|
||||
}
|
||||
|
||||
Executable
+55
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
Executable
+370
@@ -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
|
||||
|
||||
Executable
+1021
File diff suppressed because it is too large
Load Diff
Executable
+179
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
Executable
+197
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
Executable
+575
@@ -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
|
||||
|
||||
|
||||
Executable
+466
@@ -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 */
|
||||
Executable
+466
@@ -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
File diff suppressed because it is too large
Load Diff
Executable
+607
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Executable
+64
@@ -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
File diff suppressed because it is too large
Load Diff
Executable
+583
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+571
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
|
||||
Executable
+1009
File diff suppressed because it is too large
Load Diff
Executable
+1085
File diff suppressed because it is too large
Load Diff
Executable
+95
@@ -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;
|
||||
|
||||
Executable
+684
@@ -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
|
||||
Executable
+643
@@ -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
|
||||
|
||||
Executable
+1509
File diff suppressed because it is too large
Load Diff
Executable
+241
@@ -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);
|
||||
@@ -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 */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Executable
+2150
File diff suppressed because it is too large
Load Diff
Executable
+509
@@ -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
|
||||
Executable
+355
@@ -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
@@ -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);
|
||||
|
||||
Executable
+363
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
File diff suppressed because it is too large
Load Diff
+41
@@ -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
|
||||
Executable
+1767
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user