Files
espeak-ng/src/speak_lib.cpp
T
jonsd fc0bd7fe79 [1.44.42]
Phoneme definitions.
New keyword "prevoice", always add murmer before a voiced stop, not only after a vowel.
Fixed bug in nextVowel() condition.
Fixed bug is condition "isRhotic".


Command line options.
On Windows, --  means don't treat following arguments as options.
--help output now should the espeak-data path.

Tools->Lexicon.  Redesign Lexicon processing for Russian and Bulgarian.

Numbers.  Fixes for Hungarian ordinal numbers.

Fixed bug where double .. was ignored.

Fixed bug in reporting word event lengths in callbacks.
Fixed bug in reporting marker event positions in callbacks, when non-ascii text.

SSML.
Allow single as well as double quotes around strings.
Fix bugs in SSML "prosody rate".

Speed.
Only use Sonic speedups greater than x2.
Use custom eSpeak speed settings when Sonic speedup is used.

Mbrola.  Look for Mbrola voice also at /usr/share/mbrola/xx/xx

espeakedit.  Try "aplay" and then "play" to play sound.

Languages:
Kannada (kn) now in official release.
Esperanto (eo) change prosody.


git-svn-id: https://espeak.svn.sourceforge.net/svnroot/espeak/trunk@257 d46cf337-b52f-0410-862d-fd96e6ae7743
2011-03-13 13:30:04 +00:00

1240 lines
30 KiB
C++

/***************************************************************************
* Copyright (C) 2005 to 2011 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"
#include "stdio.h"
#include "ctype.h"
#include "string.h"
#include "stdlib.h"
#include "wchar.h"
#include "locale.h"
#include <assert.h>
#include <time.h>
#include "speech.h"
#include <sys/stat.h>
#ifndef PLATFORM_WINDOWS
#include <unistd.h>
#endif
#include "speak_lib.h"
#include "phoneme.h"
#include "synthesize.h"
#include "voice.h"
#include "translate.h"
#include "debug.h"
#include "fifo.h"
#include "event.h"
#include "wave.h"
unsigned char *outbuf=NULL;
extern espeak_VOICE voice_selected;
espeak_EVENT *event_list=NULL;
int event_list_ix=0;
int n_event_list;
long count_samples;
void* my_audio=NULL;
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;
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)
{//======================================================================
ENTER("dispatch_audio");
int a_wave_can_be_played = fifo_is_command_enabled();
#ifdef DEBUG_ENABLED
SHOW("*** dispatch_audio > uid=%d, [write=%p (%d bytes)], sample=%d, a_wave_can_be_played = %d\n",
(event) ? event->unique_identifier : 0, wave_test_get_write_buffer(), 2*length,
(event) ? event->sample : 0,
a_wave_can_be_played);
#endif
switch(my_mode)
{
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);
}
while(a_wave_can_be_played) {
// TBD: some event are filtered here but some insight might be given
// TBD: in synthesise.cpp for avoiding to create WORDs with size=0.
// TBD: For example sentence "or ALT)." returns three words
// "or", "ALT" and "".
// TBD: the last one has its size=0.
if (event && (event->type == espeakEVENT_WORD) && (event->length==0))
{
break;
}
espeak_ERROR a_error = event_declare(event);
if (a_error != EE_BUFFER_FULL)
{
break;
}
SHOW_TIME("dispatch_audio > EE_BUFFER_FULL\n");
usleep(10000);
a_wave_can_be_played = fifo_is_command_enabled();
}
}
break;
case AUDIO_OUTPUT_RETRIEVAL:
if (synth_callback)
{
synth_callback(outbuf, length, event);
}
break;
case AUDIO_OUTPUT_SYNCHRONOUS:
case AUDIO_OUTPUT_SYNCH_PLAYBACK:
break;
}
if (!a_wave_can_be_played)
{
SHOW_TIME("dispatch_audio > synth must be stopped!\n");
}
SHOW_TIME("LEAVE dispatch_audio\n");
return (a_wave_can_be_played==0); // 1 = stop synthesis
}
static int create_events(short* outbuf, int length, espeak_EVENT* event, uint32_t the_write_pos)
{//=====================================================================
int finished;
int i=0;
// The audio data are written to the output device.
// The list of events in event_list (index: event_list_ix) is read:
// Each event is declared to the "event" object which stores them internally.
// The event object is responsible of calling the external callback
// as soon as the relevant audio sample is played.
do
{ // for each event
espeak_EVENT* event;
if (event_list_ix == 0)
{
event = NULL;
}
else
{
event = event_list + i;
#ifdef DEBUG_ENABLED
SHOW("Synthesize: event->sample(%d) + %d = %d\n", event->sample, the_write_pos, event->sample + the_write_pos);
#endif
event->sample += the_write_pos;
}
#ifdef DEBUG_ENABLED
SHOW("*** Synthesize: i=%d (event_list_ix=%d), length=%d\n",i,event_list_ix,length);
#endif
finished = dispatch_audio((short *)outbuf, length, event);
length = 0; // the wave data are played once.
i++;
} while((i < event_list_ix) && !finished);
return finished;
}
int sync_espeak_terminated_msg( uint32_t unique_identifier, void* user_data)
{//=====================================================================
ENTER("sync_espeak_terminated_msg");
int finished=0;
memset(event_list, 0, 2*sizeof(espeak_EVENT));
event_list[0].type = espeakEVENT_MSG_TERMINATED;
event_list[0].unique_identifier = unique_identifier;
event_list[0].user_data = user_data;
event_list[1].type = espeakEVENT_LIST_TERMINATED;
event_list[1].unique_identifier = unique_identifier;
event_list[1].user_data = user_data;
if (my_mode==AUDIO_OUTPUT_PLAYBACK)
{
while(1)
{
espeak_ERROR a_error = event_declare(event_list);
if (a_error != EE_BUFFER_FULL)
{
break;
}
SHOW_TIME("sync_espeak_terminated_msg > EE_BUFFER_FULL\n");
usleep(10000);
}
}
else
{
if (synth_callback)
{
finished=synth_callback(NULL,0,event_list);
}
}
return finished;
}
#endif
static void select_output(espeak_AUDIO_OUTPUT output_type)
{//=======================================================
my_mode = 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;
break;
case AUDIO_OUTPUT_RETRIEVAL:
synchronous_mode = 0;
break;
case AUDIO_OUTPUT_SYNCHRONOUS:
break;
case AUDIO_OUTPUT_SYNCH_PLAYBACK:
option_waveout = 0;
WavegenInitSound();
break;
}
} // end of select_output
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"); // I was told that size+1 fixes a crash on 64-bit systems
return(p);
}
void Free(void *ptr)
{//=================
if(ptr != NULL)
free(ptr);
}
static void init_path(const char *path)
{//====================================
#ifdef PLATFORM_WINDOWS
HKEY RegKey;
unsigned long size;
unsigned long var_type;
char *env;
unsigned char buf[sizeof(path_home)-13];
if(path != NULL)
{
sprintf(path_home,"%s/espeak-data",path);
return;
}
if((env = getenv("ESPEAK_DATA_PATH")) != NULL)
{
sprintf(path_home,"%s/espeak-data",env);
if(GetFileLength(path_home) == -2)
return; // an espeak-data directory exists
}
buf[0] = 0;
RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Speech\\Voices\\Tokens\\eSpeak", 0, KEY_READ, &RegKey);
size = sizeof(buf);
var_type = REG_SZ;
RegQueryValueExA(RegKey, "path", 0, &var_type, buf, &size);
sprintf(path_home,"%s\\espeak-data",buf);
#else
char *env;
if(path != NULL)
{
snprintf(path_home,sizeof(path_home),"%s/espeak-data",path);
return;
}
// check for environment variable
if((env = getenv("ESPEAK_DATA_PATH")) != NULL)
{
snprintf(path_home,sizeof(path_home),"%s/espeak-data",env);
if(GetFileLength(path_home) == -2)
return; // an espeak-data directory exists
}
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(int control)
{//===============================
int param;
int result;
LoadConfig();
WavegenInit(22050,0); // 22050
if((result = LoadPhData()) != 1)
{
if(result == -1)
{
fprintf(stderr,"Failed to load espeak-data\n");
if((control & espeakINITIALIZE_DONT_EXIT) == 0)
{
exit(1);
}
}
else
fprintf(stderr,"Wrong version of espeak-data 0x%x (expects 0x%x) at %s\n",result,version_phdata,path_home);
}
memset(&voice_selected,0,sizeof(voice_selected));
SetVoiceStack(NULL);
SynthesizeInit();
InitNamedata();
for(param=0; param<N_SPEECH_PARAM; param++)
param_stack[0].parameter[param] = param_defaults[param];
return(0);
}
static espeak_ERROR Synthesize(unsigned int unique_identifier, const void *text, int flags)
{//========================================================================================
// Fill the buffer with output sound
int length;
int finished = 0;
int count_buffers = 0;
#ifdef USE_ASYNC
uint32_t a_write_pos=0;
#endif
#ifdef DEBUG_ENABLED
ENTER("Synthesize");
if (text)
{
SHOW("Synthesize > uid=%d, flags=%d, >>>text=%s<<<\n", unique_identifier, flags, text);
}
#endif
if((outbuf==NULL) || (event_list==NULL))
return(EE_INTERNAL_ERROR); // espeak_Initialize() has not been called
option_multibyte = flags & 7;
option_ssml = flags & espeakSSML;
option_phoneme_input = flags & espeakPHONEMES;
option_endpause = flags & espeakENDPAUSE;
count_samples = 0;
#ifdef USE_ASYNC
if(my_mode == AUDIO_OUTPUT_PLAYBACK)
{
a_write_pos = wave_get_write_position(my_audio);
}
#endif
if(translator == NULL)
{
SetVoiceByName("default");
}
SpeakNextClause(NULL,text,0);
if(my_mode == AUDIO_OUTPUT_SYNCH_PLAYBACK)
{
for(;;)
{
#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)
break;
}
return(EE_OK);
}
for(;;)
{
#ifdef DEBUG_ENABLED
SHOW("Synthesize > %s\n","for (next)");
#endif
out_ptr = outbuf;
out_end = &outbuf[outbuf_size];
event_list_ix = 0;
WavegenFill(0);
length = (out_ptr - outbuf)/2;
count_samples += length;
event_list[event_list_ix].type = espeakEVENT_LIST_TERMINATED; // indicates end of event list
event_list[event_list_ix].unique_identifier = my_unique_identifier;
event_list[event_list_ix].user_data = my_user_data;
count_buffers++;
if (my_mode==AUDIO_OUTPUT_PLAYBACK)
{
#ifdef USE_ASYNC
finished = create_events((short *)outbuf, length, event_list, a_write_pos);
length = 0; // the wave data are played once.
#endif
}
else
{
finished = synth_callback((short *)outbuf, length, event_list);
}
if(finished)
{
SpeakNextClause(NULL,0,2); // stop
break;
}
if(Generate(phoneme_list,&n_phoneme_list,1)==0)
{
if(WcmdqUsed() == 0)
{
// don't process the next clause until the previous clause has finished generating speech.
// This ensures that <audio> tag (which causes end-of-clause) is at a sound buffer boundary
event_list[0].type = espeakEVENT_LIST_TERMINATED;
event_list[0].unique_identifier = my_unique_identifier;
event_list[0].user_data = my_user_data;
if(SpeakNextClause(NULL,NULL,1)==0)
{
#ifdef USE_ASYNC
if (my_mode==AUDIO_OUTPUT_PLAYBACK)
{
dispatch_audio(NULL, 0, NULL); // TBD: test case
}
else
{
synth_callback(NULL, 0, event_list); // NULL buffer ptr indicates end of data
}
#else
synth_callback(NULL, 0, event_list); // NULL buffer ptr indicates end of data
#endif
break;
}
}
}
}
return(EE_OK);
} // end of Synthesize
#ifdef DEBUG_ENABLED
static const char* label[] = {
"END_OF_EVENT_LIST",
"WORD",
"SENTENCE",
"MARK",
"PLAY",
"END"};
#endif
void MarkerEvent(int type, unsigned int char_position, int value, unsigned char *out_ptr)
{//======================================================================================
// type: 1=word, 2=sentence, 3=named mark, 4=play audio, 5=end
ENTER("MarkerEvent");
espeak_EVENT *ep;
double time;
if((event_list == NULL) || (event_list_ix >= (n_event_list-2)))
return;
ep = &event_list[event_list_ix++];
ep->type = (espeak_EVENT_TYPE)type;
ep->unique_identifier = my_unique_identifier;
ep->user_data = my_user_data;
ep->text_position = char_position & 0xffffff;
ep->length = char_position >> 24;
time = (double(count_samples + mbrola_delay + (out_ptr - out_start)/2)*1000.0)/samplerate;
ep->audio_position = int(time);
ep->sample = (count_samples + mbrola_delay + (out_ptr - out_start)/2);
#ifdef DEBUG_ENABLED
SHOW("MarkerEvent > count_samples=%d, out_ptr=%x, out_start=0x%x\n",count_samples, out_ptr, out_start);
SHOW("*** MarkerEvent > type=%s, uid=%d, text_pos=%d, length=%d, audio_position=%d, sample=%d\n",
label[ep->type], ep->unique_identifier, ep->text_position, ep->length,
ep->audio_position, ep->sample);
#endif
if((type == espeakEVENT_MARK) || (type == espeakEVENT_PLAY))
ep->id.name = &namedata[value];
else
ep->id.number = value;
} // end of MarkerEvent
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)
{//===========================================================================
#ifdef DEBUG_ENABLED
ENTER("sync_espeak_Synth");
SHOW("sync_espeak_Synth > position=%d, position_type=%d, end_position=%d, flags=%d, user_data=0x%x, text=%s\n", position, position_type, end_position, flags, user_data, text);
#endif
espeak_ERROR aStatus;
InitText(flags);
my_unique_identifier = unique_identifier;
my_user_data = user_data;
switch(position_type)
{
case POS_CHARACTER:
skip_characters = position;
break;
case POS_WORD:
skip_words = position;
break;
case POS_SENTENCE:
skip_sentences = position;
break;
}
if(skip_characters || skip_words || skip_sentences)
skipping_text = 1;
end_character_position = end_position;
aStatus = Synthesize(unique_identifier, text, flags);
#ifdef USE_ASYNC
wave_flush(my_audio);
#endif
SHOW_TIME("LEAVE sync_espeak_Synth");
return aStatus;
} // end of sync_espeak_Synth
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)
{//=========================================================================
espeak_ERROR aStatus;
InitText(flags);
my_unique_identifier = unique_identifier;
my_user_data = user_data;
if(index_mark != NULL)
{
strncpy0(skip_marker, index_mark, sizeof(skip_marker));
skipping_text = 1;
}
end_character_position = end_position;
aStatus = Synthesize(unique_identifier, text, flags | espeakSSML);
SHOW_TIME("LEAVE sync_espeak_Synth_Mark");
return (aStatus);
} // end of sync_espeak_Synth_Mark
void sync_espeak_Key(const char *key)
{//==================================
// symbolic name, symbolicname_character - is there a system resource of symbolic names per language?
int letter;
int ix;
ix = utf8_in(&letter,key);
if(key[ix] == 0)
{
// a single character
sync_espeak_Char(letter);
return;
}
my_unique_identifier = 0;
my_user_data = NULL;
Synthesize(0, key,0); // speak key as a text string
}
void sync_espeak_Char(wchar_t character)
{//=====================================
// is there a system resource of character names per language?
char buf[80];
my_unique_identifier = 0;
my_user_data = NULL;
sprintf(buf,"<say-as interpret-as=\"tts:char\">&#%d;</say-as>",character);
Synthesize(0, buf,espeakSSML);
}
void sync_espeak_SetPunctuationList(const wchar_t *punctlist)
{//==========================================================
// Set the list of punctuation which are spoken for "some".
my_unique_identifier = 0;
my_user_data = NULL;
wcsncpy(option_punctlist, punctlist, N_PUNCTLIST);
option_punctlist[N_PUNCTLIST-1] = 0;
} // end of sync_espeak_SetPunctuationList
#pragma GCC visibility push(default)
ESPEAK_API void espeak_SetSynthCallback(t_espeak_callback* SynthCallback)
{//======================================================================
ENTER("espeak_SetSynthCallback");
synth_callback = SynthCallback;
#ifdef USE_ASYNC
event_set_callback(synth_callback);
#endif
}
ESPEAK_API void espeak_SetUriCallback(int (* UriCallback)(int, const char*, const char *))
{//=======================================================================================
ENTER("espeak_SetUriCallback");
uri_callback = UriCallback;
}
ESPEAK_API void espeak_SetPhonemeCallback(int (* PhonemeCallback)(const char*))
{//===========================================================================
phoneme_callback = PhonemeCallback;
}
ESPEAK_API int espeak_Initialize(espeak_AUDIO_OUTPUT output_type, int buf_length, const char *path, int options)
{//=============================================================================================================
ENTER("espeak_Initialize");
int param;
// 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)
{
if(setlocale(LC_CTYPE,"UTF-8") == NULL)
setlocale(LC_CTYPE,"");
}
#endif
init_path(path);
initialise(options);
select_output(output_type);
if(f_logespeak)
{
fprintf(f_logespeak,"INIT mode %d options 0x%x\n",output_type,options);
}
// buflength is in mS, allocate 2 bytes per sample
if(buf_length == 0)
buf_length = 200;
outbuf_size = (buf_length * samplerate)/500;
outbuf = (unsigned char*)realloc(outbuf,outbuf_size);
if((out_start = outbuf) == NULL)
return(EE_INTERNAL_ERROR);
// allocate space for event list. Allow 200 events per second.
// Add a constant to allow for very small buf_length
n_event_list = (buf_length*200)/1000 + 20;
if((event_list = (espeak_EVENT *)realloc(event_list,sizeof(espeak_EVENT) * n_event_list)) == NULL)
return(EE_INTERNAL_ERROR);
option_phonemes = 0;
option_mbrola_phonemes = 0;
option_phoneme_events = (options & 1);
VoiceReset(0);
// SetVoiceByName("default");
for(param=0; param<N_SPEECH_PARAM; param++)
param_stack[0].parameter[param] = param_defaults[param];
SetParameter(espeakRATE,175,0);
SetParameter(espeakVOLUME,100,0);
SetParameter(espeakCAPITALS,option_capitals,0);
SetParameter(espeakPUNCTUATION,option_punctuation,0);
SetParameter(espeakWORDGAP,0,0);
// DoVoiceChange(voice);
#ifdef USE_ASYNC
fifo_init();
#endif
return(samplerate);
}
ESPEAK_API 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)
{//=====================================================================================
#ifdef DEBUG_ENABLED
ENTER("espeak_Synth");
SHOW("espeak_Synth > position=%d, position_type=%d, end_position=%d, flags=%d, user_data=0x%x, text=%s\n", position, position_type, end_position, flags, user_data, text);
#endif
if(f_logespeak)
{
fprintf(f_logespeak,"\nSYNTH posn %d %d %d flags 0x%x\n%s\n",position,end_position,position_type,flags, (const char *)text);
fflush(f_logespeak);
}
espeak_ERROR a_error=EE_INTERNAL_ERROR;
static unsigned int temp_identifier;
if (unique_identifier == NULL)
{
unique_identifier = &temp_identifier;
}
*unique_identifier = 0;
if(synchronous_mode)
{
return(sync_espeak_Synth(0,text,size,position,position_type,end_position,flags,user_data));
}
#ifdef USE_ASYNC
// Create the text command
t_espeak_command* c1 = create_espeak_text(text, size, position, position_type, end_position, flags, user_data);
// Retrieve the unique identifier
*unique_identifier = c1->u.my_text.unique_identifier;
// Create the "terminated msg" command (same uid)
t_espeak_command* c2 = create_espeak_terminated_msg(*unique_identifier, user_data);
// Try to add these 2 commands (single transaction)
if (c1 && c2)
{
a_error = fifo_add_commands(c1, c2);
if (a_error != EE_OK)
{
delete_espeak_command(c1);
delete_espeak_command(c2);
c1=c2=NULL;
}
}
else
{
delete_espeak_command(c1);
delete_espeak_command(c2);
}
#endif
return a_error;
} // end of espeak_Synth
ESPEAK_API 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)
{//=========================================================================
#ifdef DEBUG_ENABLED
ENTER("espeak_Synth_Mark");
SHOW("espeak_Synth_Mark > index_mark=%s, end_position=%d, flags=%d, text=%s\n", index_mark, end_position, flags, text);
#endif
espeak_ERROR a_error=EE_OK;
static unsigned int temp_identifier;
if(f_logespeak)
{
fprintf(f_logespeak,"\nSYNTH MARK %s posn %d flags 0x%x\n%s\n",index_mark,end_position,flags, (const char *)text);
}
if (unique_identifier == NULL)
{
unique_identifier = &temp_identifier;
}
*unique_identifier = 0;
if(synchronous_mode)
{
return(sync_espeak_Synth_Mark(0,text,size,index_mark,end_position,flags,user_data));
}
#ifdef USE_ASYNC
// Create the mark command
t_espeak_command* c1 = create_espeak_mark(text, size, index_mark, end_position,
flags, user_data);
// Retrieve the unique identifier
*unique_identifier = c1->u.my_mark.unique_identifier;
// Create the "terminated msg" command (same uid)
t_espeak_command* c2 = create_espeak_terminated_msg(*unique_identifier, user_data);
// Try to add these 2 commands (single transaction)
if (c1 && c2)
{
a_error = fifo_add_commands(c1, c2);
if (a_error != EE_OK)
{
delete_espeak_command(c1);
delete_espeak_command(c2);
c1=c2=NULL;
}
}
else
{
delete_espeak_command(c1);
delete_espeak_command(c2);
}
#endif
return a_error;
} // end of espeak_Synth_Mark
ESPEAK_API espeak_ERROR espeak_Key(const char *key)
{//================================================
ENTER("espeak_Key");
// symbolic name, symbolicname_character - is there a system resource of symbolicnames per language
if(f_logespeak)
{
fprintf(f_logespeak,"\nKEY %s\n",key);
}
espeak_ERROR a_error = EE_OK;
if(synchronous_mode)
{
sync_espeak_Key(key);
return(EE_OK);
}
#ifdef USE_ASYNC
t_espeak_command* c = create_espeak_key( key, NULL);
a_error = fifo_add_command(c);
if (a_error != EE_OK)
{
delete_espeak_command(c);
}
#endif
return a_error;
}
ESPEAK_API espeak_ERROR espeak_Char(wchar_t character)
{//===========================================
ENTER("espeak_Char");
// is there a system resource of character names per language?
if(f_logespeak)
{
fprintf(f_logespeak,"\nCHAR U+%x\n",character);
}
#ifdef USE_ASYNC
espeak_ERROR a_error;
if(synchronous_mode)
{
sync_espeak_Char(character);
return(EE_OK);
}
t_espeak_command* c = create_espeak_char( character, NULL);
a_error = fifo_add_command(c);
if (a_error != EE_OK)
{
delete_espeak_command(c);
}
return a_error;
#else
sync_espeak_Char(character);
return(EE_OK);
#endif
}
ESPEAK_API espeak_ERROR espeak_SetVoiceByName(const char *name)
{//============================================================
ENTER("espeak_SetVoiceByName");
//#ifdef USE_ASYNC
// I don't think there's a need to queue change voice requests
#ifdef deleted
espeak_ERROR a_error;
if(synchronous_mode)
{
return(SetVoiceByName(name));
}
t_espeak_command* c = create_espeak_voice_name(name);
a_error = fifo_add_command(c);
if (a_error != EE_OK)
{
delete_espeak_command(c);
}
return a_error;
#else
return(SetVoiceByName(name));
#endif
} // end of espeak_SetVoiceByName
ESPEAK_API espeak_ERROR espeak_SetVoiceByProperties(espeak_VOICE *voice_selector)
{//==============================================================================
ENTER("espeak_SetVoiceByProperties");
//#ifdef USE_ASYNC
#ifdef deleted
espeak_ERROR a_error;
if(synchronous_mode)
{
return(SetVoiceByProperties(voice_selector));
}
t_espeak_command* c = create_espeak_voice_spec( voice_selector);
a_error = fifo_add_command(c);
if (a_error != EE_OK)
{
delete_espeak_command(c);
}
return a_error;
#else
return(SetVoiceByProperties(voice_selector));
#endif
} // end of espeak_SetVoiceByProperties
ESPEAK_API int espeak_GetParameter(espeak_PARAMETER parameter, int current)
{//========================================================================
ENTER("espeak_GetParameter");
// current: 0=default value, 1=current value
if(current)
{
return(param_stack[0].parameter[parameter]);
}
else
{
return(param_defaults[parameter]);
}
} // end of espeak_GetParameter
ESPEAK_API espeak_ERROR espeak_SetParameter(espeak_PARAMETER parameter, int value, int relative)
{//=============================================================================================
ENTER("espeak_SetParameter");
if(f_logespeak)
{
fprintf(f_logespeak,"SETPARAM %d %d %d\n",parameter,value,relative);
}
#ifdef USE_ASYNC
espeak_ERROR a_error;
if(synchronous_mode)
{
SetParameter(parameter,value,relative);
return(EE_OK);
}
t_espeak_command* c = create_espeak_parameter(parameter, value, relative);
a_error = fifo_add_command(c);
if (a_error != EE_OK)
{
delete_espeak_command(c);
}
return a_error;
#else
SetParameter(parameter,value,relative);
return(EE_OK);
#endif
}
ESPEAK_API espeak_ERROR espeak_SetPunctuationList(const wchar_t *punctlist)
{//================================================================
ENTER("espeak_SetPunctuationList");
// Set the list of punctuation which are spoken for "some".
#ifdef USE_ASYNC
espeak_ERROR a_error;
if(synchronous_mode)
{
sync_espeak_SetPunctuationList(punctlist);
return(EE_OK);
}
t_espeak_command* c = create_espeak_punctuation_list( punctlist);
a_error = fifo_add_command(c);
if (a_error != EE_OK)
{
delete_espeak_command(c);
}
return a_error;
#else
sync_espeak_SetPunctuationList(punctlist);
return(EE_OK);
#endif
} // end of espeak_SetPunctuationList
ESPEAK_API void espeak_SetPhonemeTrace(int value, FILE *stream)
{//============================================================
ENTER("espeak_SetPhonemes");
/* Controls the output of phoneme symbols for the text
bits 0-3:
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)
value=3 as (1), but produces IPA phoneme names rather than ascii
bit 4: produce mbrola pho data
*/
option_phonemes = value & 3;
option_mbrola_phonemes = value & 16;
f_trans = stream;
if(stream == NULL)
f_trans = stderr;
} // end of espeak_SetPhonemes
ESPEAK_API void espeak_CompileDictionary(const char *path, FILE *log, int flags)
{//=============================================================================
ENTER("espeak_CompileDictionary");
CompileDictionary(path, dictionary_name, log, NULL, flags);
} // end of espeak_CompileDirectory
ESPEAK_API espeak_ERROR espeak_Cancel(void)
{//===============================
#ifdef USE_ASYNC
ENTER("espeak_Cancel");
fifo_stop();
event_clear_all();
if(my_mode == AUDIO_OUTPUT_PLAYBACK)
{
wave_close(my_audio);
}
SHOW_TIME("espeak_Cancel > LEAVE");
#endif
embedded_value[EMBED_T] = 0; // reset echo for pronunciation announcements
return EE_OK;
} // end of espeak_Cancel
ESPEAK_API int espeak_IsPlaying(void)
{//==================================
// ENTER("espeak_IsPlaying");
#ifdef USE_ASYNC
if((my_mode == AUDIO_OUTPUT_PLAYBACK) && wave_is_busy(my_audio))
return(1);
return(fifo_is_busy());
#else
return(0);
#endif
} // end of espeak_IsPlaying
ESPEAK_API espeak_ERROR espeak_Synchronize(void)
{//=============================================
#ifdef USE_ASYNC
SHOW_TIME("espeak_Synchronize > ENTER");
while (espeak_IsPlaying())
{
usleep(20000);
}
#endif
SHOW_TIME("espeak_Synchronize > LEAVE");
return EE_OK;
} // end of espeak_Synchronize
extern void FreePhData(void);
ESPEAK_API espeak_ERROR espeak_Terminate(void)
{//===========================================
ENTER("espeak_Terminate");
#ifdef USE_ASYNC
fifo_stop();
fifo_terminate();
event_terminate();
if(my_mode == AUDIO_OUTPUT_PLAYBACK)
{
wave_close(my_audio);
wave_terminate();
out_samplerate = 0;
}
#endif
Free(event_list);
event_list = NULL;
Free(outbuf);
outbuf = NULL;
FreePhData();
if(f_logespeak)
{
fclose(f_logespeak);
f_logespeak = NULL;
}
return EE_OK;
} // end of espeak_Terminate
ESPEAK_API const char *espeak_Info(const char **ptr)
{//=================================================
if(ptr != NULL)
{
*ptr = path_home;
}
return(version_string);
}
#pragma GCC visibility pop