PageRenderTime 31ms CodeModel.GetById 14ms app.highlight 9ms RepoModel.GetById 1ms app.codeStats 0ms

/native/external/espeak/platforms/riscos/cpp/speak_riscos

http://eyes-free.googlecode.com/
#! | 1212 lines | 962 code | 250 blank | 0 comment | 0 complexity | f19876894682e2edd0b74e25dbf9e97f MD5 | raw file
   1
   2/***************************************************************************
   3 *   Copyright (C) 2005 to 2007 by Jonathan Duddington                     *
   4 *   email: jonsd@users.sourceforge.net                                    *
   5 *                                                                         *
   6 *   This program is free software; you can redistribute it and/or modify  *
   7 *   it under the terms of the GNU General Public License as published by  *
   8 *   the Free Software Foundation; either version 3 of the License, or     *
   9 *   (at your option) any later version.                                   *
  10 *                                                                         *
  11 *   This program is distributed in the hope that it will be useful,       *
  12 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
  13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
  14 *   GNU General Public License for more details.                          *
  15 *                                                                         *
  16 *   You should have received a copy of the GNU General Public License     *
  17 *   along with this program; if not, write see:                           *
  18 *               <http://www.gnu.org/licenses/>.                           *
  19 ***************************************************************************/
  20
  21#define USE_MODULE
  22
  23#include <stdio.h>
  24#include <stdlib.h>
  25#include <string.h>
  26#include <ctype.h>
  27#include <locale.h>
  28#include "kernel.h"
  29
  30#include "speech.h"
  31#include "speak_lib.h"
  32#include "phoneme.h"
  33#include "synthesize.h"
  34#include "voice.h"
  35#include "translate.h"
  36
  37#define os_X  0x20000
  38
  39// interface to the assembler section
  40extern "C" {
  41extern void DMA_Handler(void);
  42
  43
  44// used from the cmhgfile
  45extern _kernel_oserror *user_init(char *cmd_fail, int podule_base, void *pw);
  46extern _kernel_oserror *swi_handler(int swi_no, int  *r, void *pw);
  47extern _kernel_oserror *command_handler(char *arg_string, int argc, int cmd_no, void *pw);
  48extern int callback_handler(_kernel_swi_regs *r, void *pw);
  49extern int callback_entry(_kernel_swi_regs *r, void *pw);
  50extern int sound_handler(_kernel_swi_regs *r, void *pw);
  51extern int sound_entry(_kernel_swi_regs *r, void *pw);
  52
  53}
  54
  55extern int Generate(PHONEME_LIST *phoneme_list, int *n_ph, int resume);
  56extern void RiscosOpenSound();
  57extern int WcmdqUsed();
  58extern void FreePhData();
  59extern void FreeDictionary();
  60extern void Write4Bytes(FILE *f, int value);
  61
  62extern int wcmdq_head;
  63extern int wcmdq_tail;
  64extern int current_source_index;
  65
  66FILE *f_text;
  67FILE *f_wave = NULL;
  68int (* uri_callback)(int, const char *, const char *) = NULL;
  69int (* phoneme_callback)(const char *) = NULL;
  70
  71int amp = 8;     // default
  72
  73char path_home[N_PATH_HOME] = "";
  74char wavefile[120];
  75char textbuffile[L_tmpnam];
  76int  sample_rate_index;  // current value
  77int  current_voice_num=0;
  78int n_voice_files = 0;
  79
  80// output sound buffer,  2 bytes per sample
  81static unsigned short SoundBuf[4096];
  82static void *module_data;
  83static int callback_inhibit = 0;
  84static int more_text=0;
  85
  86#define N_VOICE_NAMES   60
  87static char *voice_names[40];
  88
  89#define N_STATIC_BUF 8000
  90static char static_buf[N_STATIC_BUF];
  91static _kernel_oserror errblk;
  92
  93
  94
  95USHORT voice_pcnt[N_PEAKS+1][3];
  96
  97static const char *help_text =
  98"\nspeak [options] [\"<words>\"]\n\n"
  99"-f <text file>   Text file to speak\n"
 100//"--stdin    Read text input from stdin instead of a file\n\n"
 101"If neither -f nor --stdin, <words> are spoken, or if none then text is\n"
 102"spoken from stdin, each line separately.\n\n"
 103"-a <integer>\n"
 104"\t   Amplitude, 0 to 200, default is 100\n"
 105"-g <integer>\n"
 106"\t   Word gap. Pause between words, units of 10mS at the default speed\n"
 107"-l <integer>\n"
 108"\t   Line length. If not zero (which is the default), consider\n"
 109"\t   lines less than this length as end-of-clause\n"
 110"-p <integer>\n"
 111"\t   Pitch adjustment, 0 to 99, default is 50\n"
 112"-s <integer>\n"
 113"\t   Speed in words per minute 80 to 370, default is 170\n"
 114"-v <voice name>\n"
 115"\t   Use voice file of this name from espeak-data/voices\n"
 116"-w <wave file name>\n"
 117"\t   Write output to this WAV file, rather than speaking it directly\n"
 118"-b\t   Input text is 8-bit encoding\n"
 119"-m\t   Interpret SSML markup, and ignore other < > tags\n"
 120"-q\t   Quiet, don't produce any speech (may be useful with -x)\n"
 121"-x\t   Write phoneme mnemonics to stdout\n"
 122"-X\t   Write phonemes mnemonics and translation trace to stdout\n"
 123//"--stdout   Write speech output to stdout\n"
 124"--compile=<voice name>\n"
 125"\t   Compile the pronunciation rules and dictionary in the current\n"
 126"\t   directory. =<voice name> is optional and specifies which language\n"
 127"--punct=\"<characters>\"\n"
 128"\t   Speak the names of punctuation characters during speaking.  If\n"
 129"\t   =<characters> is omitted, all punctuation is spoken.\n"
 130//"--voices=<langauge>\n"
 131//"\t   List the available voices for the specified language.\n"
 132//"\t   If <language> is omitted, then list all voices.\n"
 133"-k <integer>\n"
 134"\t   Indicate capital letters with: 1=sound, 2=the word \"capitals\",\n"
 135"\t   higher values = a pitch increase (try -k20).\n";
 136
 137
 138int GetFileLength(const char *filename)
 139{//====================================
 140	int length=0;
 141	int type;
 142	_kernel_swi_regs regs;
 143	_kernel_oserror *error;
 144
 145	regs.r[0] = 5;
 146	regs.r[1] = (int)filename;
 147	regs.r[2] = 0;
 148	regs.r[3] = 0;
 149	regs.r[4] = 0;
 150	regs.r[5] = 0;
 151
 152	error = _kernel_swi(0x20008,&regs,&regs);
 153	if(error)
 154		return(0);
 155
 156	type = regs.r[0];
 157	length = regs.r[4];
 158
 159	if(type==2)
 160		return(-2);   // a directory
 161	if((type!=1) && (type!=3))
 162		return(0);   /* not a file */
 163
 164	return(length);
 165}   /* end of GetFileLength */
 166
 167
 168
 169void ReadVoiceNames2(char *directory)
 170{//==================================
 171	int len;
 172	int *type;
 173	char *p;
 174	_kernel_swi_regs regs;
 175	_kernel_oserror *error;
 176	char buf[80];
 177	char directory2[sizeof(path_home)+100];
 178
 179	regs.r[0] = 10;
 180	regs.r[1] = (int)directory;
 181	regs.r[2] = (int)buf;
 182	regs.r[3] = 1;
 183	regs.r[4] = 0;
 184	regs.r[5] = sizeof(buf);
 185	regs.r[6] = 0;
 186
 187	while(regs.r[3] > 0)
 188	{
 189		error = _kernel_swi(0x0c+os_X,&regs,&regs);      /* OS_GBPB 10, read directory entries */
 190		if((error != NULL) || (regs.r[3] == 0))
 191		{
 192			break;
 193		}
 194		type = (int *)(&buf[16]);
 195		len = strlen(&buf[20]);
 196
 197		if(*type == 2)
 198		{
 199			// a sub-directory
 200			sprintf(directory2,"%s.%s",directory,&buf[20]);
 201			ReadVoiceNames2(directory2);
 202		}
 203		else
 204		{
 205			p = Alloc(len+1);
 206			strcpy(p,&buf[20]);
 207			voice_names[n_voice_files++] = p;
 208			if(n_voice_files >= (N_VOICE_NAMES-1))
 209				break;
 210		}
 211	}
 212}
 213
 214
 215void ReadVoiceNames()
 216{//===================
 217	char directory[sizeof(path_home)+10];
 218
 219	for(n_voice_files=0; n_voice_files<N_VOICE_NAMES; n_voice_files++)
 220		voice_names[n_voice_files] = NULL;
 221
 222	n_voice_files = 0;
 223	sprintf(directory,"%s.voices",path_home);
 224
 225	ReadVoiceNames2(directory);
 226}
 227
 228
 229#ifdef USE_MODULE
 230char *Alloc(int size)
 231/*******************/
 232{	// version of malloc() for use in RISC_OS module
 233_kernel_swi_regs regs;
 234
 235	regs.r[0] = 6;
 236	regs.r[3] = size;
 237	_kernel_swi(0x1e, &regs, &regs);   /* OS_Module 6   claim memory */
 238
 239	return(char *)regs.r[2];
 240}   /* end of module_malloc */
 241
 242
 243
 244void Free(void *ptr)
 245/*******************/
 246{  // version of free() for use in RISC_OS module
 247_kernel_swi_regs regs;
 248
 249	if(ptr == NULL)
 250		return;
 251
 252	regs.r[0] = 7;
 253	regs.r[2] = (int)(ptr);
 254	_kernel_swi(0x1e, &regs, &regs);    /* OS_Module 7   free memory */
 255}   /* end of Free */
 256
 257#else
 258
 259char *Alloc(int size)
 260{//==================
 261	char *p;
 262	if((p = (char *)malloc(size)) == NULL)
 263		fprintf(stderr,"Can't allocate memory\n");
 264	return(p);
 265}
 266
 267void Free(void **ptr)
 268{//=================
 269	if(ptr != NULL)
 270	{
 271		free(ptr);
 272	}
 273}
 274#endif
 275
 276
 277
 278
 279static int OpenWaveFile(const char *path, int rate)
 280//=================================================
 281{
 282	// Set the length of 0x7fffffff for --stdout
 283	// This will be changed to the correct length for -w (write to file)
 284	static unsigned char wave_hdr[44] = {
 285		'R','I','F','F',0,0,0,0,'W','A','V','E','f','m','t',' ',
 286		0x10,0,0,0,1,0,1,0,  9,0x3d,0,0,0x12,0x7a,0,0,
 287		2,0,0x10,0,'d','a','t','a',  0xff,0xff,0xff,0x7f};
 288
 289	if(path == NULL)
 290		return(2);
 291
 292	if(strcmp(path,"stdout")==0)
 293		f_wave = stdout;
 294	else
 295		f_wave = fopen(path,"wb");
 296
 297	if(f_wave != NULL)
 298	{
 299		fwrite(wave_hdr,1,24,f_wave);
 300		Write4Bytes(f_wave,rate);
 301		Write4Bytes(f_wave,rate * 2);
 302		fwrite(&wave_hdr[32],1,12,f_wave);
 303		return(0);
 304	}
 305	return(1);
 306}   //  end of OpenWaveFile
 307
 308
 309
 310
 311static void CloseWaveFile(int rate)
 312//=================================
 313{
 314   unsigned int pos;
 315
 316   if((f_wave == NULL) || (f_wave == stdout))
 317      return;
 318
 319   fflush(f_wave);
 320   pos = ftell(f_wave);
 321
 322	fseek(f_wave,4,SEEK_SET);
 323	Write4Bytes(f_wave,pos - 8);
 324
 325	fseek(f_wave,40,SEEK_SET);
 326	Write4Bytes(f_wave,pos - 44);
 327
 328
 329   fclose(f_wave);
 330   f_wave = NULL;
 331
 332} // end of CloseWaveFile
 333
 334
 335
 336
 337void MarkerEvent(int type, unsigned int char_position, int value, unsigned char *out_ptr)
 338{//======================================================================================
 339// Do nothing in the command-line version.
 340}  // end of MarkerEvent
 341
 342
 343static int WavegenFile(void)
 344{//=========================
 345	int finished;
 346	unsigned char wav_outbuf[1024];
 347
 348	out_ptr = out_start = wav_outbuf;
 349	out_end = wav_outbuf + sizeof(wav_outbuf);
 350
 351	finished = WavegenFill(0);
 352
 353	if(f_wave != NULL)
 354	{
 355		fwrite(wav_outbuf, 1, out_ptr - wav_outbuf, f_wave);
 356	}
 357	return(finished);
 358}  // end of WavegenFile
 359
 360
 361
 362
 363void FillSoundBuf(int size)
 364{//========================
 365// Fill the buffer with output sound
 366
 367// size is number of samples*4
 368
 369	size = size;
 370	if(size > sizeof(SoundBuf))
 371		size = sizeof(SoundBuf);
 372
 373	out_ptr = (unsigned char *)(&SoundBuf[0]);
 374	out_end = (unsigned char *)(&SoundBuf[size]);
 375	WavegenFill(1);
 376}
 377
 378
 379
 380int initialise(void)
 381{//=================
 382	sprintf(path_home,"%s.espeak-data","<eSpeak$Dir>");
 383	if(GetFileLength(path_home) != -2)
 384	{
 385		// not found, try the 10 character version of the directory name
 386		sprintf(path_home,"%s.espeak-dat","<eSpeak$Dir>");
 387	}
 388	if(GetFileLength(path_home) != -2)
 389	{
 390	   // still can't find data directory
 391	   sprintf(errblk.errmess,"Speak: Can't find data directory: '%s'\n",path_home);
 392	   return(-1);
 393	}
 394
 395	WavegenInit(22050,0);
 396	LoadPhData();
 397	SetVoiceStack(NULL);
 398	SynthesizeInit();
 399	return(0);
 400}
 401
 402
 403
 404void speak_text_string(char *data, int terminator, int len, int wait, int voice_num)
 405/**********************************************************************************/
 406/* 'wait' indictes wait until speaking is finished before returning */
 407{
 408	int  c;
 409	int ix;
 410	static static_length=0;
 411	static int user_token=0;   /* increment for each call of translate() */
 412	_kernel_swi_regs regs;
 413
 414	regs.r[0] = (int)callback_entry;
 415	regs.r[1] = (int)module_data;
 416	_kernel_swi(0x5f, &regs, &regs);
 417
 418
 419	if((voice_num >= 0) && (voice_num != current_voice_num) && (voice_num < N_VOICE_NAMES))
 420	{
 421		SetVoiceByName(voice_names[voice_num]);
 422	   WavegenSetVoice(voice);
 423	}
 424	current_voice_num = voice_num;
 425
 426	/* don't count CR as terminator if length is specified */
 427	if(len > 0) terminator = 0;
 428
 429	ix = 0;
 430	if(more_text == 0)
 431		static_length = 0;
 432	else
 433	{
 434		strcat(&static_buf[static_length]," : ");
 435		static_length+=3;
 436	}
 437
 438	if(terminator == 0)
 439	{
 440		while(((c = data[ix++]) != 0) && (static_length < N_STATIC_BUF-4))
 441		{
 442			static_buf[static_length++] = c;
 443			if(ix == len)
 444				break;
 445		}
 446	}
 447	else
 448	{
 449		while(((c = data[ix++]) != '\r') && (c != '\n') && (c != 0) && (static_length < N_STATIC_BUF-3))
 450		{
 451			static_buf[static_length++] = c;
 452			if(ix == len)
 453				break;
 454		}
 455	}
 456	static_buf[static_length] = 0;
 457
 458	if(option_waveout==0)
 459	{
 460		if(more_text == 0)
 461		{
 462			InitText(0);
 463			RiscosOpenSound();
 464			more_text = SpeakNextClause(NULL,(void *)static_buf,0);
 465		}
 466
 467		while(wait)
 468		{
 469			if((more_text==0) && (wcmdq_head == wcmdq_tail))
 470				break;
 471
 472			//we need to block to allow the callback handler to run
 473			regs.r[0] = 129;    // wait for key press
 474			regs.r[1] = 10;
 475			regs.r[2] = 0;
 476			_kernel_swi(0x06, &regs, &regs);  // OS_Byte
 477		}
 478	}
 479	else
 480	{
 481		more_text = 0;
 482		SpeakNextClause(NULL,(void *)static_buf,0);
 483
 484		for(;;)
 485		{
 486			if(WavegenFile() != 0)
 487				break;   // finished, wavegen command queue is empty
 488
 489			if(Generate(phoneme_list,&n_phoneme_list,1)==0)
 490				SpeakNextClause(NULL,NULL,1);
 491		}
 492
 493		CloseWaveFile(samplerate);
 494	}
 495
 496}   /* end of speak_text_string */
 497
 498
 499
 500
 501void speak_file(char *fname)
 502{//=========================
 503	FILE *f_in;
 504	char buf[120];
 505
 506	f_in = fopen(fname,"r");
 507	if(f_in == NULL)
 508	{
 509		fprintf(stderr,"Can't read file: '%s'",fname);
 510		return;
 511	}
 512	more_text = 1;
 513
 514	if(option_waveout == 0)
 515	{
 516		RiscosOpenSound();
 517		SpeakNextClause(f_in,NULL,0);
 518	}
 519	else
 520	{
 521		more_text = 0;
 522		SpeakNextClause(f_in,NULL,0);
 523
 524		for(;;)
 525		{
 526			if(WavegenFile() != 0)
 527				break;   // finished, wavegen command queue is empty
 528
 529			if(Generate(phoneme_list,&n_phoneme_list,1)==0)
 530				SpeakNextClause(NULL,NULL,1);
 531		}
 532
 533		CloseWaveFile(samplerate);
 534	}
 535}
 536
 537
 538
 539void set_say_options(int reg2, int reg3)
 540/**************************************/
 541/* Sets options from information in 'say' SWI */
 542/* R3 bits 0-7  stress indicator character
 543		bit 8     inhibit unpronouncable check */
 544{
 545	option_linelength = 0;
 546	option_phonemes = 0;
 547	option_waveout = 0;
 548	option_harmonic1 = 8;
 549	option_multibyte = 0;    // auto
 550	option_capitals = 0;
 551	option_punctuation = 0;
 552	option_punctlist[0] = 0;
 553}   /* end of set_say_options */
 554
 555
 556
 557void jsd_swi_functions(int *r)
 558/****************************/
 559{
 560	switch(r[0])
 561	{
 562	case 0:    /* major version */
 563		r[0] = 3;
 564		break;
 565
 566	case 1:    /* register user */
 567		break;
 568
 569	case 2:    /* deregister user */
 570		break;
 571
 572	case 3:
 573//      r[0] = (int)speech_to_phonemes((char *)r[1]);
 574		break;
 575
 576	case 4:
 577//      r[0] = reload_word_dict(NULL);
 578		break;
 579
 580	case 5:   /* get table of voice names */
 581		r[0] = (int)voice_names;
 582		break;
 583
 584	case 6:   /* update voice data, r1 = voice_number */
 585		if(r[1] < N_VOICE_NAMES)
 586		{
 587			SetVoiceByName(voice_names[r[1]]);
 588			current_voice_num = r[1];
 589			WavegenSetVoice(voice);
 590		}
 591		break;
 592
 593	case 7:   /* load voice data */
 594//      init_voice((char *)r[1]);
 595		break;
 596
 597	default:
 598		r[0] = 0;
 599		r[1] = 0;
 600		break;
 601	}
 602}   /* end of jsd_swi_functions */
 603
 604
 605
 606
 607_kernel_oserror *swi_handler(int swi_no, int  *r, void *pw)
 608/*********************************************************/
 609{
 610	int  value;
 611	int  q_length;
 612	int  speed;
 613	int  amp;
 614	value = r[0];
 615
 616	switch(swi_no)
 617	{
 618
 619	case 0:   // ready ?
 620		// returns the index into the source text of the currently speaking word
 621       if(current_source_index > 0)
 622          r[1] = current_source_index-1;
 623       else
 624          r[1] = current_source_index;      /* source index */
 625		r[2] = 0;      /* source tag */
 626		r[3] = 0;    /* for future expansion */
 627		r[4] = 0;
 628		r[5] = 0;
 629
 630		if(wcmdq_head == wcmdq_tail)
 631		{
 632			r[0] = -1;   /* ready, or nearly */
 633		}
 634		else
 635			r[0] = 0;
 636		break;
 637
 638	case 1:  /* restore old sound channel. DO NOTHING */
 639		break;
 640
 641	case 2:  /* miscellaneous functions */
 642		jsd_swi_functions(r);
 643		break;
 644
 645	case 3:   /* speak text */
 646//      _kernel_irqs_on();
 647		set_say_options(r[2],r[3]);
 648		speak_text_string((char *)r[0],'\r',r[1],0,r[2]);
 649		break;
 650
 651	case 4:   /* speak text and wait */
 652//      _kernel_irqs_on();         /* enable interrupts */
 653		set_say_options(r[2],r[3]);
 654		speak_text_string((char *)r[0],'\r',r[1],1,r[2]);
 655		break;
 656
 657	case 5:   /* stop speaking */
 658		SpeakNextClause(NULL,NULL,2);
 659		more_text = 0;
 660		break;
 661
 662	case 7:   /* pitch */
 663		// not implemented
 664		break;
 665
 666	case 8:   /* speed */
 667		speed = (value * 160)/140;
 668		SetParameter(espeakRATE,speed,0);
 669		break;
 670
 671	case 9:   /* word_gap */
 672		SetParameter(espeakWORDGAP,value,0);
 673		break;
 674
 675	case 10:  /* pitch_range */
 676		// not implemented
 677		break;
 678
 679	case 12:  /* reset */
 680		// not implemented
 681		break;
 682
 683	case 13:  /* volume */
 684		amp = (value*45)/100;
 685		SetParameter(espeakVOLUME,amp,0);
 686		WavegenSetVoice(voice);
 687		break;
 688	}
 689	return(NULL);
 690}   /* end of swi_handler */
 691
 692
 693
 694void PitchAdjust(int pitch_adjustment)
 695{//===================================
 696	int ix, factor;
 697	
 698	extern unsigned char pitch_adjust_tab[MAX_PITCH_VALUE+1];
 699
 700	voice->pitch_base = (voice->pitch_base * pitch_adjust_tab[pitch_adjustment])/128;
 701
 702	// adjust formants to give better results for a different voice pitch
 703	factor = 256 + (25 * (pitch_adjustment - 50))/50;
 704	for(ix=0; ix<=5; ix++)
 705	{
 706		voice->freq[ix] = (voice->freq2[ix] * factor)/256;
 707	}
 708}  //  end of PitchAdjustment
 709
 710
 711
 712char *param_string(char **argp)
 713{//============================
 714	char *p;
 715	int ix=0;
 716	static char buf[80];
 717
 718	p = *argp;
 719	while(*p == ' ') p++;
 720	while(!isspace(*p))
 721		buf[ix++] = *p++;
 722	buf[ix]=0;
 723
 724	*argp = p;
 725	return(buf);
 726}
 727
 728int param_number(char **argp)
 729{//==========================
 730	int value;
 731	char *p;
 732
 733	p = *argp;
 734	while(*p == ' ') p++;
 735
 736	value = atoi(p);
 737	while(!isspace(*p)) p++;
 738	*argp = p;
 739	return(value);
 740}
 741
 742void command_line(char *arg_string, int wait)
 743{//==========================================
 744
 745	int option_index = 0;
 746	int c;
 747	int value;
 748	int speed;
 749	int amp;
 750	int wordgap;
 751	int speaking = 0;
 752	int flag_stdin = 0;
 753	int flag_compile = 0;
 754	int error;
 755	int pitch_adjustment = 50;
 756	char filename[80];
 757	char voicename[40];
 758	char command[80];
 759	char *p;
 760	int ix;
 761
 762	voicename[0] = 0;
 763	wavefile[0] = 0;
 764	filename[0] = 0;
 765	option_linelength = 0;
 766	option_phonemes = 0;
 767	option_waveout = 0;
 768	option_quiet = 0;
 769	option_harmonic1 = 8;
 770	option_multibyte = 0;   // auto
 771	option_capitals = 0;
 772	option_punctuation = 0;
 773	option_punctlist[0] = 0;
 774
 775	f_trans = stdout;
 776
 777	p = arg_string;
 778
 779	for(;;)
 780	{
 781		while(*p==' ') p++;   // skip spaces
 782		if(*p == '\r') break;  // end of line
 783
 784		if(*p == '-')
 785		{
 786			// a command line argument
 787			p++;
 788			switch(*p++)
 789			{
 790			case 'b':
 791				option_multibyte = espeakCHARS_8BIT;
 792				break;
 793
 794			case 'h':
 795				printf("\nspeak text-to-speech: %s\n%s",version_string,help_text);
 796				return;
 797
 798			case 'k':
 799				option_capitals = param_number(&p);
 800				SetParameter(espeakCAPITALS,option_capitals,0);
 801				break;
 802
 803			case 'x':
 804				option_phonemes = 1;
 805				break;
 806
 807			case 'X':
 808				option_phonemes = 2;
 809				break;
 810
 811			case 'm':
 812				option_ssml = 1;
 813				break;
 814	
 815			case 'p':
 816				pitch_adjustment = param_number(&p);
 817				break;
 818
 819			case 'q':
 820				option_quiet = 1;
 821				break;
 822
 823			case 'f':
 824				strncpy0(filename,param_string(&p),sizeof(filename));
 825				break;
 826
 827			case 'l':
 828				option_linelength = param_number(&p);
 829				break;
 830
 831			case 'a':
 832				amp = param_number(&p);
 833				SetParameter(espeakVOLUME,amp,0);
 834				break;
 835
 836			case 's':
 837				speed = param_number(&p);
 838				SetParameter(espeakRATE,speed,0);
 839				break;
 840
 841			case 'g':
 842				wordgap = param_number(&p);
 843				SetParameter(espeakWORDGAP,wordgap,0);
 844				break;
 845	
 846			case 'v':
 847				strncpy0(voicename,param_string(&p),sizeof(voicename));
 848				break;
 849
 850			case 'w':
 851				option_waveout=1;
 852				strncpy0(wavefile,param_string(&p),sizeof(wavefile));
 853				break;
 854
 855			case '-':
 856				strncpy0(command,param_string(&p),sizeof(command));
 857				if(memcmp(command,"compile=",8)==0)
 858				{
 859					CompileDictionary(NULL,&command[8],NULL,NULL,0);
 860					return;
 861				}
 862				else
 863				if(strcmp(command,"help")==0)
 864				{
 865					printf("\nspeak text-to-speech: %s\n%s",version_string,help_text);
 866					return;
 867				}
 868				else
 869				if(memcmp(command,"punct",5)==0)
 870				{
 871					option_punctuation = 1;
 872					if((command[5]=='=') && (command[6]=='"'))
 873					{
 874						ix = 0;
 875						while((ix < N_PUNCTLIST) && ((option_punctlist[ix] = command[ix+7]) != 0)) ix++;
 876						option_punctlist[N_PUNCTLIST-1] = 0;
 877						option_punctuation = 2;
 878					}
 879					SetParameter(espeakPUNCTUATION,option_punctuation,0);
 880				}
 881				else
 882				{
 883					printf("Command not recognised\n");
 884				}
 885				break;
 886
 887			default:
 888				printf("Command not recognised\n");
 889				break;
 890			}
 891
 892		}
 893		else
 894		{
 895			break;
 896		}
 897	}
 898
 899	SetVoiceByName(voicename);
 900
 901	if((filename[0]==0) && (p[0]=='\r'))
 902	{
 903		// nothing to speak
 904		if(option_quiet)
 905		{
 906			SpeakNextClause(NULL,NULL,2);   // stop speaking
 907			more_text = 0;
 908		}
 909	}
 910
 911	if(option_waveout || option_quiet)
 912	{
 913		// write speech to a WAV file
 914		if(option_quiet)
 915		{
 916			OpenWaveFile(NULL,samplerate);
 917			option_waveout = 2;
 918		}
 919		else
 920		{
 921			if(OpenWaveFile(wavefile,samplerate) != 0)
 922			{
 923				fprintf(stderr,"Can't write to output file '%s'\n'",wavefile);
 924				return;
 925			}
 926		}
 927	}
 928
 929	if(pitch_adjustment != 50)
 930	{
 931		PitchAdjust(pitch_adjustment);
 932	}
 933	WavegenSetVoice(voice);
 934
 935	if(filename[0]==0)
 936		speak_text_string(p,'\r',0,wait,-1);
 937	else
 938		speak_file(filename);
 939}
 940
 941
 942
 943_kernel_oserror *command_handler(char *arg_string, int argc, int cmd_no, void *pw)
 944/********************************************************************************/
 945{
 946	switch(cmd_no)
 947	{
 948	case 0:    /* Say <string> */
 949		command_line(arg_string,0);  // for compatibility with speak V2
 950		break;
 951
 952	case 1:    /* Sayw <string */
 953		command_line(arg_string,1);
 954		break;
 955
 956	case 2:    /* speak [options] [<string>] */
 957		command_line(arg_string,0);
 958		break;
 959
 960	}
 961	return(NULL);
 962}   /* end of cmd_handler */
 963
 964
 965// sound handler data
 966int current_sound_handler=0;
 967int prev_sound_handler=0;
 968int prev_sound_data=0;
 969int prev_sound_rate=13;
 970int sound_handler_changed=0;
 971
 972
 973void RiscosCloseSound()
 974{//====================
 975_kernel_swi_regs regs;
 976	if((sound_handler_changed) && (prev_sound_handler != (int)DMA_Handler))
 977	{
 978		// check whether current handler is ours
 979		regs.r[0]=0;
 980		_kernel_swi(0x40145,&regs,&regs);
 981		if(regs.r[1] == (int)DMA_Handler)
 982		{
 983			regs.r[0]=1;
 984			regs.r[1]=prev_sound_handler;
 985			regs.r[2]=prev_sound_data;
 986			_kernel_swi(0x40145,&regs,&regs);  // Sound LinearHandler 1
 987
 988			// reset to the previous sample rate
 989			regs.r[0]=3;
 990			regs.r[1]=prev_sound_rate;
 991			_kernel_swi(0x40146,&regs,&regs);  // Sound_SampleRate 3
 992
 993			current_sound_handler = prev_sound_handler;
 994			sound_handler_changed = 0;
 995		}
 996	}
 997}  // end of RiscosCloseSound
 998
 999
1000
1001void RiscosOpenSound()
1002{//===================
1003	_kernel_swi_regs regs;
1004
1005	if(current_sound_handler != (int)DMA_Handler)
1006	{
1007		// register the sound handler
1008		regs.r[0]=1;
1009		regs.r[1]=(int)DMA_Handler;
1010		regs.r[2]=(int)module_data;
1011		_kernel_swi(0x40145,&regs,&regs);  // Sound_LinearHandler 1
1012		prev_sound_handler = regs.r[1];
1013		prev_sound_data = regs.r[2];
1014
1015		// set the sample rate
1016		regs.r[0]=3;
1017		regs.r[1]=sample_rate_index;
1018		regs.r[2]=0;
1019		_kernel_swi(0x40146,&regs,&regs);  // Sound_SampleRate
1020		prev_sound_rate = regs.r[1];
1021
1022		current_sound_handler = (int)DMA_Handler;
1023		sound_handler_changed = 1;
1024	}
1025}  //  end of RiscosOpenSound
1026
1027
1028
1029
1030
1031int callback_handler(_kernel_swi_regs *r, void *pw)
1032/*************************************************/
1033{
1034	if(Generate(phoneme_list,&n_phoneme_list,1)==0)
1035	{
1036		more_text = SpeakNextClause(NULL,NULL,1);
1037	}
1038
1039	if((WcmdqUsed() == 0) && (more_text == 0))
1040	{
1041		RiscosCloseSound();
1042	}
1043	callback_inhibit = 0;
1044
1045	return(1);
1046}   /* end of callback_handler */
1047
1048
1049
1050int sound_handler(_kernel_swi_regs *r, void *pw)
1051/**********************************************/
1052{
1053	int n_queue;
1054	int size;
1055	int *dma_buf;
1056	int x;
1057	int ix;
1058
1059	module_data = (int *)pw;
1060	dma_buf = (int *)r->r[1];
1061	size = (r->r[2] - r->r[1])/4;
1062	FillSoundBuf(size);
1063
1064	for(ix=0; ix<size; ix++)
1065	{
1066		x = SoundBuf[ix];
1067		dma_buf[ix] = x + (x << 16);
1068	}
1069
1070	n_queue = WcmdqUsed();
1071
1072	r->r[0] = 0;
1073	if(callback_inhibit == 0)
1074	{
1075		// set a callback either:
1076		// - queue is low and there is more text to be processed
1077		// - queue is empty and no more text, so callback handler will remove the sound handler
1078		if(((n_queue < 20) && (more_text != 0)) ||
1079			((n_queue==0) && (more_text == 0)))
1080		{
1081			callback_inhibit = 1;
1082			r->r[0] = 1;
1083			r->r[1] = (int)pw;
1084		}
1085	}
1086	return(1);
1087}
1088
1089
1090
1091
1092int InitSound16(int sample_rate)
1093/******************************/
1094/* Find sample rate index */
1095{
1096	int  current_rate_index;  // current value
1097
1098	int  sound_mode;
1099	int  sound_config;
1100	int  srate;
1101	int  n_srix;
1102	int  ix;
1103	_kernel_swi_regs regs;
1104	_kernel_oserror *error;
1105
1106	sound_mode = 0;
1107	regs.r[0] = 0;
1108	error = _kernel_swi(0x40144+os_X,&regs,&regs);
1109	sound_mode = regs.r[0];
1110	sound_config = regs.r[1];
1111
1112	if((error == NULL) && (sound_mode == 1))
1113	{
1114		/* 16 bit sound, find sample rate index */
1115		regs.r[0] = 0;
1116		regs.r[1] = 0;
1117		_kernel_swi(0x40146,&regs,&regs);
1118		n_srix = regs.r[1];
1119
1120		regs.r[0] = 1;
1121		regs.r[1] = 0;
1122		_kernel_swi(0x40146,&regs,&regs);
1123		current_rate_index = regs.r[1];      // current sample rate index
1124		srate = regs.r[2];
1125		for(ix=1; ix<=n_srix; ix++)
1126		{
1127			regs.r[0] = 2;
1128			regs.r[1] = ix;
1129			_kernel_swi(0x40146,&regs,&regs);
1130			srate = regs.r[2];
1131			if(srate >= (sample_rate*1024))
1132			{
1133				return(ix);
1134			}
1135		}
1136	}
1137	return(14);       // this was the index for 22050
1138}   //  end of InitSound16
1139
1140
1141
1142void RemoveCallback()
1143/*******************/
1144{
1145	_kernel_swi_regs regs;
1146
1147	regs.r[0] = (int)callback_entry;
1148	regs.r[1] = (int)module_data;
1149	_kernel_swi(0x5f, &regs, &regs);
1150}
1151
1152
1153void terminate_module(void)
1154/*************************/
1155{
1156	RiscosCloseSound();
1157	RemoveCallback();
1158	delete translator;
1159	FreePhData();
1160}   /* end of terminate_module */
1161
1162
1163void kill_module(void)
1164/********************/
1165{
1166	_kernel_swi_regs regs;
1167	regs.r[0]=4;
1168	regs.r[1]=(int)"Speak";
1169	_kernel_swi(0x1e,&regs,&regs);   /* RMKill */
1170}
1171
1172
1173_kernel_oserror *user_init(char *cmd_fail, int podule_base, void *pw)
1174/*******************************************************************/
1175{
1176	_kernel_swi_regs regs;
1177	_kernel_oserror *error;
1178	int param;
1179
1180	// It seems that the wctype functions don't work until the locale has been set
1181	// to something other than the default "C".  Then, not only Latin1 but also the
1182	// other characters give the correct results with iswalpha() etc.
1183
1184	static char *locale = "ISO8859-1";
1185   setlocale(LC_CTYPE,locale);
1186
1187	module_data = pw;
1188
1189	sample_rate_index = InitSound16(22050);
1190
1191	if(initialise() < 0)
1192	{
1193	   // exit module, errblk.errmess is set by initialise()
1194		errblk.errnum = 0x101;
1195	   return(&errblk);
1196	}
1197
1198	ReadVoiceNames();
1199	SetVoiceByName("default");
1200
1201	for(param=0; param<N_SPEECH_PARAM; param++)
1202		param_stack[0].parameter[param] = param_defaults[param];
1203
1204	SetParameter(espeakRATE,170,0);
1205	SetParameter(espeakVOLUME,65,0);
1206
1207	WavegenSetVoice(voice);
1208	atexit(terminate_module);
1209	return(NULL);
1210}   /* end of user_init */
1211
1212