1 /***************************************************************************
2  *   Copyright (C) 2005 to 2013 by Jonathan Duddington                     *
3  *   email: jonsd@users.sourceforge.net                                    *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 3 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write see:                           *
17  *               <http://www.gnu.org/licenses/>.                           *
18  ***************************************************************************/
19 
20 #include "StdAfx.h"
21 
22 #include "stdio.h"
23 #include "ctype.h"
24 #include "string.h"
25 #include "stdlib.h"
26 #include "wchar.h"
27 #include "locale.h"
28 #include <assert.h>
29 #include <time.h>
30 
31 #include "speech.h"
32 
33 #include <sys/stat.h>
34 #ifdef PLATFORM_WINDOWS
35 #include <fcntl.h>
36 #include <io.h>
37 #include <windows.h>
38 #include <winreg.h>
39 #else  /* PLATFORM_POSIX */
40 #include <unistd.h>
41 #endif
42 
43 #include "speak_lib.h"
44 #include "phoneme.h"
45 #include "synthesize.h"
46 #include "voice.h"
47 #include "translate.h"
48 #include "debug.h"
49 
50 #include "fifo.h"
51 #include "event.h"
52 #include "wave.h"
53 
54 unsigned char *outbuf=NULL;
55 
56 espeak_EVENT *event_list=NULL;
57 int event_list_ix=0;
58 int n_event_list;
59 long count_samples;
60 void* my_audio=NULL;
61 
62 static unsigned int my_unique_identifier=0;
63 static void* my_user_data=NULL;
64 static espeak_AUDIO_OUTPUT my_mode=AUDIO_OUTPUT_SYNCHRONOUS;
65 static int synchronous_mode = 1;
66 static int out_samplerate = 0;
67 static int voice_samplerate = 22050;
68 static espeak_ERROR err = EE_OK;
69 
70 t_espeak_callback* synth_callback = NULL;
71 int (* uri_callback)(int, const char *, const char *) = NULL;
72 int (* phoneme_callback)(const char *) = NULL;
73 
74 char path_home[N_PATH_HOME];   // this is the espeak-data directory
75 extern int saved_parameters[N_SPEECH_PARAM]; //Parameters saved on synthesis start
76 
77 
WVoiceChanged(voice_t * wvoice)78 void WVoiceChanged(voice_t *wvoice)
79 {//=================================
80 // Voice change in wavegen
81 	voice_samplerate = wvoice->samplerate;
82 }
83 
84 
85 #ifdef USE_ASYNC
86 
dispatch_audio(short * outbuf,int length,espeak_EVENT * event)87 static int dispatch_audio(short* outbuf, int length, espeak_EVENT* event)
88 {//======================================================================
89 	ENTER("dispatch_audio");
90 
91 	int a_wave_can_be_played = fifo_is_command_enabled();
92 
93 #ifdef DEBUG_ENABLED
94 	SHOW("*** dispatch_audio > uid=%d, [write=%p (%d bytes)], sample=%d, a_wave_can_be_played = %d\n",
95 			(event) ? event->unique_identifier : 0, wave_test_get_write_buffer(), 2*length,
96 			(event) ? event->sample : 0,
97 			a_wave_can_be_played);
98 #endif
99 
100 	switch(my_mode)
101 	{
102 	case AUDIO_OUTPUT_PLAYBACK:
103 	{
104 		int event_type=0;
105 		if(event)
106 		{
107 			event_type = event->type;
108 		}
109 
110 		if(event_type == espeakEVENT_SAMPLERATE)
111 		{
112 			voice_samplerate = event->id.number;
113 
114 			if(out_samplerate != voice_samplerate)
115 			{
116 				if(out_samplerate != 0)
117 				{
118 					// sound was previously open with a different sample rate
119 					wave_close(my_audio);
120 					sleep(1);
121 				}
122 				out_samplerate = voice_samplerate;
123 				if(!wave_init(voice_samplerate))
124 				{
125 					err = EE_INTERNAL_ERROR;
126 					return(-1);
127 				}
128 				wave_set_callback_is_output_enabled( fifo_is_command_enabled);
129 				my_audio = wave_open("alsa");
130 				event_init();
131 			}
132 		}
133 
134 		if (outbuf && length && a_wave_can_be_played)
135 		{
136 			wave_write (my_audio, (char*)outbuf, 2*length);
137 		}
138 
139 		while(a_wave_can_be_played) {
140 			// TBD: some event are filtered here but some insight might be given
141 			// TBD: in synthesise.cpp for avoiding to create WORDs with size=0.
142 			// TBD: For example sentence "or ALT)." returns three words
143 			// "or", "ALT" and "".
144 			// TBD: the last one has its size=0.
145 			if (event && (event->type == espeakEVENT_WORD) && (event->length==0))
146 			{
147 				break;
148 			}
149 			espeak_ERROR a_error = event_declare(event);
150 			if (a_error != EE_BUFFER_FULL)
151 			{
152 				break;
153 			}
154 			SHOW_TIME("dispatch_audio > EE_BUFFER_FULL\n");
155 			usleep(10000);
156 			a_wave_can_be_played = fifo_is_command_enabled();
157 		}
158 	}
159 	break;
160 
161 	case AUDIO_OUTPUT_RETRIEVAL:
162 		if (synth_callback)
163 		{
164 			synth_callback(outbuf, length, event);
165 		}
166 		break;
167 
168 	case AUDIO_OUTPUT_SYNCHRONOUS:
169 	case AUDIO_OUTPUT_SYNCH_PLAYBACK:
170 		break;
171 	}
172 
173 	if (!a_wave_can_be_played)
174 	{
175 		SHOW_TIME("dispatch_audio > synth must be stopped!\n");
176 	}
177 
178 	SHOW_TIME("LEAVE dispatch_audio\n");
179 
180 	return (a_wave_can_be_played==0); // 1 = stop synthesis, -1 = error
181 }
182 
183 
184 
create_events(short * outbuf,int length,espeak_EVENT * event,uint32_t the_write_pos)185 static int create_events(short* outbuf, int length, espeak_EVENT* event, uint32_t the_write_pos)
186 {//=====================================================================
187 	int finished;
188 	int i=0;
189 
190 	// The audio data are written to the output device.
191 	// The list of events in event_list (index: event_list_ix) is read:
192 	// Each event is declared to the "event" object which stores them internally.
193 	// The event object is responsible of calling the external callback
194 	// as soon as the relevant audio sample is played.
195 
196 	do
197 	{ // for each event
198 		espeak_EVENT* event;
199 		if (event_list_ix == 0)
200 		{
201 			event = NULL;
202 		}
203       else
204 		{
205 			event = event_list + i;
206 #ifdef DEBUG_ENABLED
207 			SHOW("Synthesize: event->sample(%d) + %d = %d\n", event->sample, the_write_pos, event->sample + the_write_pos);
208 #endif
209 			event->sample += the_write_pos;
210 		}
211 #ifdef DEBUG_ENABLED
212 		SHOW("*** Synthesize: i=%d (event_list_ix=%d), length=%d\n",i,event_list_ix,length);
213 #endif
214 		finished = dispatch_audio((short *)outbuf, length, event);
215 		length = 0; // the wave data are played once.
216 		i++;
217 	} while((i < event_list_ix) && !finished);
218 	return finished;
219 }
220 
221 
sync_espeak_terminated_msg(uint32_t unique_identifier,void * user_data)222 int sync_espeak_terminated_msg( uint32_t unique_identifier, void* user_data)
223 {//=====================================================================
224 	ENTER("sync_espeak_terminated_msg");
225 
226 	int finished=0;
227 
228 	memset(event_list, 0, 2*sizeof(espeak_EVENT));
229 
230 	event_list[0].type = espeakEVENT_MSG_TERMINATED;
231 	event_list[0].unique_identifier = unique_identifier;
232 	event_list[0].user_data = user_data;
233 	event_list[1].type = espeakEVENT_LIST_TERMINATED;
234 	event_list[1].unique_identifier = unique_identifier;
235 	event_list[1].user_data = user_data;
236 
237 	if (my_mode==AUDIO_OUTPUT_PLAYBACK)
238 	{
239 		while(1)
240 		{
241 			espeak_ERROR a_error = event_declare(event_list);
242 			if (a_error != EE_BUFFER_FULL)
243 			{
244 		 		break;
245 			}
246 			SHOW_TIME("sync_espeak_terminated_msg > EE_BUFFER_FULL\n");
247 			usleep(10000);
248 		}
249 	}
250 	else
251 	{
252 		if (synth_callback)
253 		{
254 			finished=synth_callback(NULL,0,event_list);
255 		}
256 	}
257 	return finished;
258 }
259 
260 #endif
261 
262 
select_output(espeak_AUDIO_OUTPUT output_type)263 static void select_output(espeak_AUDIO_OUTPUT output_type)
264 {//=======================================================
265 	my_mode = output_type;
266 	my_audio = NULL;
267 	synchronous_mode = 1;
268  	option_waveout = 1;   // inhibit portaudio callback from wavegen.cpp
269 	out_samplerate = 0;
270 
271 	switch(my_mode)
272 	{
273 	case AUDIO_OUTPUT_PLAYBACK:
274 		// wave_init() is now called just before the first wave_write()
275 		synchronous_mode = 0;
276 		break;
277 
278 	case AUDIO_OUTPUT_RETRIEVAL:
279 		synchronous_mode = 0;
280 		break;
281 
282 	case AUDIO_OUTPUT_SYNCHRONOUS:
283 		break;
284 
285 	case AUDIO_OUTPUT_SYNCH_PLAYBACK:
286 		option_waveout = 0;
287 		WavegenInitSound();
288 		break;
289 	}
290 }   // end of select_output
291 
292 
293 
294 
GetFileLength(const char * filename)295 int GetFileLength(const char *filename)
296 {//====================================
297 	struct stat statbuf;
298 
299 	if(stat(filename,&statbuf) != 0)
300 		return(0);
301 
302 	if((statbuf.st_mode & S_IFMT) == S_IFDIR)
303 		//	if(S_ISDIR(statbuf.st_mode))
304 		return(-2);  // a directory
305 
306 	return(statbuf.st_size);
307 }  // end of GetFileLength
308 
309 
Alloc(int size)310 char *Alloc(int size)
311 {//==================
312 	char *p;
313 	if((p = (char *)malloc(size)) == NULL)
314 		fprintf(stderr,"Can't allocate memory\n");  // I was told that size+1 fixes a crash on 64-bit systems
315 	return(p);
316 }
317 
Free(void * ptr)318 void Free(void *ptr)
319 {//=================
320 	if(ptr != NULL)
321 		free(ptr);
322 }
323 
324 
325 
init_path(const char * path)326 static void init_path(const char *path)
327 {//====================================
328 #ifdef PLATFORM_WINDOWS
329 	HKEY RegKey;
330 	unsigned long size;
331 	unsigned long var_type;
332 	char *env;
333 	unsigned char buf[sizeof(path_home)-13];
334 
335 	if(path != NULL)
336 	{
337 		sprintf(path_home,"%s/espeak-data",path);
338 		return;
339 	}
340 
341 	if((env = getenv("ESPEAK_DATA_PATH")) != NULL)
342 	{
343 		sprintf(path_home,"%s/espeak-data",env);
344 		if(GetFileLength(path_home) == -2)
345 			return;   // an espeak-data directory exists
346 	}
347 
348 	buf[0] = 0;
349 	RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Speech\\Voices\\Tokens\\eSpeak", 0, KEY_READ, &RegKey);
350 	size = sizeof(buf);
351 	var_type = REG_SZ;
352 	RegQueryValueExA(RegKey, "path", 0, &var_type, buf, &size);
353 
354 	sprintf(path_home,"%s\\espeak-data",buf);
355 
356 #else
357 	char *env;
358 
359 	if(path != NULL)
360 	{
361 		snprintf(path_home,sizeof(path_home),"%s/espeak-data",path);
362 		return;
363 	}
364 
365 	// check for environment variable
366 	if((env = getenv("ESPEAK_DATA_PATH")) != NULL)
367 	{
368 		snprintf(path_home,sizeof(path_home),"%s/espeak-data",env);
369 		if(GetFileLength(path_home) == -2)
370 			return;   // an espeak-data directory exists
371 	}
372 
373 	snprintf(path_home,sizeof(path_home),"%s/espeak-data",getenv("HOME"));
374 	if(access(path_home,R_OK) != 0)
375 	{
376 		strcpy(path_home,PATH_ESPEAK_DATA);
377 	}
378 #endif
379 }
380 
initialise(int control)381 static int initialise(int control)
382 {//===============================
383 	int param;
384 	int result;
385 	int srate = 22050;  // default sample rate 22050 Hz
386 
387 	err = EE_OK;
388 	LoadConfig();
389 
390 	if((result = LoadPhData(&srate)) != 1)  // reads sample rate from espeak-data/phontab
391 	{
392 		if(result == -1)
393 		{
394 			fprintf(stderr,"Failed to load espeak-data\n");
395 			if((control & espeakINITIALIZE_DONT_EXIT) == 0)
396 			{
397 				exit(1);
398 			}
399 		}
400 		else
401 			fprintf(stderr,"Wrong version of espeak-data 0x%x (expects 0x%x) at %s\n",result,version_phdata,path_home);
402 	}
403 	WavegenInit(srate,0);
404 
405 	memset(&current_voice_selected,0,sizeof(current_voice_selected));
406 	SetVoiceStack(NULL, "");
407 	SynthesizeInit();
408 	InitNamedata();
409 
410 	for(param=0; param<N_SPEECH_PARAM; param++)
411 		param_stack[0].parameter[param] = param_defaults[param];
412 
413 	return(0);
414 }
415 
416 
Synthesize(unsigned int unique_identifier,const void * text,int flags)417 static espeak_ERROR Synthesize(unsigned int unique_identifier, const void *text, int flags)
418 {//========================================================================================
419 	// Fill the buffer with output sound
420 	int length;
421 	int finished = 0;
422 	int count_buffers = 0;
423 #ifdef USE_ASYNC
424 	uint32_t a_write_pos=0;
425 #endif
426 
427 #ifdef DEBUG_ENABLED
428 	ENTER("Synthesize");
429 	if (text)
430 	{
431 	SHOW("Synthesize > uid=%d, flags=%d, >>>text=%s<<<\n", unique_identifier, flags, text);
432 	}
433 #endif
434 
435 	if((outbuf==NULL) || (event_list==NULL))
436 		return(EE_INTERNAL_ERROR);  // espeak_Initialize()  has not been called
437 
438 	option_multibyte = flags & 7;
439 	option_ssml = flags & espeakSSML;
440 	option_phoneme_input = flags & espeakPHONEMES;
441 	option_endpause = flags & espeakENDPAUSE;
442 
443 	count_samples = 0;
444 
445 #ifdef USE_ASYNC
446 	if(my_mode == AUDIO_OUTPUT_PLAYBACK)
447 	{
448 		a_write_pos = wave_get_write_position(my_audio);
449 	}
450 #endif
451 
452 	if(translator == NULL)
453 	{
454 		SetVoiceByName("default");
455 	}
456 
457 	SpeakNextClause(NULL,text,0);
458 
459 	if(my_mode == AUDIO_OUTPUT_SYNCH_PLAYBACK)
460 	{
461 		for(;;)
462 		{
463 #ifdef PLATFORM_WINDOWS
464 			Sleep(300);   // 0.3s
465 #else
466 #ifdef USE_NANOSLEEP
467 			struct timespec period;
468 			struct timespec remaining;
469 			period.tv_sec = 0;
470 			period.tv_nsec = 300000000;  // 0.3 sec
471 			nanosleep(&period,&remaining);
472 #else
473 			sleep(1);
474 #endif
475 #endif
476 			if(SynthOnTimer() != 0)
477 				break;
478 		}
479 		return(EE_OK);
480 	}
481 
482 	for(;;)
483 	{
484 #ifdef DEBUG_ENABLED
485 		SHOW("Synthesize > %s\n","for (next)");
486 #endif
487 		out_ptr = outbuf;
488 		out_end = &outbuf[outbuf_size];
489 		event_list_ix = 0;
490 		WavegenFill(0);
491 
492 		length = (out_ptr - outbuf)/2;
493 		count_samples += length;
494 		event_list[event_list_ix].type = espeakEVENT_LIST_TERMINATED; // indicates end of event list
495 		event_list[event_list_ix].unique_identifier = my_unique_identifier;
496 		event_list[event_list_ix].user_data = my_user_data;
497 
498 		count_buffers++;
499 		if (my_mode==AUDIO_OUTPUT_PLAYBACK)
500 		{
501 #ifdef USE_ASYNC
502 			finished = create_events((short *)outbuf, length, event_list, a_write_pos);
503 			if(finished < 0)
504 				return EE_INTERNAL_ERROR;
505 			length = 0; // the wave data are played once.
506 #endif
507 		}
508 		else
509 		{
510 			finished = synth_callback((short *)outbuf, length, event_list);
511 		}
512 		if(finished)
513 		{
514 			SpeakNextClause(NULL,0,2);  // stop
515 			break;
516 		}
517 
518 		if(Generate(phoneme_list,&n_phoneme_list,1)==0)
519 		{
520 			if(WcmdqUsed() == 0)
521 			{
522 				// don't process the next clause until the previous clause has finished generating speech.
523 				// This ensures that <audio> tag (which causes end-of-clause) is at a sound buffer boundary
524 
525 				event_list[0].type = espeakEVENT_LIST_TERMINATED;
526 				event_list[0].unique_identifier = my_unique_identifier;
527 				event_list[0].user_data = my_user_data;
528 
529 				if(SpeakNextClause(NULL,NULL,1)==0)
530 				{
531 #ifdef USE_ASYNC
532 					if (my_mode==AUDIO_OUTPUT_PLAYBACK)
533 					{
534 						if(dispatch_audio(NULL, 0, NULL) < 0) // TBD: test case
535 							return err = EE_INTERNAL_ERROR;
536 					}
537 					else
538 					{
539 						synth_callback(NULL, 0, event_list);  // NULL buffer ptr indicates end of data
540 					}
541 #else
542 					synth_callback(NULL, 0, event_list);  // NULL buffer ptr indicates end of data
543 #endif
544 					break;
545 				}
546 			}
547 		}
548 	}
549 	return(EE_OK);
550 }  //  end of Synthesize
551 
552 #ifdef DEBUG_ENABLED
553 static const char* label[] = {
554   "END_OF_EVENT_LIST",
555   "WORD",
556   "SENTENCE",
557   "MARK",
558   "PLAY",
559   "END",
560   "MSG_TERMINATED",
561   "PHONEME",
562   "SAMPLERATE",
563   "??" };
564 #endif
565 
566 
MarkerEvent(int type,unsigned int char_position,int value,int value2,unsigned char * out_ptr)567 void MarkerEvent(int type, unsigned int char_position, int value, int value2, unsigned char *out_ptr)
568 {//==================================================================================================
569 	// type: 1=word, 2=sentence, 3=named mark, 4=play audio, 5=end, 7=phoneme
570 	ENTER("MarkerEvent");
571 	espeak_EVENT *ep;
572 	double time;
573 
574 	if((event_list == NULL) || (event_list_ix >= (n_event_list-2)))
575 		return;
576 
577 	ep = &event_list[event_list_ix++];
578 	ep->type = (espeak_EVENT_TYPE)type;
579 	ep->unique_identifier = my_unique_identifier;
580 	ep->user_data = my_user_data;
581 	ep->text_position = char_position & 0xffffff;
582 	ep->length = char_position >> 24;
583 
584 	time = (double(count_samples + mbrola_delay + (out_ptr - out_start)/2)*1000.0)/samplerate;
585 	ep->audio_position = int(time);
586 	ep->sample = (count_samples + mbrola_delay + (out_ptr - out_start)/2);
587 
588 #ifdef DEBUG_ENABLED
589 	SHOW("MarkerEvent > count_samples=%d, out_ptr=%x, out_start=0x%x\n",count_samples, out_ptr, out_start);
590 	SHOW("*** MarkerEvent > type=%s, uid=%d, text_pos=%d, length=%d, audio_position=%d, sample=%d\n",
591 			label[ep->type], ep->unique_identifier, ep->text_position, ep->length,
592 			ep->audio_position, ep->sample);
593 #endif
594 
595 	if((type == espeakEVENT_MARK) || (type == espeakEVENT_PLAY))
596 		ep->id.name = &namedata[value];
597 	else
598 //#ifdef deleted
599 // temporarily removed, don't introduce until after eSpeak version 1.46.02
600 	if(type == espeakEVENT_PHONEME)
601 	{
602 		int *p;
603 		p = (int *)(ep->id.string);
604 		p[0] = value;
605 		p[1] = value2;
606 	}
607 	else
608 //#endif
609 	{
610 		ep->id.number = value;
611 	}
612 }  //  end of MarkerEvent
613 
614 
615 
616 
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)617 espeak_ERROR sync_espeak_Synth(unsigned int unique_identifier, const void *text, size_t size,
618 		      unsigned int position, espeak_POSITION_TYPE position_type,
619 		      unsigned int end_position, unsigned int flags, void* user_data)
620 {//===========================================================================
621 
622 #ifdef DEBUG_ENABLED
623 	ENTER("sync_espeak_Synth");
624 	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);
625 #endif
626 
627 	espeak_ERROR aStatus;
628 
629 	InitText(flags);
630 	my_unique_identifier = unique_identifier;
631 	my_user_data = user_data;
632 
633 	for (int i=0; i < N_SPEECH_PARAM; i++)
634 		saved_parameters[i] = param_stack[0].parameter[i];
635 
636 	switch(position_type)
637 		{
638 		case POS_CHARACTER:
639 			skip_characters = position;
640 			break;
641 
642 		case POS_WORD:
643 			skip_words = position;
644 			break;
645 
646 		case POS_SENTENCE:
647 			skip_sentences = position;
648 			break;
649 
650 		}
651 	if(skip_characters || skip_words || skip_sentences)
652 		skipping_text = 1;
653 
654 	end_character_position = end_position;
655 
656 	aStatus = Synthesize(unique_identifier, text, flags);
657 	#ifdef USE_ASYNC
658 	wave_flush(my_audio);
659 	#endif
660 
661 	SHOW_TIME("LEAVE sync_espeak_Synth");
662 	return aStatus;
663 }  //  end of sync_espeak_Synth
664 
665 
666 
667 
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)668 espeak_ERROR sync_espeak_Synth_Mark(unsigned int unique_identifier, const void *text, size_t size,
669 			   const char *index_mark, unsigned int end_position,
670 			   unsigned int flags, void* user_data)
671 {//=========================================================================
672 	espeak_ERROR aStatus;
673 
674 	InitText(flags);
675 
676 	my_unique_identifier = unique_identifier;
677 	my_user_data = user_data;
678 
679 	if(index_mark != NULL)
680 		{
681 		strncpy0(skip_marker, index_mark, sizeof(skip_marker));
682 		skipping_text = 1;
683 		}
684 
685 	end_character_position = end_position;
686 
687 
688 	aStatus = Synthesize(unique_identifier, text, flags | espeakSSML);
689 	SHOW_TIME("LEAVE sync_espeak_Synth_Mark");
690 
691 	return (aStatus);
692 }  //  end of sync_espeak_Synth_Mark
693 
694 
695 
sync_espeak_Key(const char * key)696 void sync_espeak_Key(const char *key)
697 {//==================================
698 	// symbolic name, symbolicname_character  - is there a system resource of symbolic names per language?
699 	int letter;
700 	int ix;
701 
702 	ix = utf8_in(&letter,key);
703 	if(key[ix] == 0)
704 	{
705 		// a single character
706 		sync_espeak_Char(letter);
707 		return;
708 	}
709 
710 	my_unique_identifier = 0;
711 	my_user_data = NULL;
712 	Synthesize(0, key,0);   // speak key as a text string
713 }
714 
715 
sync_espeak_Char(wchar_t character)716 void sync_espeak_Char(wchar_t character)
717 {//=====================================
718 	// is there a system resource of character names per language?
719 	char buf[80];
720 	my_unique_identifier = 0;
721 	my_user_data = NULL;
722 
723 	sprintf(buf,"<say-as interpret-as=\"tts:char\">&#%d;</say-as>",character);
724 	Synthesize(0, buf,espeakSSML);
725 }
726 
727 
728 
sync_espeak_SetPunctuationList(const wchar_t * punctlist)729 void sync_espeak_SetPunctuationList(const wchar_t *punctlist)
730 {//==========================================================
731 	// Set the list of punctuation which are spoken for "some".
732 	my_unique_identifier = 0;
733 	my_user_data = NULL;
734 
735 	option_punctlist[0] = 0;
736 	if(punctlist != NULL)
737 	{
738 		wcsncpy(option_punctlist, punctlist, N_PUNCTLIST);
739 		option_punctlist[N_PUNCTLIST-1] = 0;
740 	}
741 }  //  end of sync_espeak_SetPunctuationList
742 
743 
744 
745 
746 #pragma GCC visibility push(default)
747 
748 
espeak_SetSynthCallback(t_espeak_callback * SynthCallback)749 ESPEAK_API void espeak_SetSynthCallback(t_espeak_callback* SynthCallback)
750 {//======================================================================
751 	ENTER("espeak_SetSynthCallback");
752 	synth_callback = SynthCallback;
753 #ifdef USE_ASYNC
754 	event_set_callback(synth_callback);
755 #endif
756 }
757 
espeak_SetUriCallback(int (* UriCallback)(int,const char *,const char *))758 ESPEAK_API void espeak_SetUriCallback(int (* UriCallback)(int, const char*, const char *))
759 {//=======================================================================================
760 	ENTER("espeak_SetUriCallback");
761 	uri_callback = UriCallback;
762 }
763 
764 
espeak_SetPhonemeCallback(int (* PhonemeCallback)(const char *))765 ESPEAK_API void espeak_SetPhonemeCallback(int (* PhonemeCallback)(const char*))
766 {//===========================================================================
767 	phoneme_callback = PhonemeCallback;
768 }
769 
espeak_Initialize(espeak_AUDIO_OUTPUT output_type,int buf_length,const char * path,int options)770 ESPEAK_API int espeak_Initialize(espeak_AUDIO_OUTPUT output_type, int buf_length, const char *path, int options)
771 {//=============================================================================================================
772 ENTER("espeak_Initialize");
773 	int param;
774 
775 	// It seems that the wctype functions don't work until the locale has been set
776 	// to something other than the default "C".  Then, not only Latin1 but also the
777 	// other characters give the correct results with iswalpha() etc.
778 #ifdef PLATFORM_RISCOS
779 	setlocale(LC_CTYPE,"ISO8859-1");
780 #else
781 	if(setlocale(LC_CTYPE,"C.UTF-8") == NULL)
782 	{
783 		if(setlocale(LC_CTYPE,"UTF-8") == NULL)
784 			if(setlocale(LC_CTYPE,"en_US.UTF-8") == NULL)
785 				setlocale(LC_CTYPE,"");
786 	}
787 #endif
788 
789 	init_path(path);
790 	initialise(options);
791 	select_output(output_type);
792 
793 	if(f_logespeak)
794 	{
795 		fprintf(f_logespeak,"INIT mode %d options 0x%x\n",output_type,options);
796 	}
797 
798 	// buflength is in mS, allocate 2 bytes per sample
799 	if((buf_length == 0) || (output_type == AUDIO_OUTPUT_PLAYBACK) || (output_type == AUDIO_OUTPUT_SYNCH_PLAYBACK))
800 		buf_length = 200;
801 
802 	outbuf_size = (buf_length * samplerate)/500;
803 	outbuf = (unsigned char*)realloc(outbuf,outbuf_size);
804 	if((out_start = outbuf) == NULL)
805 		return(EE_INTERNAL_ERROR);
806 
807 	// allocate space for event list.  Allow 200 events per second.
808 	// Add a constant to allow for very small buf_length
809 	n_event_list = (buf_length*200)/1000 + 20;
810 	if((event_list = (espeak_EVENT *)realloc(event_list,sizeof(espeak_EVENT) * n_event_list)) == NULL)
811 		return(EE_INTERNAL_ERROR);
812 
813 	option_phonemes = 0;
814 	option_mbrola_phonemes = 0;
815 	option_phoneme_events = (options & (espeakINITIALIZE_PHONEME_EVENTS | espeakINITIALIZE_PHONEME_IPA));
816 
817 	VoiceReset(0);
818 //	SetVoiceByName("default");
819 
820 	for(param=0; param<N_SPEECH_PARAM; param++)
821 		param_stack[0].parameter[param] = saved_parameters[param] = param_defaults[param];
822 
823 	SetParameter(espeakRATE,175,0);
824 	SetParameter(espeakVOLUME,100,0);
825 	SetParameter(espeakCAPITALS,option_capitals,0);
826 	SetParameter(espeakPUNCTUATION,option_punctuation,0);
827 	SetParameter(espeakWORDGAP,0,0);
828 //	DoVoiceChange(voice);
829 
830 #ifdef USE_ASYNC
831 	fifo_init();
832 #endif
833 
834   return(samplerate);
835 }
836 
837 
838 
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)839 ESPEAK_API espeak_ERROR espeak_Synth(const void *text, size_t size,
840 				     unsigned int position,
841 				     espeak_POSITION_TYPE position_type,
842 				     unsigned int end_position, unsigned int flags,
843 				     unsigned int* unique_identifier, void* user_data)
844 {//=====================================================================================
845 #ifdef DEBUG_ENABLED
846 	ENTER("espeak_Synth");
847 	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);
848 #endif
849 
850 	if(f_logespeak)
851 	{
852 		fprintf(f_logespeak,"\nSYNTH posn %d %d %d flags 0x%x\n%s\n",position,end_position,position_type,flags, (const char *)text);
853 		fflush(f_logespeak);
854 	}
855 
856 	espeak_ERROR a_error=EE_INTERNAL_ERROR;
857 	static unsigned int temp_identifier;
858 
859 	if (unique_identifier == NULL)
860 	{
861 		unique_identifier = &temp_identifier;
862 	}
863 	*unique_identifier = 0;
864 
865 	if(synchronous_mode)
866 	{
867 		return(sync_espeak_Synth(0,text,size,position,position_type,end_position,flags,user_data));
868 	}
869 
870 #ifdef USE_ASYNC
871 	// Create the text command
872 	t_espeak_command* c1 = create_espeak_text(text, size, position, position_type, end_position, flags, user_data);
873 
874 	// Retrieve the unique identifier
875 	*unique_identifier = c1->u.my_text.unique_identifier;
876 
877 	// Create the "terminated msg" command (same uid)
878 	t_espeak_command* c2 = create_espeak_terminated_msg(*unique_identifier, user_data);
879 
880 	// Try to add these 2 commands (single transaction)
881 	if (c1 && c2)
882 	{
883 		a_error = fifo_add_commands(c1, c2);
884 		if (a_error != EE_OK)
885 		{
886 			delete_espeak_command(c1);
887 			delete_espeak_command(c2);
888 			c1=c2=NULL;
889 		}
890 	}
891 	else
892 	{
893 		delete_espeak_command(c1);
894 		delete_espeak_command(c2);
895 	}
896 
897 #endif
898 	return a_error;
899 }  //  end of espeak_Synth
900 
901 
902 
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)903 ESPEAK_API espeak_ERROR espeak_Synth_Mark(const void *text, size_t size,
904 					  const char *index_mark,
905 					  unsigned int end_position,
906 					  unsigned int flags,
907 					  unsigned int* unique_identifier,
908 					  void* user_data)
909 {//=========================================================================
910 #ifdef DEBUG_ENABLED
911   ENTER("espeak_Synth_Mark");
912   SHOW("espeak_Synth_Mark > index_mark=%s, end_position=%d, flags=%d, text=%s\n", index_mark, end_position, flags, text);
913 #endif
914 
915 	espeak_ERROR a_error=EE_OK;
916 	static unsigned int temp_identifier;
917 
918 	if(f_logespeak)
919 	{
920 		fprintf(f_logespeak,"\nSYNTH MARK %s posn %d flags 0x%x\n%s\n",index_mark,end_position,flags, (const char *)text);
921 	}
922 
923 
924 	if (unique_identifier == NULL)
925 	{
926 		unique_identifier = &temp_identifier;
927 	}
928 	*unique_identifier = 0;
929 
930 	if(synchronous_mode)
931 	{
932 		return(sync_espeak_Synth_Mark(0,text,size,index_mark,end_position,flags,user_data));
933 	}
934 
935 #ifdef USE_ASYNC
936 	// Create the mark command
937 	t_espeak_command* c1 = create_espeak_mark(text, size, index_mark, end_position,
938 						flags, user_data);
939 
940 	// Retrieve the unique identifier
941 	*unique_identifier = c1->u.my_mark.unique_identifier;
942 
943 	// Create the "terminated msg" command (same uid)
944 	t_espeak_command* c2 = create_espeak_terminated_msg(*unique_identifier, user_data);
945 
946 	// Try to add these 2 commands (single transaction)
947 	if (c1 && c2)
948 	{
949 		a_error = fifo_add_commands(c1, c2);
950 		if (a_error != EE_OK)
951 		{
952 			delete_espeak_command(c1);
953 			delete_espeak_command(c2);
954 			c1=c2=NULL;
955 		}
956 	}
957 	else
958 	{
959 		delete_espeak_command(c1);
960 		delete_espeak_command(c2);
961 	}
962 
963 #endif
964 	return a_error;
965 }  //  end of espeak_Synth_Mark
966 
967 
968 
espeak_Key(const char * key)969 ESPEAK_API espeak_ERROR espeak_Key(const char *key)
970 {//================================================
971 	ENTER("espeak_Key");
972 	// symbolic name, symbolicname_character  - is there a system resource of symbolicnames per language
973 
974 	if(f_logespeak)
975 	{
976 		fprintf(f_logespeak,"\nKEY %s\n",key);
977 	}
978 
979 	espeak_ERROR a_error = EE_OK;
980 
981 	if(synchronous_mode)
982 	{
983 		sync_espeak_Key(key);
984 		return(EE_OK);
985 	}
986 
987 #ifdef USE_ASYNC
988 	t_espeak_command* c = create_espeak_key( key, NULL);
989 	a_error = fifo_add_command(c);
990 	if (a_error != EE_OK)
991 	{
992 		delete_espeak_command(c);
993 	}
994 
995 #endif
996   return a_error;
997 }
998 
999 
espeak_Char(wchar_t character)1000 ESPEAK_API espeak_ERROR espeak_Char(wchar_t character)
1001 {//===========================================
1002   ENTER("espeak_Char");
1003   // is there a system resource of character names per language?
1004 
1005 	if(f_logespeak)
1006 	{
1007 		fprintf(f_logespeak,"\nCHAR U+%x\n",character);
1008 	}
1009 
1010 #ifdef USE_ASYNC
1011 	espeak_ERROR a_error;
1012 
1013 	if(synchronous_mode)
1014 	{
1015 		sync_espeak_Char(character);
1016 		return(EE_OK);
1017 	}
1018 
1019 	t_espeak_command* c = create_espeak_char( character, NULL);
1020 	a_error = fifo_add_command(c);
1021 	if (a_error != EE_OK)
1022 	{
1023 		delete_espeak_command(c);
1024 	}
1025 	return a_error;
1026 #else
1027 	sync_espeak_Char(character);
1028 	return(EE_OK);
1029 #endif
1030 }
1031 
1032 
espeak_SetVoiceByName(const char * name)1033 ESPEAK_API espeak_ERROR espeak_SetVoiceByName(const char *name)
1034 {//============================================================
1035   ENTER("espeak_SetVoiceByName");
1036 
1037 //#ifdef USE_ASYNC
1038 // I don't think there's a need to queue change voice requests
1039 #ifdef deleted
1040 	espeak_ERROR a_error;
1041 
1042 	if(synchronous_mode)
1043 	{
1044 		return(SetVoiceByName(name));
1045 	}
1046 
1047 	t_espeak_command* c = create_espeak_voice_name(name);
1048 	a_error = fifo_add_command(c);
1049 	if (a_error != EE_OK)
1050 	{
1051 		delete_espeak_command(c);
1052 	}
1053 	return a_error;
1054 #else
1055 	return(SetVoiceByName(name));
1056 #endif
1057 }  // end of espeak_SetVoiceByName
1058 
1059 
1060 
espeak_SetVoiceByProperties(espeak_VOICE * voice_selector)1061 ESPEAK_API espeak_ERROR espeak_SetVoiceByProperties(espeak_VOICE *voice_selector)
1062 {//==============================================================================
1063   ENTER("espeak_SetVoiceByProperties");
1064 
1065 //#ifdef USE_ASYNC
1066 #ifdef deleted
1067 	espeak_ERROR a_error;
1068 
1069 	if(synchronous_mode)
1070 	{
1071 		return(SetVoiceByProperties(voice_selector));
1072 	}
1073 
1074 	t_espeak_command* c = create_espeak_voice_spec( voice_selector);
1075 	a_error = fifo_add_command(c);
1076 	if (a_error != EE_OK)
1077 	{
1078 		delete_espeak_command(c);
1079 	}
1080 	return a_error;
1081 #else
1082 	return(SetVoiceByProperties(voice_selector));
1083 #endif
1084 }  // end of espeak_SetVoiceByProperties
1085 
1086 
espeak_GetParameter(espeak_PARAMETER parameter,int current)1087 ESPEAK_API int espeak_GetParameter(espeak_PARAMETER parameter, int current)
1088 {//========================================================================
1089 	ENTER("espeak_GetParameter");
1090 	// current: 0=default value, 1=current value
1091 	if(current)
1092 	{
1093 		return(param_stack[0].parameter[parameter]);
1094 	}
1095 	else
1096 	{
1097 		return(param_defaults[parameter]);
1098 	}
1099 }  //  end of espeak_GetParameter
1100 
1101 
espeak_SetParameter(espeak_PARAMETER parameter,int value,int relative)1102 ESPEAK_API espeak_ERROR espeak_SetParameter(espeak_PARAMETER parameter, int value, int relative)
1103 {//=============================================================================================
1104   ENTER("espeak_SetParameter");
1105 
1106 	if(f_logespeak)
1107 	{
1108 		fprintf(f_logespeak,"SETPARAM %d %d %d\n",parameter,value,relative);
1109 	}
1110 #ifdef USE_ASYNC
1111 	espeak_ERROR a_error;
1112 
1113 	if(synchronous_mode)
1114 	{
1115 		SetParameter(parameter,value,relative);
1116 		return(EE_OK);
1117 	}
1118 
1119 	t_espeak_command* c = create_espeak_parameter(parameter, value, relative);
1120 
1121 	a_error = fifo_add_command(c);
1122 	if (a_error != EE_OK)
1123 	{
1124 		delete_espeak_command(c);
1125 	}
1126 	return a_error;
1127 #else
1128 	SetParameter(parameter,value,relative);
1129 	return(EE_OK);
1130 #endif
1131 }
1132 
1133 
espeak_SetPunctuationList(const wchar_t * punctlist)1134 ESPEAK_API espeak_ERROR espeak_SetPunctuationList(const wchar_t *punctlist)
1135 {//================================================================
1136   ENTER("espeak_SetPunctuationList");
1137   // Set the list of punctuation which are spoken for "some".
1138 
1139 #ifdef USE_ASYNC
1140 	espeak_ERROR a_error;
1141 
1142 	if(synchronous_mode)
1143 	{
1144 		sync_espeak_SetPunctuationList(punctlist);
1145 		return(EE_OK);
1146 	}
1147 
1148 	t_espeak_command* c = create_espeak_punctuation_list( punctlist);
1149 	a_error = fifo_add_command(c);
1150 	if (a_error != EE_OK)
1151 	{
1152 		delete_espeak_command(c);
1153 	}
1154 	return a_error;
1155 #else
1156 	sync_espeak_SetPunctuationList(punctlist);
1157 	return(EE_OK);
1158 #endif
1159 }  //  end of espeak_SetPunctuationList
1160 
1161 
espeak_SetPhonemeTrace(int value,FILE * stream)1162 ESPEAK_API void espeak_SetPhonemeTrace(int value, FILE *stream)
1163 {//============================================================
1164 	ENTER("espeak_SetPhonemes");
1165 	/* Controls the output of phoneme symbols for the text
1166 		bits 0-3:
1167 		 value=0  No phoneme output (default)
1168 		 value=1  Output the translated phoneme symbols for the text
1169 		 value=2  as (1), but also output a trace of how the translation was done (matching rules and list entries)
1170 		 value=3  as (1), but produces IPA phoneme names rather than ascii
1171 		bit 4:   produce mbrola pho data
1172 	*/
1173 	option_phonemes = value & 7;
1174 	option_mbrola_phonemes = value & 16;
1175 	f_trans = stream;
1176 	if(stream == NULL)
1177 		f_trans = stderr;
1178 
1179 }   //  end of espeak_SetPhonemes
1180 
1181 
espeak_TextToPhonemes(const void ** textptr,int textmode,int phonememode)1182 ESPEAK_API const char *espeak_TextToPhonemes(const void **textptr, int textmode, int phonememode)
1183 {//=================================================================================================
1184 	/* phoneme_mode  bits 0-3: 0=only phoneme names, 1=ties, 2=ZWJ, 3=underscore separator
1185 	                 bits 4-7:   0=eSpeak phoneme names, 1=IPA
1186 	*/
1187 
1188 	option_multibyte = textmode & 7;
1189 	*textptr = TranslateClause(translator, NULL, *textptr, NULL, NULL);
1190 	return(GetTranslatedPhonemeString(phonememode));
1191 }
1192 
1193 
espeak_CompileDictionary(const char * path,FILE * log,int flags)1194 ESPEAK_API void espeak_CompileDictionary(const char *path, FILE *log, int flags)
1195 {//=============================================================================
1196 	ENTER("espeak_CompileDictionary");
1197 	CompileDictionary(path, dictionary_name, log, NULL, flags);
1198 }   //  end of espeak_CompileDirectory
1199 
1200 
espeak_Cancel(void)1201 ESPEAK_API espeak_ERROR espeak_Cancel(void)
1202 {//===============================
1203 #ifdef USE_ASYNC
1204 	ENTER("espeak_Cancel");
1205 	fifo_stop();
1206 	event_clear_all();
1207 
1208 	if(my_mode == AUDIO_OUTPUT_PLAYBACK)
1209 	{
1210 		wave_close(my_audio);
1211 	}
1212 	SHOW_TIME("espeak_Cancel > LEAVE");
1213 #endif
1214 	embedded_value[EMBED_T] = 0;    // reset echo for pronunciation announcements
1215 
1216 	for (int i=0; i < N_SPEECH_PARAM; i++)
1217 		SetParameter(i, saved_parameters[i], 0);
1218 
1219 	return EE_OK;
1220 }   //  end of espeak_Cancel
1221 
1222 
espeak_IsPlaying(void)1223 ESPEAK_API int espeak_IsPlaying(void)
1224 {//==================================
1225 //	ENTER("espeak_IsPlaying");
1226 #ifdef USE_ASYNC
1227 	if((my_mode == AUDIO_OUTPUT_PLAYBACK) && wave_is_busy(my_audio))
1228 		return(1);
1229 
1230 	return(fifo_is_busy());
1231 #else
1232 	return(0);
1233 #endif
1234 }   //  end of espeak_IsPlaying
1235 
1236 
espeak_Synchronize(void)1237 ESPEAK_API espeak_ERROR espeak_Synchronize(void)
1238 {//=============================================
1239 	espeak_ERROR berr = err;
1240 #ifdef USE_ASYNC
1241 	SHOW_TIME("espeak_Synchronize > ENTER");
1242 	while (espeak_IsPlaying())
1243 	{
1244 		usleep(20000);
1245 	}
1246 #endif
1247 	err = EE_OK;
1248 	SHOW_TIME("espeak_Synchronize > LEAVE");
1249 	return berr;
1250 }   //  end of espeak_Synchronize
1251 
1252 
1253 extern void FreePhData(void);
1254 extern void FreeVoiceList(void);
1255 
espeak_Terminate(void)1256 ESPEAK_API espeak_ERROR espeak_Terminate(void)
1257 {//===========================================
1258 	ENTER("espeak_Terminate");
1259 #ifdef USE_ASYNC
1260 	fifo_stop();
1261 	fifo_terminate();
1262 	event_terminate();
1263 
1264 	if(my_mode == AUDIO_OUTPUT_PLAYBACK)
1265 	{
1266 		wave_close(my_audio);
1267 		wave_terminate();
1268 		out_samplerate = 0;
1269 	}
1270 
1271 #endif
1272 	Free(event_list);
1273 	event_list = NULL;
1274 	Free(outbuf);
1275 	outbuf = NULL;
1276 	FreePhData();
1277 	FreeVoiceList();
1278 
1279 	if(f_logespeak)
1280 	{
1281 		fclose(f_logespeak);
1282 		f_logespeak = NULL;
1283 	}
1284 
1285 	return EE_OK;
1286 }   //  end of espeak_Terminate
1287 
espeak_Info(const char ** ptr)1288 ESPEAK_API const char *espeak_Info(const char **ptr)
1289 {//=================================================
1290 	if(ptr != NULL)
1291 	{
1292 		*ptr = path_home;
1293 	}
1294 	return(version_string);
1295 }
1296 
1297 #pragma GCC visibility pop
1298 
1299 
1300