PageRenderTime 72ms CodeModel.GetById 14ms app.highlight 53ms RepoModel.GetById 1ms app.codeStats 0ms

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

http://eyes-free.googlecode.com/
C++ | 633 lines | 485 code | 106 blank | 42 comment | 94 complexity | 17b584a37e076a99b22c322f07b51ce0 MD5 | raw file
  1/***************************************************************************
  2 *   Copyright (C) 2006 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 <stdio.h>
 21#include <stdlib.h>
 22#include <string.h>
 23#include <getopt.h>
 24#include <time.h>
 25#include <sys/stat.h>
 26
 27#include "speak_lib.h"
 28
 29// This version of the command-line speak program uses the
 30// libespeak.so.1  library
 31
 32
 33
 34static const char *help_text =
 35"\nspeak [options] [\"<words>\"]\n\n"
 36"-f <text file>   Text file to speak\n"
 37"--stdin    Read text input from stdin instead of a file\n\n"
 38"If neither -f nor --stdin, <words> are spoken, or if none then text is\n"
 39"spoken from stdin, each line separately.\n\n"
 40"-a <integer>\n"
 41"\t   Amplitude, 0 to 200, default is 100\n"
 42"-g <integer>\n"
 43"\t   Word gap. Pause between words, units of 10mS at the default speed\n"
 44"-l <integer>\n"
 45"\t   Line length. If not zero (which is the default), consider\n"
 46"\t   lines less than this length as end-of-clause\n"
 47"-p <integer>\n"
 48"\t   Pitch adjustment, 0 to 99, default is 50\n"
 49"-s <integer>\n"
 50"\t   Speed in words per minute, 80 to 370, default is 170\n"
 51"-v <voice name>\n"
 52"\t   Use voice file of this name from espeak-data/voices\n"
 53"-w <wave file name>\n"
 54"\t   Write output to this WAV file, rather than speaking it directly\n"
 55"-b\t   Input text is 8-bit encoding\n"
 56"-m\t   Interpret SSML markup, and ignore other < > tags\n"
 57"-q\t   Quiet, don't produce any speech (may be useful with -x)\n"
 58"-x\t   Write phoneme mnemonics to stdout\n"
 59"-X\t   Write phonemes mnemonics and translation trace to stdout\n"
 60"-z\t   No final sentence pause at the end of the text\n"
 61"--stdout   Write speech output to stdout\n"
 62"--compile=<voice name>\n"
 63"\t   Compile the pronunciation rules and dictionary in the current\n"
 64"\t   directory. =<voice name> is optional and specifies which language\n"
 65"--punct=\"<characters>\"\n"
 66"\t   Speak the names of punctuation characters during speaking.  If\n"
 67"\t   =<characters> is omitted, all punctuation is spoken.\n"
 68"--split=\"<minutes>\"\n"
 69"\t   Starts a new WAV file every <minutes>.  Used with -w\n"
 70"--voices=<language>\n"
 71"\t   List the available voices for the specified language.\n"
 72"\t   If <language> is omitted, then list all voices.\n"
 73"-k <integer>\n"
 74"\t   Indicate capital letters with: 1=sound, 2=the word \"capitals\",\n"
 75"\t   higher values = a pitch increase (try -k20).\n";
 76
 77
 78
 79
 80int samplerate;
 81int quiet = 0;
 82unsigned int samples_total = 0;
 83unsigned int samples_split = 0;
 84unsigned int wavefile_count = 0;
 85
 86FILE *f_wavfile = NULL;
 87char filetype[5];
 88char wavefile[200];
 89
 90
 91int GetFileLength(const char *filename)
 92{//====================================
 93	struct stat statbuf;
 94
 95	if(stat(filename,&statbuf) != 0)
 96		return(0);
 97
 98	if((statbuf.st_mode & S_IFMT) == S_IFDIR)
 99		return(-2);  // a directory
100
101	return(statbuf.st_size);
102}  // end of GetFileLength
103
104
105void strncpy0(char *dest, const char *source, int size)
106{//====================================================
107	if(source!=NULL)
108	{
109		strncpy(dest,source,size);
110		dest[size-1] = 0;
111	}
112}
113
114
115void DisplayVoices(FILE *f_out, char *language)
116{//============================================
117	int ix;
118	const char *p;
119	int len;
120	int count;
121	int scores = 0;
122	const espeak_VOICE *v;
123	const char *lang_name;
124	char age_buf[12];
125	const espeak_VOICE **voices;
126	espeak_VOICE voice_select;
127
128	static char genders[4] = {' ','M','F',' '};
129
130	if((language != NULL) && (language[0] != 0))
131	{
132		// display only voices for the specified language, in order of priority
133		voice_select.languages = language;
134		voice_select.age = 0;
135		voice_select.gender = 0;
136		voice_select.name = NULL;
137		voices = espeak_ListVoices(&voice_select);
138		scores = 1;
139	}
140	else
141	{
142		voices = espeak_ListVoices(NULL);
143	}
144
145	fprintf(f_out,"Pty Language Age/Gender VoiceName       File        Other Langs\n");
146
147	for(ix=0; (v = voices[ix]) != NULL; ix++)
148	{
149		count = 0;
150		p = v->languages;
151		while(*p != 0)
152		{
153			len = strlen(p+1);
154			lang_name = p+1;
155
156			if(v->age == 0)
157				strcpy(age_buf,"   ");
158			else
159				sprintf(age_buf,"%3d",v->age);
160
161			if(count==0)
162			{
163				fprintf(f_out,"%2d  %-12s%s%c  %-17s %-11s ",
164               p[0],lang_name,age_buf,genders[v->gender],v->name,v->identifier);
165			}
166			else
167			{
168				fprintf(f_out,"(%s %d)",lang_name,p[0]);
169			}
170			count++;
171			p += len+2;
172		}
173//		if(scores)
174//			fprintf(f_out,"%3d  ",v->score);
175		fputc('\n',f_out);
176	}
177}   //  end of DisplayVoices
178
179
180
181
182static void Write4Bytes(FILE *f, int value)
183{//=================================
184// Write 4 bytes to a file, least significant first
185	int ix;
186
187	for(ix=0; ix<4; ix++)
188	{
189		fputc(value & 0xff,f);
190		value = value >> 8;
191	}
192}
193
194
195
196int OpenWavFile(char *path, int rate)
197//===================================
198{
199	static unsigned char wave_hdr[44] = {
200		'R','I','F','F',0x24,0xf0,0xff,0x7f,'W','A','V','E','f','m','t',' ',
201		0x10,0,0,0,1,0,1,0,  9,0x3d,0,0,0x12,0x7a,0,0,
202		2,0,0x10,0,'d','a','t','a',  0x00,0xf0,0xff,0x7f};
203
204	if(path == NULL)
205		return(2);
206
207	if(path[0] == 0)
208		return(0);
209
210	if(strcmp(path,"stdout")==0)
211		f_wavfile = stdout;
212	else
213		f_wavfile = fopen(path,"wb");
214
215	if(f_wavfile != NULL)
216	{
217		fwrite(wave_hdr,1,24,f_wavfile);
218		Write4Bytes(f_wavfile,rate);
219		Write4Bytes(f_wavfile,rate * 2);
220		fwrite(&wave_hdr[32],1,12,f_wavfile);
221		return(0);
222	}
223	return(1);
224}   //  end of OpenWavFile
225
226
227
228static void CloseWavFile()
229//========================
230{
231	unsigned int pos;
232
233	if((f_wavfile==NULL) || (f_wavfile == stdout))
234		return;
235
236	fflush(f_wavfile);
237	pos = ftell(f_wavfile);
238
239	fseek(f_wavfile,4,SEEK_SET);
240	Write4Bytes(f_wavfile,pos - 8);
241
242	fseek(f_wavfile,40,SEEK_SET);
243	Write4Bytes(f_wavfile,pos - 44);
244
245	fclose(f_wavfile);
246	f_wavfile = NULL;
247
248} // end of CloseWavFile
249
250
251static int SynthCallback(short *wav, int numsamples, espeak_EVENT *events)
252{//========================================================================
253	char fname[210];
254
255	if(quiet) return(0);  // -q quiet mode
256
257	if(wav == NULL)
258	{
259		CloseWavFile();
260		return(0);
261	}
262
263	if(samples_split > 0)
264	{
265		// start a new WAV file when this limit is reached, at the next sentence boundary
266		while(events->type != 0)
267		{
268			if((events->type == espeakEVENT_SENTENCE) && (samples_total > samples_split))
269			{
270				CloseWavFile();
271				samples_total = 0;
272			}
273			events++;
274		}
275	}
276
277	if(f_wavfile == NULL)
278	{
279		sprintf(fname,"%s_%.2d%s",wavefile,++wavefile_count,filetype);
280		if(OpenWavFile(fname, samplerate) != 0)
281			return(1);
282	}
283
284	if(numsamples > 0)
285	{
286		samples_total += numsamples;
287		fwrite(wav,numsamples*2,1,f_wavfile);
288	}
289	return(0);
290}
291
292
293
294int main (int argc, char **argv)
295//==============================
296{
297	static struct option long_options[] =
298		{
299		/* These options set a flag. */
300//		{"verbose", no_argument,       &verbose_flag, 1},
301//		{"brief",   no_argument,       &verbose_flag, 0},
302
303		/* These options don't set a flag.
304			We distinguish them by their indices. */
305		{"help",    no_argument,       0, 'h'},
306		{"stdin",   no_argument,       0, 0x100},
307		{"compile-debug", optional_argument, 0, 0x101},
308		{"compile", optional_argument, 0, 0x102},
309		{"punct",   optional_argument, 0, 0x103},
310		{"voices",  optional_argument, 0, 0x104},
311		{"stdout",  no_argument,       0, 0x105},
312		{"split",   optional_argument, 0, 0x106},
313		{0, 0, 0, 0}
314		};
315
316	static const char* err_load = "Failed to read ";
317
318
319	FILE *f_text=NULL;
320	char *p_text=NULL;
321
322	int option_index = 0;
323	int c;
324	int ix;
325	int flag_stdin = 0;
326	int flag_compile = 0;
327	int filesize = 0;
328	int synth_flags = espeakCHARS_AUTO | espeakPHONEMES | espeakENDPAUSE;
329
330	int volume = -1;
331	int speed = -1;
332	int pitch = -1;
333	int wordgap = -1;
334	int option_capitals = -1;
335	int option_punctuation = -1;
336	int option_phonemes = -1;
337	int option_linelength = 0;
338	int option_waveout = 0;
339
340	char filename[120];
341	char voicename[40];
342	char voice_mbrola[20];
343	char dictname[40];
344#define N_PUNCTLIST  100
345	wchar_t option_punctlist[N_PUNCTLIST];
346
347	voicename[0] = 0;
348	voice_mbrola[0] = 0;
349	dictname[0] = 0;
350	wavefile[0] = 0;
351	filename[0] = 0;
352	option_punctlist[0] = 0;
353
354	while(true)
355	{
356		c = getopt_long (argc, argv, "a:bf:g:hk:l:mp:qs:v:w:xXz",
357					long_options, &option_index);
358
359		/* Detect the end of the options. */
360		if (c == -1)
361			break;
362
363		switch (c)
364		{
365		case 'b':
366			synth_flags |= espeakCHARS_8BIT;
367			break;
368
369		case 'h':
370			printf("\n");
371			printf("eSpeak text-to-speech: %s\n%s",espeak_Info(NULL),help_text);
372			exit(0);
373			break;
374
375		case 'k':
376			option_capitals = atoi(optarg);
377			break;
378
379		case 'x':
380			option_phonemes = 1;
381			break;
382
383		case 'X':
384			option_phonemes = 2;
385			break;
386
387		case 'm':
388			synth_flags |= espeakSSML;
389			break;
390
391		case 'p':
392			pitch = atoi(optarg);
393			break;
394
395		case 'q':
396			quiet = 1;
397			break;
398
399		case 'f':
400			strncpy0(filename,optarg,sizeof(filename));
401			break;
402
403		case 'l':
404			option_linelength = atoi(optarg);
405			break;
406
407		case 'a':
408			volume = atoi(optarg);
409			break;
410
411		case 's':
412			speed = atoi(optarg);
413			break;
414
415		case 'g':
416			wordgap = atoi(optarg);
417			break;
418
419		case 'v':
420			strncpy0(voicename,optarg,sizeof(voicename));
421			break;
422
423		case 'w':
424			option_waveout = 1;
425			strncpy0(wavefile,optarg,sizeof(filename));
426			break;
427
428		case 'z':  // remove pause from the end of a sentence
429			synth_flags &= ~espeakENDPAUSE;
430			break;
431
432		case 0x100:		// --stdin
433			flag_stdin = 1;
434			break;
435
436		case 0x105:		// --stdout
437			option_waveout = 1;
438			strcpy(wavefile,"stdout");
439			break;
440
441		case 0x101:    // --compile-debug
442		case 0x102:		// --compile
443			strncpy0(voicename,optarg,sizeof(voicename));
444			flag_compile = c;
445			quiet = 1;
446			break;
447
448		case 0x103:		// --punct
449			option_punctuation = 1;
450			if(optarg != NULL)
451			{
452				ix = 0;
453				while((ix < N_PUNCTLIST) && ((option_punctlist[ix] = optarg[ix]) != 0)) ix++;
454				option_punctlist[N_PUNCTLIST-1] = 0;
455				option_punctuation = 2;
456			}
457			break;
458
459		case 0x104:   // --voices
460			espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS,0,NULL,0);
461			DisplayVoices(stdout,optarg);
462			exit(0);
463
464		case 0x106:   // -- split
465			if(optarg == NULL)
466				samples_split = 30;  // default 30 minutes
467			else
468				samples_split = atoi(optarg);
469			break;
470
471		default:
472			exit(0);
473		}
474	}
475
476
477	if(option_waveout || quiet)
478	{
479		// writing to a file (or no output), we can use synchronous mode
480		samplerate = espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS,0,NULL,0);
481		samples_split = (samplerate * samples_split) * 60;
482
483		espeak_SetSynthCallback(SynthCallback);
484		if(samples_split)
485		{
486			char *extn;
487			extn = strrchr(wavefile,'.');
488			if((extn != NULL) && ((wavefile + strlen(wavefile) - extn) <= 4))
489			{
490				strcpy(filetype,extn);
491				*extn = 0;
492			}
493		}
494		else
495		if(option_waveout)
496		{
497			if(OpenWavFile(wavefile,samplerate) != 0)
498				exit(4);
499		}
500	}
501	else
502	{
503		// play the sound output
504		samplerate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK,0,NULL,0);
505	}
506	
507
508	if(voicename[0] == 0)
509		strcpy(voicename,"default");
510
511	if(espeak_SetVoiceByName(voicename) != EE_OK)
512	{
513		fprintf(stderr,"%svoice '%s'\n",err_load,voicename);
514		exit(2);
515	}
516
517	if(flag_compile)
518	{
519		// This must be done after the voice is set
520		espeak_CompileDictionary("", stderr, flag_compile & 0x1);
521		exit(0);
522	}
523
524	// set any non-default values of parameters. This must be done after espeak_Initialize()
525	if(speed > 0)
526		espeak_SetParameter(espeakRATE,speed,0);
527	if(volume >= 0)
528		espeak_SetParameter(espeakVOLUME,volume,0);
529	if(pitch >= 0)
530		espeak_SetParameter(espeakPITCH,pitch,0);
531	if(option_capitals >= 0)
532		espeak_SetParameter(espeakCAPITALS,option_capitals,0);
533	if(option_punctuation >= 0)
534		espeak_SetParameter(espeakPUNCTUATION,option_punctuation,0);
535	if(wordgap >= 0)
536		espeak_SetParameter(espeakWORDGAP,wordgap,0);
537	if(option_linelength > 0)
538		espeak_SetParameter(espeakLINELENGTH,option_linelength,0);
539	if(option_punctuation == 2)
540		espeak_SetPunctuationList(option_punctlist);
541	if(option_phonemes >= 0)
542		espeak_SetPhonemeTrace(option_phonemes,stderr);
543
544	if(filename[0]==0)
545	{
546		if((optind < argc) && (flag_stdin == 0))
547		{
548			// there's a non-option parameter, and no -f or --stdin
549			// use it as text
550			p_text = argv[optind];
551		}
552		else
553		{
554			f_text = stdin;
555			if(flag_stdin == 0)
556			{
557				flag_stdin = 2;
558			}
559		}
560	}
561	else
562	{
563		filesize = GetFileLength(filename);
564		f_text = fopen(filename,"r");
565	}
566
567	if((f_text == NULL) && (p_text == NULL))
568	{
569		fprintf(stderr,"%sfile '%s'\n",err_load,filename);
570		exit(1);
571	}
572
573
574	if(p_text != NULL)
575	{
576		int size;
577		size = strlen(p_text);
578		espeak_Synth(p_text,size+1,0,POS_CHARACTER,0,synth_flags,NULL,NULL);
579	}
580	else
581	if(flag_stdin)
582	{
583		int max = 1000;
584		p_text = (char *)malloc(max);
585
586		if(flag_stdin == 2)
587		{
588			// line by line input on stdin
589			while(fgets(p_text,max,stdin) != NULL)
590			{
591				p_text[max-1] = 0;
592				espeak_Synth(p_text,max,0,POS_CHARACTER,0,synth_flags,NULL,NULL);
593
594			}
595		}
596		else
597		{
598			// bulk input on stdin
599			ix = 0;
600			while(!feof(stdin))
601			{
602				p_text[ix++] = fgetc(stdin);
603				if(ix >= (max-1))
604				{
605					max += 1000;
606					p_text = (char *)realloc(p_text,max);
607				}
608			}
609			if(ix > 0)
610			{
611				p_text[ix-1] = 0;
612				espeak_Synth(p_text,ix+1,0,POS_CHARACTER,0,synth_flags,NULL,NULL);
613			}
614		}
615	}
616	else
617	if(f_text != NULL)
618	{
619		if((p_text = (char *)malloc(filesize+1)) == NULL)
620		{
621			fprintf(stderr,"Failed to allocate memory %d bytes",filesize);
622			exit(3);
623		}
624
625		fread(p_text,1,filesize,f_text);
626		p_text[filesize]=0;
627		espeak_Synth(p_text,filesize+1,0,POS_CHARACTER,0,synth_flags,NULL,NULL);
628		fclose(f_text);
629	}
630
631	espeak_Synchronize();
632	return(0);
633}