PageRenderTime 88ms CodeModel.GetById 8ms app.highlight 73ms RepoModel.GetById 1ms app.codeStats 0ms

/native/external/espeak/src/speak.cpp

http://eyes-free.googlecode.com/
C++ | 846 lines | 658 code | 137 blank | 51 comment | 127 complexity | 94e25c8e3b6e3b24bed02e0015e2f0bb MD5 | raw file
  1/***************************************************************************
  2 *   Copyright (C) 2005 to 2007 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 "speech.h"
 23
 24#include <stdio.h>
 25#include <stdlib.h>
 26#include <string.h>
 27
 28#ifndef PLATFORM_DOS
 29#ifdef PLATFORM_WINDOWS
 30#include <windows.h>
 31#include <winreg.h>
 32#else
 33#include <unistd.h>
 34#endif
 35#endif
 36
 37#ifndef NEED_GETOPT
 38#include <getopt.h>
 39#endif
 40#include <time.h>
 41#include <signal.h>
 42#include <locale.h>
 43#include <sys/stat.h>
 44
 45#include "speak_lib.h"
 46#include "phoneme.h"
 47#include "synthesize.h"
 48#include "voice.h"
 49#include "translate.h"
 50
 51
 52
 53extern void Write4Bytes(FILE *f, int value);
 54char path_home[N_PATH_HOME];    // this is the espeak-data directory
 55
 56char filetype[5];
 57char wavefile[200];
 58int (* uri_callback)(int, const char *, const char *) = NULL;
 59int (* phoneme_callback)(const char *) = NULL;
 60
 61FILE *f_wave = NULL;
 62int quiet = 0;
 63unsigned int samples_total = 0;
 64unsigned int samples_split = 0;
 65unsigned int wavefile_count = 0;
 66int end_of_sentence = 0;
 67
 68static const char *help_text =
 69"\nspeak [options] [\"<words>\"]\n\n"
 70"-f <text file>   Text file to speak\n"
 71"--stdin    Read text input from stdin instead of a file\n\n"
 72"If neither -f nor --stdin, <words> are spoken, or if none then text is\n"
 73"spoken from stdin, each line separately.\n\n"
 74"-a <integer>\n"
 75"\t   Amplitude, 0 to 200, default is 100\n"
 76"-g <integer>\n"
 77"\t   Word gap. Pause between words, units of 10mS at the default speed\n"
 78"-l <integer>\n"
 79"\t   Line length. If not zero (which is the default), consider\n"
 80"\t   lines less than this length as end-of-clause\n"
 81"-p <integer>\n"
 82"\t   Pitch adjustment, 0 to 99, default is 50\n"
 83"-s <integer>\n"
 84"\t   Speed in words per minute 80 to 370, default is 170\n"
 85"-v <voice name>\n"
 86"\t   Use voice file of this name from espeak-data/voices\n"
 87"-w <wave file name>\n"
 88"\t   Write output to this WAV file, rather than speaking it directly\n"
 89"-b\t   Input text is 8-bit encoding\n"
 90"-m\t   Interpret SSML markup, and ignore other < > tags\n"
 91"-q\t   Quiet, don't produce any speech (may be useful with -x)\n"
 92"-x\t   Write phoneme mnemonics to stdout\n"
 93"-X\t   Write phonemes mnemonics and translation trace to stdout\n"
 94"-z\t   No final sentence pause at the end of the text\n"
 95"--stdout   Write speech output to stdout\n"
 96"--compile=<voice name>\n"
 97"\t   Compile the pronunciation rules and dictionary in the current\n"
 98"\t   directory. =<voice name> is optional and specifies which language\n"
 99"--punct=\"<characters>\"\n"
100"\t   Speak the names of punctuation characters during speaking.  If\n"
101"\t   =<characters> is omitted, all punctuation is spoken.\n"
102"--split=\"<minutes>\"\n"
103"\t   Starts a new WAV file every <minutes>.  Used with -w\n"
104"--voices=<language>\n"
105"\t   List the available voices for the specified language.\n"
106"\t   If <language> is omitted, then list all voices.\n"
107"-k <integer>\n"
108"\t   Indicate capital letters with: 1=sound, 2=the word \"capitals\",\n"
109"\t   higher values = a pitch increase (try -k20).\n";
110
111
112void DisplayVoices(FILE *f_out, char *language);
113
114USHORT voice_pcnt[N_PEAKS+1][3];
115
116
117
118int GetFileLength(const char *filename)
119{//====================================
120	struct stat statbuf;
121
122	if(stat(filename,&statbuf) != 0)
123		return(0);
124
125	if((statbuf.st_mode & S_IFMT) == S_IFDIR)
126//	if(S_ISDIR(statbuf.st_mode))
127		return(-2);  // a directory
128
129	return(statbuf.st_size);
130}  // end of GetFileLength
131
132
133char *Alloc(int size)
134{//==================
135	char *p;
136	if((p = (char *)malloc(size)) == NULL)
137		fprintf(stderr,"Can't allocate memory\n");
138	return(p);
139}
140
141void Free(void *ptr)
142{//=================
143	if(ptr != NULL)
144		free(ptr);
145}
146
147
148void DisplayVoices(FILE *f_out, char *language)
149{//============================================
150	int ix;
151	const char *p;
152	int len;
153	int count;
154	int scores = 0;
155	const espeak_VOICE *v;
156	const char *lang_name;
157	char age_buf[12];
158	const espeak_VOICE **voices;
159	espeak_VOICE voice_select;
160
161	static char genders[4] = {' ','M','F',' '};
162
163	if((language != NULL) && (language[0] != 0))
164	{
165		// display only voices for the specified language, in order of priority
166		voice_select.languages = language;
167		voice_select.age = 0;
168		voice_select.gender = 0;
169		voice_select.name = NULL;
170		voices = espeak_ListVoices(&voice_select);
171		scores = 1;
172	}
173	else
174	{
175		voices = espeak_ListVoices(NULL);
176	}
177
178	fprintf(f_out,"Pty Language Age/Gender VoiceName       File        Other Langs\n");
179
180	for(ix=0; (v = voices[ix]) != NULL; ix++)
181	{
182		count = 0;
183		p = v->languages;
184		while(*p != 0)
185		{
186			len = strlen(p+1);
187			lang_name = p+1;
188
189			if(v->age == 0)
190				strcpy(age_buf,"   ");
191			else
192				sprintf(age_buf,"%3d",v->age);
193
194			if(count==0)
195			{
196				fprintf(f_out,"%2d  %-12s%s%c  %-17s %-11s ",
197               p[0],lang_name,age_buf,genders[v->gender],v->name,v->identifier);
198			}
199			else
200			{
201				fprintf(f_out,"(%s %d)",lang_name,p[0]);
202			}
203			count++;
204			p += len+2;
205		}
206//		if(scores)
207//			fprintf(f_out,"%3d  ",v->score);
208		fputc('\n',f_out);
209	}
210}   //  end of DisplayVoices
211
212
213
214
215static int OpenWaveFile(const char *path, int rate)
216//=================================================
217{
218	// Set the length of 0x7ffff000 for --stdout
219	// This will be changed to the correct length for -w (write to file)
220	static unsigned char wave_hdr[44] = {
221		'R','I','F','F',0x24,0xf0,0xff,0x7f,'W','A','V','E','f','m','t',' ',
222		0x10,0,0,0,1,0,1,0,  9,0x3d,0,0,0x12,0x7a,0,0,
223		2,0,0x10,0,'d','a','t','a',  0x00,0xf0,0xff,0x7f};
224
225	if(path == NULL)
226		return(2);
227
228	if(strcmp(path,"stdout")==0)
229		f_wave = stdout;
230	else
231		f_wave = fopen(path,"wb");
232
233	if(f_wave != NULL)
234	{
235		fwrite(wave_hdr,1,24,f_wave);
236		Write4Bytes(f_wave,rate);
237		Write4Bytes(f_wave,rate * 2);
238		fwrite(&wave_hdr[32],1,12,f_wave);
239		return(0);
240	}
241	return(1);
242}   //  end of OpenWaveFile
243
244
245
246
247static void CloseWaveFile()
248//=========================
249{
250   unsigned int pos;
251
252   if((f_wave == NULL) || (f_wave == stdout))
253      return;
254
255   fflush(f_wave);
256   pos = ftell(f_wave);
257
258	fseek(f_wave,4,SEEK_SET);
259	Write4Bytes(f_wave,pos - 8);
260
261	fseek(f_wave,40,SEEK_SET);
262	Write4Bytes(f_wave,pos - 44);
263
264
265   fclose(f_wave);
266   f_wave = NULL;
267
268} // end of CloseWaveFile
269
270
271
272
273void MarkerEvent(int type, unsigned int char_position, int value, unsigned char *out_ptr)
274{//======================================================================================
275// Do nothing in the command-line version.
276	if(type == 2)
277		end_of_sentence = 1;
278}  // end of MarkerEvent
279
280
281static int WavegenFile(void)
282{//=========================
283	int finished;
284	unsigned char wav_outbuf[512];
285	char fname[210];
286
287	out_ptr = out_start = wav_outbuf;
288	out_end = wav_outbuf + sizeof(wav_outbuf);
289
290	finished = WavegenFill(0);
291
292	if(quiet)
293		return(finished);
294
295	if(f_wave == NULL)
296	{
297		sprintf(fname,"%s_%.2d%s",wavefile,++wavefile_count,filetype);
298		if(OpenWaveFile(fname, samplerate) != 0)
299			return(1);
300	}
301
302	if(end_of_sentence)
303	{
304		end_of_sentence = 0;
305		if((samples_split > 0 ) && (samples_total > samples_split))
306		{
307			CloseWaveFile();
308			samples_total = 0;
309		}
310	}
311
312	if(f_wave != NULL)
313	{
314		samples_total += (out_ptr - wav_outbuf)/2;
315		fwrite(wav_outbuf, 1, out_ptr - wav_outbuf, f_wave);
316	}
317	return(finished);
318}  // end of WavegenFile
319
320
321
322static void init_path(char *argv0)
323{//===============================
324#ifdef PLATFORM_WINDOWS
325	HKEY RegKey;
326	unsigned long size;
327	unsigned long var_type;
328	char *p;
329	char *env;
330	unsigned char buf[sizeof(path_home)-12];
331
332	if(((env = getenv("ESPEAK_DATA_PATH")) != NULL) && ((strlen(env)+12) < sizeof(path_home)))
333	{
334		sprintf(path_home,"%s\\espeak-data",env);
335		if(GetFileLength(path_home) == -2)
336			return;   // an espeak-data directory exists in the directory specified by environment variable
337	}
338
339	strcpy(path_home,argv0);
340	if((p = strrchr(path_home,'\\')) != NULL)
341	{
342		strcpy(&p[1],"espeak-data");
343		if(GetFileLength(path_home) == -2)
344			return;   // an espeak-data directory exists in the same directory as the espeak program
345	}
346
347	// otherwise, look in the Windows Registry
348	buf[0] = 0;
349	RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Speech\\Voices\\Tokens\\eSpeak", 0, KEY_READ, &RegKey);
350	size = sizeof(buf);
351	var_type = REG_SZ;
352	RegQueryValueEx(RegKey, "path", 0, &var_type, buf, &size);
353
354	sprintf(path_home,"%s\\espeak-data",buf);
355#else
356#ifdef PLATFORM_DOS
357		strcpy(path_home,PATH_ESPEAK_DATA);
358#else
359	char *env;
360	if((env = getenv("ESPEAK_DATA_PATH")) != NULL)
361	{
362		snprintf(path_home,sizeof(path_home),"%s/espeak-data",env);
363		if(GetFileLength(path_home) == -2)
364			return;   // an espeak-data directory exists 
365	}
366
367	snprintf(path_home,sizeof(path_home),"%s/espeak-data",getenv("HOME"));
368	if(access(path_home,R_OK) != 0)
369	{
370		strcpy(path_home,PATH_ESPEAK_DATA);
371	}
372#endif
373#endif
374}
375
376
377static int initialise(void)
378{//========================
379	int param;
380	int result;
381
382	// It seems that the wctype functions don't work until the locale has been set
383	// to something other than the default "C".  Then, not only Latin1 but also the
384	// other characters give the correct results with iswalpha() etc.
385#ifdef PLATFORM_RISCOS
386   setlocale(LC_CTYPE,"ISO8859-1");
387#else
388	if(setlocale(LC_CTYPE,"en_US.UTF-8") == NULL)
389	{
390		if(setlocale(LC_CTYPE,"UTF-8") == NULL)
391			setlocale(LC_CTYPE,"");
392	}
393#endif
394
395
396	WavegenInit(22050,0);   // 22050
397	if((result = LoadPhData()) != 1)
398	{
399		if(result == -1)
400			fprintf(stderr,"Failed to load espeak-data\n");
401		else
402			fprintf(stderr,"Wrong version of espeak-data 0x%x (expects 0x%x)\n",result,version_phdata);
403	}
404	LoadConfig();
405	SetVoiceStack(NULL);
406	SynthesizeInit();
407
408	for(param=0; param<N_SPEECH_PARAM; param++)
409		param_stack[0].parameter[param] = param_defaults[param];
410
411	return(0);
412}
413
414
415static void StopSpeak(int unused)
416{//==============================
417	signal(SIGINT,SIG_IGN);
418	// DEBUG
419//	printf("\n*** Interrupting speech output (use Ctrl-D to actually quit).\n");
420	fflush(stdout);
421	SpeakNextClause(NULL,NULL,5);
422	signal(SIGINT,StopSpeak);
423}  //  end of StopSpeak()
424
425#ifdef NEED_GETOPT
426	struct option {
427		char *name;
428		int has_arg;
429		int *flag;
430		int val;
431	};
432	static int optind;
433	static int optional_argument;
434	static const char *arg_opts = "afklpsvw";  // which options have arguments
435	static char *opt_string="";
436#define no_argument 0
437#define optional_argument 2
438#endif
439
440int main (int argc, char **argv)
441//==============================
442{
443	static struct option long_options[] =
444		{
445		/* These options set a flag. */
446//		{"verbose", no_argument,       &verbose_flag, 1},
447//		{"brief",   no_argument,       &verbose_flag, 0},
448
449		/* These options don't set a flag.
450			We distinguish them by their indices. */
451		{"help",    no_argument,       0, 'h'},
452		{"stdin",   no_argument,       0, 0x100},
453		{"compile-debug", optional_argument, 0, 0x101},
454		{"compile", optional_argument, 0, 0x102},
455		{"punct",   optional_argument, 0, 0x103},
456		{"voices",  optional_argument, 0, 0x104},
457		{"stdout",  no_argument,       0, 0x105},
458		{"split",   optional_argument, 0, 0x106},
459		{0, 0, 0, 0}
460		};
461
462	static const char *err_load = "Failed to read ";
463
464	FILE *f_text=NULL;
465	const char *p_text=NULL;
466
467	int option_index = 0;
468	int c;
469	int value;
470	int speed=170;
471	int ix;
472	char *optarg2;
473	int amp = 100;     // default
474	int wordgap = 0;
475	int speaking = 0;
476	int flag_stdin = 0;
477	int flag_compile = 0;
478	int pitch_adjustment = 50;
479	char filename[120];
480	char voicename[40];
481	char dictname[40];
482
483	voicename[0] = 0;
484	mbrola_name[0] = 0;
485	dictname[0] = 0;
486	wavefile[0] = 0;
487	filename[0] = 0;
488	option_linelength = 0;
489	option_phonemes = 0;
490	option_waveout = 0;
491	option_wordgap = 0;
492	option_endpause = 1;
493	option_multibyte = espeakCHARS_AUTO;  // auto
494	f_trans = stdout;
495
496	init_path(argv[0]);
497
498#ifdef NEED_GETOPT
499	optind = 1;
500	while(optind < argc)
501	{
502		int len;
503		char *p;
504
505		if((c = *opt_string) == 0)
506		{
507			opt_string = argv[optind];
508			if(opt_string[0] != '-')
509				break;
510
511			optind++;
512			opt_string++;
513			c = *opt_string;
514		}
515		opt_string++;
516		p = optarg2 = opt_string;
517
518		if(c == '-')
519		{
520			opt_string="";
521			for(ix=0; ;ix++)
522			{
523				if(long_options[ix].name == 0)
524					break;
525				len = strlen(long_options[ix].name);
526				if(memcmp(long_options[ix].name,p,len)==0)
527				{
528					c = long_options[ix].val;
529					optarg2 = NULL;
530
531					if((long_options[ix].has_arg != 0) && (p[len]=='='))
532					{
533						optarg2 = &p[len+1];
534					}
535					break;
536				}
537			}
538		}
539		else
540		if(strchr(arg_opts,c) != NULL)
541		{
542			opt_string="";
543			if(optarg2[0]==0)
544			{
545				// the option's value is in the next argument
546				optarg2 = argv[optind++];
547			}
548		}
549#else
550	while(true)
551	{
552		c = getopt_long (argc, argv, "a:bf:g:hk:l:p:qs:v:w:xXmz",
553					long_options, &option_index);
554
555		/* Detect the end of the options. */
556		if (c == -1)
557			break;
558		optarg2 = optarg;
559#endif
560
561		switch (c)
562		{
563		case 'b':
564			option_multibyte = espeakCHARS_8BIT;
565			break;
566
567		case 'h':
568			printf("\nspeak text-to-speech: %s\n%s",version_string,help_text);
569			exit(0);
570			break;
571
572		case 'k':
573			option_capitals = atoi(optarg2);
574			break;
575
576		case 'x':
577			option_phonemes = 1;
578			break;
579
580		case 'X':
581			option_phonemes = 2;
582			break;
583
584		case 'm':
585			option_ssml = 1;
586			break;
587
588		case 'p':
589			pitch_adjustment = atoi(optarg2);
590			if(pitch_adjustment > 99) pitch_adjustment = 99;
591			break;
592
593		case 'q':
594			quiet = 1;
595			break;
596
597		case 'f':
598			strncpy0(filename,optarg2,sizeof(filename));
599			break;
600
601		case 'l':
602			value = 0;
603			value = atoi(optarg2);
604			option_linelength = value;
605			break;
606
607		case 'a':
608			amp = atoi(optarg2);
609			break;
610
611		case 's':
612			speed = atoi(optarg2);
613			break;
614
615		case 'g':
616			wordgap = atoi(optarg2);
617			break;
618
619		case 'v':
620			strncpy0(voicename,optarg2,sizeof(voicename));
621			break;
622
623		case 'w':
624			option_waveout = 1;
625			strncpy0(wavefile,optarg2,sizeof(wavefile));
626			break;
627
628		case 'z':
629			option_endpause = 0;
630			break;
631
632		case 0x100:		// --stdin
633			flag_stdin = 1;
634			break;
635
636		case 0x105:		// --stdout
637			option_waveout = 1;
638			strcpy(wavefile,"stdout");
639			break;
640
641		case 0x101:    // --compile-debug
642		case 0x102:		// --compile
643			if(optarg2 != NULL)
644				strncpy0(voicename,optarg2,sizeof(voicename));
645			flag_compile = c;
646			break;
647
648		case 0x103:		// --punct
649			option_punctuation = 1;
650			if(optarg2 != NULL)
651			{
652				ix = 0;
653				while((ix < N_PUNCTLIST) && ((option_punctlist[ix] = optarg2[ix]) != 0)) ix++;
654				option_punctlist[N_PUNCTLIST-1] = 0;
655				option_punctuation = 2;
656			}
657			break;
658
659		case 0x104:   // --voices
660			DisplayVoices(stdout,optarg2);
661			exit(0);
662
663
664		case 0x106:   // -- split
665			if(optarg2 == NULL)
666				samples_split = 30;  // default 30 minutes
667			else
668				samples_split = atoi(optarg2);
669			break;
670		default:
671			exit(0);
672		}
673	}
674
675	initialise();
676
677
678	if(flag_compile)
679	{
680		LoadVoice(voicename,5);
681
682#ifdef PLATFORM_DOS
683		char path_dsource[sizeof(path_home)+20];
684		strcpy(path_dsource,path_home);
685		path_dsource[strlen(path_home)-11] = 0;  // remove "espeak-data" from the end
686		strcat(path_dsource,"dictsource\\");
687		CompileDictionary(path_dsource,dictionary_name,NULL,NULL, flag_compile & 0x1);
688#else
689#ifdef PLATFORM_WINDOWS
690		char path_dsource[sizeof(path_home)+20];
691		strcpy(path_dsource,path_home);
692		path_dsource[strlen(path_home)-11] = 0;  // remove "espeak-data" from the end
693		strcat(path_dsource,"dictsource\\");
694		CompileDictionary(path_dsource,dictionary_name,NULL,NULL, flag_compile & 0x1);
695#else
696		CompileDictionary(NULL,dictionary_name,NULL,NULL, flag_compile & 0x1);
697#endif
698#endif
699		exit(0);
700	}
701
702
703	if(voicename[0] == 0)
704		strcpy(voicename,"default");
705
706	if(SetVoiceByName(voicename) != EE_OK)
707	{
708		fprintf(stderr,"%svoice '%s'\n",err_load,voicename);
709		exit(2);
710	}
711
712	SetParameter(espeakRATE,speed,0);
713	SetParameter(espeakVOLUME,amp,0);
714	SetParameter(espeakCAPITALS,option_capitals,0);
715	SetParameter(espeakPUNCTUATION,option_punctuation,0);
716	SetParameter(espeakWORDGAP,wordgap,0);
717
718	if(pitch_adjustment != 50)
719	{
720		SetParameter(espeakPITCH,pitch_adjustment,0);
721	}
722	DoVoiceChange(voice);
723
724	if(filename[0]==0)
725	{
726		if((optind < argc) && (flag_stdin == 0))
727		{
728			// there's a non-option parameter, and no -f or --stdin
729			// use it as text
730			p_text = argv[optind];
731		}
732		else
733		{
734			f_text = stdin;
735			if(flag_stdin == 0)
736				option_linelength = -1;  // single input lines on stdin
737		}
738	}
739	else
740	{
741		f_text = fopen(filename,"r");
742	}
743
744	if((f_text == NULL) && (p_text == NULL))
745	{
746		fprintf(stderr,"%sfile '%s'\n",err_load,filename);
747		exit(1);
748	}
749
750	if(option_waveout || quiet)
751	{
752		if(quiet)
753		{
754			// no sound output
755			OpenWaveFile(NULL,samplerate);
756			option_waveout = 1;
757		}
758		else
759		{
760			// write sound output to a WAV file
761			samples_split = (samplerate * samples_split) * 60;
762
763			if(samples_split)
764			{
765				// don't open the wav file until we start generating speech
766				char *extn;
767				extn = strrchr(wavefile,'.');
768				if((extn != NULL) && ((wavefile + strlen(wavefile) - extn) <= 4))
769				{
770					strcpy(filetype,extn);
771					*extn = 0;
772				}
773			}
774			else
775			if(OpenWaveFile(wavefile,samplerate) != 0)
776			{
777				fprintf(stderr,"Can't write to output file '%s'\n'",wavefile);
778				exit(3);
779			}
780		}
781
782		InitText(0);
783		SpeakNextClause(f_text,p_text,0);
784
785		ix = 1;
786		for(;;)
787		{
788			if(WavegenFile() != 0)
789			{
790				if(ix == 0)
791					break;   // finished, wavegen command queue is empty
792			}
793
794			if(Generate(phoneme_list,&n_phoneme_list,1)==0)
795			{
796				ix = SpeakNextClause(NULL,NULL,1);
797			}
798		}
799
800		CloseWaveFile();
801	}
802	else
803	{
804		// Silence on ^C or SIGINT
805//		signal(SIGINT,StopSpeak);
806
807		// output sound using portaudio
808		WavegenInitSound();
809
810		InitText(0);
811		SpeakNextClause(f_text,p_text,0);
812
813		if(option_quiet)
814		{
815			while(SpeakNextClause(NULL,NULL,1) != 0);
816			return(0);
817		}
818
819#ifdef USE_PORTAUDIO
820		speaking = 1;
821		while(speaking)
822		{
823			// NOTE: if nanosleep() isn't recognised on your system, try replacing
824			// this by  sleep(1);
825#ifdef PLATFORM_WINDOWS
826			Sleep(300);   // 0.3s
827#else
828#ifdef USE_NANOSLEEP
829			struct timespec period;
830			struct timespec remaining;
831			period.tv_sec = 0;
832			period.tv_nsec = 300000000;  // 0.3 sec
833			nanosleep(&period,&remaining);
834#else
835			sleep(1);
836#endif
837#endif
838			if(SynthOnTimer() != 0)
839				speaking = 0;
840		}
841#else
842		fprintf(stderr,"-w option must be used because the program was built without a sound interface\n");
843#endif  // USE_PORTAUDIO
844	}
845	return(0);
846}