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(¤t_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