mirror of
https://github.com/rhasspy/espeak-ng.git
synced 2026-11-06 23:16:21 +00:00
Merge branch 'master' into build
Conflicts: src/Makefile.espeakedit
This commit is contained in:
+2
-2
@@ -49,12 +49,12 @@ endif
|
||||
|
||||
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 \
|
||||
synthdata.cpp synthesize.cpp translate.cpp mbrowrap.cpp \
|
||||
tr_languages.cpp voices.cpp wavegen.cpp phonemelist.cpp klatt.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 \
|
||||
synthdata.cpp synthesize.cpp translate.cpp mbrowrap.cpp \
|
||||
tr_languages.cpp voices.cpp wavegen.cpp phonemelist.cpp \
|
||||
espeak_command.cpp event.cpp fifo.cpp $(WAVE) debug.cpp klatt.cpp
|
||||
|
||||
|
||||
+17
-10
@@ -1,20 +1,27 @@
|
||||
SRCS=\
|
||||
compiledata.cpp compiledict.cpp debug.cpp dictionary.cpp espeak_command.cpp espeakedit.cpp \
|
||||
event.cpp extras.cpp fifo.cpp formantdlg.cpp intonation.cpp klatt.cpp menus.cpp numbers.cpp \
|
||||
options.cpp phonemelist.cpp prosodydisplay.cpp readclause.cpp setlengths.cpp speak_lib.cpp \
|
||||
spect.cpp spectdisplay.cpp spectseq.cpp synthdata.cpp synthesize.cpp synth_mbrola.cpp \
|
||||
translate.cpp transldlg.cpp tr_languages.cpp voicedlg.cpp voices.cpp vowelchart.cpp wave.cpp \
|
||||
wavegen.cpp
|
||||
# Makefile for 'espeakedit' program
|
||||
|
||||
SRCS= compiledata.cpp compiledict.cpp debug.cpp dictionary.cpp espeak_command.cpp \
|
||||
espeakedit.cpp event.cpp extras.cpp fifo.cpp formantdlg.cpp intonation.cpp \
|
||||
klatt.cpp mbrowrap.cpp menus.cpp numbers.cpp options.cpp phonemelist.cpp \
|
||||
prosodydisplay.cpp readclause.cpp setlengths.cpp speak_lib.cpp spect.cpp \
|
||||
spectdisplay.cpp spectseq.cpp synthdata.cpp synthesize.cpp synth_mbrola.cpp \
|
||||
translate.cpp transldlg.cpp tr_languages.cpp voicedlg.cpp voices.cpp vowelchart.cpp \
|
||||
wave.cpp wavegen.cpp
|
||||
|
||||
OBJS=$(patsubst %.cpp,%.o,$(SRCS))
|
||||
|
||||
WX_LIBS = -pthread -lwx_gtk2u_xrc-2.6 -lwx_gtk2u_qa-2.6 -lwx_gtk2u_html-2.6 -lwx_gtk2u_adv-2.6 -lwx_gtk2u_core-2.6 -lwx_baseu_xml-2.6 -lwx_baseu_net-2.6 -lwx_baseu-2.6
|
||||
WX_LIBS = -pthread -lwx_gtk2u_xrc-2.6 -lwx_gtk2u_qa-2.6 -lwx_gtk2u_html-2.6 \
|
||||
-lwx_gtk2u_adv-2.6 -lwx_gtk2u_core-2.6 -lwx_baseu_xml-2.6 -lwx_baseu_net-2.6 -lwx_baseu-2.6
|
||||
|
||||
LIBS=-lstdc++ -lportaudio
|
||||
|
||||
CPPFLAGS = -Wall -g -fexceptions -I/usr/lib/wx/include/gtk2-unicode-release-2.6 -I/usr/include/wx-2.6 -DGTK_NO_CHECK_CASTS -D__WXGTK__ -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES -D_LARGEFILE_SOURCE=1 -DNO_GCC_PRAGMA -D_ESPEAKEDIT
|
||||
CPPFLAGS = -Wall -g -fexceptions -I/usr/lib/wx/include/gtk2-unicode-release-2.6
|
||||
-I/usr/include/wx-2.6 \
|
||||
-DGTK_NO_CHECK_CASTS -D__WXGTK__ -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES
|
||||
-D_LARGEFILE_SOURCE=1 -DNO_GCC_PRAGMA -D_ESPEAKEDIT
|
||||
|
||||
CXXFLAGS = -O2 -g0 -Wall -g -fexceptions -I/usr/lib/wx/include/gtk2-unicode-release-2.6 -I/usr/include/wx-2.6 -DGTK_NO_CHECK_CASTS -D__WXGTK__ -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES -D_LARGEFILE_SOURCE=1 -DNO_GCC_PRAGMA -D_ESPEAKEDIT
|
||||
CXXFLAGS = -O2 -g0 -Wall -g -fexceptions -I/usr/lib/wx/include/gtk2-unicode-release-2.6 -I/usr/include/wx-2.6 \
|
||||
-DGTK_NO_CHECK_CASTS -D__WXGTK__ -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES -D_LARGEFILE_SOURCE=1 -DNO_GCC_PRAGMA -D_ESPEAKEDIT
|
||||
|
||||
all: espeakedit
|
||||
|
||||
|
||||
+4
-4
@@ -1744,12 +1744,12 @@ static int LoadWavefile(FILE *f, const char *fname)
|
||||
sr2 = Read4Bytes(f);
|
||||
fseek(f,40,SEEK_SET);
|
||||
|
||||
if((sr1 != samplerate) || (sr2 != sr1*2))
|
||||
if((sr1 != samplerate_native) || (sr2 != sr1*2))
|
||||
{
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
if(sr1 != samplerate)
|
||||
if(sr1 != samplerate_native)
|
||||
{
|
||||
fprintf(f_errors,"Wrong samplerate %d, wants %d\n",sr1,samplerate);
|
||||
fprintf(f_errors,"Wrong samplerate %d, wants %d\n",sr1,samplerate_native);
|
||||
error("Wrong samplerate: %s",fname);
|
||||
}
|
||||
if(sr2 != sr1*2)
|
||||
@@ -1764,7 +1764,7 @@ static int LoadWavefile(FILE *f, const char *fname)
|
||||
if((fd_temp = mkstemp(fname_temp)) >= 0)
|
||||
{
|
||||
close(fd_temp);
|
||||
sprintf(command,"sox \"%s%s.wav\" -r %d -c 1 -w %s polyphase\n",path_source,fname,samplerate,fname_temp);
|
||||
sprintf(command,"sox \"%s%s.wav\" -r %d -c 1 -w %s polyphase\n",path_source,fname,samplerate_native,fname_temp);
|
||||
if(system(command) < 0)
|
||||
{
|
||||
error("Failed to resample: %s",command);
|
||||
|
||||
+49
-28
@@ -66,8 +66,10 @@ static const char *help_text =
|
||||
"\t directory. <voice name> specifies the language\n"
|
||||
"--path=\"<path>\"\n"
|
||||
"\t Specifies the directory containing the espeak-data directory\n"
|
||||
"--pho\n"
|
||||
"\t Write mbrola phoneme data (.pho) to stdout, or to the file in --phonout\n"
|
||||
"--phonout=\"<filename>\"\n"
|
||||
"\t Write output from -x -X commands, and mbrola phoneme data, to this file\n"
|
||||
"\t Write phoneme output from -x -X and --pho to this file\n"
|
||||
"--punct=\"<characters>\"\n"
|
||||
"\t Speak the names of punctuation characters during speaking. If\n"
|
||||
"\t =<characters> is omitted, all punctuation is spoken.\n"
|
||||
@@ -87,6 +89,7 @@ int samplerate;
|
||||
int quiet = 0;
|
||||
unsigned int samples_total = 0;
|
||||
unsigned int samples_split = 0;
|
||||
unsigned int samples_split_seconds = 0;
|
||||
unsigned int wavefile_count = 0;
|
||||
|
||||
FILE *f_wavfile = NULL;
|
||||
@@ -218,15 +221,18 @@ int OpenWavFile(char *path, int rate)
|
||||
else
|
||||
f_wavfile = fopen(path,"wb");
|
||||
|
||||
if(f_wavfile != NULL)
|
||||
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,12,f_wavfile);
|
||||
return(0);
|
||||
fprintf(stderr,"Can't write to: '%s'\n",path);
|
||||
return(1);
|
||||
}
|
||||
return(1);
|
||||
|
||||
|
||||
fwrite(wave_hdr,1,24,f_wavfile);
|
||||
Write4Bytes(f_wavfile,rate);
|
||||
Write4Bytes(f_wavfile,rate * 2);
|
||||
fwrite(&wave_hdr[32],1,12,f_wavfile);
|
||||
return(0);
|
||||
} // end of OpenWavFile
|
||||
|
||||
|
||||
@@ -266,25 +272,40 @@ static int SynthCallback(short *wav, int numsamples, espeak_EVENT *events)
|
||||
return(0);
|
||||
}
|
||||
|
||||
if(samples_split > 0)
|
||||
while(events->type != 0)
|
||||
{
|
||||
// start a new WAV file when this limit is reached, at the next sentence boundary
|
||||
while(events->type != 0)
|
||||
if(events->type == espeakEVENT_SAMPLERATE)
|
||||
{
|
||||
if((events->type == espeakEVENT_SENTENCE) && (samples_total > samples_split))
|
||||
samplerate = events->id.number;
|
||||
samples_split = samples_split_seconds * samplerate;
|
||||
}
|
||||
else
|
||||
if(events->type == espeakEVENT_SENTENCE)
|
||||
{
|
||||
// start a new WAV file when the limit is reached, at this sentence boundary
|
||||
if((samples_split > 0) && (samples_total > samples_split))
|
||||
{
|
||||
CloseWavFile();
|
||||
samples_total = 0;
|
||||
wavefile_count++;
|
||||
}
|
||||
events++;
|
||||
}
|
||||
events++;
|
||||
}
|
||||
|
||||
if(f_wavfile == NULL)
|
||||
{
|
||||
sprintf(fname,"%s_%.2d%s",wavefile,++wavefile_count,filetype);
|
||||
if(OpenWavFile(fname, samplerate) != 0)
|
||||
return(1);
|
||||
if(samples_split > 0)
|
||||
{
|
||||
sprintf(fname,"%s_%.2d%s",wavefile,wavefile_count+1,filetype);
|
||||
if(OpenWavFile(fname, samplerate) != 0)
|
||||
return(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(OpenWavFile(wavefile, samplerate) != 0)
|
||||
return(1);
|
||||
}
|
||||
}
|
||||
|
||||
if(numsamples > 0)
|
||||
@@ -332,7 +353,8 @@ int main (int argc, char **argv)
|
||||
{"stdout", no_argument, 0, 0x105},
|
||||
{"split", optional_argument, 0, 0x106},
|
||||
{"path", required_argument, 0, 0x107},
|
||||
{"phonout", required_argument, 0, 0x108},
|
||||
{"phonout", required_argument, 0, 0x108},
|
||||
{"pho", no_argument, 0, 0x109},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
@@ -360,7 +382,8 @@ int main (int argc, char **argv)
|
||||
int wordgap = -1;
|
||||
int option_capitals = -1;
|
||||
int option_punctuation = -1;
|
||||
int option_phonemes = -1;
|
||||
int option_phonemes = 0;
|
||||
int option_mbrola_phonemes = 0;
|
||||
int option_linelength = 0;
|
||||
int option_waveout = 0;
|
||||
|
||||
@@ -550,9 +573,9 @@ int main (int argc, char **argv)
|
||||
|
||||
case 0x106: // -- split
|
||||
if(optarg2 == NULL)
|
||||
samples_split = 30; // default 30 minutes
|
||||
samples_split = 30 * 60; // default 30 minutes
|
||||
else
|
||||
samples_split = atoi(optarg2);
|
||||
samples_split = atoi(optarg2) * 60;
|
||||
break;
|
||||
|
||||
case 0x107: // --path
|
||||
@@ -566,6 +589,10 @@ int main (int argc, char **argv)
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x109: // --pho
|
||||
option_mbrola_phonemes = 8;
|
||||
break;
|
||||
|
||||
default:
|
||||
exit(0);
|
||||
}
|
||||
@@ -576,7 +603,7 @@ int main (int argc, char **argv)
|
||||
{
|
||||
// writing to a file (or no output), we can use synchronous mode
|
||||
samplerate = espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS,0,data_path,0);
|
||||
samples_split = (samplerate * samples_split) * 60;
|
||||
samples_split = samplerate * samples_split_seconds;
|
||||
|
||||
espeak_SetSynthCallback(SynthCallback);
|
||||
if(samples_split)
|
||||
@@ -589,12 +616,6 @@ int main (int argc, char **argv)
|
||||
*extn = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
if(option_waveout)
|
||||
{
|
||||
if(OpenWavFile(wavefile,samplerate) != 0)
|
||||
exit(4);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -641,7 +662,7 @@ int main (int argc, char **argv)
|
||||
espeak_SetParameter(espeakLINELENGTH,option_linelength,0);
|
||||
if(option_punctuation == 2)
|
||||
espeak_SetPunctuationList(option_punctlist);
|
||||
espeak_SetPhonemeTrace(option_phonemes,f_phonemes_out);
|
||||
espeak_SetPhonemeTrace(option_phonemes | option_mbrola_phonemes,f_phonemes_out);
|
||||
|
||||
if(filename[0]==0)
|
||||
{
|
||||
|
||||
@@ -129,8 +129,6 @@ char param[80];
|
||||
|
||||
if(argc > 1)
|
||||
{
|
||||
extern void VoiceReset(int control);
|
||||
|
||||
p = argv[1];
|
||||
j = 0;
|
||||
while((param[j] = p[j]) != 0) j++;
|
||||
|
||||
-205
@@ -1,205 +0,0 @@
|
||||
#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 3 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
|
||||
@@ -0,0 +1,578 @@
|
||||
/*
|
||||
* mbrowrap -- A wrapper library around the mbrola binary
|
||||
* providing a subset of the API from the Windows mbrola DLL.
|
||||
*
|
||||
* Copyright (C) 2010 by Nicolas Pitre <nico@fluxnic.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 3 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.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "mbrowrap.h"
|
||||
|
||||
|
||||
/*
|
||||
* mbrola instance parameters
|
||||
*/
|
||||
|
||||
enum mbr_state {
|
||||
MBR_INACTIVE = 0,
|
||||
MBR_IDLE,
|
||||
MBR_NEWDATA,
|
||||
MBR_AUDIO,
|
||||
MBR_WEDGED
|
||||
};
|
||||
|
||||
static enum mbr_state mbr_state;
|
||||
|
||||
static char *mbr_voice_path;
|
||||
static int mbr_cmd_fd, mbr_audio_fd, mbr_error_fd, mbr_proc_stat;
|
||||
static pid_t mbr_pid;
|
||||
static int mbr_samplerate;
|
||||
static float mbr_volume = 1.0;
|
||||
static char mbr_errorbuf[160];
|
||||
|
||||
struct datablock {
|
||||
struct datablock *next;
|
||||
int done;
|
||||
int size;
|
||||
char buffer[1]; /* 1 or more, dynamically allocated */
|
||||
};
|
||||
|
||||
static struct datablock *mbr_pending_data_head, *mbr_pending_data_tail;
|
||||
|
||||
/*
|
||||
* Private support code.
|
||||
*/
|
||||
|
||||
static void log(const char *msg, ...)
|
||||
{
|
||||
va_list params;
|
||||
|
||||
va_start(params, msg);
|
||||
vfprintf(stderr, msg, params);
|
||||
fputc('\n', stderr);
|
||||
va_end(params);
|
||||
}
|
||||
|
||||
static void err(const char *err, ...)
|
||||
{
|
||||
va_list params;
|
||||
|
||||
va_start(params, err);
|
||||
vsnprintf(mbr_errorbuf, sizeof(mbr_errorbuf), err, params);
|
||||
va_end(params);
|
||||
log("mbrola error: \"%s\"", mbr_errorbuf);
|
||||
}
|
||||
|
||||
static int create_pipes(int p1[2], int p2[2], int p3[2])
|
||||
{
|
||||
int error=0;
|
||||
|
||||
if (pipe(p1) != -1) {
|
||||
if (pipe(p2) != -1) {
|
||||
if (pipe(p3) != -1) {
|
||||
return 0;
|
||||
} else
|
||||
error = errno;
|
||||
close(p2[0]);
|
||||
close(p2[1]);
|
||||
} else
|
||||
error = errno;
|
||||
close(p1[0]);
|
||||
close(p1[1]);
|
||||
}
|
||||
|
||||
err("pipe(): %s", strerror(error));
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void close_pipes(int p1[2], int p2[2], int p3[2])
|
||||
{
|
||||
close(p1[0]);
|
||||
close(p1[1]);
|
||||
close(p2[0]);
|
||||
close(p2[1]);
|
||||
close(p3[0]);
|
||||
close(p3[1]);
|
||||
}
|
||||
|
||||
static int start_mbrola(const char *voice_path)
|
||||
{
|
||||
int error, p_stdin[2], p_stdout[2], p_stderr[2];
|
||||
char charbuf[20];
|
||||
|
||||
if (mbr_state != MBR_INACTIVE) {
|
||||
err("mbrola init request when already initialized");
|
||||
return -1;
|
||||
}
|
||||
|
||||
error = create_pipes(p_stdin, p_stdout, p_stderr);
|
||||
if (error)
|
||||
return -1;
|
||||
|
||||
mbr_pid = fork();
|
||||
|
||||
if (mbr_pid == -1) {
|
||||
error = errno;
|
||||
close_pipes(p_stdin, p_stdout, p_stderr);
|
||||
err("fork(): %s", strerror(error));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mbr_pid == 0) {
|
||||
int i;
|
||||
|
||||
if (dup2(p_stdin[0], 0) == -1 ||
|
||||
dup2(p_stdout[1], 1) == -1 ||
|
||||
dup2(p_stderr[1], 2) == -1) {
|
||||
snprintf(mbr_errorbuf, sizeof(mbr_errorbuf),
|
||||
"dup2(): %s\n", strerror(errno));
|
||||
write(p_stderr[1], mbr_errorbuf, strlen(mbr_errorbuf));
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
for (i = p_stderr[1]; i > 2; i--)
|
||||
close(i);
|
||||
signal(SIGHUP, SIG_IGN);
|
||||
signal(SIGINT, SIG_IGN);
|
||||
signal(SIGQUIT, SIG_IGN);
|
||||
signal(SIGTERM, SIG_IGN);
|
||||
|
||||
snprintf(charbuf, sizeof(charbuf), "%g", mbr_volume);
|
||||
execlp("mbrola", "mbrola", "-e", "-v", charbuf,
|
||||
voice_path, "-", "-.wav", (char *)NULL);
|
||||
/* if execution reaches this point then the exec() failed */
|
||||
snprintf(mbr_errorbuf, sizeof(mbr_errorbuf),
|
||||
"mbrola: %s\n", strerror(errno));
|
||||
write(2, mbr_errorbuf, strlen(mbr_errorbuf));
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
snprintf(charbuf, sizeof(charbuf), "/proc/%d/stat", mbr_pid);
|
||||
mbr_proc_stat = open(charbuf, O_RDONLY);
|
||||
if (mbr_proc_stat == -1) {
|
||||
error = errno;
|
||||
close_pipes(p_stdin, p_stdout, p_stderr);
|
||||
waitpid(mbr_pid, NULL, 0);
|
||||
mbr_pid = 0;
|
||||
err("/proc is unaccessible: %s", strerror(error));
|
||||
return -1;
|
||||
}
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
if (fcntl(p_stdin[1], F_SETFL, O_NONBLOCK) == -1 ||
|
||||
fcntl(p_stdout[0], F_SETFL, O_NONBLOCK) == -1 ||
|
||||
fcntl(p_stderr[0], F_SETFL, O_NONBLOCK) == -1) {
|
||||
error = errno;
|
||||
close_pipes(p_stdin, p_stdout, p_stderr);
|
||||
waitpid(mbr_pid, NULL, 0);
|
||||
mbr_pid = 0;
|
||||
err("fcntl(): %s", strerror(error));
|
||||
return -1;
|
||||
}
|
||||
|
||||
mbr_cmd_fd = p_stdin[1];
|
||||
mbr_audio_fd = p_stdout[0];
|
||||
mbr_error_fd = p_stderr[0];
|
||||
close(p_stdin[0]);
|
||||
close(p_stdout[1]);
|
||||
close(p_stderr[1]);
|
||||
|
||||
mbr_state = MBR_IDLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stop_mbrola(void)
|
||||
{
|
||||
if (mbr_state == MBR_INACTIVE)
|
||||
return;
|
||||
close(mbr_proc_stat);
|
||||
close(mbr_cmd_fd);
|
||||
close(mbr_audio_fd);
|
||||
close(mbr_error_fd);
|
||||
if (mbr_pid) {
|
||||
kill(mbr_pid, SIGTERM);
|
||||
waitpid(mbr_pid, NULL, 0);
|
||||
mbr_pid = 0;
|
||||
}
|
||||
mbr_state = MBR_INACTIVE;
|
||||
}
|
||||
|
||||
static void free_pending_data(void)
|
||||
{
|
||||
struct datablock *p, *head = mbr_pending_data_head;
|
||||
while (head) {
|
||||
p = head;
|
||||
head = head->next;
|
||||
free(p);
|
||||
}
|
||||
mbr_pending_data_head = NULL;
|
||||
mbr_pending_data_tail = NULL;
|
||||
}
|
||||
|
||||
static int mbrola_has_errors(void)
|
||||
{
|
||||
int result, error;
|
||||
|
||||
result = read(mbr_error_fd, mbr_errorbuf, sizeof(mbr_errorbuf)-1);
|
||||
|
||||
if (result == -1) {
|
||||
if (errno == EAGAIN)
|
||||
return 0;
|
||||
err("read(error): %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (result != 0) {
|
||||
mbr_errorbuf[result] = 0;
|
||||
if (mbr_errorbuf[result - 1] == '\n')
|
||||
mbr_errorbuf[result - 1] = 0;
|
||||
/* inhibit the reset signal message */
|
||||
if (strncmp(mbr_errorbuf, "Got a reset signal", 18) == 0) {
|
||||
mbr_errorbuf[0] = 0;
|
||||
return 0;
|
||||
}
|
||||
/* don't consider this fatal at this point */
|
||||
error = 0;
|
||||
} else {
|
||||
/* EOF on stderr, assume mbrola died. */
|
||||
pid_t pid;
|
||||
int status, len;
|
||||
const char *msg;
|
||||
char msgbuf[80];
|
||||
|
||||
pid = waitpid(mbr_pid, &status, WNOHANG);
|
||||
if (!pid) {
|
||||
msg = "mbrola closed stderr and did not exit";
|
||||
} else {
|
||||
mbr_pid = 0;
|
||||
if (WIFSIGNALED(status)) {
|
||||
int sig = WTERMSIG(status);
|
||||
snprintf(msgbuf, sizeof(msgbuf),
|
||||
"mbrola died by signal %d", sig);
|
||||
msg = msgbuf;
|
||||
} else if (WIFEXITED(status)) {
|
||||
int exst = WEXITSTATUS(status);
|
||||
snprintf(msgbuf, sizeof(msgbuf),
|
||||
"mbrola exited with status %d", exst);
|
||||
msg = msgbuf;
|
||||
} else {
|
||||
msg = "mbrola died and wait status is weird";
|
||||
}
|
||||
}
|
||||
len = strlen(mbr_errorbuf);
|
||||
if (!len)
|
||||
snprintf(mbr_errorbuf, sizeof(mbr_errorbuf), "%s", msg);
|
||||
else
|
||||
snprintf(mbr_errorbuf+len, sizeof(mbr_errorbuf)-len,
|
||||
", (%s)", msg);
|
||||
error = -1;
|
||||
}
|
||||
|
||||
log("mbrola error: \"%s\"", mbr_errorbuf);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int send_to_mbrola(const char *cmd)
|
||||
{
|
||||
ssize_t result;
|
||||
int len;
|
||||
|
||||
if (!mbr_pid)
|
||||
return -1;
|
||||
|
||||
len = strlen(cmd);
|
||||
result = write(mbr_cmd_fd, cmd, len);
|
||||
|
||||
if (result == -1) {
|
||||
int error = errno;
|
||||
if (error == EPIPE && mbrola_has_errors()) {
|
||||
return -1;
|
||||
} else if (error == EAGAIN) {
|
||||
result = 0;
|
||||
} else {
|
||||
err("write(): %s", strerror(error));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (result != len) {
|
||||
struct datablock *data;
|
||||
data = (struct datablock *)malloc(sizeof(*data) + len - result);
|
||||
if (data) {
|
||||
data->next = NULL;
|
||||
data->done = 0;
|
||||
data->size = len - result;
|
||||
memcpy(data->buffer, cmd + result, len - result);
|
||||
result = len;
|
||||
if (!mbr_pending_data_head)
|
||||
mbr_pending_data_head = data;
|
||||
else
|
||||
mbr_pending_data_tail->next = data;
|
||||
mbr_pending_data_tail = data;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int mbrola_is_idle(void)
|
||||
{
|
||||
char *p;
|
||||
char buffer[20]; /* looking for "12345 (mbrola) S" so 20 is plenty*/
|
||||
|
||||
/* look in /proc to determine if mbrola is still running or sleeping */
|
||||
if (lseek(mbr_proc_stat, 0, SEEK_SET) != 0)
|
||||
return 0;
|
||||
if (read(mbr_proc_stat, buffer, sizeof(buffer)) != sizeof(buffer))
|
||||
return 0;
|
||||
p = (char *)memchr(buffer, ')', sizeof(buffer));
|
||||
if (!p || (unsigned)(p - buffer) >= sizeof(buffer) - 2)
|
||||
return 0;
|
||||
return (p[1] == ' ' && p[2] == 'S');
|
||||
}
|
||||
|
||||
static ssize_t receive_from_mbrola(void *buffer, size_t bufsize)
|
||||
{
|
||||
int result, wait = 1;
|
||||
size_t cursize = 0;
|
||||
|
||||
if (!mbr_pid)
|
||||
return -1;
|
||||
|
||||
do {
|
||||
struct pollfd pollfd[3];
|
||||
nfds_t nfds = 0;
|
||||
int idle;
|
||||
|
||||
pollfd[0].fd = mbr_audio_fd;
|
||||
pollfd[0].events = POLLIN;
|
||||
nfds++;
|
||||
|
||||
pollfd[1].fd = mbr_error_fd;
|
||||
pollfd[1].events = POLLIN;
|
||||
nfds++;
|
||||
|
||||
if (mbr_pending_data_head) {
|
||||
pollfd[2].fd = mbr_cmd_fd;
|
||||
pollfd[2].events = POLLOUT;
|
||||
nfds++;
|
||||
}
|
||||
|
||||
idle = mbrola_is_idle();
|
||||
result = poll(pollfd, nfds, idle ? 0 : wait);
|
||||
if (result == -1) {
|
||||
err("poll(): %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (result == 0) {
|
||||
if (idle) {
|
||||
mbr_state = MBR_IDLE;
|
||||
break;
|
||||
} else {
|
||||
if (wait >= 5000 * (4-1)/4) {
|
||||
mbr_state = MBR_WEDGED;
|
||||
err("mbrola process is stalled");
|
||||
break;
|
||||
} else {
|
||||
wait *= 4;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
wait = 1;
|
||||
|
||||
if (pollfd[1].revents && mbrola_has_errors())
|
||||
return -1;
|
||||
|
||||
if (mbr_pending_data_head && pollfd[2].revents) {
|
||||
struct datablock *head = mbr_pending_data_head;
|
||||
char *data = head->buffer + head->done;
|
||||
int left = head->size - head->done;
|
||||
result = write(mbr_cmd_fd, data, left);
|
||||
if (result == -1) {
|
||||
int error = errno;
|
||||
if (error == EPIPE && mbrola_has_errors())
|
||||
return -1;
|
||||
err("write(): %s", strerror(error));
|
||||
return -1;
|
||||
}
|
||||
if (result != left) {
|
||||
head->done += result;
|
||||
} else {
|
||||
mbr_pending_data_head = head->next;
|
||||
free(head);
|
||||
if (!mbr_pending_data_head)
|
||||
mbr_pending_data_tail = NULL;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (pollfd[0].revents) {
|
||||
char *curpos = (char *)buffer + cursize;
|
||||
size_t space = bufsize - cursize;
|
||||
ssize_t obtained = read(mbr_audio_fd, curpos, space);
|
||||
if (obtained == -1) {
|
||||
err("read(): %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
cursize += obtained;
|
||||
mbr_state = MBR_AUDIO;
|
||||
}
|
||||
} while (cursize < bufsize);
|
||||
|
||||
return cursize;
|
||||
}
|
||||
|
||||
/*
|
||||
* API functions.
|
||||
*/
|
||||
|
||||
int init_MBR(const char *voice_path)
|
||||
{
|
||||
int error, result;
|
||||
unsigned char wavhdr[45];
|
||||
|
||||
error = start_mbrola(voice_path);
|
||||
if (error)
|
||||
return -1;
|
||||
|
||||
result = send_to_mbrola("#\n");
|
||||
if (result != 2) {
|
||||
stop_mbrola();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* we should actually be getting only 44 bytes */
|
||||
result = receive_from_mbrola(wavhdr, 45);
|
||||
if (result != 44) {
|
||||
err("unable to get .wav header from mbrola");
|
||||
stop_mbrola();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* parse wavhdr to get mbrola voice samplerate */
|
||||
if (memcmp(wavhdr, "RIFF", 4) != 0 ||
|
||||
memcmp(wavhdr+8, "WAVEfmt ", 8) != 0) {
|
||||
err("mbrola did not return a .wav header");
|
||||
stop_mbrola();
|
||||
return -1;
|
||||
}
|
||||
mbr_samplerate = wavhdr[24] + (wavhdr[25]<<8) +
|
||||
(wavhdr[26]<<16) + (wavhdr[27]<<24);
|
||||
//log("mbrola: voice samplerate = %d", mbr_samplerate);
|
||||
|
||||
/* remember the voice path for setVolumeRatio_MBR() */
|
||||
if (mbr_voice_path != voice_path) {
|
||||
free(mbr_voice_path);
|
||||
mbr_voice_path = strdup(voice_path);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void close_MBR(void)
|
||||
{
|
||||
stop_mbrola();
|
||||
free_pending_data();
|
||||
free(mbr_voice_path);
|
||||
mbr_voice_path = NULL;
|
||||
mbr_volume = 1.0;
|
||||
}
|
||||
|
||||
int reset_MBR()
|
||||
{
|
||||
int result, success = 1;
|
||||
char dummybuf[4096];
|
||||
|
||||
if (mbr_state == MBR_IDLE)
|
||||
return 1;
|
||||
if (!mbr_pid)
|
||||
return 0;
|
||||
if (kill(mbr_pid, SIGUSR1) == -1)
|
||||
success = 0;
|
||||
free_pending_data();
|
||||
result = write(mbr_cmd_fd, "\n#\n", 3);
|
||||
if (result != 3)
|
||||
success = 0;
|
||||
do {
|
||||
result = read(mbr_audio_fd, dummybuf, sizeof(dummybuf));
|
||||
} while (result > 0);
|
||||
if (result != -1 || errno != EAGAIN)
|
||||
success = 0;
|
||||
if (!mbrola_has_errors() && success)
|
||||
mbr_state = MBR_IDLE;
|
||||
return success;
|
||||
}
|
||||
|
||||
int read_MBR(void *buffer, int nb_samples)
|
||||
{
|
||||
int result = receive_from_mbrola(buffer, nb_samples * 2);
|
||||
if (result > 0)
|
||||
result /= 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
int write_MBR(const char *data)
|
||||
{
|
||||
mbr_state = MBR_NEWDATA;
|
||||
return send_to_mbrola(data);
|
||||
}
|
||||
|
||||
int flush_MBR(void)
|
||||
{
|
||||
return send_to_mbrola("\n#\n") == 3;
|
||||
}
|
||||
|
||||
int getFreq_MBR(void)
|
||||
{
|
||||
return mbr_samplerate;
|
||||
}
|
||||
|
||||
void setVolumeRatio_MBR(float value)
|
||||
{
|
||||
if (value == mbr_volume)
|
||||
return;
|
||||
mbr_volume = value;
|
||||
if (mbr_state != MBR_IDLE)
|
||||
return;
|
||||
/*
|
||||
* We have no choice but to kill and restart mbrola with
|
||||
* the new argument here.
|
||||
*/
|
||||
stop_mbrola();
|
||||
init_MBR(mbr_voice_path);
|
||||
}
|
||||
|
||||
int lastErrorStr_MBR(char *buffer, int bufsize)
|
||||
{
|
||||
int result = snprintf(buffer, bufsize, "%s", mbr_errorbuf);
|
||||
return result >= bufsize ? (bufsize - 1) : result;
|
||||
}
|
||||
|
||||
void resetError_MBR(void)
|
||||
{
|
||||
mbr_errorbuf[0] = 0;
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* mbrowrap -- A wrapper library around the mbrola binary
|
||||
* providing a subset of the API from the Windows mbrola DLL.
|
||||
*
|
||||
* Copyright (C) 2010 by Nicolas Pitre <nico@fluxnic.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 3 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.
|
||||
*/
|
||||
|
||||
#ifndef MBROWRAP_H
|
||||
#define MBROWRAP_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Initialize mbrola. The 'voice_path' argument must contain the
|
||||
* path and file name to the mbrola voice database to be used. Returned
|
||||
* value is 0 on success, or an error code otherwise (currently only -1
|
||||
* is returned. If not successful, lastErrorStr_MBR() will provide the
|
||||
* error reason. If this is successful, then close_MBR() must be called
|
||||
* before init_MBR() can be called again.
|
||||
*/
|
||||
int init_MBR(const char *voice_path);
|
||||
|
||||
/*
|
||||
* Stop mbrola and release any resources. It is necessary to call
|
||||
* this after a successful call to init_MBR() before init_MBR() can be
|
||||
* called again.
|
||||
*/
|
||||
void close_MBR(void);
|
||||
|
||||
/*
|
||||
* Stop any ongoing processing and flush all buffers. After this call
|
||||
* any synthesis request will start afresh. A non-zero value is returned
|
||||
* on success, or 0 on failure. If not successful, lastErrorStr_MBR() will
|
||||
* provide the error reason.
|
||||
*/
|
||||
int reset_MBR();
|
||||
|
||||
/*
|
||||
* Return at most 'nb_samples' audio samples into 'buffer'. The returned
|
||||
* value is the actual number of samples returned, or -1 on error.
|
||||
* If not successful, lastErrorStr_MBR() will provide the error reason.
|
||||
* Samples are always 16-bit little endian.
|
||||
*/
|
||||
int read_MBR(void *buffer, int nb_samples);
|
||||
|
||||
/*
|
||||
* Write a NULL terminated string of phoneme in the input buffer.
|
||||
* Return the number of chars actually written, or -1 on error.
|
||||
* If not successful, lastErrorStr_MBR() will provide the error reason.
|
||||
*/
|
||||
int write_MBR(const char *data);
|
||||
|
||||
/*
|
||||
* Send a flush command to the mbrola input stream.
|
||||
* This is currently similar to write_MBR("#\n"). Return 1 on success
|
||||
* or 0 on failure. If not successful, lastErrorStr_MBR() will provide
|
||||
* the error reason.
|
||||
*/
|
||||
int flush_MBR(void);
|
||||
|
||||
/*
|
||||
* Return the audio sample frequency of the used voice database.
|
||||
*/
|
||||
int getFreq_MBR(void);
|
||||
|
||||
/*
|
||||
* Overall volume.
|
||||
*/
|
||||
void setVolumeRatio_MBR(float value);
|
||||
|
||||
/*
|
||||
* Copy into 'buffer' at most 'bufsize' bytes from the latest error
|
||||
* message. This may also contain non-fatal errors from mbrola. The
|
||||
* returned value is the actual number of bytes copied. When no error
|
||||
* message is pending then an empty string is returned. Consecutive
|
||||
* calls to lastErrorStr_MBR() will return the same message unless it
|
||||
* is explicitly cleared with resetError_MBR().
|
||||
*/
|
||||
int lastErrorStr_MBR(char *buffer, int bufsize);
|
||||
|
||||
/*
|
||||
* Clear any pending error message.
|
||||
*/
|
||||
void resetError_MBR(void);
|
||||
|
||||
/*
|
||||
* Tolerance to missing diphones (always active so this is ignored)
|
||||
*/
|
||||
static inline void setNoError_MBR(int no_error) { }
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -140,7 +140,6 @@ void SetSpeed(int control)
|
||||
int s1;
|
||||
int wpm;
|
||||
int wpm2;
|
||||
int test=0;
|
||||
|
||||
speed.loud_consonants = 0;
|
||||
speed.min_sample_len = 450;
|
||||
@@ -172,43 +171,6 @@ void SetSpeed(int control)
|
||||
speed.loud_consonants = (wpm - 360) / 8;
|
||||
}
|
||||
|
||||
#ifdef deleted
|
||||
if(wpm >= 450)
|
||||
{
|
||||
// TEST
|
||||
speed1 = 6;
|
||||
speed2 = speed3 = 5;
|
||||
speed.pause_factor = 12;
|
||||
speed.clause_pause_factor = 15;
|
||||
speed.wav_factor = 70;
|
||||
speed.lenmod_factor = 53;
|
||||
speed.min_sample_len = 400;
|
||||
return;
|
||||
}
|
||||
if(wpm > 390)
|
||||
{
|
||||
// TEST
|
||||
speed1 = 6;
|
||||
speed2 = speed3 = 5;
|
||||
speed.pause_factor = 13;
|
||||
speed.clause_pause_factor = 15;
|
||||
|
||||
if(wpm >= 440)
|
||||
{
|
||||
speed.wav_factor = 72 - (wpm - 440)/4;
|
||||
speed.lenmod_factor = 60 - (wpm - 440)/2;
|
||||
speed.min_sample_len = 420 - (wpm - 440);
|
||||
}
|
||||
else
|
||||
{
|
||||
speed.wav_factor = 74;
|
||||
speed.lenmod_factor = 65 - (wpm - 400)/10;
|
||||
speed.min_sample_len = 450 - (wpm - 400)/2;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
wpm2 = wpm;
|
||||
if(wpm > 359) wpm2 = 359;
|
||||
if(wpm < 80) wpm2 = 80;
|
||||
@@ -269,9 +231,6 @@ void SetSpeed(int control)
|
||||
speed.min_sample_len = 420 - (wpm - 440);
|
||||
}
|
||||
|
||||
if(test > 0)
|
||||
speed.wav_factor = test;
|
||||
|
||||
speed.pause_factor = (256 * s1)/115; // full speed adjustment, used for pause length
|
||||
speed.clause_pause_factor = 0;
|
||||
|
||||
|
||||
+12
-3
@@ -98,8 +98,10 @@ static const char *help_text =
|
||||
"\t directory. <voice name> specifies the language\n"
|
||||
"--path=\"<path>\"\n"
|
||||
"\t Specifies the directory containing the espeak-data directory\n"
|
||||
"--pho\n"
|
||||
"\t Write mbrola phoneme data (.pho) to stdout, or to the file in --phonout\n"
|
||||
"--phonout=\"<filename>\"\n"
|
||||
"\t Write output from -x -X commands, and mbrola phoneme data, to this file\n"
|
||||
"\t Write phoneme output from -x -X and --pho to this file\n"
|
||||
"--punct=\"<characters>\"\n"
|
||||
"\t Speak the names of punctuation characters during speaking. If\n"
|
||||
"\t =<characters> is omitted, all punctuation is spoken.\n"
|
||||
@@ -214,7 +216,9 @@ void DisplayVoices(FILE *f_out, char *language)
|
||||
} // end of DisplayVoices
|
||||
|
||||
|
||||
|
||||
void WVoiceChanged(voice_t *wvoice)
|
||||
{
|
||||
}
|
||||
|
||||
static int OpenWaveFile(const char *path, int rate)
|
||||
//=================================================
|
||||
@@ -472,7 +476,8 @@ int main (int argc, char **argv)
|
||||
{"stdout", no_argument, 0, 0x105},
|
||||
{"split", optional_argument, 0, 0x106},
|
||||
{"path", required_argument, 0, 0x107},
|
||||
{"phonout", required_argument, 0, 0x108},
|
||||
{"phonout", required_argument, 0, 0x108},
|
||||
{"pho", no_argument, 0, 0x109},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
@@ -702,6 +707,10 @@ int main (int argc, char **argv)
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x109: // --pho
|
||||
option_mbrola_phonemes = 8;
|
||||
break;
|
||||
|
||||
default:
|
||||
exit(0);
|
||||
}
|
||||
|
||||
+49
-10
@@ -59,6 +59,9 @@ static unsigned int my_unique_identifier=0;
|
||||
static void* my_user_data=NULL;
|
||||
static espeak_AUDIO_OUTPUT my_mode=AUDIO_OUTPUT_SYNCHRONOUS;
|
||||
static int synchronous_mode = 1;
|
||||
static int out_samplerate = 0;
|
||||
static int voice_samplerate = 22050;
|
||||
|
||||
t_espeak_callback* synth_callback = NULL;
|
||||
int (* uri_callback)(int, const char *, const char *) = NULL;
|
||||
int (* phoneme_callback)(const char *) = NULL;
|
||||
@@ -66,6 +69,13 @@ int (* phoneme_callback)(const char *) = NULL;
|
||||
char path_home[N_PATH_HOME]; // this is the espeak-data directory
|
||||
|
||||
|
||||
void WVoiceChanged(voice_t *wvoice)
|
||||
{//=================================
|
||||
// Voice change in wavegen
|
||||
voice_samplerate = wvoice->samplerate;
|
||||
}
|
||||
|
||||
|
||||
#ifdef USE_ASYNC
|
||||
|
||||
static int dispatch_audio(short* outbuf, int length, espeak_EVENT* event)
|
||||
@@ -85,6 +95,34 @@ static int dispatch_audio(short* outbuf, int length, espeak_EVENT* event)
|
||||
{
|
||||
case AUDIO_OUTPUT_PLAYBACK:
|
||||
{
|
||||
int event_type=0;
|
||||
int event_data=0;
|
||||
if(event)
|
||||
{
|
||||
event_type = event->type;
|
||||
event_data = event->id.number;
|
||||
}
|
||||
|
||||
if(event_type == espeakEVENT_SAMPLERATE)
|
||||
{
|
||||
voice_samplerate = event->id.number;
|
||||
|
||||
if(out_samplerate != voice_samplerate)
|
||||
{
|
||||
if(out_samplerate != 0)
|
||||
{
|
||||
// sound was previously open with a different sample rate
|
||||
wave_close(my_audio);
|
||||
sleep(1);
|
||||
}
|
||||
out_samplerate = voice_samplerate;
|
||||
wave_init(voice_samplerate);
|
||||
wave_set_callback_is_output_enabled( fifo_is_command_enabled);
|
||||
my_audio = wave_open("alsa");
|
||||
event_init();
|
||||
}
|
||||
}
|
||||
|
||||
if (outbuf && length && a_wave_can_be_played)
|
||||
{
|
||||
wave_write (my_audio, (char*)outbuf, 2*length);
|
||||
@@ -220,17 +258,13 @@ static void select_output(espeak_AUDIO_OUTPUT output_type)
|
||||
my_audio = NULL;
|
||||
synchronous_mode = 1;
|
||||
option_waveout = 1; // inhibit portaudio callback from wavegen.cpp
|
||||
out_samplerate = 0;
|
||||
|
||||
switch(my_mode)
|
||||
{
|
||||
case AUDIO_OUTPUT_PLAYBACK:
|
||||
// wave_init() is now called just before the first wave_write()
|
||||
synchronous_mode = 0;
|
||||
#ifdef USE_ASYNC
|
||||
wave_init();
|
||||
wave_set_callback_is_output_enabled( fifo_is_command_enabled);
|
||||
my_audio = wave_open("alsa");
|
||||
event_init();
|
||||
#endif
|
||||
break;
|
||||
|
||||
case AUDIO_OUTPUT_RETRIEVAL:
|
||||
@@ -734,19 +768,21 @@ ENTER("espeak_Initialize");
|
||||
return(EE_INTERNAL_ERROR);
|
||||
|
||||
option_phonemes = 0;
|
||||
option_mbrola_phonemes = 0;
|
||||
option_phoneme_events = (options & 1);
|
||||
|
||||
SetVoiceByName("default");
|
||||
VoiceReset(0);
|
||||
// SetVoiceByName("default");
|
||||
|
||||
for(param=0; param<N_SPEECH_PARAM; param++)
|
||||
param_stack[0].parameter[param] = param_defaults[param];
|
||||
|
||||
SetParameter(espeakRATE,170,0);
|
||||
SetParameter(espeakRATE,175,0);
|
||||
SetParameter(espeakVOLUME,100,0);
|
||||
SetParameter(espeakCAPITALS,option_capitals,0);
|
||||
SetParameter(espeakPUNCTUATION,option_punctuation,0);
|
||||
SetParameter(espeakWORDGAP,0,0);
|
||||
DoVoiceChange(voice);
|
||||
// DoVoiceChange(voice);
|
||||
|
||||
#ifdef USE_ASYNC
|
||||
fifo_init();
|
||||
@@ -1087,8 +1123,10 @@ ESPEAK_API void espeak_SetPhonemeTrace(int value, FILE *stream)
|
||||
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)
|
||||
bit 3: produce mbrola pho data
|
||||
*/
|
||||
option_phonemes = value;
|
||||
option_phonemes = value & 3;
|
||||
option_mbrola_phonemes = value & 8;
|
||||
f_trans = stream;
|
||||
if(stream == NULL)
|
||||
f_trans = stderr;
|
||||
@@ -1163,6 +1201,7 @@ ESPEAK_API espeak_ERROR espeak_Terminate(void)
|
||||
{
|
||||
wave_close(my_audio);
|
||||
wave_terminate();
|
||||
out_samplerate = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+7
-6
@@ -50,12 +50,13 @@ Revision 5
|
||||
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 or clause
|
||||
espeakEVENT_MSG_TERMINATED, // End of message
|
||||
espeakEVENT_PHONEME // Phoneme, if enabled in espeak_Initialize()
|
||||
espeakEVENT_SENTENCE = 2, // Start of sentence
|
||||
espeakEVENT_MARK = 3, // Mark
|
||||
espeakEVENT_PLAY = 4, // Audio element
|
||||
espeakEVENT_END = 5, // End of sentence or clause
|
||||
espeakEVENT_MSG_TERMINATED = 6, // End of message
|
||||
espeakEVENT_PHONEME = 7, // Phoneme, if enabled in espeak_Initialize()
|
||||
espeakEVENT_SAMPLERATE = 8 // internal use, set sample rate
|
||||
} espeak_EVENT_TYPE;
|
||||
|
||||
|
||||
|
||||
@@ -44,7 +44,6 @@
|
||||
|
||||
#ifdef LIBRARY
|
||||
#define USE_ASYNC
|
||||
//#define USE_MBROLA_LIB
|
||||
#endif
|
||||
|
||||
#ifdef _ESPEAKEDIT
|
||||
|
||||
+104
-229
@@ -35,15 +35,11 @@
|
||||
|
||||
extern int Read4Bytes(FILE *f);
|
||||
extern void SetPitch2(voice_t *voice, int pitch1, int pitch2, int *pitch_base, int *pitch_range);
|
||||
|
||||
#ifdef USE_MBROLA_LIB
|
||||
|
||||
extern unsigned char *outbuf;
|
||||
|
||||
#ifndef PLATFORM_WINDOWS
|
||||
|
||||
#include "mbrolib.h"
|
||||
void * mb_handle;
|
||||
#include "mbrowrap.h"
|
||||
|
||||
#else
|
||||
#include <windows.h>
|
||||
@@ -64,7 +60,7 @@ PROCVV reset_MBR;
|
||||
PROCIV lastError_MBR;
|
||||
PROCVCI lastErrorStr_MBR;
|
||||
PROCVI setNoError_MBR;
|
||||
PROCVI setFreq_MBR;
|
||||
PROCIV getFreq_MBR;
|
||||
PROCVF setVolumeRatio_MBR;
|
||||
|
||||
|
||||
@@ -103,13 +99,11 @@ void unload_MBR()
|
||||
}
|
||||
|
||||
#endif // windows
|
||||
#endif // USE_MBROLA_LIB
|
||||
|
||||
|
||||
static MBROLA_TAB *mbrola_tab = NULL;
|
||||
static int mbrola_control = 0;
|
||||
|
||||
|
||||
int option_mbrola_phonemes;
|
||||
|
||||
|
||||
espeak_ERROR LoadMbrolaTable(const char *mbrola_voice, const char *phtrans, int srate)
|
||||
@@ -133,37 +127,35 @@ espeak_ERROR LoadMbrolaTable(const char *mbrola_voice, const char *phtrans, int
|
||||
}
|
||||
|
||||
sprintf(path,"%s/mbrola/%s",path_home,mbrola_voice);
|
||||
#ifdef USE_MBROLA_LIB
|
||||
#ifdef PLATFORM_POSIX
|
||||
if(GetFileLength(path) <= 0)
|
||||
{
|
||||
// mbrola voice file not found, look in /usr/share
|
||||
sprintf(path,"/usr/share/mbrola/%s",mbrola_voice);
|
||||
}
|
||||
#endif
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
if(load_MBR() == FALSE) // load mbrola.dll
|
||||
return(EE_INTERNAL_ERROR);
|
||||
#endif
|
||||
|
||||
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(srate);
|
||||
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)
|
||||
if((f_in = fopen(path,"r")) == NULL) {
|
||||
close_MBR();
|
||||
return(EE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if((mbrola_tab = (MBROLA_TAB *)realloc(mbrola_tab,size)) == NULL)
|
||||
{
|
||||
fclose(f_in);
|
||||
close_MBR();
|
||||
return(EE_INTERNAL_ERROR);
|
||||
}
|
||||
|
||||
@@ -176,26 +168,16 @@ espeak_ERROR LoadMbrolaTable(const char *mbrola_voice, const char *phtrans, int
|
||||
fread(mbrola_tab,size,1,f_in);
|
||||
fclose(f_in);
|
||||
|
||||
|
||||
#ifdef USE_MBROLA_LIB
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
setVolumeRatio_MBR((float)(mbrola_control & 0xff) /16.0f);
|
||||
#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;
|
||||
// srate = getFreq_MBR();
|
||||
samplerate = srate;
|
||||
if(srate == 22050)
|
||||
SetParameter(espeakVOICETYPE,0,0);
|
||||
else
|
||||
SetParameter(espeakVOICETYPE,1,0);
|
||||
strcpy(mbrola_name,mbrola_voice);
|
||||
mbrola_delay = 3800; // improve synchronization of events
|
||||
// mbrola_delay = 3800; // improve synchronization of events
|
||||
mbrola_delay = 1000; // improve synchronization of events
|
||||
return(EE_OK);
|
||||
} // end of LoadMbrolaTable
|
||||
|
||||
@@ -383,155 +365,10 @@ static char *WritePitch(int env, int pitch1, int pitch2, int split, int final)
|
||||
} // end of WritePitch
|
||||
|
||||
|
||||
#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;
|
||||
int sign=0;
|
||||
|
||||
do {
|
||||
word = embedded_list[embix++];
|
||||
value = word >> 8;
|
||||
command = word & 0x1f;
|
||||
|
||||
if((word & 0x60) == 0x60)
|
||||
sign = -1;
|
||||
else
|
||||
if((word & 0x60) == 0x40)
|
||||
sign = 1;
|
||||
|
||||
if(command < N_EMBEDDED_VALUES)
|
||||
{
|
||||
if(sign == 0)
|
||||
embedded_value[command] = value;
|
||||
else
|
||||
embedded_value[command] += (value * sign);
|
||||
}
|
||||
|
||||
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
|
||||
static 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
|
||||
event_list[event_list_ix].user_data = 0;
|
||||
}
|
||||
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
|
||||
|
||||
static 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
|
||||
event_list[event_list_ix].user_data = 0;
|
||||
}
|
||||
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)
|
||||
{//======================================================================
|
||||
int MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, int resume, FILE *f_mbrola)
|
||||
{//=================================================================================
|
||||
// Generate a mbrola pho file
|
||||
unsigned int name;
|
||||
int phix;
|
||||
int len;
|
||||
int len1;
|
||||
PHONEME_TAB *ph;
|
||||
@@ -552,21 +389,21 @@ void MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, FILE *f_mbrola)
|
||||
char buf[80];
|
||||
char mbr_buf[120];
|
||||
|
||||
#ifdef USE_MBROLA_LIB
|
||||
int embedded_ix=0;
|
||||
int word_count=0;
|
||||
static int phix;
|
||||
static int embedded_ix;
|
||||
static int word_count;
|
||||
|
||||
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
|
||||
if (!resume) {
|
||||
phix = 1;
|
||||
embedded_ix = 0;
|
||||
word_count = 0;
|
||||
}
|
||||
|
||||
for(phix=1; phix < n_phonemes; phix++)
|
||||
while (phix < n_phonemes)
|
||||
{
|
||||
if (WcmdqFree() < MIN_WCMDQ)
|
||||
return 1;
|
||||
|
||||
mbr_buf[0] = 0;
|
||||
|
||||
p = &plist[phix];
|
||||
@@ -576,24 +413,24 @@ void MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, FILE *f_mbrola)
|
||||
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);
|
||||
DoEmbedded(embedded_ix, p->sourceix);
|
||||
}
|
||||
if(p->newword & 4)
|
||||
MbrolaMarker(espeakEVENT_SENTENCE, (p->sourceix & 0x7ff) + clause_start_char, 0, count_sentences);
|
||||
|
||||
if(p->newword & 4)
|
||||
DoMarker(espeakEVENT_SENTENCE, (p->sourceix & 0x7ff) + clause_start_char, 0, count_sentences);
|
||||
if(p->newword & 1)
|
||||
MbrolaMarker(espeakEVENT_WORD, (p->sourceix & 0x7ff) + clause_start_char, p->sourceix >> 11, clause_start_word + word_count++);
|
||||
#endif
|
||||
DoMarker(espeakEVENT_WORD, (p->sourceix & 0x7ff) + clause_start_char, p->sourceix >> 11, clause_start_word + word_count++);
|
||||
|
||||
name = GetMbrName(p,ph,ph_prev,ph_next,&name2,&len_percent,&control);
|
||||
if(control & 1)
|
||||
phix++;
|
||||
|
||||
if(name == 0)
|
||||
if(name == 0) {
|
||||
phix++;
|
||||
continue; // ignore this phoneme
|
||||
}
|
||||
|
||||
if((ph->type == phPAUSE) && (name == ph->mnemonic))
|
||||
{
|
||||
@@ -607,9 +444,7 @@ void MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, FILE *f_mbrola)
|
||||
else
|
||||
len = (80 * speed.wav_factor)/256;
|
||||
|
||||
#ifdef USE_MBROLA_LIB
|
||||
MbrolaMarker(espeakEVENT_PHONEME, (p->sourceix & 0x7ff) + clause_start_char, 0, ph->mnemonic);
|
||||
#endif
|
||||
DoMarker(espeakEVENT_PHONEME, (p->sourceix & 0x7ff) + clause_start_char, 0, ph->mnemonic);
|
||||
|
||||
sprintf(buf,"%s\t",WordToString(name));
|
||||
strcat(mbr_buf,buf);
|
||||
@@ -720,6 +555,7 @@ void MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, FILE *f_mbrola)
|
||||
|
||||
if(pause)
|
||||
{
|
||||
len += PauseLength(pause,0);
|
||||
sprintf(buf,"_ \t%d\n",PauseLength(pause,0));
|
||||
strcat(mbr_buf,buf);
|
||||
pause = 0;
|
||||
@@ -731,39 +567,78 @@ void MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, FILE *f_mbrola)
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef USE_MBROLA_LIB
|
||||
if(MbrolaSynth(mbr_buf) != 0)
|
||||
return;
|
||||
#endif
|
||||
int res = write_MBR(mbr_buf);
|
||||
if (res < 0)
|
||||
return 0; /* don't get stuck on error */
|
||||
if (res == 0)
|
||||
return 1;
|
||||
wcmdq[wcmdq_tail][0] = WCMD_MBROLA_DATA;
|
||||
wcmdq[wcmdq_tail][1] = len;
|
||||
WcmdqInc();
|
||||
}
|
||||
|
||||
phix++;
|
||||
}
|
||||
|
||||
#ifdef USE_MBROLA_LIB
|
||||
MbrolaSynth(NULL);
|
||||
#endif
|
||||
if(!f_mbrola)
|
||||
{
|
||||
flush_MBR();
|
||||
|
||||
// flush the mbrola output buffer
|
||||
wcmdq[wcmdq_tail][0] = WCMD_MBROLA_DATA;
|
||||
wcmdq[wcmdq_tail][1] = 500;
|
||||
WcmdqInc();
|
||||
}
|
||||
|
||||
return 0;
|
||||
} // end of MbrolaTranslate
|
||||
|
||||
|
||||
#ifdef TEST_MBROLA
|
||||
|
||||
static PHONEME_LIST mbrola_phlist;
|
||||
static int mbrola_n_ph;
|
||||
static int mbrola_phix;
|
||||
|
||||
|
||||
int MbrolaFill(int fill_zeros)
|
||||
{//===========================
|
||||
}
|
||||
|
||||
int MbrolaGenerate(PHONEME_LIST *phoneme_list, int *n_ph, int resume)
|
||||
{//==================================================================
|
||||
if(resume == 0)
|
||||
FILE *f_mbrola = NULL;
|
||||
|
||||
if(*n_ph == 0)
|
||||
return(0);
|
||||
|
||||
if(option_mbrola_phonemes)
|
||||
{
|
||||
mbrola_phlist = phoneme_list;
|
||||
mbrola_n_ph = n_ph;
|
||||
mbrola_phix = 0;
|
||||
// send mbrola data to a file, not to the mbrola library
|
||||
f_mbrola = f_trans;
|
||||
}
|
||||
|
||||
resume(0); // finished phoneme list
|
||||
int again = MbrolaTranslate(phoneme_list, *n_ph, resume, f_mbrola);
|
||||
if (!again)
|
||||
*n_ph = 0;
|
||||
return again;
|
||||
}
|
||||
|
||||
|
||||
int MbrolaFill(int length, int resume)
|
||||
{//===================================
|
||||
// Read audio data from Mbrola (length is in milisecs)
|
||||
|
||||
static int n_samples;
|
||||
int req_samples, result;
|
||||
|
||||
if (!resume)
|
||||
n_samples = samplerate * length / 1000;
|
||||
|
||||
req_samples = (out_end - out_ptr)/2;
|
||||
if (req_samples > n_samples)
|
||||
req_samples = n_samples;
|
||||
result = read_MBR((short *)out_ptr, req_samples);
|
||||
if (result <= 0)
|
||||
return 0;
|
||||
out_ptr += result*2;
|
||||
n_samples -= result;
|
||||
return n_samples ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
void MbrolaReset(void)
|
||||
{//===================
|
||||
// Reset the Mbrola engine and flush the pending audio
|
||||
|
||||
reset_MBR();
|
||||
}
|
||||
#endif
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@
|
||||
#include "translate.h"
|
||||
#include "wave.h"
|
||||
|
||||
const char *version_string = "1.43.46 20.Jun.10";
|
||||
const char *version_string = "1.43.48 28.Jun.10";
|
||||
const int version_phdata = 0x014342;
|
||||
|
||||
int option_device_number = -1;
|
||||
|
||||
+15
-57
@@ -135,7 +135,7 @@ static void EndPitch(int voice_break)
|
||||
syllable_centre = -1;
|
||||
memset(vowel_transition,0,sizeof(vowel_transition));
|
||||
}
|
||||
} // end of Synthesize::EndPitch
|
||||
} // end of EndPitch
|
||||
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ static void DoAmplitude(int amp, unsigned char *amp_env)
|
||||
q[2] = (long)amp_env;
|
||||
q[3] = amp;
|
||||
WcmdqInc();
|
||||
} // end of Synthesize::DoAmplitude
|
||||
} // end of DoAmplitude
|
||||
|
||||
|
||||
|
||||
@@ -181,7 +181,7 @@ static void DoPitch(unsigned char *env, int pitch1, int pitch2)
|
||||
q[2] = (long)env;
|
||||
q[3] = (pitch1 << 16) + pitch2;
|
||||
WcmdqInc();
|
||||
} // end of Synthesize::DoPitch
|
||||
} // end of DoPitch
|
||||
|
||||
|
||||
|
||||
@@ -223,7 +223,7 @@ static void DoPause(int length, int control)
|
||||
wcmdq[wcmdq_tail][1] = len;
|
||||
WcmdqInc();
|
||||
last_frame = NULL;
|
||||
} // end of Synthesize::DoPause
|
||||
} // end of DoPause
|
||||
|
||||
|
||||
extern int seq_len_adjust; // temporary fix to advance the start point for playing the wav sample
|
||||
@@ -253,9 +253,9 @@ static int DoSample2(int index, int which, int std_length, int control, int leng
|
||||
else
|
||||
{
|
||||
// increase consonant amplitude at high speeds, depending on the peak consonant amplitude
|
||||
x = ((35 - wav_scale) * speed.loud_consonants);
|
||||
if(x < 0) x = 0;
|
||||
wav_scale = (wav_scale * (x+256))/256;
|
||||
// x = ((35 - wav_scale) * speed.loud_consonants);
|
||||
// if(x < 0) x = 0;
|
||||
// wav_scale = (wav_scale * (x+256))/256;
|
||||
}
|
||||
|
||||
if(std_length > 0)
|
||||
@@ -383,32 +383,6 @@ static int DoSample2(int index, int which, int std_length, int control, int leng
|
||||
} // end of DoSample2
|
||||
|
||||
|
||||
#ifdef deleted
|
||||
int DoSample(PHONEME_TAB *ph1, PHONEME_TAB *ph2, int which, int length_mod, int amp)
|
||||
{//====================== ==========================================================
|
||||
int index;
|
||||
int match_level;
|
||||
int amp2;
|
||||
int result;
|
||||
|
||||
EndPitch(1);
|
||||
index = LookupSound(ph1,ph2,which & 0xff,&match_level,0);
|
||||
if((index & 0x800000) == 0)
|
||||
return(0); // not wavefile data
|
||||
|
||||
amp2 = wavefile_amp;
|
||||
if(amp != 0)
|
||||
amp2 = (amp * wavefile_amp)/20;
|
||||
|
||||
if(amp == -1)
|
||||
amp2 = amp;
|
||||
|
||||
result = DoSample2(index,which,length_mod,0,amp2);
|
||||
last_frame = NULL;
|
||||
return(result);
|
||||
} // end of DoSample
|
||||
#endif
|
||||
|
||||
|
||||
int DoSample3(PHONEME_DATA *phdata, int length_mod, int amp)
|
||||
{//=========================================================
|
||||
@@ -1198,8 +1172,8 @@ if(which==1)
|
||||
|
||||
|
||||
|
||||
static void DoMarker(int type, int char_posn, int length, int value)
|
||||
{//=================================================================
|
||||
void DoMarker(int type, int char_posn, int length, int value)
|
||||
{//==========================================================
|
||||
// This could be used to return an index to the word currently being spoken
|
||||
// Type 1=word, 2=sentence, 3=named marker, 4=play audio, 5=end
|
||||
wcmdq[wcmdq_tail][0] = WCMD_MARKER;
|
||||
@@ -1208,7 +1182,7 @@ static void DoMarker(int type, int char_posn, int length, int value)
|
||||
wcmdq[wcmdq_tail][3] = value;
|
||||
WcmdqInc();
|
||||
|
||||
} // end of Synthesize::DoMarker
|
||||
} // end of DoMarker
|
||||
|
||||
|
||||
void DoVoiceChange(voice_t *v)
|
||||
@@ -1224,8 +1198,8 @@ void DoVoiceChange(voice_t *v)
|
||||
}
|
||||
|
||||
|
||||
static void DoEmbedded(int &embix, int sourceix)
|
||||
{//=============================================
|
||||
void DoEmbedded(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;
|
||||
@@ -1305,14 +1279,12 @@ int Generate(PHONEME_LIST *phoneme_list, int *n_ph, int resume)
|
||||
PHONEME_DATA phdata_tone;
|
||||
FMT_PARAMS fmtp;
|
||||
|
||||
#ifdef TEST_MBROLA
|
||||
if(mbrola_name[0] != 0)
|
||||
return(MbrolaGenerate(phoneme_list,n_ph,resume));
|
||||
#endif
|
||||
|
||||
if(option_quiet)
|
||||
return(0);
|
||||
|
||||
if(mbrola_name[0] != 0)
|
||||
return(MbrolaGenerate(phoneme_list,n_ph,resume));
|
||||
|
||||
if(resume == 0)
|
||||
{
|
||||
ix = 1;
|
||||
@@ -1899,20 +1871,6 @@ int SpeakNextClause(FILE *f_in, const void *text_in, int control)
|
||||
return(1);
|
||||
}
|
||||
|
||||
if(mbrola_name[0] != 0)
|
||||
{
|
||||
#ifdef USE_MBROLA_LIB
|
||||
MbrolaTranslate(phoneme_list,n_phoneme_list,NULL);
|
||||
#else
|
||||
{
|
||||
FILE *f_mbrola;
|
||||
if((f_mbrola = f_trans) == stderr)
|
||||
f_mbrola = stdout;
|
||||
MbrolaTranslate(phoneme_list,n_phoneme_list,f_mbrola);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Generate(phoneme_list,&n_phoneme_list,0);
|
||||
WavegenOpenSound();
|
||||
|
||||
|
||||
+7
-2
@@ -462,6 +462,7 @@ extern unsigned char pitch_adjust_tab[MAX_PITCH_VALUE+1];
|
||||
#define WCMD_MARKER 10
|
||||
#define WCMD_VOICE 11
|
||||
#define WCMD_EMBEDDED 12
|
||||
#define WCMD_MBROLA_DATA 13
|
||||
|
||||
|
||||
#define N_WCMDQ 160
|
||||
@@ -557,8 +558,12 @@ espeak_ERROR SetVoiceByName(const char *name);
|
||||
espeak_ERROR SetVoiceByProperties(espeak_VOICE *voice_selector);
|
||||
espeak_ERROR LoadMbrolaTable(const char *mbrola_voice, const char *phtrans, int srate);
|
||||
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 MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, int resume, FILE *f_mbrola);
|
||||
int MbrolaGenerate(PHONEME_LIST *phoneme_list, int *n_ph, int resume);
|
||||
int MbrolaFill(int length, int resume);
|
||||
void MbrolaReset(void);
|
||||
void DoEmbedded(int &embix, int sourceix);
|
||||
void DoMarker(int type, int char_posn, int length, int value);
|
||||
//int DoSample(PHONEME_TAB *ph1, PHONEME_TAB *ph2, int which, int length_mod, int amp);
|
||||
int DoSample3(PHONEME_DATA *phdata, int length_mod, int amp);
|
||||
int DoSpect2(PHONEME_TAB *this_ph, int which, FMT_PARAMS *fmt_params, PHONEME_LIST *plist, int modulation);
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2005 to 2007 by Jonathan Duddington *
|
||||
* email: 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 3 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 see: *
|
||||
* <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#include "StdAfx.h"
|
||||
|
||||
@@ -881,7 +881,6 @@ SetLengthMods(tr,3); // all equal
|
||||
static const short stress_lengths_nl[8] = {160,135, 210,210, 0, 0, 260,280};
|
||||
|
||||
tr->langopts.stress_rule = STRESSPOSN_1L;
|
||||
tr->langopts.vowel_pause = 1;
|
||||
tr->langopts.vowel_pause = 0x30; // ??
|
||||
tr->langopts.param[LOPT_DIERESES] = 1;
|
||||
tr->langopts.param[LOPT_PREFIXES] = 1;
|
||||
|
||||
@@ -545,6 +545,7 @@ extern int option_tone_flags;
|
||||
extern int option_waveout;
|
||||
extern int option_quiet;
|
||||
extern int option_phonemes;
|
||||
extern int option_mbrola_phonemes;
|
||||
extern int option_phoneme_events;
|
||||
extern int option_linelength; // treat lines shorter than this as end-of-clause
|
||||
extern int option_multibyte;
|
||||
|
||||
@@ -41,6 +41,7 @@ typedef struct {
|
||||
int formant_factor; // adjust nominal formant frequencies by this because of the voice's pitch (256ths)
|
||||
int consonant_amp; // amplitude of unvoiced consonants
|
||||
int consonant_ampv; // amplitude of the noise component of voiced consonants
|
||||
int samplerate;
|
||||
int klattv[8];
|
||||
|
||||
// parameters used by Wavegen
|
||||
@@ -77,6 +78,8 @@ espeak_VOICE *SelectVoiceByName(espeak_VOICE **voices, const char *name);
|
||||
voice_t *LoadVoice(const char *voice_name, int control);
|
||||
voice_t *LoadVoiceVariant(const char *voice_name, int variant);
|
||||
void DoVoiceChange(voice_t *v);
|
||||
void WVoiceChanged(voice_t *wvoice);
|
||||
void WavegenSetVoice(voice_t *v);
|
||||
void ReadTonePoints(char *string, int *tone_pts);
|
||||
void VoiceReset(int control);
|
||||
|
||||
|
||||
@@ -408,6 +408,7 @@ void VoiceReset(int tone_only)
|
||||
voice->voicing = 64;
|
||||
voice->consonant_amp = 100;
|
||||
voice->consonant_ampv = 100;
|
||||
voice->samplerate = 22050;
|
||||
memset(voice->klattv,0,sizeof(voice->klattv));
|
||||
memset(speed.fast_settings,0,sizeof(speed.fast_settings));
|
||||
|
||||
@@ -921,6 +922,7 @@ voice_t *LoadVoice(const char *vname, int control)
|
||||
phtrans[0] = 0;
|
||||
sscanf(p,"%s %s %d",name,phtrans,&srate);
|
||||
LoadMbrolaTable(name,phtrans,srate);
|
||||
voice->samplerate = srate;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
+93
-91
@@ -56,15 +56,16 @@ enum {ONE_BILLION=1000000000};
|
||||
static t_wave_callback* my_callback_is_output_enabled=NULL;
|
||||
|
||||
#define N_WAV_BUF 10
|
||||
#define SAMPLE_RATE 22050
|
||||
#define MAX_SAMPLE_RATE 22050
|
||||
#define FRAMES_PER_BUFFER 512
|
||||
#define BUFFER_LENGTH (SAMPLE_RATE*2*sizeof(uint16_t))
|
||||
#define THRESHOLD (BUFFER_LENGTH/5)
|
||||
#define BUFFER_LENGTH (MAX_SAMPLE_RATE*2*sizeof(uint16_t))
|
||||
//#define THRESHOLD (BUFFER_LENGTH/5)
|
||||
static char myBuffer[BUFFER_LENGTH];
|
||||
static char* myRead=NULL;
|
||||
static char* myWrite=NULL;
|
||||
static int out_channels=1;
|
||||
static int my_stream_could_start=0;
|
||||
static int wave_samplerate;
|
||||
|
||||
static int mInCallbackFinishedState = false;
|
||||
#if (USE_PORTAUDIO == 18)
|
||||
@@ -168,89 +169,89 @@ static int pa_callback(void *inputBuffer, void *outputBuffer,
|
||||
PaStreamCallbackFlags flags, void *userData )
|
||||
#endif
|
||||
{
|
||||
int aResult=0; // paContinue
|
||||
char* aWrite = myWrite;
|
||||
size_t n = out_channels*sizeof(uint16_t)*framesPerBuffer;
|
||||
int aResult=0; // paContinue
|
||||
char* aWrite = myWrite;
|
||||
size_t n = out_channels*sizeof(uint16_t)*framesPerBuffer;
|
||||
|
||||
myReadPosition += framesPerBuffer;
|
||||
SHOW("pa_callback > myReadPosition=%u, framesPerBuffer=%lu (n=0x%x) \n",(int)myReadPosition, framesPerBuffer, n);
|
||||
myReadPosition += framesPerBuffer;
|
||||
SHOW("pa_callback > myReadPosition=%u, framesPerBuffer=%lu (n=0x%x) \n",(int)myReadPosition, framesPerBuffer, n);
|
||||
|
||||
if (aWrite >= myRead)
|
||||
{
|
||||
if((size_t)(aWrite - myRead) >= n)
|
||||
if (aWrite >= myRead)
|
||||
{
|
||||
memcpy(outputBuffer, myRead, n);
|
||||
myRead += n;
|
||||
if((size_t)(aWrite - myRead) >= n)
|
||||
{
|
||||
memcpy(outputBuffer, myRead, n);
|
||||
myRead += n;
|
||||
}
|
||||
else
|
||||
{
|
||||
SHOW_TIME("pa_callback > underflow");
|
||||
aResult=1; // paComplete;
|
||||
mInCallbackFinishedState = true;
|
||||
size_t aUsedMem=0;
|
||||
aUsedMem = (size_t)(aWrite - myRead);
|
||||
if (aUsedMem)
|
||||
{
|
||||
memcpy(outputBuffer, myRead, aUsedMem);
|
||||
}
|
||||
char* p = (char*)outputBuffer + aUsedMem;
|
||||
memset(p, 0, n - aUsedMem);
|
||||
// myReadPosition += aUsedMem/(out_channels*sizeof(uint16_t));
|
||||
myRead = aWrite;
|
||||
}
|
||||
}
|
||||
else
|
||||
else // myRead > aWrite
|
||||
{
|
||||
SHOW_TIME("pa_callback > underflow");
|
||||
aResult=1; // paComplete;
|
||||
mInCallbackFinishedState = true;
|
||||
size_t aUsedMem=0;
|
||||
aUsedMem = (size_t)(aWrite - myRead);
|
||||
if (aUsedMem)
|
||||
{
|
||||
memcpy(outputBuffer, myRead, aUsedMem);
|
||||
}
|
||||
char* p = (char*)outputBuffer + aUsedMem;
|
||||
memset(p, 0, n - aUsedMem);
|
||||
// myReadPosition += aUsedMem/(out_channels*sizeof(uint16_t));
|
||||
myRead = aWrite;
|
||||
}
|
||||
}
|
||||
else // myRead > aWrite
|
||||
{
|
||||
if ((size_t)(myBuffer + BUFFER_LENGTH - myRead) >= n)
|
||||
{
|
||||
memcpy(outputBuffer, myRead, n);
|
||||
myRead += n;
|
||||
}
|
||||
else if ((size_t)(aWrite + BUFFER_LENGTH - myRead) >= n)
|
||||
{
|
||||
int aTopMem = myBuffer + BUFFER_LENGTH - myRead;
|
||||
if (aTopMem)
|
||||
{
|
||||
SHOW("pa_callback > myRead=0x%x, aTopMem=0x%x\n",(int)myRead, (int)aTopMem);
|
||||
memcpy(outputBuffer, myRead, aTopMem);
|
||||
}
|
||||
int aRest = n - aTopMem;
|
||||
if (aRest)
|
||||
{
|
||||
SHOW("pa_callback > myRead=0x%x, aRest=0x%x\n",(int)myRead, (int)aRest);
|
||||
char* p = (char*)outputBuffer + aTopMem;
|
||||
memcpy(p, myBuffer, aRest);
|
||||
}
|
||||
myRead = myBuffer + aRest;
|
||||
}
|
||||
else
|
||||
{
|
||||
SHOW_TIME("pa_callback > underflow");
|
||||
aResult=1; // paComplete;
|
||||
if ((size_t)(myBuffer + BUFFER_LENGTH - myRead) >= n)
|
||||
{
|
||||
memcpy(outputBuffer, myRead, n);
|
||||
myRead += n;
|
||||
}
|
||||
else if ((size_t)(aWrite + BUFFER_LENGTH - myRead) >= n)
|
||||
{
|
||||
int aTopMem = myBuffer + BUFFER_LENGTH - myRead;
|
||||
if (aTopMem)
|
||||
{
|
||||
SHOW("pa_callback > myRead=0x%x, aTopMem=0x%x\n",(int)myRead, (int)aTopMem);
|
||||
memcpy(outputBuffer, myRead, aTopMem);
|
||||
}
|
||||
int aRest = n - aTopMem;
|
||||
if (aRest)
|
||||
{
|
||||
SHOW("pa_callback > myRead=0x%x, aRest=0x%x\n",(int)myRead, (int)aRest);
|
||||
char* p = (char*)outputBuffer + aTopMem;
|
||||
memcpy(p, myBuffer, aRest);
|
||||
}
|
||||
myRead = myBuffer + aRest;
|
||||
}
|
||||
else
|
||||
{
|
||||
SHOW_TIME("pa_callback > underflow");
|
||||
aResult=1; // paComplete;
|
||||
|
||||
int aTopMem = myBuffer + BUFFER_LENGTH - myRead;
|
||||
if (aTopMem)
|
||||
{
|
||||
SHOW("pa_callback > myRead=0x%x, aTopMem=0x%x\n",(int)myRead, (int)aTopMem);
|
||||
memcpy(outputBuffer, myRead, aTopMem);
|
||||
}
|
||||
int aRest = aWrite - myBuffer;
|
||||
if (aRest)
|
||||
{
|
||||
SHOW("pa_callback > myRead=0x%x, aRest=0x%x\n",(int)myRead, (int)aRest);
|
||||
char* p = (char*)outputBuffer + aTopMem;
|
||||
memcpy(p, myBuffer, aRest);
|
||||
}
|
||||
int aTopMem = myBuffer + BUFFER_LENGTH - myRead;
|
||||
if (aTopMem)
|
||||
{
|
||||
SHOW("pa_callback > myRead=0x%x, aTopMem=0x%x\n",(int)myRead, (int)aTopMem);
|
||||
memcpy(outputBuffer, myRead, aTopMem);
|
||||
}
|
||||
int aRest = aWrite - myBuffer;
|
||||
if (aRest)
|
||||
{
|
||||
SHOW("pa_callback > myRead=0x%x, aRest=0x%x\n",(int)myRead, (int)aRest);
|
||||
char* p = (char*)outputBuffer + aTopMem;
|
||||
memcpy(p, myBuffer, aRest);
|
||||
}
|
||||
|
||||
size_t aUsedMem = aTopMem + aRest;
|
||||
char* p = (char*)outputBuffer + aUsedMem;
|
||||
memset(p, 0, n - aUsedMem);
|
||||
// myReadPosition += aUsedMem/(out_channels*sizeof(uint16_t));
|
||||
myRead = aWrite;
|
||||
size_t aUsedMem = aTopMem + aRest;
|
||||
char* p = (char*)outputBuffer + aUsedMem;
|
||||
memset(p, 0, n - aUsedMem);
|
||||
// myReadPosition += aUsedMem/(out_channels*sizeof(uint16_t));
|
||||
myRead = aWrite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SHOW("pa_callback > myRead=%x\n",(int)myRead);
|
||||
SHOW("pa_callback > myRead=%x\n",(int)myRead);
|
||||
|
||||
|
||||
// #if USE_PORTAUDIO == 18
|
||||
@@ -288,7 +289,7 @@ static int pa_callback(void *inputBuffer, void *outputBuffer,
|
||||
#endif
|
||||
|
||||
|
||||
return(aResult);
|
||||
return(aResult);
|
||||
//#endif
|
||||
|
||||
} // end of WaveCallBack
|
||||
@@ -335,7 +336,7 @@ static int wave_open_sound()
|
||||
out_channels = 1;
|
||||
|
||||
#if USE_PORTAUDIO == 18
|
||||
// err = Pa_OpenDefaultStream(&pa_stream,0,1,paInt16,SAMPLE_RATE,FRAMES_PER_BUFFER,N_WAV_BUF,pa_callback,(void *)userdata);
|
||||
// err = Pa_OpenDefaultStream(&pa_stream,0,1,paInt16,wave_samplerate,FRAMES_PER_BUFFER,N_WAV_BUF,pa_callback,(void *)userdata);
|
||||
|
||||
PaDeviceID playbackDevice = Pa_GetDefaultOutputDeviceID();
|
||||
|
||||
@@ -351,7 +352,7 @@ static int wave_open_sound()
|
||||
paInt16,
|
||||
NULL,
|
||||
/* general parameters */
|
||||
SAMPLE_RATE, FRAMES_PER_BUFFER, 0,
|
||||
wave_samplerate, FRAMES_PER_BUFFER, 0,
|
||||
//paClipOff | paDitherOff,
|
||||
paNoFlag,
|
||||
pa_callback, (void *)userdata);
|
||||
@@ -376,12 +377,12 @@ static int wave_open_sound()
|
||||
paInt16,
|
||||
NULL,
|
||||
/* general parameters */
|
||||
SAMPLE_RATE, FRAMES_PER_BUFFER, 0,
|
||||
wave_samplerate, FRAMES_PER_BUFFER, 0,
|
||||
//paClipOff | paDitherOff,
|
||||
paNoFlag,
|
||||
pa_callback, (void *)userdata);
|
||||
// err = Pa_OpenDefaultStream(&pa_stream,0,2,paInt16,
|
||||
// SAMPLE_RATE,
|
||||
// wave_samplerate,
|
||||
// FRAMES_PER_BUFFER,
|
||||
// N_WAV_BUF,pa_callback,(void *)userdata);
|
||||
SHOW("wave_open_sound > Pa_OpenDefaultStream(2): err=%d (%s)\n",err, Pa_GetErrorText(err));
|
||||
@@ -395,7 +396,7 @@ static int wave_open_sound()
|
||||
&pa_stream,
|
||||
NULL, /* no input */
|
||||
&myOutputParameters,
|
||||
SAMPLE_RATE,
|
||||
wave_samplerate,
|
||||
framesPerBuffer,
|
||||
paNoFlag,
|
||||
// paClipOff | paDitherOff,
|
||||
@@ -410,7 +411,7 @@ static int wave_open_sound()
|
||||
&pa_stream,
|
||||
NULL, /* no input */
|
||||
&myOutputParameters,
|
||||
SAMPLE_RATE,
|
||||
wave_samplerate,
|
||||
framesPerBuffer,
|
||||
paNoFlag,
|
||||
// paClipOff | paDitherOff,
|
||||
@@ -427,14 +428,14 @@ static int wave_open_sound()
|
||||
&pa_stream,
|
||||
NULL, /* no input */
|
||||
&myOutputParameters,
|
||||
SAMPLE_RATE,
|
||||
wave_samplerate,
|
||||
framesPerBuffer,
|
||||
paNoFlag,
|
||||
// paClipOff | paDitherOff,
|
||||
pa_callback,
|
||||
(void *)userdata);
|
||||
|
||||
// err = Pa_OpenDefaultStream(&pa_stream,0,2,paInt16,(double)SAMPLE_RATE,FRAMES_PER_BUFFER,pa_callback,(void *)userdata);
|
||||
// err = Pa_OpenDefaultStream(&pa_stream,0,2,paInt16,(double)wave_samplerate,FRAMES_PER_BUFFER,pa_callback,(void *)userdata);
|
||||
}
|
||||
mInCallbackFinishedState = false;
|
||||
#endif
|
||||
@@ -526,7 +527,7 @@ static void select_device(const char* the_api)
|
||||
defaultAlsaIndex = hostInfo->defaultOutputDevice;
|
||||
const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo( defaultAlsaIndex );
|
||||
update_output_parameters(defaultAlsaIndex, deviceInfo);
|
||||
if (Pa_IsFormatSupported(NULL, &myOutputParameters, SAMPLE_RATE) == 0)
|
||||
if (Pa_IsFormatSupported(NULL, &myOutputParameters, wave_samplerate) == 0)
|
||||
{
|
||||
SHOW( "select_device > ALSA (default), name=%s (#%d)\n", deviceInfo->name, defaultAlsaIndex);
|
||||
selectedIndex = defaultAlsaIndex;
|
||||
@@ -541,7 +542,7 @@ static void select_device(const char* the_api)
|
||||
|
||||
update_output_parameters(i, deviceInfo);
|
||||
|
||||
if (Pa_IsFormatSupported(NULL, &myOutputParameters, SAMPLE_RATE) == 0)
|
||||
if (Pa_IsFormatSupported(NULL, &myOutputParameters, wave_samplerate) == 0)
|
||||
{
|
||||
SHOW( "select_device > ALSA, name=%s (#%d)\n", deviceInfo->name, i);
|
||||
|
||||
@@ -613,12 +614,13 @@ void wave_set_callback_is_output_enabled(t_wave_callback* cb)
|
||||
//<wave_init
|
||||
|
||||
// TBD: the arg could be "alsa", "oss",...
|
||||
void wave_init()
|
||||
void wave_init(int srate)
|
||||
{
|
||||
ENTER("wave_init");
|
||||
PaError err;
|
||||
|
||||
pa_stream = NULL;
|
||||
wave_samplerate = srate;
|
||||
mInCallbackFinishedState = false;
|
||||
init_buffer();
|
||||
|
||||
@@ -1023,7 +1025,7 @@ int wave_get_remaining_time(uint32_t sample, uint32_t* time)
|
||||
{
|
||||
// TBD: take in account time suplied by portaudio V18 API
|
||||
a_time = sample - myReadPosition;
|
||||
a_time = 0.5 + (a_time * 1000.0) / SAMPLE_RATE;
|
||||
a_time = 0.5 + (a_time * 1000.0) / wave_samplerate;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1050,7 +1052,7 @@ void *wave_test_get_write_buffer()
|
||||
// notdef USE_PORTAUDIO
|
||||
|
||||
|
||||
void wave_init() {}
|
||||
void wave_init(int srate) {}
|
||||
void* wave_open(const char* the_api) {return (void *)1;}
|
||||
size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSize) {return theSize;}
|
||||
int wave_close(void* theHandler) {return 0;}
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
|
||||
extern int option_device_number;
|
||||
|
||||
extern void wave_init();
|
||||
extern void wave_init(int samplerate);
|
||||
// TBD: the arg could be "alsa", "oss",...
|
||||
extern void* wave_open(const char* the_api);
|
||||
|
||||
|
||||
+5
-4
@@ -65,7 +65,6 @@ enum {
|
||||
|
||||
static t_wave_callback* my_callback_is_output_enabled=NULL;
|
||||
|
||||
#define SAMPLE_RATE 22050
|
||||
#define ESPEAK_FORMAT PA_SAMPLE_S16LE
|
||||
#define ESPEAK_CHANNEL 1
|
||||
|
||||
@@ -90,6 +89,7 @@ static int time_offset_msec = 0;
|
||||
static int just_flushed = 0;
|
||||
|
||||
static int connected = 0;
|
||||
static int wave_samplerate;
|
||||
|
||||
#define CHECK_DEAD_GOTO(label, warn) do { \
|
||||
if (!mainloop || \
|
||||
@@ -475,7 +475,7 @@ static int pulse_open()
|
||||
pthread_mutex_init( &pulse_mutex, (const pthread_mutexattr_t *)NULL);
|
||||
|
||||
ss.format = ESPEAK_FORMAT;
|
||||
ss.rate = SAMPLE_RATE;
|
||||
ss.rate = wave_samplerate;
|
||||
ss.channels = ESPEAK_CHANNEL;
|
||||
|
||||
if (!pa_sample_spec_valid(&ss))
|
||||
@@ -677,11 +677,12 @@ void wave_set_callback_is_output_enabled(t_wave_callback* cb)
|
||||
//>
|
||||
//<wave_init
|
||||
|
||||
void wave_init()
|
||||
void wave_init(int srate)
|
||||
{
|
||||
ENTER("wave_init");
|
||||
|
||||
stream = NULL;
|
||||
wave_samplerate = srate;
|
||||
|
||||
pulse_open();
|
||||
}
|
||||
@@ -846,7 +847,7 @@ int wave_get_remaining_time(uint32_t sample, uint32_t* time)
|
||||
{
|
||||
// TBD: take in account time suplied by portaudio V18 API
|
||||
a_time = sample - a_timing_info.read_index;
|
||||
a_time = 0.5 + (a_time * 1000.0) / SAMPLE_RATE;
|
||||
a_time = 0.5 + (a_time * 1000.0) / wave_samplerate;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+5
-3
@@ -77,12 +77,14 @@ static uint32_t last_play_position=0;
|
||||
//
|
||||
//<wave_init
|
||||
|
||||
void wave_init() {
|
||||
void wave_init(int srate) {
|
||||
ENTER("wave_init");
|
||||
|
||||
audio_info_t ainfo;
|
||||
char *audio_device = NULL;
|
||||
|
||||
wave_samplerate = srate;
|
||||
|
||||
audio_device = getenv("AUDIODEV");
|
||||
if (audio_device != NULL) {
|
||||
if ((sun_audio_fd = open(audio_device, O_WRONLY)) < 0) {
|
||||
@@ -108,7 +110,7 @@ void wave_init() {
|
||||
SHOW("wave_init() play buffer size: %d\n", ainfo.play.buffer_size);
|
||||
ainfo.play.encoding = AUDIO_ENCODING_LINEAR;
|
||||
ainfo.play.channels = 1;
|
||||
ainfo.play.sample_rate = SAMPLE_RATE;
|
||||
ainfo.play.sample_rate = wave_samplerate;
|
||||
ainfo.play.precision = SAMPLE_SIZE;
|
||||
|
||||
if (ioctl(sun_audio_fd, AUDIO_SETINFO, &ainfo) == -1) {
|
||||
@@ -520,7 +522,7 @@ int wave_get_remaining_time(uint32_t sample, uint32_t* time)
|
||||
(actual_index <= ainfo.play.samples)) {
|
||||
*time = 0;
|
||||
} else {
|
||||
a_time = ((actual_index - ainfo.play.samples) * 1000) / SAMPLE_RATE;
|
||||
a_time = ((actual_index - ainfo.play.samples) * 1000) / wave_samplerate;
|
||||
*time = (uint32_t) a_time;
|
||||
}
|
||||
SHOW("wave_get_remaining_time for %d: %d\n", sample, *time);
|
||||
|
||||
+8
-5
@@ -288,6 +288,8 @@ void WcmdqStop()
|
||||
#ifdef USE_PORTAUDIO
|
||||
Pa_AbortStream(pa_stream);
|
||||
#endif
|
||||
if(mbrola_name[0] != 0)
|
||||
MbrolaReset();
|
||||
}
|
||||
|
||||
|
||||
@@ -1563,6 +1565,8 @@ void WavegenSetVoice(voice_t *v)
|
||||
option_harmonic1 = 6;
|
||||
}
|
||||
WavegenSetEcho();
|
||||
MarkerEvent(espeakEVENT_SAMPLERATE,0,wvoice->samplerate,out_ptr);
|
||||
// WVoiceChanged(wvoice);
|
||||
}
|
||||
|
||||
|
||||
@@ -1797,11 +1801,6 @@ int WavegenFill(int fill_zeros)
|
||||
static int resume=0;
|
||||
static int echo_complete=0;
|
||||
|
||||
#ifdef TEST_MBROLA
|
||||
if(mbrola_name[0] != 0)
|
||||
return(MbrolaFill(fill_zeros));
|
||||
#endif
|
||||
|
||||
while(out_ptr < out_end)
|
||||
{
|
||||
if(WcmdqUsed() <= 0)
|
||||
@@ -1904,6 +1903,10 @@ int WavegenFill(int fill_zeros)
|
||||
case WCMD_EMBEDDED:
|
||||
SetEmbedded(q[1],q[2]);
|
||||
break;
|
||||
|
||||
case WCMD_MBROLA_DATA:
|
||||
result = MbrolaFill(length, resume);
|
||||
break;
|
||||
}
|
||||
|
||||
if(result==0)
|
||||
|
||||
Reference in New Issue
Block a user