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