PageRenderTime 111ms CodeModel.GetById 10ms app.highlight 90ms RepoModel.GetById 1ms app.codeStats 1ms

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

http://eyes-free.googlecode.com/
C++ | 1628 lines | 1247 code | 287 blank | 94 comment | 292 complexity | 9f3a10693542d29d1e0f1ec7b3f9fc32 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, see:                                 *
  17 *               <http://www.gnu.org/licenses/>.                           *
  18 ***************************************************************************/
  19
  20#include "StdAfx.h"
  21
  22#include <stdio.h>
  23#include <ctype.h>
  24#include <wctype.h>
  25#include <stdlib.h>
  26#include <string.h>
  27#include <math.h>
  28
  29#include "speak_lib.h"
  30#include "speech.h"
  31#include "phoneme.h"
  32#include "synthesize.h"
  33#include "voice.h"
  34#include "translate.h"
  35
  36
  37extern FILE *f_log;
  38static void SmoothSpect(void);
  39
  40
  41// list of phonemes in a clause
  42int n_phoneme_list=0;
  43PHONEME_LIST phoneme_list[N_PHONEME_LIST];
  44
  45char mbrola_name[20];
  46
  47int speed_factor1;
  48int speed_factor2;
  49int speed_min_sample_len;
  50
  51static int  last_pitch_cmd;
  52static int  last_amp_cmd;
  53static frame_t  *last_frame;
  54static int  last_wcmdq;
  55static int  pitch_length;
  56static int  amp_length;
  57static int  modn_flags;
  58
  59static int  syllable_start;
  60static int  syllable_end;
  61static int  syllable_centre;
  62
  63static voice_t *new_voice=NULL;
  64
  65int n_soundicon_tab=N_SOUNDICON_SLOTS;
  66SOUND_ICON soundicon_tab[N_SOUNDICON_TAB];
  67
  68#define RMS_GLOTTAL1 35   // vowel before glottal stop
  69#define RMS_START  28  // 14 - 30
  70
  71#define VOWEL_FRONT_LENGTH  50
  72
  73
  74
  75// a dummy phoneme_list entry which looks like a pause
  76static PHONEME_LIST next_pause;
  77
  78
  79const char *WordToString(unsigned int word)
  80{//========================================
  81// Convert a phoneme mnemonic word into a string
  82	int  ix;
  83	static char buf[5];
  84
  85	for(ix=0; ix<3; ix++)
  86		buf[ix] = word >> (ix*8);
  87	buf[4] = 0;
  88	return(buf);
  89}
  90
  91
  92void SynthesizeInit()
  93{//==================
  94	last_pitch_cmd = 0;
  95	last_amp_cmd = 0;
  96	last_frame = NULL;
  97	syllable_centre = -1;
  98
  99	// initialise next_pause, a dummy phoneme_list entry
 100//	next_pause.ph = phoneme_tab[phonPAUSE];   // this must be done after voice selection
 101	next_pause.type = phPAUSE;
 102	next_pause.newword = 0;
 103}
 104
 105
 106
 107static void EndAmplitude(void)
 108{//===========================
 109	if(amp_length > 0)
 110	{
 111		if(wcmdq[last_amp_cmd][1] == 0)
 112			wcmdq[last_amp_cmd][1] = amp_length;
 113		amp_length = 0;
 114	}
 115}
 116
 117
 118
 119static void EndPitch(int voice_break)
 120{//==================================
 121	// posssible end of pitch envelope, fill in the length
 122	if((pitch_length > 0) && (last_pitch_cmd >= 0))
 123	{
 124		if(wcmdq[last_pitch_cmd][1] == 0)
 125			wcmdq[last_pitch_cmd][1] = pitch_length;
 126		pitch_length = 0;
 127	}
 128
 129	if(voice_break)
 130	{
 131		last_wcmdq = -1;
 132		last_frame = NULL;
 133		syllable_end = wcmdq_tail;
 134		SmoothSpect();
 135		syllable_centre = -1;
 136		memset(vowel_transition,0,sizeof(vowel_transition));
 137	}
 138}  // end of Synthesize::EndPitch
 139
 140
 141
 142static void DoAmplitude(int amp, unsigned char *amp_env)
 143{//=====================================================
 144	long *q;
 145
 146	last_amp_cmd = wcmdq_tail;
 147	amp_length = 0;       // total length of vowel with this amplitude envelope
 148
 149	q = wcmdq[wcmdq_tail];
 150	q[0] = WCMD_AMPLITUDE;
 151	q[1] = 0;        // fill in later from amp_length
 152	q[2] = (long)amp_env;
 153	q[3] = amp;
 154	WcmdqInc();
 155}  // end of Synthesize::DoAmplitude
 156
 157
 158
 159static void DoPitch(unsigned char *env, int pitch1, int pitch2)
 160{//============================================================
 161	long *q;
 162
 163	EndPitch(0);
 164
 165	if(pitch1 == 1024)
 166	{
 167		// pitch was not set
 168		pitch1 = 24;
 169		pitch2 = 33;
 170		env = envelope_data[PITCHfall];
 171	}
 172	last_pitch_cmd = wcmdq_tail;
 173	pitch_length = 0;       // total length of spect with this pitch envelope
 174
 175	if(pitch2 < 0)
 176		pitch2 = 0;
 177
 178	q = wcmdq[wcmdq_tail];
 179	q[0] = WCMD_PITCH;
 180	q[1] = 0;   // length, fill in later from pitch_length
 181	q[2] = (long)env;
 182	q[3] = (pitch1 << 16) + pitch2;
 183	WcmdqInc();
 184}  //  end of Synthesize::DoPitch
 185
 186
 187
 188int PauseLength(int pause, int control)
 189{//====================================
 190	int len;
 191
 192	if(control == 0)
 193		len = (pause * speed_factor1)/256;
 194	else
 195		len = (pause * speed_factor2)/256;
 196
 197	if(len < 5) len = 5;      // mS, limit the amount to which pauses can be shortened
 198	return(len);
 199}
 200
 201
 202static void DoPause(int length, int control)
 203{//=========================================
 204// control = 1, less shortening at fast speeds
 205	int len;
 206
 207	len = PauseLength(length, control);
 208
 209	len = (len * samplerate) / 1000;  // convert from mS to number of samples
 210
 211	EndPitch(1);
 212	wcmdq[wcmdq_tail][0] = WCMD_PAUSE;
 213	wcmdq[wcmdq_tail][1] = len;
 214	WcmdqInc();
 215	last_frame = NULL;
 216}  // end of Synthesize::DoPause
 217
 218
 219extern int seq_len_adjust;   // temporary fix to advance the start point for playing the wav sample
 220
 221
 222static int DoSample2(int index, int which, int length_mod, int amp)
 223{//================================================================
 224	int length;
 225	int length1;
 226	int format;
 227	int min_length;
 228	int start=0;
 229	long *q;
 230	unsigned char *p;
 231
 232	index = index & 0x7fffff;
 233	p = &wavefile_data[index];
 234	format = p[2];
 235	length1 = (p[1] * 256);
 236	length1 += p[0];    //  length in bytes
 237
 238	if(seq_len_adjust > 0)
 239	{
 240		start = (seq_len_adjust * samplerate)/1000;
 241		if(format == 0)
 242			start *= 2;
 243		length1 -= start;
 244		index += start;
 245	}
 246
 247
 248	if(length_mod > 0)
 249		length = (length1 * length_mod) / 256;
 250	else
 251		length = length1;
 252
 253
 254	length = (length * speed_factor2)/256;
 255	min_length = speed_min_sample_len;
 256	if(format==0)
 257		min_length *= 2;
 258
 259	if(length < min_length)
 260		length = min_length;
 261
 262	if(length > length1)
 263		length = length1;  // don't exceed wavefile length
 264
 265	if(format==0)
 266		length /= 2;     // 2 byte samples
 267
 268
 269	index += 4;
 270
 271	if(amp >= 0)
 272	{
 273		last_wcmdq = wcmdq_tail;
 274		q = wcmdq[wcmdq_tail];
 275		if(which & 0x100)
 276			q[0] = WCMD_WAVE2;    // mix this with synthesised wave
 277		else
 278			q[0] = WCMD_WAVE;
 279		q[1] = length;   // length in samples
 280		q[2] = long(&wavefile_data[index]);
 281		q[3] = format + (amp << 8);
 282		WcmdqInc();
 283	}
 284	return(length);
 285}  // end of Synthesize::DoSample2
 286
 287
 288int DoSample(PHONEME_TAB *ph1, PHONEME_TAB *ph2, int which, int length_mod, int amp)
 289{//====================== ==========================================================
 290	int index;
 291	int match_level;
 292	int amp2;
 293	int result;
 294
 295	EndPitch(1);
 296	index = LookupSound(ph1,ph2,which & 0xff,&match_level,0);
 297	if((index & 0x800000) == 0)
 298		return(0);             // not wavefile data
 299
 300	amp2 = wavefile_amp;
 301	if(amp != 0)
 302		amp2 = (amp * wavefile_amp)/20;
 303
 304	if(amp == -1)
 305		amp2 = amp;
 306
 307	result = DoSample2(index,which,length_mod,amp2);
 308	last_frame = NULL;
 309	return(result);
 310}  // end of Synthesize::DoSample
 311
 312
 313
 314
 315static frame_t *AllocFrame()
 316{//=========================
 317	// Allocate a temporary spectrum frame for the wavegen queue. Use a pool which is big
 318	// enough to use a round-robin without checks.
 319	// Only needed for modifying spectra for blending to consonants
 320
 321#define N_FRAME_POOL  N_WCMDQ
 322	static int ix=0;
 323	static frame_t frame_pool[N_FRAME_POOL];
 324
 325	ix++;
 326	if(ix >= N_FRAME_POOL)
 327		ix = 0;
 328	return(&frame_pool[ix]);
 329}
 330
 331
 332static void set_frame_rms(frame_t *fr, int new_rms)
 333{//=================================================
 334// Each frame includes its RMS amplitude value, so to set a new
 335// RMS just adjust the formant amplitudes by the appropriate ratio
 336
 337	int x;
 338	int h;
 339	int ix;
 340
 341	static const short sqrt_tab[200] = {
 342	  0, 64, 90,110,128,143,156,169,181,192,202,212,221,230,239,247,
 343	256,263,271,278,286,293,300,306,313,320,326,332,338,344,350,356,
 344	362,367,373,378,384,389,394,399,404,409,414,419,424,429,434,438,
 345	443,448,452,457,461,465,470,474,478,483,487,491,495,499,503,507,
 346	512,515,519,523,527,531,535,539,543,546,550,554,557,561,565,568,
 347	572,576,579,583,586,590,593,596,600,603,607,610,613,617,620,623,
 348	627,630,633,636,640,643,646,649,652,655,658,662,665,668,671,674,
 349	677,680,683,686,689,692,695,698,701,704,706,709,712,715,718,721,
 350	724,726,729,732,735,738,740,743,746,749,751,754,757,759,762,765,
 351	768,770,773,775,778,781,783,786,789,791,794,796,799,801,804,807,
 352	809,812,814,817,819,822,824,827,829,832,834,836,839,841,844,846,
 353	849,851,853,856,858,861,863,865,868,870,872,875,877,879,882,884,
 354	886,889,891,893,896,898,900,902};
 355
 356	if(fr->rms == 0) return;    // check for divide by zero
 357	x = (new_rms * 64)/fr->rms;
 358	if(x >= 200) x = 199;
 359
 360	x = sqrt_tab[x];   // sqrt(new_rms/fr->rms)*0x200;
 361
 362	for(ix=0; ix<N_PEAKS; ix++)
 363	{
 364		h = fr->fheight[ix] * x;
 365		fr->fheight[ix] = h/0x200;
 366	}
 367}   /* end of set_frame_rms */
 368
 369
 370
 371static void formants_reduce_hf(frame_t *fr, int level)
 372{//====================================================
 373//  change height of peaks 2 to 8, percentage
 374	int  ix;
 375	int  x;
 376
 377	for(ix=2; ix<N_PEAKS; ix++)
 378	{
 379		x = fr->fheight[ix] * level;
 380		fr->fheight[ix] = x/100;
 381	}
 382}
 383
 384
 385static frame_t *CopyFrame(frame_t *frame1, int copy)
 386{//=================================================
 387//  create a copy of the specified frame in temporary buffer
 388	frame_t *frame2;
 389
 390	if((copy==0) && (frame1->frflags & FRFLAG_COPIED))
 391	{
 392		// this frame has already been copied in temporary rw memory
 393		return(frame1);
 394	}
 395
 396	frame2 = AllocFrame();
 397	if(frame2 != NULL)
 398	{
 399		memcpy(frame2,frame1,sizeof(frame_t));
 400		frame2->length = 0;
 401		frame2->frflags |= FRFLAG_COPIED;
 402	}
 403	return(frame2);
 404}
 405
 406
 407static frame_t *DuplicateLastFrame(frameref_t *seq, int n_frames, int length)
 408{//==========================================================================
 409	frame_t *fr;
 410
 411	seq[n_frames-1].length = length;
 412	fr = CopyFrame(seq[n_frames-1].frame,1);
 413	seq[n_frames].frame = fr;
 414	seq[n_frames].length = 0;
 415	return fr;
 416}
 417
 418
 419static void AdjustFormants(frame_t *fr, int target, int min, int max, int f1_adj, int f3_adj, int hf_reduce, int flags)
 420{//====================================================================================================================
 421	int x;
 422
 423//hf_reduce = 70;      // ?? using fixed amount rather than the parameter??
 424
 425	target = (target * voice->formant_factor)/256;
 426
 427	x = (target - fr->ffreq[2]) / 2;
 428	if(x > max) x = max;
 429	if(x < min) x = min;
 430	fr->ffreq[2] += x;
 431	fr->ffreq[3] += f3_adj;
 432
 433	if(flags & 0x20)
 434	{
 435		f3_adj = -f3_adj;   //. reverse direction for f4,f5 change
 436	}
 437	fr->ffreq[4] += f3_adj;
 438	fr->ffreq[5] += f3_adj;
 439
 440	if(f1_adj==1)
 441	{
 442		x = (235 - fr->ffreq[1]);
 443		if(x < -100) x = -100;
 444		if(x > -60) x = -60;
 445		fr->ffreq[1] += x;
 446	}
 447	if(f1_adj==2)
 448	{
 449		x = (235 - fr->ffreq[1]);
 450		if(x < -300) x = -300;
 451		if(x > -150) x = -150;
 452		fr->ffreq[1] += x;
 453		fr->ffreq[0] += x;
 454	}
 455	if(f1_adj==3)
 456	{
 457		x = (100 - fr->ffreq[1]);
 458		if(x < -400) x = -400;
 459		if(x > -300) x = -400;
 460		fr->ffreq[1] += x;
 461		fr->ffreq[0] += x;
 462	}
 463	formants_reduce_hf(fr,hf_reduce); 
 464}
 465
 466
 467int VowelCloseness(frame_t *fr)
 468{//============================
 469// return a value 0-3 depending on the vowel's f1
 470	int f1;
 471
 472	if((f1 = fr->ffreq[1]) < 300)
 473		return(3);
 474	if(f1 < 400)
 475		return(2);
 476	if(f1 < 500)
 477		return(1);
 478	return(0);
 479}
 480
 481
 482int FormantTransition2(frameref_t *seq, int &n_frames, unsigned int data1, unsigned int data2, PHONEME_TAB *other_ph, int which)
 483{//==============================================================================================================================
 484	int ix;
 485	int formant;
 486	int next_rms;
 487
 488	int len;
 489	int rms;
 490	int f1;
 491	int f2;
 492	int f2_min;
 493	int f2_max;
 494	int f3_adj;
 495	int f3_amp;
 496	int flags;
 497	int vcolour;
 498
 499#define N_VCOLOUR  2
 500// percentage change for each formant in 256ths
 501static short vcolouring[N_VCOLOUR][5] = {
 502	{243,272,256,256,256},         // palatal consonant follows
 503	{256,256,240,240,240},         // retroflex
 504};
 505
 506	frame_t *fr = NULL;
 507
 508	if(n_frames < 2)
 509		return(0);
 510
 511	len = (data1 & 0x3f) * 2;
 512	rms = (data1 >> 6) & 0x3f;
 513	flags = (data1 >> 12);
 514
 515	f2 = (data2 & 0x3f) * 50;
 516	f2_min = (((data2 >> 6) & 0x1f) - 15) * 50;
 517	f2_max = (((data2 >> 11) & 0x1f) - 15) * 50;
 518	f3_adj = (((data2 >> 16) & 0x1f) - 15) * 50;
 519	f3_amp = ((data2 >> 21) & 0x1f) * 8;
 520	f1 = ((data2 >> 26) & 0x7);
 521	vcolour = (data2 >> 29);
 522
 523//	fprintf(stderr,"FMT%d %3s  %3d-%3d f1=%d  f2=%4d %4d %4d  f3=%4d %3d\n",
 524//		which,WordToString(other_ph->mnemonic),len,rms,f1,f2,f2_min,f2_max,f3_adj,f3_amp);
 525
 526	if(other_ph->mnemonic == '?')
 527		flags |= 8;
 528
 529	if(which == 1)
 530	{
 531		/* entry to vowel */
 532		fr = CopyFrame(seq[0].frame,0);
 533		seq[0].frame = fr;
 534		seq[0].length = VOWEL_FRONT_LENGTH;
 535		if(len > 0)
 536			seq[0].length = len;
 537		seq[0].frflags |= FRFLAG_LEN_MOD;              // reduce length modification
 538		fr->frflags |= FRFLAG_LEN_MOD;
 539
 540		next_rms = seq[1].frame->rms;
 541
 542		if(f2 != 0)
 543		{
 544			if(rms & 0x20)
 545			{
 546				set_frame_rms(fr,(next_rms * (rms & 0x1f))/30);
 547			}
 548			AdjustFormants(fr, f2, f2_min, f2_max, f1, f3_adj, f3_amp, flags);
 549
 550			if((rms & 0x20) == 0)
 551			{
 552				set_frame_rms(fr,rms*2);
 553			}
 554		}
 555		else
 556		{
 557			if(flags & 8)
 558				set_frame_rms(fr,(next_rms*24)/32);
 559			else
 560				set_frame_rms(fr,RMS_START);
 561		}
 562
 563		if(flags & 8)
 564		{
 565//			set_frame_rms(fr,next_rms - 5);
 566			modn_flags = 0x800 + (VowelCloseness(fr) << 8);
 567		}
 568	}
 569	else
 570	{
 571		// exit from vowel
 572		rms = rms*2;
 573		if((f2 != 0) || (flags != 0))
 574		{
 575
 576			if(flags & 8)
 577			{
 578				fr = CopyFrame(seq[n_frames-1].frame,0);
 579				seq[n_frames-1].frame = fr;
 580				rms = RMS_GLOTTAL1;
 581	
 582				// degree of glottal-stop effect depends on closeness of vowel (indicated by f1 freq)
 583				modn_flags = 0x400 + (VowelCloseness(fr) << 8);
 584			}
 585			else
 586			{
 587				fr = DuplicateLastFrame(seq,n_frames++,len);
 588				if(len > 36)
 589					seq_len_adjust += (len - 36);
 590	
 591				if(f2 != 0)
 592				{
 593					AdjustFormants(fr, f2, f2_min, f2_max, f1, f3_adj, f3_amp, flags);
 594				}
 595			}
 596
 597			set_frame_rms(fr,rms);
 598
 599			if((vcolour > 0) && (vcolour <= N_VCOLOUR))
 600			{
 601				for(ix=0; ix<n_frames; ix++)
 602				{
 603					fr = CopyFrame(seq[ix].frame,0);
 604					seq[ix].frame = fr;
 605					
 606					for(formant=1; formant<=5; formant++)
 607					{
 608						int x;
 609						x = fr->ffreq[formant] * vcolouring[vcolour-1][formant-1];
 610						fr->ffreq[formant] = x / 256;
 611					}
 612				}
 613			}
 614		}
 615	}
 616
 617	if(fr != NULL)
 618	{
 619		if(flags & 4)
 620			fr->frflags |= FRFLAG_FORMANT_RATE;
 621		if(flags & 2)
 622			fr->frflags |= FRFLAG_BREAK;       // don't merge with next frame
 623	}
 624
 625	if(flags & 0x40)
 626		DoPause(12,0);  // add a short pause after the consonant
 627
 628	if(flags & 16)
 629		return(len);
 630	return(0);
 631} //  end of FormantTransition2
 632
 633
 634
 635static void SmoothSpect(void)
 636{//==========================
 637	// Limit the rate of frequence change of formants, to reduce chirping
 638
 639	long *q;
 640	frame_t *frame;
 641	frame_t *frame2;
 642	frame_t *frame1;
 643	frame_t *frame_centre;
 644	int ix;
 645	int len;
 646	int pk;
 647	int modified;
 648	int allowed;
 649	int diff;
 650
 651	if(syllable_start == syllable_end)
 652		return;
 653
 654	if((syllable_centre < 0) || (syllable_centre == syllable_start))
 655	{
 656		syllable_start = syllable_end;
 657		return;
 658	}
 659
 660	q = wcmdq[syllable_centre];
 661	frame_centre = (frame_t *)q[2];
 662
 663	// backwards
 664	ix = syllable_centre -1;
 665	frame = frame2 = frame_centre;
 666	for(;;)
 667	{
 668		if(ix < 0) ix = N_WCMDQ-1;
 669		q = wcmdq[ix];
 670
 671		if(q[0] == WCMD_PAUSE || q[0] == WCMD_WAVE)
 672			break;
 673
 674		if(q[0] == WCMD_SPECT || q[0] == WCMD_SPECT2)
 675		{
 676			len = q[1] & 0xffff;
 677
 678			frame1 = (frame_t *)q[3];
 679			if(frame1 == frame)
 680			{
 681				q[3] = (long)frame2;
 682				frame1 = frame2;
 683			}
 684			else
 685				break;  // doesn't follow on from previous frame
 686
 687			frame = frame2 = (frame_t *)q[2];
 688			modified = 0;
 689
 690			if(frame->frflags & FRFLAG_BREAK)
 691				break;
 692
 693			if(frame->frflags & FRFLAG_FORMANT_RATE)
 694				len = (len * 12)/10;      // allow slightly greater rate of change for this frame (was 12/10)
 695
 696			for(pk=0; pk<6; pk++)
 697			{
 698				int f1, f2;
 699
 700				if((frame->frflags & FRFLAG_BREAK_LF) && (pk < 3))
 701					continue;
 702
 703				f1 = frame1->ffreq[pk];
 704				f2 = frame->ffreq[pk];
 705
 706				// backwards
 707				if((diff = f2 - f1) > 0)
 708				{
 709					allowed = f1*2 + f2;
 710				}
 711				else
 712				{
 713					allowed = f1 + f2*2;
 714				}
 715
 716				// the allowed change is specified as percentage (%*10) of the frequency
 717				// take "frequency" as 1/3 from the lower freq
 718				allowed = (allowed * formant_rate[pk])/3000;
 719				allowed = (allowed * len)/256;
 720
 721				if(diff > allowed)
 722				{
 723					if(modified == 0)
 724					{
 725						frame2 = CopyFrame(frame,0);
 726						modified = 1;
 727					}
 728					frame2->ffreq[pk] = frame1->ffreq[pk] + allowed;
 729					q[2] = (long)frame2;
 730				}
 731				else
 732				if(diff < -allowed)
 733				{
 734					if(modified == 0)
 735					{
 736						frame2 = CopyFrame(frame,0);
 737						modified = 1;
 738					}
 739					frame2->ffreq[pk] = frame1->ffreq[pk] - allowed;
 740					q[2] = (long)frame2;
 741				}
 742			}
 743		}
 744
 745		if(ix == syllable_start)
 746			break;
 747		ix--;
 748	}
 749
 750	// forwards
 751	ix = syllable_centre;
 752
 753	frame = NULL;
 754	for(;;)
 755	{
 756		q = wcmdq[ix];
 757
 758		if(q[0] == WCMD_PAUSE || q[0] == WCMD_WAVE)
 759			break;
 760
 761		if(q[0] == WCMD_SPECT || q[0] == WCMD_SPECT2)
 762		{
 763
 764			len = q[1] & 0xffff;
 765
 766			frame1 = (frame_t *)q[2];
 767			if(frame != NULL)
 768			{
 769				if(frame1 == frame)
 770				{
 771					q[2] = (long)frame2;
 772					frame1 = frame2;
 773				}
 774				else
 775					break;  // doesn't follow on from previous frame
 776			}
 777
 778			frame = frame2 = (frame_t *)q[3];
 779			modified = 0;
 780
 781			if(frame1->frflags & FRFLAG_BREAK)
 782				break;
 783
 784			if(frame1->frflags & FRFLAG_FORMANT_RATE)
 785				len = (len *6)/5;      // allow slightly greater rate of change for this frame
 786
 787			for(pk=0; pk<6; pk++)
 788			{
 789				int f1, f2;
 790				f1 = frame1->ffreq[pk];
 791				f2 = frame->ffreq[pk];
 792
 793				// forwards
 794				if((diff = f2 - f1) > 0)
 795				{
 796					allowed = f1*2 + f2;
 797				}
 798				else
 799				{
 800					allowed = f1 + f2*2;
 801				}
 802				allowed = (allowed * formant_rate[pk])/3000;
 803				allowed = (allowed * len)/256;
 804
 805				if(diff > allowed)
 806				{
 807					if(modified == 0)
 808					{
 809						frame2 = CopyFrame(frame,0);
 810						modified = 1;
 811					}
 812					frame2->ffreq[pk] = frame1->ffreq[pk] + allowed;
 813					q[3] = (long)frame2;
 814				}
 815				else
 816				if(diff < -allowed)
 817				{
 818					if(modified == 0)
 819					{
 820						frame2 = CopyFrame(frame,0);
 821						modified = 1;
 822					}
 823					frame2->ffreq[pk] = frame1->ffreq[pk] - allowed;
 824					q[3] = (long)frame2;
 825				}
 826			}
 827		}
 828
 829		ix++;
 830		if(ix >= N_WCMDQ) ix = 0;
 831		if(ix == syllable_end)
 832			break;
 833	}
 834
 835	syllable_start = syllable_end;
 836}
 837
 838
 839static void StartSyllable(void)
 840{//============================
 841	// start of syllable, if not already started
 842	if(syllable_end == syllable_start)
 843		syllable_end = wcmdq_tail;
 844}
 845
 846
 847int DoSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB *next_ph,
 848		int which, PHONEME_LIST *plist, int modulation)
 849{//===================================================================================
 850	// which  1  start of phoneme,   2 body and end
 851	// length_mod: 256 = 100%
 852	// modulation: -1 = don't write to wcmdq
 853
 854	int  n_frames;
 855	frameref_t *frames;
 856	int  frameix;
 857	frame_t *frame1;
 858	frame_t *frame2;
 859	frame_t *fr;
 860	int  ix;
 861	long *q;
 862	int  len;
 863	int  match_level;
 864	int  frame_length;
 865	int  frame1_length;
 866	int  frame2_length;
 867	int  length_factor;
 868	int  length_mod;
 869	int  total_len = 0;
 870	static int wave_flag = 0;
 871	int wcmd_spect = WCMD_SPECT;
 872
 873	length_mod = plist->length;
 874	if(length_mod==0) length_mod=256;
 875
 876if(which==1)
 877{
 878	// limit the shortening of sonorants before shortened (eg. unstressed vowels)
 879	if((this_ph->type==phLIQUID) || (prev_ph->type==phLIQUID) || (prev_ph->type==phNASAL))
 880	{
 881		if(length_mod < (len = translator->langopts.param[LOPT_SONORANT_MIN]))
 882		{
 883			length_mod = len;
 884		}
 885	}
 886}
 887
 888	modn_flags = 0;
 889	frames = LookupSpect(this_ph,prev_ph,next_ph,which,&match_level,&n_frames, plist);
 890	if(frames == NULL)
 891		return(0);   // not found
 892
 893	if(wavefile_ix == 0)
 894	{
 895		if(wave_flag)
 896		{
 897			// cancel any wavefile that was playing previously
 898			wcmd_spect = WCMD_SPECT2;
 899			wave_flag = 0;
 900		}
 901		else
 902		{
 903			wcmd_spect = WCMD_SPECT;
 904		}
 905	}
 906
 907	frame1 = frames[0].frame;
 908	frame1_length = frames[0].length;
 909	if(last_frame != NULL)
 910	{
 911		if(((last_frame->length < 2) || (last_frame->frflags & FRFLAG_VOWEL_CENTRE))
 912			&& !(last_frame->frflags & FRFLAG_BREAK))
 913		{
 914			// last frame of previous sequence was zero-length, replace with first of this sequence
 915			wcmdq[last_wcmdq][3] = (long)frame1;
 916
 917			if(last_frame->frflags & FRFLAG_BREAK_LF)
 918			{
 919				// but flag indicates keep HF peaks in last segment
 920				fr = CopyFrame(frame1,1);
 921				for(ix=3; ix<N_PEAKS; ix++)
 922				{
 923					fr->ffreq[ix] = last_frame->ffreq[ix];
 924					fr->fheight[ix] = last_frame->fheight[ix];
 925				}
 926				wcmdq[last_wcmdq][3] = (long)fr;
 927			}
 928		}
 929	}
 930
 931	if((this_ph->type == phVOWEL) && (which == 2))
 932	{
 933		SmoothSpect();    // process previous syllable
 934
 935		// remember the point in the output queue of the centre of the vowel
 936		syllable_centre = wcmdq_tail;
 937	}
 938
 939	frame_length = frame1_length;
 940	for(frameix=1; frameix<n_frames; frameix++)
 941	{
 942		frame2 = frames[frameix].frame;
 943		frame2_length = frames[frameix].length;
 944
 945		if((wavefile_ix != 0) && ((frame1->frflags & FRFLAG_DEFER_WAV)==0))
 946		{
 947			// there is a wave file to play along with this synthesis
 948			seq_len_adjust = 0;
 949			DoSample2(wavefile_ix,which+0x100,0,wavefile_amp);
 950			wave_flag = 1;
 951			wavefile_ix = 0;
 952		}
 953
 954		length_factor = length_mod;
 955		if(frame1->frflags & FRFLAG_LEN_MOD)     // reduce effect of length mod
 956		{
 957			length_factor = (length_mod*4 + 256*3)/7;
 958		}
 959		len = (frame_length * samplerate)/1000;
 960		len = (len * length_factor)/256;
 961
 962		if(modulation >= 0)
 963		{
 964			if(frame1->frflags & FRFLAG_MODULATE)
 965			{
 966				modulation = 6;
 967			}
 968			if((frameix == n_frames-1) && (modn_flags & 0xf00))
 969				modulation |= modn_flags;   // before or after a glottal stop
 970		}
 971
 972		pitch_length += len;
 973		amp_length += len;
 974
 975		if(frame_length < 2)
 976		{
 977			last_frame = NULL;
 978			frame_length = frame2_length;
 979			frame1 = frame2;
 980		}
 981		else
 982		{
 983			last_wcmdq = wcmdq_tail;
 984
 985			if(modulation >= 0)
 986			{
 987				q = wcmdq[wcmdq_tail];
 988				q[0] = wcmd_spect;
 989				q[1] = len + (modulation << 16);
 990				q[2] = long(frame1);
 991				q[3] = long(frame2);
 992	
 993				WcmdqInc();
 994			}
 995			last_frame = frame1 = frame2;
 996			frame_length = frame2_length;
 997			total_len += len;
 998		}
 999	}
1000	return(total_len);
1001}  // end of Synthesize::DoSpect
1002
1003
1004static void DoMarker(int type, int char_posn, int length, int value)
1005{//=================================================================
1006// This could be used to return an index to the word currently being spoken
1007// Type 1=word, 2=sentence, 3=named marker, 4=play audio, 5=end
1008	wcmdq[wcmdq_tail][0] = WCMD_MARKER;
1009	wcmdq[wcmdq_tail][1] = type;
1010	wcmdq[wcmdq_tail][2] = (char_posn & 0xffffff) | (length << 24);
1011	wcmdq[wcmdq_tail][3] = value;
1012	WcmdqInc();
1013
1014}  // end of Synthesize::DoMarker
1015
1016
1017void DoVoiceChange(voice_t *v)
1018{//===========================
1019// allocate memory for a copy of the voice data, and free it in wavegenfill()
1020	voice_t *v2;
1021
1022	v2 = (voice_t *)malloc(sizeof(voice_t));
1023	memcpy(v2,v,sizeof(voice_t));
1024	wcmdq[wcmdq_tail][0] = WCMD_VOICE;
1025	wcmdq[wcmdq_tail][1] = (long)(v2);
1026	WcmdqInc();
1027}
1028
1029
1030static void DoEmbedded(int &embix, int sourceix)
1031{//=============================================
1032	// There were embedded commands in the text at this point
1033	unsigned int word;  // bit 7=last command for this word, bits 5,6 sign, bits 0-4 command
1034	unsigned int value;
1035	int command;
1036
1037	do {
1038		word = embedded_list[embix++];
1039		value = word >> 8;
1040		command = word & 0x7f;
1041
1042		switch(command & 0x1f)
1043		{
1044		case EMBED_S:   // speed
1045			SetEmbedded((command & 0x60) + EMBED_S2,value);   // adjusts embedded_value[EMBED_S2]
1046			SetSpeed(2);
1047			break;
1048
1049		case EMBED_I:   // play dynamically loaded wav data (sound icon)
1050			if((int)value < n_soundicon_tab)
1051			{
1052				if(soundicon_tab[value].length != 0)
1053				{
1054					DoPause(10,0);   // ensure a break in the speech
1055					wcmdq[wcmdq_tail][0] = WCMD_WAVE;
1056					wcmdq[wcmdq_tail][1] = soundicon_tab[value].length;
1057					wcmdq[wcmdq_tail][2] = (long)soundicon_tab[value].data + 44;  // skip WAV header
1058					wcmdq[wcmdq_tail][3] = 0x1500;   // 16 bit data, amp=21
1059					WcmdqInc();
1060				}
1061			}
1062			break;
1063
1064		case EMBED_M:   // named marker
1065			DoMarker(espeakEVENT_MARK, (sourceix & 0x7ff) + clause_start_char, 0, value);
1066			break;
1067
1068		case EMBED_U:   // play sound
1069			DoMarker(espeakEVENT_PLAY, count_characters+1, 0, value);  // always occurs at end of clause
1070			break;
1071
1072		default:
1073			DoPause(10,0);   // ensure a break in the speech
1074			wcmdq[wcmdq_tail][0] = WCMD_EMBEDDED;
1075			wcmdq[wcmdq_tail][1] = command;
1076			wcmdq[wcmdq_tail][2] = value;
1077			WcmdqInc();
1078			break;
1079		}
1080	} while ((word & 0x80) == 0);
1081}
1082
1083
1084void SwitchDictionary()
1085{//====================
1086}
1087
1088
1089int Generate(PHONEME_LIST *phoneme_list, int *n_ph, int resume)
1090{//============================================================
1091	static int  ix;
1092	static int  embedded_ix;
1093	static int  word_count;
1094	PHONEME_LIST *prev;
1095	PHONEME_LIST *next;
1096	PHONEME_LIST *next2;
1097	PHONEME_LIST *p;
1098	int  released;
1099	int  stress;
1100	int  modulation;
1101	int  pre_voiced;
1102	int  free_min;
1103	unsigned char *pitch_env=NULL;
1104	unsigned char *amp_env;
1105	PHONEME_TAB *ph;
1106	PHONEME_TAB *prev_ph;
1107	static int sourceix=0;
1108
1109#ifdef TEST_MBROLA
1110	if(mbrola_name[0] != 0)
1111		return(MbrolaGenerate(phoneme_list,n_ph,resume));
1112#endif
1113
1114	if(option_quiet)
1115		return(0);
1116
1117	if(resume == 0)
1118	{
1119		ix = 1;
1120		embedded_ix=0;
1121		word_count = 0;
1122		pitch_length = 0;
1123		amp_length = 0;
1124		last_frame = NULL;
1125		last_wcmdq = -1;
1126		syllable_start = wcmdq_tail;
1127		syllable_end = wcmdq_tail;
1128		syllable_centre = -1;
1129		last_pitch_cmd = -1;
1130		memset(vowel_transition,0,sizeof(vowel_transition));
1131	}
1132
1133	while(ix < (*n_ph))
1134	{
1135		p = &phoneme_list[ix];
1136
1137		if(p->type == phPAUSE)
1138			free_min = 5;
1139		else
1140		if(p->type != phVOWEL)
1141			free_min = 10;     // we need less Q space for non-vowels, and we need to generate phonemes after a vowel so that the pitch_length is filled in
1142		else
1143			free_min = MIN_WCMDQ;  // 22
1144
1145		if(WcmdqFree() <= free_min)
1146			return(1);  // wait
1147
1148		prev = &phoneme_list[ix-1];
1149		next = &phoneme_list[ix+1];
1150		next2 = &phoneme_list[ix+2];
1151
1152		if(p->synthflags & SFLAG_EMBEDDED)
1153		{
1154			DoEmbedded(embedded_ix, p->sourceix);
1155		}
1156
1157		if(p->newword)
1158		{
1159			if(translator->langopts.param[LOPT_WORD_MERGE] == 0)
1160				last_frame = NULL;
1161
1162			sourceix = (p->sourceix & 0x7ff) + clause_start_char;
1163
1164			if(p->newword & 4)
1165				DoMarker(espeakEVENT_SENTENCE, sourceix, 0, count_sentences);  // start of sentence
1166
1167//			if(p->newword & 2)
1168//				DoMarker(espeakEVENT_END, count_characters, 0, count_sentences);  // end of clause
1169
1170			if(p->newword & 1)
1171				DoMarker(espeakEVENT_WORD, sourceix, p->sourceix >> 11, clause_start_word + word_count++);
1172		}
1173
1174		EndAmplitude();
1175
1176		if(p->prepause > 0)
1177			DoPause(p->prepause,1);
1178
1179		if(option_phoneme_events && (p->type != phVOWEL))
1180		{
1181			// Note, for vowels, do the phoneme event after the vowel-start
1182			DoMarker(espeakEVENT_PHONEME, sourceix, 0, p->ph->mnemonic);
1183		}
1184
1185		switch(p->type)
1186		{
1187		case phPAUSE:
1188			DoPause(p->length,0);
1189			break;
1190
1191		case phSTOP:
1192			released = 0;
1193			if(next->type==phVOWEL) released = 1;
1194			if(next->type==phLIQUID && !next->newword) released = 1;
1195
1196			if(released)
1197				DoSample(p->ph,next->ph,2,0,0);
1198			else
1199				DoSample(p->ph,phoneme_tab[phonPAUSE],2,0,0);
1200			break;
1201
1202		case phFRICATIVE:
1203			if(p->synthflags & SFLAG_LENGTHEN)
1204				DoSample(p->ph,next->ph,2,p->length,0);  // play it twice for [s:] etc.
1205			DoSample(p->ph,next->ph,2,p->length,0);
1206			break;
1207
1208		case phVSTOP:
1209			pre_voiced = 0;
1210			if(next->type==phVOWEL)
1211			{
1212				DoAmplitude(p->amp,NULL);
1213				DoPitch(envelope_data[p->env],p->pitch1,p->pitch2);
1214				pre_voiced = 1;
1215			}
1216			else
1217			if((next->type==phLIQUID) && !next->newword)
1218			{
1219				DoAmplitude(next->amp,NULL);
1220				DoPitch(envelope_data[next->env],next->pitch1,next->pitch2);
1221				pre_voiced = 1;
1222			}
1223			else
1224			{
1225				if(last_pitch_cmd < 0)
1226				{
1227					DoAmplitude(next->amp,NULL);
1228					DoPitch(envelope_data[p->env],p->pitch1,p->pitch2);
1229				}
1230			}
1231
1232			if((prev->type==phVOWEL) || (prev->ph->phflags & phVOWEL2))
1233			{
1234				// a period of voicing before the release
1235				DoSpect(p->ph,phoneme_tab[phonSCHWA],next->ph,1,p,0);
1236				if(p->synthflags & SFLAG_LENGTHEN)
1237				{
1238					DoPause(20,0);
1239					DoSpect(p->ph,phoneme_tab[phonSCHWA],next->ph,1,p,0);
1240				}
1241			}
1242			else
1243			{
1244				if(p->synthflags & SFLAG_LENGTHEN)
1245				{
1246					DoPause(50,0);
1247				}
1248			}
1249
1250			if(pre_voiced)
1251			{
1252				// followed by a vowel, or liquid + vowel
1253				StartSyllable();
1254				DoSpect(p->ph,prev->ph,next->ph,2,p,0);
1255			}
1256			else
1257			{
1258//				if((prev->type != phVOWEL) && ((prev->ph->phflags & phVOICED)==0) && ((next->ph->phflags & phVOICED)==0))
1259//					DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE_SHORT],2,p,0);
1260//				else
1261					DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE],2,p,0);
1262//				DoSpect(p->ph,prev->ph,next->ph,2,p,0);
1263			}
1264			break;
1265
1266		case phVFRICATIVE:
1267			if(next->type==phVOWEL)
1268			{
1269				DoAmplitude(p->amp,NULL);
1270				DoPitch(envelope_data[p->env],p->pitch1,p->pitch2);
1271			}
1272			else
1273			if(next->type==phLIQUID)
1274			{
1275				DoAmplitude(next->amp,NULL);
1276				DoPitch(envelope_data[next->env],next->pitch1,next->pitch2);
1277			}
1278			else
1279			{
1280				if(last_pitch_cmd < 0)
1281				{
1282					DoAmplitude(p->amp,NULL);
1283					DoPitch(envelope_data[p->env],p->pitch1,p->pitch2);
1284				}
1285			}
1286
1287			if((next->type==phVOWEL) || ((next->type==phLIQUID)) && (next->newword==0))  // ?? test 14.Aug.2007
1288			{
1289				StartSyllable();
1290				if(p->synthflags & SFLAG_LENGTHEN)
1291					DoSpect(p->ph,prev->ph,next->ph,2,p,0);
1292				DoSpect(p->ph,prev->ph,next->ph,2,p,0);
1293			}
1294			else
1295			{
1296				if(p->synthflags & SFLAG_LENGTHEN)
1297					DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE],2,p,0);
1298				DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE],2,p,0);
1299			}
1300			break;
1301
1302		case phNASAL:
1303			if(!(p->synthflags & SFLAG_SEQCONTINUE))
1304			{
1305				DoAmplitude(p->amp,NULL);
1306				DoPitch(envelope_data[p->env],p->pitch1,p->pitch2);
1307			}
1308
1309			if(prev->type==phNASAL)
1310			{
1311				last_frame = NULL;
1312			}
1313
1314			if(next->type==phVOWEL)
1315			{
1316				StartSyllable();
1317				DoSpect(p->ph,prev->ph,next->ph,1,p,0);
1318			}
1319			else
1320			if(prev->type==phVOWEL && (p->synthflags & SFLAG_SEQCONTINUE))
1321			{
1322				DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE],2,p,0);
1323			}
1324			else
1325			{
1326				last_frame = NULL;  // only for nasal ?
1327				if(next->type == phLIQUID)
1328					DoSpect(p->ph,prev->ph,phoneme_tab[phonSONORANT],2,p,0);
1329				else
1330					DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE],2,p,0);
1331				last_frame = NULL;
1332			}
1333
1334			break;
1335
1336		case phLIQUID:
1337			modulation = 0;
1338			if(p->ph->phflags & phTRILL)
1339				modulation = 5;
1340
1341			prev_ph = prev->ph;
1342//			if(p->newword)
1343//				prev_ph = phoneme_tab[phonPAUSE];    // pronounce fully at the start of a word
1344
1345			if(!(p->synthflags & SFLAG_SEQCONTINUE))
1346			{
1347				DoAmplitude(p->amp,NULL);
1348				DoPitch(envelope_data[p->env],p->pitch1,p->pitch2);
1349			}
1350
1351			if(prev->type==phNASAL)
1352			{
1353				last_frame = NULL;
1354			}
1355
1356			if(next->type==phVOWEL)
1357			{
1358				StartSyllable();
1359				DoSpect(p->ph,prev_ph,next->ph,1,p,modulation);  // (,)r
1360			}
1361			else
1362			if(prev->type==phVOWEL && (p->synthflags & SFLAG_SEQCONTINUE))
1363			{
1364				DoSpect(p->ph,prev_ph,next->ph,1,p,modulation);
1365			}
1366			else
1367			{
1368				DoSpect(p->ph,prev_ph,next->ph,1,p,modulation);
1369			}
1370
1371			break;
1372
1373		case phVOWEL:
1374			ph = p->ph;
1375			stress = p->tone & 0xf;
1376
1377			// vowel transition from the preceding phoneme
1378			vowel_transition0 = vowel_transition[0];
1379			vowel_transition1 = vowel_transition[1];
1380
1381			pitch_env = envelope_data[p->env];
1382			amp_env = NULL;
1383			if(p->tone_ph != 0)
1384			{
1385				pitch_env = LookupEnvelope(phoneme_tab[p->tone_ph]->spect);
1386				amp_env = LookupEnvelope(phoneme_tab[p->tone_ph]->after);
1387			}
1388
1389			StartSyllable();
1390
1391			modulation = 2;
1392			if(stress <= 1)
1393				modulation = 1;  // 16ths
1394			else
1395			if(stress >= 7)
1396				modulation = 3;
1397
1398			if(prev->type == phVSTOP || prev->type == phVFRICATIVE)
1399			{
1400				DoAmplitude(p->amp,amp_env);
1401				DoPitch(pitch_env,p->pitch1,p->pitch2);  // don't use prevocalic rising tone
1402				DoSpect(ph,prev->ph,next->ph,1,p,modulation);
1403			}
1404			else
1405			if(prev->type==phLIQUID || prev->type==phNASAL)
1406			{
1407				DoAmplitude(p->amp,amp_env);
1408				DoSpect(ph,prev->ph,next->ph,1,p,modulation);   // continue with pre-vocalic rising tone
1409				DoPitch(pitch_env,p->pitch1,p->pitch2);
1410			}
1411			else
1412			{
1413				if(!(p->synthflags & SFLAG_SEQCONTINUE))
1414				{
1415					DoAmplitude(p->amp,amp_env);
1416					DoPitch(pitch_env,p->pitch1,p->pitch2);
1417				}
1418
1419				DoSpect(ph,prev->ph,next->ph,1,p,modulation);
1420			}
1421
1422			if(option_phoneme_events)
1423			{
1424				DoMarker(espeakEVENT_PHONEME, sourceix, 0, p->ph->mnemonic);
1425			}
1426
1427			DoSpect(p->ph,prev->ph,next->ph,2,p,modulation);
1428
1429			memset(vowel_transition,0,sizeof(vowel_transition));
1430			break;
1431		}
1432		ix++;
1433	}
1434	EndPitch(1);
1435	if(*n_ph > 0)
1436	{
1437		DoMarker(espeakEVENT_END, count_characters, 0, count_sentences);  // end of clause
1438		*n_ph = 0;
1439	}
1440
1441	return(0);  // finished the phoneme list
1442}  //  end of Generate
1443
1444
1445
1446
1447static int timer_on = 0;
1448static int paused = 0;
1449
1450int SynthOnTimer()
1451{//===============
1452	if(!timer_on)
1453	{
1454		return(WavegenCloseSound());
1455	}
1456
1457	do {
1458		if(Generate(phoneme_list,&n_phoneme_list,1)==0)
1459		{
1460			SpeakNextClause(NULL,NULL,1);
1461		}
1462	} while(skipping_text);
1463
1464	return(0);
1465}
1466
1467
1468int SynthStatus()
1469{//==============
1470	return(timer_on | paused);
1471}
1472
1473
1474
1475int SpeakNextClause(FILE *f_in, const void *text_in, int control)
1476{//==============================================================
1477// Speak text from file (f_in) or memory (text_in)
1478// control 0: start
1479//    either f_in or text_in is set, the other must be NULL
1480
1481// The other calls have f_in and text_in = NULL
1482// control 1: speak next text
1483//         2: stop
1484//         3: pause (toggle)
1485//         4: is file being read (0=no, 1=yes)
1486//         5: interrupt and flush current text.
1487
1488	int clause_tone;
1489	char *voice_change;
1490	static FILE *f_text=NULL;
1491	static const void *p_text=NULL;
1492
1493	if(control == 4)
1494	{
1495		if((f_text == NULL) && (p_text == NULL))
1496			return(0);
1497		else
1498			return(1);
1499	}
1500
1501	if(control == 2)
1502	{
1503		// stop speaking
1504		timer_on = 0;
1505		p_text = NULL;
1506		if(f_text != NULL)
1507		{
1508			fclose(f_text);
1509			f_text=NULL;
1510		}
1511		n_phoneme_list = 0;
1512		WcmdqStop();
1513		return(0);
1514	}
1515
1516	if(control == 3)
1517	{
1518		// toggle pause
1519		if(paused == 0)
1520		{
1521			timer_on = 0;
1522			paused = 2;
1523		}
1524		else
1525		{
1526			WavegenOpenSound();
1527			timer_on = 1;
1528			paused = 0;
1529			Generate(phoneme_list,&n_phoneme_list,0);   // re-start from beginning of clause
1530		}
1531		return(0);
1532	}
1533
1534	if(control == 5)
1535	{
1536		// stop speaking, but continue looking for text
1537		n_phoneme_list = 0;
1538		WcmdqStop();
1539		return(0);
1540	}
1541
1542	if((f_in != NULL) || (text_in != NULL))
1543	{
1544		f_text = f_in;
1545		p_text = text_in;
1546		timer_on = 1;
1547		paused = 0;
1548	}
1549
1550	if((f_text==NULL) && (p_text==NULL))
1551	{
1552		skipping_text = 0;
1553		timer_on = 0;
1554		return(0);
1555	}
1556
1557	if((f_text != NULL) && feof(f_text))
1558	{
1559		timer_on = 0;
1560		fclose(f_text);
1561		f_text=NULL;
1562		return(0);
1563	}
1564
1565	if(current_phoneme_table != voice->phoneme_tab_ix)
1566	{
1567		SelectPhonemeTable(voice->phoneme_tab_ix);
1568	}
1569
1570	// read the next clause from the input text file, translate it, and generate
1571	// entries in the wavegen command queue
1572	p_text = translator->TranslateClause(f_text,p_text,&clause_tone,&voice_change);
1573
1574	translator->CalcPitches(clause_tone);
1575	translator->CalcLengths();
1576
1577	translator->GetTranslatedPhonemeString(translator->phon_out,sizeof(translator->phon_out));
1578	if(option_phonemes > 0)
1579	{
1580		fprintf(f_trans,"%s\n",translator->phon_out);
1581
1582		if(!iswalpha(0x010d))
1583		{
1584			// check that c-caron is recognized as an alphabetic character
1585			fprintf(stderr,"Warning: Accented letters are not recognized, eg: U+010D\nSet LC_CTYPE to a UTF-8 locale\n");
1586		}
1587	}
1588	if(phoneme_callback != NULL)
1589	{
1590		phoneme_callback(translator->phon_out);
1591	}
1592
1593
1594	if(skipping_text)
1595	{
1596		n_phoneme_list = 0;
1597		return(1);
1598	}
1599
1600	if(mbrola_name[0] != 0)
1601	{
1602#ifdef USE_MBROLA_LIB
1603		MbrolaTranslate(phoneme_list,n_phoneme_list,NULL);
1604#else
1605		MbrolaTranslate(phoneme_list,n_phoneme_list,stdout);
1606#endif
1607	}
1608
1609	Generate(phoneme_list,&n_phoneme_list,0);
1610	WavegenOpenSound();
1611
1612	if(voice_change != NULL)
1613	{
1614		// voice change at the end of the clause (i.e. clause was terminated by a voice change)
1615		new_voice = LoadVoiceVariant(voice_change,0); // add a Voice instruction to wavegen at the end of the clause
1616	}
1617
1618	if(new_voice)
1619	{
1620		// finished the current clause, now change the voice if there was an embedded
1621		// change voice command at the end of it (i.e. clause was broken at the change voice command)
1622		DoVoiceChange(voice);
1623		new_voice = NULL;
1624	}
1625
1626	return(1);
1627}  //  end of SpeakNextClause
1628