1 /*
2  * Copyright (C) 2005 to 2014 by Jonathan Duddington
3  * email: jonsd@users.sourceforge.net
dasum_(integer * n,double * dx,integer * incx)4  * Copyright (C) 2013-2017 Reece H. Dunn
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, see: <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "config.h"
21 
22 #include <ctype.h>
23 #include <errno.h>
24 #include <stdarg.h>
25 //#include <stdbool.h>
26 #include <stdint.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/stat.h>
31 #include <time.h>
32 #include <unistd.h>
33 
34 #include "espeak_ng.h"
35 #include "speak_lib.h"
36 #include "encoding.h"
37 
38 #include "error.h"
39 #include "speech.h"
40 #include "synthesize.h"
41 #include "translate.h"
42 
43 typedef struct {
44 	unsigned int value;
45 	char *name;
46 } NAMETAB;
47 
48 NAMETAB *manifest = NULL;
49 int n_manifest;
50 char phsrc[sizeof(path_home)+40]; // Source: path to the 'phonemes' source file.
51 
52 extern ESPEAK_NG_API int utf8_in(int *c, const char *buf);
53 
54 
55 typedef struct {
56 	const char *mnem;
57 	int type;
58 	int data;
59 } keywtab_t;
60 
61 #define k_AND     1
62 #define k_OR      2
63 #define k_THEN    3
64 #define k_NOT     4
65 
66 #define kTHISSTRESS 0x800
67 
68 // keyword types
69 enum {
70 	tPHONEME_TYPE = 1,
71 	tPHONEME_FLAG,
72 	tTRANSITION,
73 	tSTATEMENT,
74 	tINSTRN1,
75 	tWHICH_PHONEME,
76 	tTEST,
77 };
78 
79 static keywtab_t k_conditions[] = {
80 	{ "AND",  0, k_AND },
81 	{ "OR",   0, k_OR },
82 	{ "THEN", 0, k_THEN },
83 	{ "NOT",  0, k_NOT },
84 
85 	{ "prevPh",    tWHICH_PHONEME,  0 },
86 	{ "thisPh",    tWHICH_PHONEME,  1 },
87 	{ "nextPh",    tWHICH_PHONEME,  2 },
88 	{ "next2Ph",   tWHICH_PHONEME,  3 },
89 	{ "nextPhW",   tWHICH_PHONEME,  4 },
90 	{ "prevPhW",   tWHICH_PHONEME,  5 },
daxpy_(integer * n,double * da,double * dx,integer * incx,double * dy,integer * incy)91 	{ "next2PhW",  tWHICH_PHONEME,  6 },
92 	{ "nextVowel", tWHICH_PHONEME,  7 },
93 	{ "prevVowel", tWHICH_PHONEME,  8 },
94 	{ "next3PhW",  tWHICH_PHONEME,  9 },
95 	{ "prev2PhW",  tWHICH_PHONEME, 10 },
96 
97 	{ "PreVoicing",  tTEST, 0xf01 },
98 	{ "KlattSynth",  tTEST, 0xf02 },
99 	{ "MbrolaSynth", tTEST, 0xf03 },
100 
101 	{ NULL, 0, 0 }
102 };
103 
104 static keywtab_t k_properties[] = {
105 	{ "isPause",      0, CONDITION_IS_PHONEME_TYPE | phPAUSE },
106 	{ "isVowel",      0, CONDITION_IS_PHONEME_TYPE | phVOWEL },
107 	{ "isNasal",      0, CONDITION_IS_PHONEME_TYPE | phNASAL },
108 	{ "isLiquid",     0, CONDITION_IS_PHONEME_TYPE | phLIQUID },
109 	{ "isUStop",      0, CONDITION_IS_PHONEME_TYPE | phSTOP },
110 	{ "isVStop",      0, CONDITION_IS_PHONEME_TYPE | phVSTOP },
111 	{ "isVFricative", 0, CONDITION_IS_PHONEME_TYPE | phVFRICATIVE },
112 
113 	{ "isPalatal",  0, CONDITION_IS_PHFLAG_SET | phFLAGBIT_PALATAL },
114 	{ "isLong",     0, CONDITION_IS_PHFLAG_SET | phFLAGBIT_LONG },
115 	{ "isRhotic",   0, CONDITION_IS_PHFLAG_SET | phFLAGBIT_RHOTIC },
116 	{ "isSibilant", 0, CONDITION_IS_PHFLAG_SET | phFLAGBIT_SIBILANT },
117 	{ "isFlag1",    0, CONDITION_IS_PHFLAG_SET | phFLAGBIT_FLAG1 },
118 	{ "isFlag2",    0, CONDITION_IS_PHFLAG_SET | phFLAGBIT_FLAG2 },
119 
120 	{ "isVelar", 0, CONDITION_IS_PLACE_OF_ARTICULATION | phPLACE_VELAR },
121 
122 	{ "isDiminished",  0, CONDITION_IS_OTHER | STRESS_IS_DIMINISHED },
123 	{ "isUnstressed",  0, CONDITION_IS_OTHER | STRESS_IS_UNSTRESSED },
124 	{ "isNotStressed", 0, CONDITION_IS_OTHER | STRESS_IS_NOT_STRESSED },
125 	{ "isStressed",    0, CONDITION_IS_OTHER | STRESS_IS_SECONDARY },
126 	{ "isMaxStress",   0, CONDITION_IS_OTHER | STRESS_IS_PRIMARY },
127 
128 	{ "isPause2",           0, CONDITION_IS_OTHER | isBreak },
129 	{ "isWordStart",        0, CONDITION_IS_OTHER | isWordStart },
130 	{ "isWordEnd",          0, CONDITION_IS_OTHER | isWordEnd },
131 	{ "isAfterStress",      0, CONDITION_IS_OTHER | isAfterStress },
132 	{ "isNotVowel",         0, CONDITION_IS_OTHER | isNotVowel },
133 	{ "isFinalVowel",       0, CONDITION_IS_OTHER | isFinalVowel },
134 	{ "isVoiced",           0, CONDITION_IS_OTHER | isVoiced },
135 	{ "isFirstVowel",       0, CONDITION_IS_OTHER | isFirstVowel },
136 	{ "isSecondVowel",      0, CONDITION_IS_OTHER | isSecondVowel },
137 	{ "isTranslationGiven", 0, CONDITION_IS_OTHER | isTranslationGiven },
138 
139 	{ NULL, 0, 0 }
140 };
141 
142 enum {
143 	kPHONEMESTART = 1,
144 	kUTF8_BOM,
145 	kPROCEDURE,
146 	kENDPHONEME,
147 	kENDPROCEDURE,
148 	kPHONEMETABLE,
149 	kINCLUDE,
150 	kIMPORT_PH,
151 
152 	kSTARTTYPE,
153 	kENDTYPE,
154 	kSTRESSTYPE,
155 	kVOICINGSWITCH,
156 
157 	kIF,
158 	kELSE,
159 	kELIF,
160 	kENDIF,
161 	kCALLPH,
162 
163 	kSWITCH_PREVVOWEL,
164 	kSWITCH_NEXTVOWEL,
165 	kENDSWITCH,
166 
167 	kFMT,
168 	kWAV,
169 	kVOWELSTART,
170 	kVOWELENDING,
171 	kANDWAV,
172 
173 	kVOWELIN,
174 	kVOWELOUT,
175 	kTONESPEC,
176 
177 	kRETURN,
178 	kCONTINUE,
179 };
180 
181 enum {
182 	kTUNE = 1,
183 	kENDTUNE,
dcopy_(integer * n,double * dx,integer * incx,double * dy,integer * incy)184 	kTUNE_PREHEAD,
185 	kTUNE_ONSET,
186 	kTUNE_HEAD,
187 	kTUNE_HEADENV,
188 	kTUNE_HEADEXTEND,
189 	kTUNE_HEADLAST,
190 	kTUNE_NUCLEUS0,
191 	kTUNE_NUCLEUS1,
192 	kTUNE_SPLIT,
193 };
194 
195 static unsigned const char utf8_bom[] = { 0xef, 0xbb, 0xbf, 0 };
196 
197 static keywtab_t k_intonation[] = {
198 	{ "tune",       0, kTUNE },
199 	{ "endtune",    0, kENDTUNE },
200 	{ "prehead",    0, kTUNE_PREHEAD },
201 	{ "onset",      0, kTUNE_ONSET },
202 	{ "head",       0, kTUNE_HEAD },
203 	{ "headenv",    0, kTUNE_HEADENV },
204 	{ "headextend", 0, kTUNE_HEADEXTEND },
205 	{ "headlast",   0, kTUNE_HEADLAST },
206 	{ "nucleus0",   0, kTUNE_NUCLEUS0 },
207 	{ "nucleus",    0, kTUNE_NUCLEUS1 },
208 	{ "split",      0, kTUNE_SPLIT },
209 
210 	{ NULL, 0, -1 }
211 };
212 
213 static keywtab_t keywords[] = {
214 	{ "liquid",  tPHONEME_TYPE, phLIQUID },
215 	{ "pause",   tPHONEME_TYPE, phPAUSE },
216 	{ "stress",  tPHONEME_TYPE, phSTRESS },
217 	{ "virtual", tPHONEME_TYPE, phVIRTUAL },
218 	{ "delete_phoneme", tPHONEME_TYPE, phDELETED },
219 
220 	// keywords
221 	{ "phonemetable",         tSTATEMENT, kPHONEMETABLE },
222 	{ "include",              tSTATEMENT, kINCLUDE },
223 	{ (const char *)utf8_bom, tSTATEMENT, kUTF8_BOM },
224 
225 	{ "phoneme",        tSTATEMENT, kPHONEMESTART },
226 	{ "procedure",      tSTATEMENT, kPROCEDURE },
227 	{ "endphoneme",     tSTATEMENT, kENDPHONEME },
228 	{ "endprocedure",   tSTATEMENT, kENDPROCEDURE },
229 	{ "import_phoneme", tSTATEMENT, kIMPORT_PH },
230 	{ "stress_type",    tSTATEMENT, kSTRESSTYPE },
231 	{ "starttype",      tSTATEMENT, kSTARTTYPE },
232 	{ "endtype",        tSTATEMENT, kENDTYPE },
233 	{ "voicingswitch",  tSTATEMENT, kVOICINGSWITCH },
234 
235 	{ "IF",     tSTATEMENT, kIF },
236 	{ "ELSE",   tSTATEMENT, kELSE },
237 	{ "ELIF",   tSTATEMENT, kELIF },
238 	{ "ELSEIF", tSTATEMENT, kELIF },  // same as ELIF
239 	{ "ENDIF",  tSTATEMENT, kENDIF },
240 	{ "CALL",   tSTATEMENT, kCALLPH },
241 	{ "RETURN", tSTATEMENT, kRETURN },
242 
243 	{ "PrevVowelEndings", tSTATEMENT, kSWITCH_PREVVOWEL },
244 	{ "NextVowelStarts",  tSTATEMENT, kSWITCH_NEXTVOWEL },
245 	{ "EndSwitch",        tSTATEMENT, kENDSWITCH },
246 
247 	{ "Tone",        tSTATEMENT, kTONESPEC },
248 	{ "FMT",         tSTATEMENT, kFMT },
249 	{ "WAV",         tSTATEMENT, kWAV },
250 	{ "VowelStart",  tSTATEMENT, kVOWELSTART },
251 	{ "VowelEnding", tSTATEMENT, kVOWELENDING },
252 	{ "addWav",      tSTATEMENT, kANDWAV },
253 
254 	{ "Vowelin",  tSTATEMENT, kVOWELIN },
255 	{ "Vowelout", tSTATEMENT, kVOWELOUT },
256 	{ "Continue", tSTATEMENT, kCONTINUE },
257 
258 	{ "ChangePhoneme",       tINSTRN1, i_CHANGE_PHONEME },
259 	{ "ChangeNextPhoneme",   tINSTRN1, i_REPLACE_NEXT_PHONEME },
260 	{ "InsertPhoneme",       tINSTRN1, i_INSERT_PHONEME },
261 	{ "AppendPhoneme",       tINSTRN1, i_APPEND_PHONEME },
262 	{ "IfNextVowelAppend",   tINSTRN1, i_APPEND_IFNEXTVOWEL },
263 	{ "ChangeIfDiminished",  tINSTRN1, i_CHANGE_IF | STRESS_IS_DIMINISHED },
264 	{ "ChangeIfUnstressed",  tINSTRN1, i_CHANGE_IF | STRESS_IS_UNSTRESSED },
265 	{ "ChangeIfNotStressed", tINSTRN1, i_CHANGE_IF | STRESS_IS_NOT_STRESSED },
266 	{ "ChangeIfStressed",    tINSTRN1, i_CHANGE_IF | STRESS_IS_SECONDARY },
267 	{ "ChangeIfStressed",    tINSTRN1, i_CHANGE_IF | STRESS_IS_PRIMARY },
268 
269 	{ "PauseBefore", tINSTRN1, i_PAUSE_BEFORE },
270 	{ "PauseAfter",  tINSTRN1, i_PAUSE_AFTER },
271 	{ "length",      tINSTRN1, i_SET_LENGTH },
272 	{ "LongLength",  tINSTRN1, i_LONG_LENGTH },
273 	{ "LengthAdd",   tINSTRN1, i_ADD_LENGTH },
274 	{ "lengthmod",   tINSTRN1, i_LENGTH_MOD },
275 	{ "ipa",         tINSTRN1, i_IPA_NAME },
276 
ddot_(integer * n,double * dx,integer * incx,double * dy,integer * incy)277 	// flags
278 	{ "unstressed",   tPHONEME_FLAG, phUNSTRESSED },
279 	{ "nolink",       tPHONEME_FLAG, phNOLINK },
280 	{ "brkafter",     tPHONEME_FLAG, phBRKAFTER },
281 	{ "rhotic",       tPHONEME_FLAG, phRHOTIC },
282 	{ "lengthenstop", tPHONEME_FLAG, phLENGTHENSTOP },
283 	{ "nopause",      tPHONEME_FLAG, phNOPAUSE },
284 	{ "prevoice",     tPHONEME_FLAG, phPREVOICE },
285 
286 	{ "flag1", tPHONEME_FLAG, phFLAG1 },
287 	{ "flag2", tPHONEME_FLAG, phFLAG2 },
288 
289 	// vowel transition attributes
290 	{ "len=",   tTRANSITION,  1 },
291 	{ "rms=",   tTRANSITION,  2 },
292 	{ "f1=",    tTRANSITION,  3 },
293 	{ "f2=",    tTRANSITION,  4 },
294 	{ "f3=",    tTRANSITION,  5 },
295 	{ "brk",    tTRANSITION,  6 },
296 	{ "rate",   tTRANSITION,  7 },
297 	{ "glstop", tTRANSITION,  8 },
298 	{ "lenadd", tTRANSITION,  9 },
299 	{ "f4",     tTRANSITION, 10 },
300 	{ "gpaus",  tTRANSITION, 11 },
301 	{ "colr=",  tTRANSITION, 12 },
302 	{ "amp=",   tTRANSITION, 13 },  // set rms of 1st frame as fraction of rms of 2nd frame  (1/30ths)
303 
304 	{ NULL, 0, -1 }
305 };
306 
307 static keywtab_t *keyword_tabs[] = {
308 	keywords, k_conditions, k_properties, k_intonation
309 };
310 
311 static PHONEME_TAB *phoneme_out;
312 
313 static int n_phcodes_list[N_PHONEME_TABS];
314 static PHONEME_TAB_LIST phoneme_tab_list2[N_PHONEME_TABS];
315 static PHONEME_TAB *phoneme_tab2;
316 static int phoneme_flags;
317 
318 #define N_PROCS 50
319 int n_procs;
320 int proc_addr[N_PROCS];
321 char proc_names[40][N_PROCS];
322 
323 #define MAX_PROG_BUF 2000
324 USHORT *prog_out;
325 USHORT *prog_out_max;
326 USHORT prog_buf[MAX_PROG_BUF+20];
327 
328 static espeak_ng_STATUS ReadPhondataManifest(espeak_ng_ERROR_CONTEXT *context)
329 {
330 	// Read the phondata-manifest file
331 	FILE *f;
332 	int n_lines = 0;
333 	int ix;
334 	char *p;
335 	unsigned int value;
336 	char buf[sizeof(path_home)+40];
337 	char name[120];
338 
339 	sprintf(buf, "%s%c%s", path_home, PATHSEP, "phondata-manifest");
340 	if ((f = fopen(buf, "r")) == NULL)
341 		return create_file_error_context(context, static_cast<espeak_ng_STATUS> (errno), buf);
342 
343 	while (fgets(buf, sizeof(buf), f) != NULL)
344 		n_lines++;
345 
346 	rewind(f);
347 
348 	if (manifest != NULL) {
349 		for (ix = 0; ix < n_manifest; ix++)
350 			free(manifest[ix].name);
351 	}
352 
353 	if (n_lines == 0) {
354 		fclose(f);
355 		return ENS_EMPTY_PHONEME_MANIFEST;
356 	}
357 
358 	NAMETAB *new_manifest = (NAMETAB *)realloc(manifest, n_lines * sizeof(NAMETAB));
359 	if (new_manifest == NULL) {
360 		fclose(f);
361 		free(manifest);
362 		return static_cast<espeak_ng_STATUS> (ENOMEM);
363 	} else
364 		manifest = new_manifest;
365 
366 	n_manifest = 0;
367 	while (fgets(buf, sizeof(buf), f) != NULL) {
368 		if (!isalpha(buf[0]))
369 			continue;
370 
371 		if (sscanf(&buf[2], "%x %s", &value, name) == 2) {
372 			if ((p = (char *)malloc(strlen(name)+1)) != NULL) {
dgbmv_(const char * trans,integer * m,integer * n,integer * kl,integer * ku,double * alpha,double * a,integer * lda,double * x,integer * incx,double * beta,double * y,integer * incy)373 				strcpy(p, name);
374 				manifest[n_manifest].value = value;
375 				manifest[n_manifest].name = p;
376 				n_manifest++;
377 			}
378 		}
379 	}
380 	fclose(f);
381 
382 	return ENS_OK;
383 }
384 
385 static int n_phoneme_tabs;
386 static int n_phcodes;
387 
388 // outout files
389 static FILE *f_phdata;
390 static FILE *f_phindex;
391 static FILE *f_phtab;
392 static FILE *f_phcontents;
393 static FILE *f_errors = NULL;
394 static FILE *f_prog_log = NULL;
395 static FILE *f_report;
396 
397 static FILE *f_in;
398 static int f_in_linenum;
399 static int f_in_displ;
400 
401 static int linenum;
402 static int count_references = 0;
403 static int duplicate_references = 0;
404 static int count_frames = 0;
405 static int error_count = 0;
406 static int resample_count = 0;
407 static int resample_fails = 0;
408 static int then_count = 0;
409 static int after_if = 0;
410 
411 static char current_fname[80];
412 
413 static int markers_used[8];
414 
415 typedef struct {
416 	void *link;
417 	int value;
418 	int ph_mnemonic;
419 	short ph_table;
420 	char string[1];
421 } REF_HASH_TAB;
422 
423 static REF_HASH_TAB *ref_hash_tab[256];
424 
425 #define N_ENVELOPES  30
426 int n_envelopes = 0;
427 char envelope_paths[N_ENVELOPES][80];
428 unsigned char envelope_dat[N_ENVELOPES][ENV_LEN];
429 
430 typedef struct {
431 	FILE *file;
432 	int linenum;
433 	char fname[80];
434 } STACK;
435 
436 #define N_STACK  12
437 int stack_ix;
438 STACK stack[N_STACK];
439 
440 #define N_IF_STACK 12
441 int if_level;
442 typedef struct {
443 	USHORT *p_then;
444 	USHORT *p_else;
445 	int returned;
446 } IF_STACK;
447 IF_STACK if_stack[N_IF_STACK];
448 
449 enum {
450 	tENDFILE = 1,
451 	tSTRING,
452 	tNUMBER,
453 	tSIGNEDNUMBER,
454 	tPHONEMEMNEM,
455 	tOPENBRACKET,
456 	tKEYWORD,
457 	tCONDITION,
458 	tPROPERTIES,
459 	tINTONATION,
460 };
461 
462 int item_type;
463 int item_terminator;
464 #define N_ITEM_STRING 256
465 char item_string[N_ITEM_STRING];
466 
467 static int ref_sorter(char **a, char **b)
468 {
469 	int ix;
470 
471 	REF_HASH_TAB *p1 = (REF_HASH_TAB *)(*a);
472 	REF_HASH_TAB *p2 = (REF_HASH_TAB *)(*b);
473 
474 	ix = strcoll(p1->string, p2->string);
475 	if (ix != 0)
476 		return ix;
477 
478 	ix = p1->ph_table - p2->ph_table;
479 	if (ix != 0)
480 		return ix;
481 
482 	return p1->ph_mnemonic - p2->ph_mnemonic;
483 }
484 
485 static void CompileReport(void)
486 {
487 	int ix;
488 	int hash;
489 	int n;
490 	REF_HASH_TAB *p;
491 	REF_HASH_TAB **list;
492 	const char *data_path;
493 	int prev_table;
494 	int procedure_num;
495 	int prev_mnemonic;
496 
497 	if (f_report == NULL)
498 		return;
499 
500 	// make a list of all the references and sort it
501 	list = (REF_HASH_TAB **)malloc((count_references)* sizeof(REF_HASH_TAB *));
502 	if (list == NULL)
503 		return;
504 
505 	fprintf(f_report, "\n%d phoneme tables\n", n_phoneme_tabs);
506 	fprintf(f_report, "          new total\n");
507 	for (ix = 0; ix < n_phoneme_tabs; ix++)
508 		fprintf(f_report, "%8s %3d %4d\n", phoneme_tab_list2[ix].name, phoneme_tab_list2[ix].n_phonemes, n_phcodes_list[ix]+1);
509 	fputc('\n', f_report);
510 
511 	fprintf(f_report, "Data file      Used by\n");
512 	ix = 0;
513 	for (hash = 0; (hash < 256) && (ix < count_references); hash++) {
514 		p = ref_hash_tab[hash];
515 		while (p != NULL) {
516 			list[ix++] = p;
517 			p = (REF_HASH_TAB *)(p->link);
518 		}
519 	}
520 	n = ix;
521 	qsort((void *)list, n, sizeof(REF_HASH_TAB *), (int (*)(const void *, const void *))ref_sorter);
522 
523 	data_path = "";
524 	prev_mnemonic = 0;
525 	prev_table = 0;
526 	for (ix = 0; ix < n; ix++) {
527 		int j = 0;
528 
529 		if (strcmp(list[ix]->string, data_path) != 0) {
530 			data_path = list[ix]->string;
531 			j = strlen(data_path);
532 			fprintf(f_report, "%s", data_path);
533 		} else if ((list[ix]->ph_table == prev_table) && (list[ix]->ph_mnemonic == prev_mnemonic))
534 			continue; // same phoneme, don't list twice
535 
536 		while (j < 14) {
537 			fputc(' ', f_report); // pad filename with spaces
538 			j++;
539 		}
540 
541 		prev_mnemonic = list[ix]->ph_mnemonic;
542 		if ((prev_mnemonic >> 24) == 'P') {
543 			// a procedure, not a phoneme
544 			procedure_num = atoi(WordToString(prev_mnemonic));
545 			fprintf(f_report, "  %s  %s", phoneme_tab_list2[prev_table = list[ix]->ph_table].name, proc_names[procedure_num]);
546 		} else
547 			fprintf(f_report, "  [%s] %s", WordToString(prev_mnemonic), phoneme_tab_list2[prev_table = list[ix]->ph_table].name);
548 		fputc('\n', f_report);
549 	}
550 
551 	for (ix = 0; ix < n; ix++) {
552 		free(list[ix]);
553 		list[ix] = NULL;
554 	}
555 
556 	free(list);
557 	list = NULL;
558 }
559 
560 static void error(const char *format, ...)
561 {
562 	va_list args;
563 	va_start(args, format);
564 
565 	fprintf(f_errors, "%s(%d): ", current_fname, linenum-1);
566 	vfprintf(f_errors, format, args);
567 	fprintf(f_errors, "\n");
568 	error_count++;
569 
570 	va_end(args);
571 }
572 
573 static void error_from_status(espeak_ng_STATUS status, const char *context)
574 {
575 	char message[512];
576 	espeak_ng_GetStatusCodeMessage(status, message, sizeof(message));
577 	if (context)
578 		error("%s: '%s'.", message, context);
579 	else
580 		error("%s.", message);
581 }
582 
583 static unsigned int StringToWord(const char *string)
584 {
585 	// Pack 4 characters into a word
586 	int ix;
587 	unsigned char c;
588 	unsigned int word;
589 
590 	if (string == NULL)
591 		return 0;
592 
593 	word = 0;
594 	for (ix = 0; ix < 4; ix++) {
595 		if (string[ix] == 0) break;
596 		c = string[ix];
597 		word |= (c << (ix*8));
598 	}
599 	return word;
600 }
601 
602 static MNEM_TAB reserved_phonemes[] = {
603 	{ "_\001",  phonCONTROL },      // NOT USED
604 	{ "%",      phonSTRESS_U },
605 	{ "%%",     phonSTRESS_D },
606 	{ ",",      phonSTRESS_2 },
607 	{ ",,",     phonSTRESS_3 },
608 	{ "'",      phonSTRESS_P },
609 	{ "''",     phonSTRESS_P2 },
610 	{ "=",      phonSTRESS_PREV },  //  stress previous syllable
611 	{ "_:",     phonPAUSE },        //  pause
612 	{ "_",      phonPAUSE_SHORT },  //  short pause
613 	{ "_!",     phonPAUSE_NOLINK }, //  short pause, no link
614 	{ ":",      phonLENGTHEN },
615 	{ "@",      phonSCHWA },
616 	{ "@-",     phonSCHWA_SHORT },
617 	{ "||",     phonEND_WORD },
618 	{ "1",      phonDEFAULTTONE },  // (numeral 1)  default tone (for tone language)
619 	{ "#X1",    phonCAPITAL },      // capital letter indication
620 	{ "?",      phonGLOTTALSTOP },  // glottal stop
621 	{ "-",      phonSYLLABIC },     // syllabic consonant
622 	{ "_^_",    phonSWITCH },       //  Change language
623 	{ "_X1",    phonX1 },           // a language specific action
624 	{ "_|",     phonPAUSE_VSHORT }, // very short pause
625 	{ "_::",    phonPAUSE_LONG },   // long pause
626 	{ "t#",     phonT_REDUCED },    // reduced version of [t]
627 	{ "'!",     phonSTRESS_TONIC }, // stress - emphasized
628 	{ "_;_",    phonPAUSE_CLAUSE }, // clause pause
629 
630 	{ "#@",     phonVOWELTYPES },   // vowel type groups, these should be consecutive
631 	{ "#a",     phonVOWELTYPES+1 },
632 	{ "#e",     phonVOWELTYPES+2 },
633 	{ "#i",     phonVOWELTYPES+3 },
634 	{ "#o",     phonVOWELTYPES+4 },
635 	{ "#u",     phonVOWELTYPES+5 },
636 
637 	{ NULL, 0 }
638 };
639 
640 static void ReservePhCodes()
641 {
642 	// Reserve phoneme codes which have fixed numbers so that they can be
643 	// referred to from the program code.
644 	unsigned int word;
645 	MNEM_TAB *p;
646 
647 	p = reserved_phonemes;
648 	while (p->mnem != NULL) {
649 		word = StringToWord(p->mnem);
650 		phoneme_tab2[p->value].mnemonic = word;
651 		phoneme_tab2[p->value].code = p->value;
652 		if (n_phcodes <= p->value)
653 			n_phcodes = p->value+1;
654 		p++;
655 	}
656 }
657 
658 static int LookupPhoneme(const char *string, int control)
659 {
660 	// control = 0   explicit declaration
661 	// control = 1   declare phoneme if not found
662 	// control = 2   start looking after control & stress phonemes
663 
664 	int ix;
665 	int start;
666 	int use;
667 	unsigned int word;
668 
669 	if (strcmp(string, "NULL") == 0)
670 		return 1;
671 
672 	ix = strlen(string);
673 	if ((ix == 0) || (ix > 4))
674 		error("Bad phoneme name '%s'", string);
675 	word = StringToWord(string);
676 
677 	// don't use phoneme number 0, reserved for string terminator
678 	start = 1;
679 
680 	if (control == 2) {
681 		// don't look for control and stress phonemes (allows these characters to be
682 		// used for other purposes)
683 		start = 8;
684 	}
685 
686 	use = 0;
687 	for (ix = start; ix < n_phcodes; ix++) {
688 		if (phoneme_tab2[ix].mnemonic == word)
689 			return ix;
690 
691 		if ((use == 0) && (phoneme_tab2[ix].mnemonic == 0))
692 			use = ix;
693 	}
694 
695 	if (use == 0) {
696 		if (control == 0)
697 			return -1;
698 		if (n_phcodes >= N_PHONEME_TAB-1)
699 			return -1; // phoneme table is full
700 		use = n_phcodes++;
701 	}
702 
703 	// add this phoneme to the phoneme table
704 	phoneme_tab2[use].mnemonic = word;
705 	phoneme_tab2[use].type = phINVALID;
706 	phoneme_tab2[use].program = linenum; // for error report if the phoneme remains undeclared
707 	return use;
708 }
709 
710 static unsigned int get_char()
711 {
712 	unsigned int c;
713 	c = fgetc(f_in);
714 	if (c == '\n')
715 		linenum++;
716 	return c;
717 }
718 
719 static void unget_char(unsigned int c)
720 {
721 	ungetc(c, f_in);
722 	if (c == '\n')
723 		linenum--;
724 }
725 
dgemm_(const char * transa,const char * transb,integer * m,integer * n,integer * k,double * alpha,double * a,integer * lda,double * b,integer * ldb,double * beta,double * c__,integer * ldc)726 static int CheckNextChar()
727 {
728 	int c;
729 	while (((c = get_char()) == ' ') || (c == '\t'))
730 		;
731 	unget_char(c);
732 	return c;
733 }
734 
735 static int NextItem(int type)
736 {
737 	int acc;
738 	unsigned char c = 0;
739 	unsigned char c2;
740 	int ix;
741 	int sign;
742 	char *p;
743 	keywtab_t *pk;
744 
745 	item_type = -1;
746 
747 	f_in_displ = ftell(f_in);
748 	f_in_linenum = linenum;
749 
750 	while (!feof(f_in)) {
751 		c = get_char();
752 		if (c == '/') {
753 			if ((c2 = get_char()) == '/') {
754 				// comment, ignore to end of line
755 				while (!feof(f_in) && ((c = get_char()) != '\n'))
756 					;
757 			} else
758 				unget_char(c2);
759 		}
760 		if (!isspace(c))
761 			break;
762 	}
763 	if (feof(f_in))
764 		return -2;
765 
766 	if (c == '(') {
767 		if (type == tOPENBRACKET)
768 			return 1;
769 		return -1;
770 	}
771 
772 	ix = 0;
773 	while (!feof(f_in) && !isspace(c) && (c != '(') && (c != ')') && (c != ',')) {
774 		if (c == '\\')
775 			c = get_char();
776 		item_string[ix++] = c;
777 		c = get_char();
778 		if (feof(f_in))
779 			break;
780 		if (item_string[ix-1] == '=')
781 			break;
782 	}
783 	item_string[ix] = 0;
784 
785 	while (isspace(c))
786 		c = get_char();
787 
788 	item_terminator = ' ';
789 	if ((c == ')') || (c == '(') || (c == ','))
790 		item_terminator = c;
791 
792 	if ((c == ')') || (c == ','))
793 		c = ' ';
794 
795 	if (!feof(f_in))
796 		unget_char(c);
797 
798 	if (type == tSTRING)
799 		return 0;
800 
801 	if ((type == tNUMBER) || (type == tSIGNEDNUMBER)) {
802 		acc = 0;
803 		sign = 1;
804 		p = item_string;
805 
806 		if ((*p == '-') && (type == tSIGNEDNUMBER)) {
807 			sign = -1;
808 			p++;
809 		}
810 		if (!isdigit(*p)) {
811 			if ((type == tNUMBER) && (*p == '-'))
812 				error("Expected an unsigned number");
813 			else
814 				error("Expected a number");
815 		}
816 		while (isdigit(*p)) {
817 			acc *= 10;
818 			acc += (*p - '0');
819 			p++;
820 		}
821 		return acc * sign;
822 	}
823 
824 	if ((type >= tKEYWORD) && (type <= tINTONATION)) {
825 		pk = keyword_tabs[type-tKEYWORD];
826 		while (pk->mnem != NULL) {
827 			if (strcmp(item_string, pk->mnem) == 0) {
828 				item_type = pk->type;
829 				return pk->data;
830 			}
831 			pk++;
832 		}
833 		item_type = -1;
834 		return -1; // keyword not found
835 	}
836 	if (type == tPHONEMEMNEM)
837 		return LookupPhoneme(item_string, 2);
838 	return -1;
839 }
840 
841 static int NextItemMax(int max)
842 {
843 	// Get a number, but restrict value to max
844 	int value;
845 
846 	value = NextItem(tNUMBER);
847 	if (value > max) {
848 		error("Value %d is greater than maximum %d", value, max);
849 		value = max;
850 	}
851 	return value;
852 }
853 
854 static int NextItemBrackets(int type, int control)
855 {
856 	// Expect a parameter inside parantheses
857 	// control: bit 0  0= need (
858 	//          bit 1  1= allow comma
859 
860 	int value;
861 
862 	if ((control & 1) == 0) {
863 		if (!NextItem(tOPENBRACKET))
864 			error("Expected '('");
865 	}
866 
867 	value = NextItem(type);
868 	if ((control & 2) && (item_terminator == ','))
869 		return value;
870 
871 	if (item_terminator != ')')
872 		error("Expected ')'");
873 	return value;
874 }
875 
876 static void UngetItem()
877 {
878 	fseek(f_in, f_in_displ, SEEK_SET);
879 	linenum = f_in_linenum;
880 }
881 
882 static int Range(int value, int divide, int min, int max)
883 {
884 	if (value < 0)
885 		value -= divide/2;
886 	else
887 		value += divide/2;
888 	value = value / divide;
889 
890 	if (value > max)
891 		value = max;
892 	if (value < min)
893 		value = min;
894 	return value - min;
895 }
896 
897 static int CompileVowelTransition(int which)
898 {
899 	// Compile a vowel transition
900 	int key;
901 	int len = 0;
902 	int rms = 0;
903 	int f1 = 0;
904 	int f2 = 0;
905 	int f2_min = 0;
906 	int f2_max = 0;
907 	int f3_adj = 0;
908 	int f3_amp = 0;
909 	int flags = 0;
910 	int vcolour = 0;
911 	int x;
912 	int instn = i_VOWELIN;
913 	int word1;
914 	int word2;
915 
916 	if (which == 1) {
917 		len = 50 / 2; // defaults for transition into vowel
918 		rms = 25 / 2;
919 
920 		if (phoneme_out->type == phSTOP) {
921 			len = 42 / 2; // defaults for transition into vowel
922 			rms = 30 / 2;
923 		}
924 	} else if (which == 2) {
925 		instn = i_VOWELOUT;
926 		len = 36 / 2; // defaults for transition out of vowel
927 		rms = 16 / 2;
928 	}
929 
930 	for (;;) {
931 		key = NextItem(tKEYWORD);
932 		if (item_type != tTRANSITION) {
933 			UngetItem();
934 			break;
935 		}
936 
937 		switch (key & 0xf)
938 		{
939 		case 1:
940 			len = Range(NextItem(tNUMBER), 2, 0, 63) & 0x3f;
941 			flags |= 1;
942 			break;
943 		case 2:
944 			rms = Range(NextItem(tNUMBER), 2, 0, 31) & 0x1f;
945 			flags |= 1;
946 			break;
947 		case 3:
948 			f1 = NextItem(tNUMBER);
949 			break;
950 		case 4:
951 			f2 = Range(NextItem(tNUMBER), 50, 0, 63) & 0x3f;
952 			f2_min = Range(NextItem(tSIGNEDNUMBER), 50, -15, 15) & 0x1f;
953 			f2_max = Range(NextItem(tSIGNEDNUMBER), 50, -15, 15) & 0x1f;
954 			if (f2_min > f2_max) {
955 				x = f2_min;
956 				f2_min = f2_max;
957 				f2_max = x;
958 			}
959 			break;
960 		case 5:
961 			f3_adj = Range(NextItem(tSIGNEDNUMBER), 50, -15, 15) & 0x1f;
962 			f3_amp = Range(NextItem(tNUMBER), 8, 0, 15) & 0x1f;
963 			break;
964 		case 6:
965 			flags |= 2; // break
966 			break;
967 		case 7:
968 			flags |= 4; // rate
969 			break;
970 		case 8:
971 			flags |= 8; // glstop
972 			break;
973 		case 9:
974 			flags |= 16; // lenadd
975 			break;
976 		case 10:
977 			flags |= 32;  // f4
978 			break;
979 		case 11:
980 			flags |= 64;  // pause
981 			break;
982 		case 12:
983 			vcolour = NextItem(tNUMBER);
984 			break;
985 		case 13:
986 			// set rms of 1st frame as fraction of rms of 2nd frame  (1/30ths)
987 			rms = (Range(NextItem(tNUMBER), 1, 0, 31) & 0x1f) | 0x20;
988 			flags |= 1;
989 			break;
990 		}
991 	}
992 	word1 = len + (rms << 6) + (flags << 12);
993 	word2 =  f2 + (f2_min << 6) + (f2_max << 11) + (f3_adj << 16) + (f3_amp << 21) + (f1 << 26) + (vcolour << 29);
994 	prog_out[0] = instn + ((word1 >> 16) & 0xff);
995 	prog_out[1] = word1;
996 	prog_out[2] = word2 >> 16;
997 	prog_out[3] = word2;
998 	prog_out += 4;
999 
1000 	return 0;
1001 }
1002 #if ! DATA_FROM_SOURCECODE_FILES
1003 static espeak_ng_STATUS LoadSpect(const char *path, int control, int *addr)
1004 {
1005 	SpectSeq *spectseq;
1006 	int peak;
1007 	int frame;
1008 	int n_frames;
1009 	int ix;
1010 	int x, x2;
1011 	int rms;
1012 	float total;
1013 	float pkheight;
1014 	int marker1_set = 0;
1015 	int frame_vowelbreak = 0;
1016 	int klatt_flag = 0;
1017 	SpectFrame *fr;
1018 	frame_t *fr_out;
1019 	char filename[sizeof(path_home)+20];
1020 
1021 	SPECT_SEQ seq_out;
1022 	SPECT_SEQK seqk_out;
1023 
1024 	// create SpectSeq and import data
1025 	spectseq = SpectSeqCreate();
1026 	if (spectseq == NULL)
1027 		return static_cast<espeak_ng_STATUS> (ENOMEM);
1028 
1029 	snprintf(filename, sizeof(filename), "%s/%s", phsrc, path);
1030 	espeak_ng_STATUS status = LoadSpectSeq(spectseq, filename);
1031 	if (status != ENS_OK) {
1032 		error("Bad vowel file: '%s'", path);
1033 		SpectSeqDestroy(spectseq);
1034 		return status;
1035 	}
1036 
1037 	// do we need additional klatt data ?
1038 	for (frame = 0; frame < spectseq->numframes; frame++) {
1039 		for (ix = 5; ix < N_KLATTP2; ix++) {
1040 			if (spectseq->frames[frame]->klatt_param[ix] != 0)
1041 				klatt_flag = FRFLAG_KLATT;
1042 		}
1043 	}
1044 
1045 	*addr = ftell(f_phdata);
1046 
1047 	seq_out.n_frames = 0;
1048 	seq_out.sqflags = 0;
1049 	seq_out.length_total = 0;
1050 
1051 	total = 0;
1052 	for (frame = 0; frame < spectseq->numframes; frame++) {
1053 		if (spectseq->frames[frame]->keyframe) {
1054 			if (seq_out.n_frames == 1)
1055 				frame_vowelbreak = frame;
1056 			if (spectseq->frames[frame]->markers & 0x2) {
1057 				// marker 1 is set
1058 				marker1_set = 1;
1059 			}
1060 
1061 			seq_out.n_frames++;
1062 			if (frame > 0)
1063 				total += spectseq->frames[frame-1]->length;
1064 		}
1065 	}
1066 	seq_out.length_total = (int)total;
1067 
1068 	if ((control & 1) && (marker1_set == 0)) {
1069 		// This is a vowel, but no Vowel Break marker is set
1070 		// set a marker flag for the second frame of a vowel
1071 		spectseq->frames[frame_vowelbreak]->markers |= FRFLAG_VOWEL_CENTRE;
1072 	}
1073 
1074 	n_frames = 0;
1075 	for (frame = 0; frame < spectseq->numframes; frame++) {
1076 		fr = spectseq->frames[frame];
1077 
1078 		if (fr->keyframe) {
1079 			if (klatt_flag)
1080 				fr_out = &seqk_out.frame[n_frames];
1081 			else
1082 				fr_out = (frame_t *)&seq_out.frame[n_frames];
1083 
1084 			x = (int)(fr->length + 0.5); // round to nearest mS
1085 			if (x > 255) x = 255;
1086 			fr_out->length = x;
1087 
1088 			fr_out->frflags = fr->markers | klatt_flag;
1089 
1090 			rms = (int)GetFrameRms(fr, spectseq->amplitude);
1091 			if (rms > 255) rms = 255;
1092 			fr_out->rms = rms;
1093 
1094 			if (n_frames == (seq_out.n_frames-1))
1095 				fr_out->length = 0; // give last frame zero length
dgemv_(const char * trans,integer * m,integer * n,double * alpha,double * a,integer * lda,double * x,integer * incx,double * beta,double * y,integer * incy)1096 
1097 			// write: peak data
1098 			count_frames++;
1099 			for (peak = 0; peak < 8; peak++) {
1100 				if (peak < 7)
1101 					fr_out->ffreq[peak] = fr->peaks[peak].pkfreq;
1102 
1103 				pkheight = spectseq->amplitude * fr->amp_adjust * fr->peaks[peak].pkheight;
1104 				pkheight = pkheight/640000;
1105 				if (pkheight > 255) pkheight = 255;
1106 				fr_out->fheight[peak] = (int)pkheight;
1107 
1108 				if (peak < 6) {
1109 					x =  fr->peaks[peak].pkwidth/4;
1110 					if (x > 255) x = 255;
1111 					fr_out->fwidth[peak] = x;
1112 
1113 					if (peak < 3) {
1114 						x2 =  fr->peaks[peak].pkright/4;
1115 						if (x2 > 255) x2 = 255;
1116 						fr_out->fright[peak] = x2;
1117 					}
1118 				}
1119 
1120 				if (peak < 4) {
1121 					x = fr->peaks[peak].klt_bw / 2;
1122 					if (x > 255) x = 255;
1123 					fr_out->bw[peak] = x;
1124 				}
1125 			}
1126 
1127 			for (ix = 0; ix < 5; ix++) {
1128 				fr_out->klattp[ix] = fr->klatt_param[ix];
1129 
1130 				fr_out->klattp[KLATT_FNZ] = fr->klatt_param[KLATT_FNZ] / 2;
1131 			}
1132 
1133 			if (klatt_flag) {
1134 				// additional klatt parameters
1135 				for (ix = 0; ix < 5; ix++)
1136 					fr_out->klattp2[ix] = fr->klatt_param[ix+5];
1137 
1138 				for (peak = 0; peak < 7; peak++) {
1139 					fr_out->klatt_ap[peak] = fr->peaks[peak].klt_ap;
1140 
1141 					x = fr->peaks[peak].klt_bp / 2;
1142 					if (x > 255) x = 255;
1143 					fr_out->klatt_bp[peak] = x;
1144 				}
1145 				fr_out->spare = 0;
1146 			}
1147 
1148 			if (fr_out->bw[1] == 0) {
1149 				fr_out->bw[0] = 89 / 2;
1150 				fr_out->bw[1] = 90 / 2;
1151 				fr_out->bw[2] = 140 / 2;
1152 				fr_out->bw[3] = 260 / 2;
1153 			}
1154 
1155 			n_frames++;
1156 		}
1157 	}
1158 
1159 	if (klatt_flag) {
1160 		seqk_out.n_frames = seq_out.n_frames;
1161 		seqk_out.sqflags = seq_out.sqflags;
1162 		seqk_out.length_total = seq_out.length_total;
1163 
1164 		ix = (char *)(&seqk_out.frame[seqk_out.n_frames]) - (char *)(&seqk_out);
1165 		fwrite(&seqk_out, ix, 1, f_phdata);
1166 		while (ix & 3)
1167 		{
1168 			// round up to multiple of 4 bytes
1169 			fputc(0, f_phdata);
1170 			ix++;
1171 		}
1172 	} else {
1173 		ix = (char *)(&seq_out.frame[seq_out.n_frames]) - (char *)(&seq_out);
1174 		fwrite(&seq_out, ix, 1, f_phdata);
1175 		while (ix & 3)
1176 		{
1177 			// round up to multiple of 4 bytes
1178 			fputc(0, f_phdata);
1179 			ix++;
1180 		}
1181 	}
1182 
1183 	SpectSeqDestroy(spectseq);
1184 	return ENS_OK;
1185 }
1186 #endif
1187 static int LoadWavefile(FILE *f, const char *fname)
1188 {
1189 	int displ;
1190 	unsigned char c1;
1191 	unsigned char c3;
1192 	int c2;
1193 	int sample;
1194 	int sample2;
1195 	float x;
1196 	int max = 0;
1197 	int length;
1198 	int sr1, sr2;
1199 	int failed;
1200 	int len;
1201 	int resample_wav = 0;
1202 	const char *fname2;
1203 	char fname_temp[100];
1204 	char msg[120];
1205 	int scale_factor = 0;
1206 
1207 	fseek(f, 24, SEEK_SET);
1208 	sr1 = Read4Bytes(f);
1209 	sr2 = Read4Bytes(f);
1210 	fseek(f, 40, SEEK_SET);
1211 
1212 	if ((sr1 != samplerate_native) || (sr2 != sr1*2)) {
1213 		char command[sizeof(path_home)+250];
1214 
1215 		failed = 0;
1216 
1217 #if 1
1218 		int fd_temp;
1219 		strcpy(fname_temp, "/tmp/espeakXXXXXX");
1220 		if ((fd_temp = mkstemp(fname_temp)) >= 0)
1221 			close(fd_temp);
1222 #else
1223 		strcpy(fname_temp, tmpnam(NULL));
1224 #endif
1225 
1226 		fname2 = fname;
1227 		len = strlen(fname);
1228 		if (strcmp(&fname[len-4], ".wav") == 0) {
1229 			strcpy(msg, fname);
1230 			msg[len-4] = 0;
1231 			fname2 = msg;
1232 		}
1233 
1234 		sprintf(command, "sox \"%s/%s.wav\" -r %d -c1 -t wav %s\n", phsrc, fname2, samplerate_native, fname_temp);
1235 		if (system(command) != 0)
1236 			failed = 1;
1237 
1238 		if (failed || (GetFileLength(fname_temp) <= 0)) {
1239 			if (resample_fails < 2)
1240 				error("Resample command failed: %s", command);
1241 			resample_fails++;
1242 
1243 			if (sr1 != samplerate_native)
1244 				error("Can't resample (%d to %d): %s", sr1, samplerate_native, fname);
1245 			else
1246 				error("WAV file is not mono: %s", fname);
1247 			remove(fname_temp);
1248 			return 0;
1249 		}
1250 
1251 		f = fopen(fname_temp, "rb");
1252 		if (f == NULL) {
1253 			error("Can't read temp file: %s", fname_temp);
1254 			return 0;
1255 		}
1256 		if (f_report != NULL)
1257 			fprintf(f_report, "resampled  %s\n", fname);
1258 		resample_count++;
1259 		resample_wav = 1;
1260 		fseek(f, 40, SEEK_SET); // skip past the WAV header, up to before "data length"
1261 	}
1262 
1263 	displ = ftell(f_phdata);
1264 
1265 	// data contains:  4 bytes of length (n_samples * 2), followed by 2-byte samples (lsb byte first)
1266 	length = Read4Bytes(f);
1267 
1268 	while (true) {
1269 		int c;
1270 
1271 		if ((c = fgetc(f)) == EOF)
1272 			break;
1273 		c1 = (unsigned char)c;
1274 
1275 		if ((c = fgetc(f)) == EOF)
1276 			break;
1277 		c3 = (unsigned char)c;
1278 
1279 		c2 = c3 << 24;
1280 		c2 = c2 >> 16; // sign extend
1281 
1282 		sample = (c1 & 0xff) + c2;
1283 
1284 		if (sample > max)
1285 			max = sample;
1286 		else if (sample < -max)
1287 			max = -sample;
1288 	}
1289 
1290 	scale_factor = (max / 127) + 1;
1291 
1292 	#define MIN_FACTOR   -1 // was 6, disable use of 16 bit samples
1293 	if (scale_factor > MIN_FACTOR) {
1294 		length = length/2 + (scale_factor << 16);
1295 	}
1296 
1297 	Write4Bytes(f_phdata, length);
1298 	fseek(f, 44, SEEK_SET);
1299 
1300 	while (!feof(f)) {
1301 		c1 = fgetc(f);
1302 		c3 = fgetc(f);
1303 		c2 = c3 << 24;
1304 		c2 = c2 >> 16; // sign extend
1305 
1306 		sample = (c1 & 0xff) + c2;
1307 
1308 		if (feof(f)) break;
1309 
1310 		if (scale_factor <= MIN_FACTOR) {
1311 			fputc(sample & 0xff, f_phdata);
1312 			fputc(sample >> 8, f_phdata);
1313 		} else {
1314 			x = ((float)sample / scale_factor) + 0.5;
1315 			sample2 = (int)x;
1316 			if (sample2 > 127)
1317 				sample2 = 127;
1318 			if (sample2 < -128)
1319 				sample2 = -128;
1320 			fputc(sample2, f_phdata);
1321 		}
1322 	}
1323 
1324 	length = ftell(f_phdata);
1325 	while ((length & 3) != 0) {
1326 		// pad to a multiple of 4 bytes
1327 		fputc(0, f_phdata);
1328 		length++;
1329 	}
1330 
1331 	if (resample_wav != 0) {
1332 		fclose(f);
1333 		remove(fname_temp);
1334 	}
1335 	return displ | 0x800000; // set bit 23 to indicate a wave file rather than a spectrum
1336 }
1337 
1338 static espeak_ng_STATUS LoadEnvelope(FILE *f, const char *fname, int *displ)
1339 {
1340 	char buf[128];
1341 
1342 	if (displ)
1343 		*displ = ftell(f_phdata);
1344 
1345 	if (fseek(f, 12, SEEK_SET) == -1)
1346 		return static_cast<espeak_ng_STATUS> (errno);
1347 
1348 	if (fread(buf, 128, 1, f) != 128)
1349 		return static_cast<espeak_ng_STATUS> (errno);
1350 	fwrite(buf, 128, 1, f_phdata);
1351 
1352 	if (n_envelopes < N_ENVELOPES) {
1353 		strncpy0(envelope_paths[n_envelopes], fname, sizeof(envelope_paths[0]));
1354 		memcpy(envelope_dat[n_envelopes], buf, sizeof(envelope_dat[0]));
1355 		n_envelopes++;
1356 	}
1357 
1358 	return ENS_OK;
1359 }
1360 
1361 // Generate a hash code from the specified string
1362 static int Hash8(const char *string)
1363 {
1364 	int c;
1365 	int chars = 0;
1366 	int hash = 0;
1367 
1368 	while ((c = *string++) != 0) {
1369 		c = tolower(c) - 'a';
1370 		hash = hash * 8 + c;
1371 		hash = (hash & 0x1ff) ^ (hash >> 8); // exclusive or
1372 		chars++;
1373 	}
1374 
1375 	return (hash+chars) & 0xff;
1376 }
1377 
1378 static int LoadEnvelope2(FILE *f, const char *fname)
1379 {
1380 	int ix, ix2;
1381 	int n;
1382 	int x, y;
1383 	int displ;
1384 	int n_points;
1385 	double yy;
1386 	char line_buf[128];
1387 	float env_x[20];
1388 	float env_y[20];
1389 	int env_lin[20];
1390 	unsigned char env[ENV_LEN];
1391 
dger_(integer * m,integer * n,double * alpha,double * x,integer * incx,double * y,integer * incy,double * a,integer * lda)1392 	n_points = 0;
1393 	(void) fgets(line_buf, sizeof(line_buf), f); // skip first line
1394 	while (!feof(f)) {
1395 		if (fgets(line_buf, sizeof(line_buf), f) == NULL)
1396 			break;
1397 
1398 		env_lin[n_points] = 0;
1399 		n = sscanf(line_buf, "%f %f %d", &env_x[n_points], &env_y[n_points], &env_lin[n_points]);
1400 		if (n >= 2) {
1401 			env_x[n_points] *= (float)1.28; // convert range 0-100 to 0-128
1402 			n_points++;
1403 		}
1404 	}
1405 	if (n_points > 0) {
1406 		env_x[n_points] = env_x[n_points-1];
1407 		env_y[n_points] = env_y[n_points-1];
1408 	}
1409 
1410 	ix = -1;
1411 	ix2 = 0;
1412 	if (n_points > 0) for (x = 0; x < ENV_LEN; x++) {
1413 		if (n_points > 3 && x > env_x[ix+4])
1414 			ix++;
1415 		if (n_points > 2 && x >= env_x[ix2+1])
1416 			ix2++;
1417 
1418 		if (env_lin[ix2] > 0) {
1419 			yy = env_y[ix2] + (env_y[ix2+1] - env_y[ix2]) * ((float)x - env_x[ix2]) / (env_x[ix2+1] - env_x[ix2]);
1420 			y = (int)(yy * 2.55);
1421 		} else if (n_points > 3)
1422 			y = (int)(polint(&env_x[ix], &env_y[ix], 4, x) * 2.55); // convert to range 0-255
1423 		else
1424 			y = (int)(polint(&env_x[ix], &env_y[ix], 3, x) * 2.55);
1425 		if (y < 0) y = 0;
1426 		if (y > 255) y = 255;
1427 		env[x] = y;
1428 	}
1429 
1430 	if (n_envelopes < N_ENVELOPES) {
1431 		strncpy0(envelope_paths[n_envelopes], fname, sizeof(envelope_paths[0]));
1432 		memcpy(envelope_dat[n_envelopes], env, ENV_LEN);
1433 		n_envelopes++;
1434 	}
1435 
1436 	displ = ftell(f_phdata);
1437 	fwrite(env, 1, 128, f_phdata);
1438 
1439 	return displ;
1440 }
1441 #if ! DATA_FROM_SOURCECODE_FILES
1442 static espeak_ng_STATUS LoadDataFile(const char *path, int control, int *addr)
1443 {
1444 	// load spectrum sequence or sample data from a file.
1445 	// return index into spect or sample data area. bit 23=1 if a sample
1446 
1447 	FILE *f;
1448 	int id;
1449 	int hash;
1450 	int type_code = ' ';
1451 	REF_HASH_TAB *p, *p2;
1452 	char buf[sizeof(path_home)+150];
1453 
1454 	if (strcmp(path, "NULL") == 0)
1455 		return ENS_OK;
1456 	if (strcmp(path, "DFT") == 0) {
1457 		*addr = 1;
1458 		return ENS_OK;
1459 	}
1460 
1461 	count_references++;
1462 
1463 	hash = Hash8(path);
1464 	p = ref_hash_tab[hash];
1465 	while (p != NULL) {
1466 		if (strcmp(path, p->string) == 0) {
1467 			duplicate_references++;
1468 			*addr = p->value; // already loaded this data
1469 			break;
1470 		}
1471 		p = (REF_HASH_TAB *)p->link;
1472 	}
1473 
1474 	if (*addr == 0) {
1475 		sprintf(buf, "%s/%s", phsrc, path);
1476 
1477 		if ((f = fopen(buf, "rb")) == NULL) {
1478 			sprintf(buf, "%s/%s.wav", phsrc, path);
1479 			if ((f = fopen(buf, "rb")) == NULL) {
1480 				error("Can't read file: %s", path);
1481 				return static_cast<espeak_ng_STATUS> (errno);
1482 			}
1483 		}
1484 
1485 		id = Read4Bytes(f);
1486 		rewind(f);
1487 
1488 		espeak_ng_STATUS status = ENS_OK;
1489 		if (id == 0x43455053) {
1490 			status = LoadSpect(path, control, addr);
1491 			type_code = 'S';
1492 		} else if (id == 0x46464952) {
1493 			*addr = LoadWavefile(f, path);
1494 			type_code = 'W';
1495 		} else if (id == 0x43544950) {
1496 			status = LoadEnvelope(f, path, addr);
1497 			type_code = 'E';
1498 		} else if (id == 0x45564E45) {
1499 			*addr = LoadEnvelope2(f, path);
1500 			type_code = 'E';
1501 		} else {
1502 			error("File not SPEC or RIFF: %s", path);
1503 			*addr = -1;
1504 			status = ENS_UNSUPPORTED_PHON_FORMAT;
1505 		}
1506 		fclose(f);
1507 
1508 		if (status != ENS_OK)
1509 			return status;
1510 
1511 		if (*addr > 0)
1512 			fprintf(f_phcontents, "%c  0x%.5x  %s\n", type_code, *addr & 0x7fffff, path);
1513 	}
1514 
1515 	// add this item to the hash table
1516 	if (*addr > 0) {
1517 		p = ref_hash_tab[hash];
1518 		p2 = (REF_HASH_TAB *)malloc(sizeof(REF_HASH_TAB)+strlen(path)+1);
1519 		if (p2 == NULL)
1520 			return static_cast<espeak_ng_STATUS> (ENOMEM);
1521 		p2->value = *addr;
1522 		p2->ph_mnemonic = phoneme_out->mnemonic; // phoneme which uses this file
1523 		p2->ph_table = n_phoneme_tabs-1;
1524 		strcpy(p2->string, path);
1525 		p2->link = (char *)p;
1526 		ref_hash_tab[hash] = p2;
1527 	}
1528 
1529 	return ENS_OK;
1530 }
1531 
1532 static void CompileToneSpec(void)
1533 {
1534 	int pitch1 = 0;
1535 	int pitch2 = 0;
1536 	int pitch_env = 0;
1537 	int amp_env = 0;
1538 
1539 	pitch1 = NextItemBrackets(tNUMBER, 2);
1540 	pitch2 = NextItemBrackets(tNUMBER, 3);
1541 
1542 	if (item_terminator == ',') {
1543 		NextItemBrackets(tSTRING, 3);
1544 		LoadDataFile(item_string, 0, &pitch_env);
1545 	}
1546 
1547 	if (item_terminator == ',') {
1548 		NextItemBrackets(tSTRING, 1);
1549 		LoadDataFile(item_string, 0, &amp_env);
1550 	}
1551 
1552 	if (pitch1 < pitch2) {
1553 		phoneme_out->start_type = pitch1;
1554 		phoneme_out->end_type = pitch2;
1555 	} else {
1556 		phoneme_out->start_type = pitch2;
1557 		phoneme_out->end_type = pitch1;
1558 	}
1559 
1560 	if (pitch_env != 0) {
1561 		*prog_out++ = i_PITCHENV + ((pitch_env >> 16) & 0xf);
1562 		*prog_out++ = pitch_env;
1563 	}
1564 	if (amp_env != 0) {
1565 		*prog_out++ = i_AMPENV + ((amp_env >> 16) & 0xf);
1566 		*prog_out++ = amp_env;
1567 	}
1568 }
1569 
1570 
dnrm2_(integer * n,double * x,integer * incx)1571 
1572 static void CompileSound(int keyword, int isvowel)
1573 {
1574 	int addr = 0;
1575 	int value = 0;
1576 	char path[N_ITEM_STRING];
1577 	static int sound_instns[] = { i_FMT, i_WAV, i_VWLSTART, i_VWLENDING, i_WAVADD };
1578 
1579 	NextItemBrackets(tSTRING, 2);
1580 	strcpy(path, item_string);
1581 	if (item_terminator == ',') {
1582 		if ((keyword == kVOWELSTART) || (keyword == kVOWELENDING)) {
1583 			value = NextItemBrackets(tSIGNEDNUMBER, 1);
1584 			if (value > 127) {
1585 				value = 127;
1586 				error("Parameter > 127");
1587 			}
1588 			if (value < -128) {
1589 				value = -128;
1590 				error("Parameter < -128");
1591 			}
1592 		} else {
1593 			value = NextItemBrackets(tNUMBER, 1);
1594 			if (value > 255) {
1595 				value = 255;
1596 				error("Parameter > 255");
1597 			}
1598 		}
1599 	}
1600 	LoadDataFile(path, isvowel, &addr);
1601 	addr = addr / 4; // addr is words not bytes
1602 
1603 	*prog_out++ = sound_instns[keyword-kFMT] + ((value & 0xff) << 4) + ((addr >> 16) & 0xf);
1604 	*prog_out++ = addr & 0xffff;
1605 }
1606 #endif
1607 /*
1608    Condition
1609    bits 14,15   1
1610    bit 13       1 = AND, 0 = OR
1611    bit 12       spare
1612    bit 8-11
1613    =0-3       p,t,n,n2   data=phoneme code
1614    =4-7       p,t,n,n2   data=(bits5-7: phtype, place, property, special) (bits0-4: data)
1615    =8         data = stress bitmap
1616    =9         special tests
1617  */
1618 static int CompileIf(int elif)
1619 {
1620 	int key;
1621 	int finish = 0;
1622 	int word = 0;
1623 	int word2;
1624 	int data;
1625 	int bitmap;
1626 	int brackets;
1627 	int not_flag;
1628 	USHORT *prog_last_if = NULL;
1629 
1630 	then_count = 2;
1631 	after_if = 1;
1632 
1633 	while (!finish) {
1634 		not_flag = 0;
1635 		word2 = 0;
1636 		if (prog_out >= prog_out_max) {
1637 			error("Phoneme program too large");
1638 			return 0;
1639 		}
1640 
1641 		if ((key = NextItem(tCONDITION)) < 0)
1642 			error("Expected a condition, not '%s'", item_string);
1643 
1644 		if ((item_type == 0) && (key == k_NOT)) {
1645 			not_flag = 1;
1646 			if ((key = NextItem(tCONDITION)) < 0)
1647 				error("Expected a condition, not '%s'", item_string);
1648 		}
1649 
1650 		if (item_type == tWHICH_PHONEME) {
1651 			// prevPh(), thisPh(), nextPh(), next2Ph() etc
drot_(integer * n,double * dx,integer * incx,double * dy,integer * incy,double * c__,double * s)1652 			if (key >= 6) {
1653 				// put the 'which' code in the next instruction
1654 				word2 = key;
1655 				key = 6;
1656 			}
1657 			key = key << 8;
1658 
1659 			data = NextItemBrackets(tPROPERTIES, 0);
1660 			if (data >= 0)
1661 				word = key + data + 0x700;
1662 			else {
1663 				data = LookupPhoneme(item_string, 2);
1664 				word = key + data;
1665 			}
1666 		} else if (item_type == tTEST) {
1667 			if (key == kTHISSTRESS) {
1668 				bitmap = 0;
1669 				brackets = 2;
1670 				do {
1671 					data = NextItemBrackets(tNUMBER, brackets);
1672 					if (data > 7)
1673 						error("Expected list of stress levels");
1674 					bitmap |= (1 << data);
1675 
1676 					brackets = 3;
1677 				} while (item_terminator == ',');
1678 				word = i_StressLevel | bitmap;
1679 			} else
1680 				word = key;
1681 		} else {
1682 			error("Unexpected keyword '%s'", item_string);
1683 
1684 			if ((strcmp(item_string, "phoneme") == 0) || (strcmp(item_string, "endphoneme") == 0))
1685 				return -1;
1686 		}
1687 
1688 		// output the word
1689 		prog_last_if = prog_out;
1690 		*prog_out++ = word | i_CONDITION;
1691 
1692 		if (word2 != 0)
1693 			*prog_out++ = word2;
1694 		if (not_flag)
1695 			*prog_out++ = i_NOT;
1696 
1697 		// expect AND, OR, THEN
1698 		switch (NextItem(tCONDITION))
1699 		{
1700 		case k_AND:
1701 			break;
1702 		case k_OR:
1703 			if (prog_last_if != NULL)
1704 				*prog_last_if |=  i_OR;
1705 			break;
1706 		case k_THEN:
1707 			finish = 1;
1708 			break;
1709 		default:
1710 			error("Expected AND, OR, THEN");
1711 			break;
1712 		}
1713 	}
1714 
1715 	if (elif == 0) {
1716 		if_level++;
1717 		if_stack[if_level].p_else = NULL;
1718 	}
1719 
1720 	if_stack[if_level].returned = 0;
1721 	if_stack[if_level].p_then = prog_out;
1722 	*prog_out++ = i_JUMP_FALSE;
1723 
drotg_(double * da,double * db,double * c__,double * s)1724 	return 0;
1725 }
1726 
1727 static void FillThen(int add)
1728 {
1729 	USHORT *p;
1730 	int offset;
1731 
1732 	p = if_stack[if_level].p_then;
1733 	if (p != NULL) {
1734 		offset = prog_out - p + add;
1735 
1736 		if ((then_count == 1) && (if_level == 1)) {
1737 			// The THEN part only contains one statement, we can remove the THEN jump
1738 			// and the interpreter will implicitly skip the statement.
1739 			while (p < prog_out) {
1740 				p[0] = p[1];
1741 				p++;
1742 			}
1743 			prog_out--;
1744 		} else {
1745 			if (offset > MAX_JUMP)
1746 				error("IF block is too long");
1747 			*p = i_JUMP_FALSE + offset;
1748 		}
1749 		if_stack[if_level].p_then = NULL;
1750 	}
1751 
1752 	then_count = 0;
1753 }
1754 
1755 static int CompileElse(void)
1756 {
1757 	USHORT *ref;
1758 	USHORT *p;
1759 
1760 	if (if_level < 1) {
1761 		error("ELSE not expected");
1762 		return 0;
1763 	}
1764 
1765 	if (if_stack[if_level].returned == 0)
1766 		FillThen(1);
1767 	else
1768 		FillThen(0);
1769 
1770 	if (if_stack[if_level].returned == 0) {
1771 		ref = prog_out;
1772 		*prog_out++ = 0;
1773 
1774 		if ((p = if_stack[if_level].p_else) != NULL)
1775 			*ref = ref - p; // backwards offset to the previous else
1776 		if_stack[if_level].p_else = ref;
1777 	}
1778 
1779 	return 0;
1780 }
1781 
1782 static int CompileElif(void)
1783 {
1784 	if (if_level < 1) {
1785 		error("ELIF not expected");
1786 		return 0;
1787 	}
drotm_(integer * n,double * dx,integer * incx,double * dy,integer * incy,double * dparam)1788 
1789 	CompileElse();
1790 	CompileIf(1);
1791 	return 0;
1792 }
1793 
1794 static int CompileEndif(void)
1795 {
1796 	USHORT *p;
1797 	int chain;
1798 	int offset;
1799 
1800 	if (if_level < 1) {
1801 		error("ENDIF not expected");
1802 		return 0;
1803 	}
1804 
1805 	FillThen(0);
1806 
1807 	if ((p = if_stack[if_level].p_else) != NULL) {
1808 		do {
1809 			chain = *p; // a chain of previous else links
1810 
1811 			offset = prog_out - p;
1812 			if (offset > MAX_JUMP)
1813 				error("IF block is too long");
1814 			*p = i_JUMP + offset;
1815 
1816 			p -= chain;
1817 		} while (chain > 0);
1818 	}
1819 
1820 	if_level--;
1821 	return 0;
1822 }
1823 
1824 static int CompileSwitch(int type)
1825 {
1826 	// Type 0:  EndSwitch
1827 	//      1:  SwitchPrevVowelType
1828 	//      2:  SwitchNextVowelType
1829 
1830 	if (type == 0) {
1831 		// check the instructions in the Switch
1832 		return 0;
1833 	}
1834 
1835 	if (type == 1)
1836 		*prog_out++ = i_SWITCH_PREVVOWEL+6;
1837 	if (type == 2)
1838 		*prog_out++ = i_SWITCH_NEXTVOWEL+6;
1839 	return 0;
1840 }
1841 
1842 static PHONEME_TAB_LIST *FindPhonemeTable(const char *string)
1843 {
1844 	int ix;
1845 
1846 	for (ix = 0; ix < n_phoneme_tabs; ix++) {
1847 		if (strcmp(phoneme_tab_list2[ix].name, string) == 0)
1848 			return &phoneme_tab_list2[ix];
1849 	}
1850 	error("Unknown phoneme table: '%s'", string);
1851 	return NULL;
1852 }
1853 
1854 static PHONEME_TAB *FindPhoneme(const char *string)
1855 {
1856 	PHONEME_TAB_LIST *phtab = NULL;
1857 	int ix;
1858 	unsigned int mnem;
1859 	char *phname;
1860 	char buf[200];
1861 
1862 	// is this the name of a phoneme which is in scope
1863 	if ((strlen(string) <= 4) && ((ix = LookupPhoneme(string, 0)) != -1))
1864 		return &phoneme_tab2[ix];
1865 
1866 	// no, treat the name as phonemetable/phoneme
1867 	strcpy(buf, string);
1868 	if ((phname = strchr(buf, '/')) != 0)
1869 		*phname++ = 0;
1870 
1871 	phtab = FindPhonemeTable(buf);
1872 	if (phtab == NULL)
1873 		return NULL; // phoneme table not found
1874 
1875 	mnem = StringToWord(phname);
1876 	for (ix = 1; ix < 256; ix++) {
1877 		if (mnem == phtab->phoneme_tab_ptr[ix].mnemonic)
1878 			return &phtab->phoneme_tab_ptr[ix];
1879 	}
1880 
1881 	error("Phoneme reference not found: '%s'", string);
1882 	return NULL;
1883 }
1884 
1885 static void ImportPhoneme(void)
1886 {
1887 	unsigned int ph_mnem;
1888 	unsigned int ph_code;
1889 	PHONEME_TAB *ph;
1890 
1891 	NextItem(tSTRING);
1892 
1893 	if ((ph = FindPhoneme(item_string)) == NULL) {
1894 		error("Cannot find phoneme '%s' to import.", item_string);
1895 		return;
1896 	}
1897 
1898 	if (phoneme_out->phflags != 0 ||
1899 	    phoneme_out->type != phINVALID ||
1900 	    phoneme_out->start_type != 0 ||
1901 	    phoneme_out->end_type != 0 ||
1902 	    phoneme_out->std_length != 0 ||
1903 	    phoneme_out->length_mod != 0) {
1904 		error("Phoneme import will override set properties.");
1905 	}
1906 
1907 	ph_mnem = phoneme_out->mnemonic;
1908 	ph_code = phoneme_out->code;
1909 	memcpy(phoneme_out, ph, sizeof(PHONEME_TAB));
1910 	phoneme_out->mnemonic = ph_mnem;
1911 	phoneme_out->code = ph_code;
1912 	if (phoneme_out->type != phVOWEL)
1913 		phoneme_out->end_type = 0;  // voicingswitch, this must be set later to refer to a local phoneme
1914 }
1915 
1916 static void CallPhoneme(void)
1917 {
1918 	PHONEME_TAB *ph;
1919 	int ix;
1920 	int addr = 0;
1921 
1922 	NextItem(tSTRING);
1923 
1924 	// first look for a procedure name
1925 	for (ix = 0; ix < n_procs; ix++) {
1926 		if (strcmp(proc_names[ix], item_string) == 0) {
1927 			addr = proc_addr[ix];
1928 			break;
1929 		}
1930 	}
1931 	if (ix == n_procs) {
1932 		// procedure not found, try a phoneme name
1933 		if ((ph = FindPhoneme(item_string)) == NULL)
1934 			return;
1935 		addr = ph->program;
1936 
1937 		if (phoneme_out->type == phINVALID) {
1938 			// Phoneme type has not been set. Copy it from the called phoneme
1939 			phoneme_out->type = ph->type;
1940 			phoneme_out->start_type = ph->start_type;
1941 			phoneme_out->end_type = ph->end_type;
1942 			phoneme_out->std_length = ph->std_length;
1943 			phoneme_out->length_mod = ph->length_mod;
1944 
1945 			phoneme_flags = ph->phflags & ~phARTICULATION;
1946 		}
1947 	}
1948 
1949 	*prog_out++ = i_CALLPH + (addr >> 16);
1950 	*prog_out++ = addr;
1951 }
1952 
1953 static void DecThenCount()
1954 {
1955 	if (then_count > 0)
1956 		then_count--;
1957 }
1958 
1959 #if ! DATA_FROM_SOURCECODE_FILES
1960 static int CompilePhoneme(int compile_phoneme)
1961 {
1962 	int endphoneme = 0;
1963 	int keyword;
1964 	int value;
1965 	int phcode = 0;
1966 	int flags;
1967 	int ix;
1968 	int start;
1969 	int count;
1970 	int c;
1971 	char *p;
1972 	int vowel_length_factor = 100; // for testing
1973 	char number_buf[12];
1974 	char ipa_buf[N_ITEM_STRING+1];
1975 	PHONEME_TAB phoneme_out2;
1976 	PHONEME_PROG_LOG phoneme_prog_log;
1977 
1978 	prog_out = prog_buf;
1979 	prog_out_max = &prog_buf[MAX_PROG_BUF-1];
1980 	if_level = 0;
1981 	if_stack[0].returned = 0;
1982 	after_if = 0;
1983 	phoneme_flags = 0;
1984 
1985 	NextItem(tSTRING);
1986 	if (compile_phoneme) {
1987 		phcode = LookupPhoneme(item_string, 1); // declare phoneme if not already there
1988 		if (phcode == -1) return 0;
drotmg_(double * dd1,double * dd2,double * dx1,double * dy1,double * dparam)1989 		phoneme_out = &phoneme_tab2[phcode];
1990 	} else {
1991 		// declare a procedure
1992 		if (n_procs >= N_PROCS) {
1993 			error("Too many procedures");
1994 			return 0;
1995 		}
1996 		strcpy(proc_names[n_procs], item_string);
1997 		phoneme_out = &phoneme_out2;
1998 		sprintf(number_buf, "%.3dP", n_procs);
1999 		phoneme_out->mnemonic = StringToWord(number_buf);
2000 	}
2001 
2002 	phoneme_out->code = phcode;
2003 	phoneme_out->program = 0;
2004 	phoneme_out->type = phINVALID;
2005 	phoneme_out->std_length = 0;
2006 	phoneme_out->start_type = 0;
2007 	phoneme_out->end_type = 0;
2008 	phoneme_out->length_mod = 0;
2009 	phoneme_out->phflags = 0;
2010 
2011 	while (!endphoneme && !feof(f_in)) {
2012 		if ((keyword = NextItem(tKEYWORD)) < 0) {
2013 			if (keyword == -2) {
2014 				error("Missing 'endphoneme' before end-of-file"); // end of file
2015 				break;
2016 			}
2017 
2018 			phoneme_feature_t feature = phoneme_feature_from_string(item_string);
2019 			espeak_ng_STATUS status = phoneme_add_feature(phoneme_out, feature);
2020 			if (status == ENS_OK)
2021 				continue;
2022 			error_from_status(status, item_string);
2023 			continue;
2024 		}
2025 
2026 		switch (item_type)
2027 		{
2028 		case tPHONEME_TYPE:
2029 			if (phoneme_out->type != phINVALID) {
2030 				if (phoneme_out->type == phFRICATIVE && keyword == phLIQUID)
2031 					; // apr liquid => ok
2032 				else
2033 					error("More than one phoneme type: %s", item_string);
2034 			}
2035 			phoneme_out->type = keyword;
2036 			break;
2037 		case tPHONEME_FLAG:
2038 			phoneme_flags |= keyword;
2039 			break;
2040 		case tINSTRN1:
2041 			// instruction group 0, with 8 bit operands which set data in PHONEME_DATA
2042 			switch (keyword)
2043 			{
2044 			case i_CHANGE_PHONEME:
2045 			case i_APPEND_PHONEME:
2046 			case i_APPEND_IFNEXTVOWEL:
2047 			case i_INSERT_PHONEME:
2048 			case i_REPLACE_NEXT_PHONEME:
2049 			case i_VOICING_SWITCH:
2050 			case i_CHANGE_IF | STRESS_IS_DIMINISHED:
2051 			case i_CHANGE_IF | STRESS_IS_UNSTRESSED:
2052 			case i_CHANGE_IF | STRESS_IS_NOT_STRESSED:
2053 			case i_CHANGE_IF | STRESS_IS_SECONDARY:
2054 			case i_CHANGE_IF | STRESS_IS_PRIMARY:
2055 				value = NextItemBrackets(tPHONEMEMNEM, 0);
2056 				*prog_out++ = (keyword << 8) + value;
2057 				DecThenCount();
2058 				break;
2059 			case i_PAUSE_BEFORE:
2060 				value = NextItemMax(255);
2061 				*prog_out++ = (i_PAUSE_BEFORE << 8) + value;
2062 				DecThenCount();
2063 				break;
2064 			case i_PAUSE_AFTER:
2065 				value = NextItemMax(255);
2066 				*prog_out++ = (i_PAUSE_AFTER << 8) + value;
2067 				DecThenCount();
2068 				break;
2069 			case i_SET_LENGTH:
2070 				value = NextItemMax(511);
2071 				if (phoneme_out->type == phVOWEL)
2072 					value = (value * vowel_length_factor)/100;
2073 
2074 				if (after_if == 0)
2075 					phoneme_out->std_length = value/2;
2076 				else {
2077 					*prog_out++ = (i_SET_LENGTH << 8) + value/2;
2078 					DecThenCount();
2079 				}
2080 				break;
2081 			case i_ADD_LENGTH:
2082 				value = NextItem(tSIGNEDNUMBER) / 2;
2083 				*prog_out++ = (i_ADD_LENGTH << 8) + (value & 0xff);
2084 				DecThenCount();
2085 				break;
2086 			case i_LENGTH_MOD:
2087 				value = NextItem(tNUMBER);
2088 				phoneme_out->length_mod = value;
2089 				break;
2090 			case i_IPA_NAME:
2091 				NextItem(tSTRING);
2092 
2093 				if (strcmp(item_string, "NULL") == 0)
2094 					strcpy(item_string, " ");
2095 
2096 				// copy the string, recognize characters in the form U+9999
2097 				flags = 0;
2098 				count = 0;
2099 				ix = 1;
2100 
2101 				for (p = item_string; *p != 0;) {
2102 					p += utf8_in(&c, p);
2103 
2104 					if ((c == '|') && (count > 0)) {
2105 						// '|' means don't allow a tie or joiner before this letter
2106 						flags |= (1 << (count -1));
2107 					} else if ((c == 'U') && (p[0] == '+')) {
2108 						int j;
2109 						// U+9999
2110 						p++;
2111 						memcpy(number_buf, p, 4); // U+ should be followed by 4 hex digits
2112 						number_buf[4] = 0;
2113 						c = '#';
2114 						sscanf(number_buf, "%x", (unsigned int *)&c);
2115 
2116 						// move past the 4 hexdecimal digits
2117 						for (j = 0; j < 4; j++) {
2118 							if (!isalnum(*p))
2119 								break;
2120 							p++;
2121 						}
2122 						ix += utf8_out(c, &ipa_buf[ix]);
2123 						count++;
2124 					} else {
2125 						ix += utf8_out(c, &ipa_buf[ix]);
2126 						count++;
2127 					}
2128 				}
2129 				ipa_buf[0] = flags;
2130 				ipa_buf[ix] = 0;
2131 
2132 				start = 1;
2133 				if (flags != 0)
2134 					start = 0; // only include the flags byte if bits are set
2135 				value = strlen(&ipa_buf[start]); // number of UTF-8 bytes
2136 
2137 				*prog_out++ = (i_IPA_NAME << 8) + value;
2138 				for (ix = 0; ix < value; ix += 2)
2139 					*prog_out++ = (ipa_buf[ix+start] << 8) + (ipa_buf[ix+start+1] & 0xff);
2140 				DecThenCount();
2141 				break;
2142 			}
2143 			break;
2144 		case tSTATEMENT:
2145 			switch (keyword)
2146 			{
2147 			case kIMPORT_PH:
2148 				ImportPhoneme();
2149 				phoneme_flags = phoneme_out->phflags;
2150 				break;
2151 			case kSTARTTYPE:
2152 				phcode = NextItem(tPHONEMEMNEM);
2153 				if (phcode == -1)
2154 					phcode = LookupPhoneme(item_string, 1);
2155 				phoneme_out->start_type = phcode;
2156 				if (phoneme_out->type == phINVALID)
2157 					error("a phoneme type or manner of articulation should be specified before starttype");
2158 				break;
2159 			case kENDTYPE:
2160 				phcode = NextItem(tPHONEMEMNEM);
2161 				if (phcode == -1)
2162 					phcode = LookupPhoneme(item_string, 1);
2163 				if (phoneme_out->type == phINVALID)
2164 					error("a phoneme type or manner of articulation should be specified before endtype");
2165 				else if (phoneme_out->type == phVOWEL)
2166 					phoneme_out->end_type = phcode;
2167 				else if (phcode != phoneme_out->start_type)
2168 					error("endtype must equal starttype for consonants");
2169 				break;
2170 			case kVOICINGSWITCH:
2171 				phcode = NextItem(tPHONEMEMNEM);
2172 				if (phcode == -1)
2173 					phcode = LookupPhoneme(item_string, 1);
2174 				if (phoneme_out->type == phVOWEL)
2175 					error("voicingswitch cannot be used on vowels");
2176 				else
2177 					phoneme_out->end_type = phcode; // use end_type field for consonants as voicing_switch
2178 				break;
2179 			case kSTRESSTYPE:
2180 				value = NextItem(tNUMBER);
2181 				phoneme_out->std_length = value;
2182 				if (prog_out > prog_buf) {
2183 					error("stress phonemes can't contain program instructions");
2184 					prog_out = prog_buf;
2185 				}
2186 				break;
2187 			case kIF:
2188 				endphoneme = CompileIf(0);
2189 				break;
2190 			case kELSE:
2191 				endphoneme = CompileElse();
2192 				break;
2193 			case kELIF:
2194 				endphoneme = CompileElif();
2195 				break;
2196 			case kENDIF:
2197 				endphoneme = CompileEndif();
2198 				break;
2199 			case kENDSWITCH:
2200 				break;
2201 			case kSWITCH_PREVVOWEL:
2202 				endphoneme = CompileSwitch(1);
2203 				break;
2204 			case kSWITCH_NEXTVOWEL:
2205 				endphoneme = CompileSwitch(2);
2206 				break;
2207 			case kCALLPH:
2208 				CallPhoneme();
2209 				DecThenCount();
2210 				break;
2211 			case kFMT:
2212 				if_stack[if_level].returned = 1;
2213 				DecThenCount();
2214 				if (phoneme_out->type == phVOWEL)
2215 					CompileSound(keyword, 1);
2216 				else
2217 					CompileSound(keyword, 0);
2218 				break;
2219 			case kWAV:
2220 				if_stack[if_level].returned = 1;
2221 				// fallthrough:
2222 			case kVOWELSTART:
2223 			case kVOWELENDING:
2224 			case kANDWAV:
2225 				DecThenCount();
2226 				CompileSound(keyword, 0);
2227 				break;
2228 			case kVOWELIN:
2229 				DecThenCount();
2230 				endphoneme = CompileVowelTransition(1);
2231 				break;
2232 			case kVOWELOUT:
2233 				DecThenCount();
2234 				endphoneme = CompileVowelTransition(2);
2235 				break;
2236 			case kTONESPEC:
2237 				DecThenCount();
2238 				CompileToneSpec();
2239 				break;
2240 			case kCONTINUE:
2241 				*prog_out++ = INSTN_CONTINUE;
2242 				DecThenCount();
2243 				break;
2244 			case kRETURN:
2245 				*prog_out++ = INSTN_RETURN;
2246 				DecThenCount();
2247 				break;
2248 			case kINCLUDE:
2249 			case kPHONEMETABLE:
2250 				error("Missing 'endphoneme' before '%s'", item_string);  // drop through to endphoneme
2251 				// fallthrough:
2252 			case kENDPHONEME:
2253 			case kENDPROCEDURE:
2254 				endphoneme = 1;
dsbmv_(const char * uplo,integer * n,integer * k,double * alpha,double * a,integer * lda,double * x,integer * incx,double * beta,double * y,integer * incy)2255 				if (if_level > 0)
2256 					error("Missing ENDIF");
2257 				if ((prog_out > prog_buf) && (if_stack[0].returned == 0))
2258 					*prog_out++ = INSTN_RETURN;
2259 				break;
2260 			}
2261 			break;
2262 		}
2263 	}
2264 
2265 	if (endphoneme != 1)
2266 		error("'endphoneme' not expected here");
2267 
2268 	if (compile_phoneme) {
2269 		if (phoneme_out->type == phINVALID) {
2270 			error("Phoneme type is missing");
2271 			phoneme_out->type = 0;
2272 		}
2273 		phoneme_out->phflags |= phoneme_flags;
2274 
2275 		if (phoneme_out->phflags & phVOICED) {
2276 			if (phoneme_out->type == phSTOP)
2277 				phoneme_out->type = phVSTOP;
2278 			else if (phoneme_out->type == phFRICATIVE)
2279 				phoneme_out->type = phVFRICATIVE;
2280 		}
2281 
2282 		if (phoneme_out->std_length == 0) {
2283 			if (phoneme_out->type == phVOWEL)
2284 				phoneme_out->std_length = 180/2; // default length for vowel
2285 		}
2286 
2287 		phoneme_out->phflags |= phLOCAL; // declared in this phoneme table
2288 
2289 		if (phoneme_out->type == phDELETED)
2290 			phoneme_out->mnemonic = 0x01; // will not be recognised
2291 	}
2292 
2293 	if (prog_out > prog_buf) {
2294 		// write out the program for this phoneme
2295 		fflush(f_phindex);
2296 		phoneme_out->program = ftell(f_phindex) / sizeof(USHORT);
2297 
2298 		if (f_prog_log != NULL) {
2299 			phoneme_prog_log.addr = phoneme_out->program;
2300 			phoneme_prog_log.length = prog_out - prog_buf;
2301 			fwrite(&phoneme_prog_log, 1, sizeof(phoneme_prog_log), f_prog_log);
2302 		}
2303 
2304 		if (compile_phoneme == 0)
2305 			proc_addr[n_procs++] =  ftell(f_phindex) / sizeof(USHORT);
2306 		fwrite(prog_buf, sizeof(USHORT), prog_out - prog_buf, f_phindex);
2307 	}
2308 
2309 	return 0;
2310 }
2311 
2312 static void WritePhonemeTables()
2313 {
2314 	int ix;
2315 	int j;
2316 	int n;
2317 	int value;
2318 	int count;
2319 	PHONEME_TAB *p;
2320 
2321 	value = n_phoneme_tabs;
2322 	fputc(value, f_phtab);
2323 	fputc(0, f_phtab);
2324 	fputc(0, f_phtab);
2325 	fputc(0, f_phtab);
2326 
2327 	for (ix = 0; ix < n_phoneme_tabs; ix++) {
2328 		p = phoneme_tab_list2[ix].phoneme_tab_ptr;
2329 		n = n_phcodes_list[ix];
2330 		memset(&p[n], 0, sizeof(p[n]));
2331 		p[n].mnemonic = 0; // terminate the phoneme table
2332 
2333 		// count number of locally declared phonemes
2334 		count = 0;
2335 		for (j = 0; j < n; j++) {
2336 			if (ix == 0)
2337 				p[j].phflags |= phLOCAL; // write all phonemes in the base phoneme table
2338 
2339 			if (p[j].phflags & phLOCAL)
2340 				count++;
2341 		}
2342 		phoneme_tab_list2[ix].n_phonemes = count+1;
2343 
2344 		fputc(count+1, f_phtab);
2345 		fputc(phoneme_tab_list2[ix].includes, f_phtab);
2346 		fputc(0, f_phtab);
2347 		fputc(0, f_phtab);
2348 
2349 		fwrite(phoneme_tab_list2[ix].name, 1, N_PHONEME_TAB_NAME, f_phtab);
2350 
2351 		for (j = 0; j < n; j++) {
2352 			if (p[j].phflags & phLOCAL) {
2353 				// this bit is set temporarily to incidate a local phoneme, declared in
2354 				// in the current phoneme file
2355 				p[j].phflags &= ~phLOCAL;
2356 				fwrite(&p[j], sizeof(PHONEME_TAB), 1, f_phtab);
2357 			}
2358 		}
2359 		fwrite(&p[n], sizeof(PHONEME_TAB), 1, f_phtab); // include the extra list-terminator phoneme entry
2360 		free(p);
2361 	}
2362 }
2363 
2364 static void EndPhonemeTable()
2365 {
2366 	int ix;
2367 
2368 	if (n_phoneme_tabs == 0)
2369 		return;
2370 
2371 	// check that all referenced phonemes have been declared
2372 	for (ix = 0; ix < n_phcodes; ix++) {
2373 		if (phoneme_tab2[ix].type == phINVALID) {
2374 			error("Phoneme [%s] not declared, referenced at line %d",
2375 			      WordToString(phoneme_tab2[ix].mnemonic), (int)(phoneme_tab2[ix].program));
2376 			error_count++;
2377 			phoneme_tab2[ix].type = 0; // prevent the error message repeating
2378 		}
2379 	}
2380 
2381 	n_phcodes_list[n_phoneme_tabs-1] = n_phcodes;
2382 }
2383 
2384 static void StartPhonemeTable(const char *name)
2385 {
2386 	int ix;
2387 	int j;
2388 	PHONEME_TAB *p;
2389 
2390 	if (n_phoneme_tabs >= N_PHONEME_TABS-1) {
2391 		error("Too many phonemetables");
2392 		return;
2393 	}
2394 	p = (PHONEME_TAB *)calloc(sizeof(PHONEME_TAB), N_PHONEME_TAB);
2395 
2396 	if (p == NULL) {
2397 		error("Out of memory");
2398 		return;
2399 	}
2400 
2401 	memset(&phoneme_tab_list2[n_phoneme_tabs], 0, sizeof(PHONEME_TAB_LIST));
2402 	phoneme_tab_list2[n_phoneme_tabs].phoneme_tab_ptr = phoneme_tab2 = p;
2403 	memset(phoneme_tab_list2[n_phoneme_tabs].name, 0, sizeof(phoneme_tab_list2[n_phoneme_tabs].name));
2404 	strncpy0(phoneme_tab_list2[n_phoneme_tabs].name, name, N_PHONEME_TAB_NAME);
2405 	n_phcodes = 1;
2406 	phoneme_tab_list2[n_phoneme_tabs].includes = 0;
2407 
2408 	if (n_phoneme_tabs > 0) {
2409 		NextItem(tSTRING); // name of base phoneme table
2410 		for (ix = 0; ix < n_phoneme_tabs; ix++) {
2411 			if (strcmp(item_string, phoneme_tab_list2[ix].name) == 0) {
2412 				phoneme_tab_list2[n_phoneme_tabs].includes = ix+1;
2413 
2414 				// initialise the new phoneme table with the contents of this one
2415 				memcpy(phoneme_tab2, phoneme_tab_list2[ix].phoneme_tab_ptr, sizeof(PHONEME_TAB)*N_PHONEME_TAB);
2416 				n_phcodes = n_phcodes_list[ix];
2417 
2418 				// clear "local phoneme" bit"
2419 				for (j = 0; j < n_phcodes; j++)
2420 					phoneme_tab2[j].phflags &= ~phLOCAL;
2421 				break;
2422 			}
2423 		}
2424 		if (ix == n_phoneme_tabs && strcmp(item_string, "_") != 0)
2425 			error("Can't find base phonemetable '%s'", item_string);
2426 	} else
2427 		ReservePhCodes();
2428 
2429 	n_phoneme_tabs++;
2430 }
2431 
2432 static void CompilePhonemeFiles()
2433 {
2434 	int item;
2435 	FILE *f;
2436 	char buf[sizeof(path_home)+120];
2437 
2438 	linenum = 1;
2439 
2440 	count_references = 0;
2441 	duplicate_references = 0;
2442 	count_frames = 0;
2443 	n_procs = 0;
2444 
2445 	for (;;) {
2446 		if (feof(f_in)) {
2447 			// end of file, go back to previous from, from which this was included
2448 
2449 			if (stack_ix == 0)
2450 				break; // end of top level, finished
2451 			fclose(f_in);
2452 			f_in = stack[--stack_ix].file;
2453 			strcpy(current_fname, stack[stack_ix].fname);
2454 			linenum = stack[stack_ix].linenum;
2455 		}
2456 
2457 		item = NextItem(tKEYWORD);
2458 
2459 		switch (item)
2460 		{
2461 		case kUTF8_BOM:
2462 			break; // ignore bytes 0xef 0xbb 0xbf
2463 		case kINCLUDE:
2464 			NextItem(tSTRING);
2465 			sprintf(buf, "%s/%s", phsrc, item_string);
2466 
2467 			if ((stack_ix < N_STACK) && (f = fopen(buf, "rb")) != NULL) {
2468 				stack[stack_ix].linenum = linenum;
2469 				strcpy(stack[stack_ix].fname, current_fname);
2470 				stack[stack_ix++].file = f_in;
2471 
2472 				f_in = f;
2473 				strncpy0(current_fname, item_string, sizeof(current_fname));
2474 				linenum = 1;
2475 			} else
2476 				error("Missing file: %s", item_string);
2477 			break;
2478 		case kPHONEMETABLE:
2479 			EndPhonemeTable();
2480 			NextItem(tSTRING); // name of the new phoneme table
2481 			StartPhonemeTable(item_string);
2482 			break;
2483 		case kPHONEMESTART:
2484 			if (n_phoneme_tabs == 0) {
2485 				error("phonemetable is missing");
2486 				return;
2487 			}
2488 			CompilePhoneme(1);
2489 			break;
2490 		case kPROCEDURE:
2491 			CompilePhoneme(0);
2492 			break;
2493 		default:
2494 			if (!feof(f_in))
2495 				error("Keyword 'phoneme' expected");
2496 			break;
2497 		}
2498 	}
2499 	memset(&phoneme_tab2[n_phcodes+1], 0, sizeof(phoneme_tab2[n_phcodes+1]));
2500 	phoneme_tab2[n_phcodes+1].mnemonic = 0; // terminator
2501 }
2502 
2503 #pragma GCC visibility push(default)
2504 
2505 espeak_ng_STATUS
2506 espeak_ng_CompilePhonemeData(long rate,
2507                              FILE *log,
2508                              espeak_ng_ERROR_CONTEXT *context)
2509 {
2510 	return espeak_ng_CompilePhonemeDataPath(rate, NULL, NULL, log, context);
2511 }
2512 
2513 espeak_ng_STATUS
2514 espeak_ng_CompilePhonemeDataPath(long rate,
2515                                  const char *source_path,
2516                                  const char *destination_path,
2517                                  FILE *log,
2518                                  espeak_ng_ERROR_CONTEXT *context)
2519 {
2520 	if (!log) log = stderr;
2521 
2522 	char fname[sizeof(path_home)+40];
2523 	char phdst[sizeof(path_home)+40]; // Destination: path to the phondata/phontab/phonindex output files.
2524 
2525 	if (source_path) {
2526 		sprintf(phsrc, "%s", source_path);
2527 	} else {
2528 		sprintf(phsrc, "%s/../phsource", path_home);
2529 	}
2530 
2531 	if (destination_path) {
2532 		sprintf(phdst, "%s", destination_path);
2533 	} else {
2534 		sprintf(phdst, "%s", path_home);
2535 	}
2536 
2537 	samplerate_native = samplerate = rate;
2538 	LoadPhData(NULL, NULL);
2539 	if (LoadVoice("", 0) == NULL)
2540 		return ENS_VOICE_NOT_FOUND;
2541 
2542 	WavegenInit(rate, 0);
2543 	WavegenSetVoice(voice);
2544 
2545 	n_envelopes = 0;
2546 	error_count = 0;
2547 	resample_count = 0;
2548 	memset(markers_used, 0, sizeof(markers_used));
2549 
2550 	f_errors = log;
2551 
2552 	strncpy0(current_fname, "phonemes", sizeof(current_fname));
2553 
2554 	sprintf(fname, "%s/phonemes", phsrc);
2555 	fprintf(log, "Compiling phoneme data: %s\n", fname);
2556 	f_in = fopen(fname, "rb");
2557 	if (f_in == NULL)
2558 		return create_file_error_context(context, static_cast<espeak_ng_STATUS> (errno), fname);
2559 
2560 	sprintf(fname, "%s/%s", phsrc, "compile_report");
2561 	f_report = fopen(fname, "w");
2562 	if (f_report == NULL) {
2563 		int error = errno;
2564 		fclose(f_in);
2565 		return create_file_error_context(context, static_cast<espeak_ng_STATUS> (error), fname);
2566 	}
2567 
2568 	sprintf(fname, "%s/%s", phdst, "phondata-manifest");
2569 	if ((f_phcontents = fopen(fname, "w")) == NULL)
2570 		f_phcontents = stderr;
2571 
2572 	fprintf(f_phcontents,
2573 	        "# This file lists the type of data that has been compiled into the\n"
2574 	        "# phondata file\n"
2575 	        "#\n"
2576 	        "# The first character of a line indicates the type of data:\n"
2577 	        "#   S - A SPECT_SEQ structure\n"
2578 	        "#   W - A wavefile segment\n"
2579 	        "#   E - An envelope\n"
2580 	        "#\n"
2581 	        "# Address is the displacement within phondata of this item\n"
2582 	        "#\n"
2583 	        "#  Address  Data file\n"
2584 	        "#  -------  ---------\n");
2585 
2586 	sprintf(fname, "%s/%s", phdst, "phondata");
2587 	f_phdata = fopen(fname, "wb");
2588 	if (f_phdata == NULL) {
2589 		int error = errno;
2590 		fclose(f_in);
2591 		fclose(f_report);
2592 		fclose(f_phcontents);
2593 		return create_file_error_context(context, static_cast<espeak_ng_STATUS> (error), fname);
2594 	}
2595 
2596 	sprintf(fname, "%s/%s", phdst, "phonindex");
2597 	f_phindex = fopen(fname, "wb");
2598 	if (f_phindex == NULL) {
2599 		int error = errno;
2600 		fclose(f_in);
2601 		fclose(f_report);
2602 		fclose(f_phcontents);
2603 		fclose(f_phdata);
2604 		return create_file_error_context(context, static_cast<espeak_ng_STATUS> (error), fname);
dscal_(integer * n,double * da,double * dx,integer * incx)2605 	}
2606 
2607 	sprintf(fname, "%s/%s", phdst, "phontab");
2608 	f_phtab = fopen(fname, "wb");
2609 	if (f_phtab == NULL) {
2610 		int error = errno;
2611 		fclose(f_in);
2612 		fclose(f_report);
2613 		fclose(f_phcontents);
2614 		fclose(f_phdata);
2615 		fclose(f_phindex);
2616 		return create_file_error_context(context, static_cast<espeak_ng_STATUS> (error), fname);
2617 	}
2618 
2619 	sprintf(fname, "%s/compile_prog_log", phsrc);
2620 	f_prog_log = fopen(fname, "wb");
2621 
2622 	// write a word so that further data doesn't start at displ=0
2623 	Write4Bytes(f_phdata, version_phdata);
2624 	Write4Bytes(f_phdata, samplerate_native);
2625 	Write4Bytes(f_phindex, version_phdata);
2626 
2627 	memset(ref_hash_tab, 0, sizeof(ref_hash_tab));
2628 
2629 	n_phoneme_tabs = 0;
2630 	stack_ix = 0;
2631 	StartPhonemeTable("base");
2632 	CompilePhonemeFiles();
2633 
2634 	EndPhonemeTable();
2635 	WritePhonemeTables();
2636 
2637 	fprintf(f_errors, "\nRefs %d,  Reused %d\n", count_references, duplicate_references);
2638 
2639 	fclose(f_in);
2640 	fclose(f_phcontents);
2641 	fclose(f_phdata);
2642 	fclose(f_phindex);
2643 	fclose(f_phtab);
2644 	if (f_prog_log != NULL)
2645 		fclose(f_prog_log);
2646 
2647 	LoadPhData(NULL, NULL);
2648 
2649 	CompileReport();
2650 
2651 	fclose(f_report);
2652 
2653 	if (resample_count > 0) {
2654 		fprintf(f_errors, "\n%d WAV files resampled to %d Hz\n", resample_count, samplerate_native);
2655 		fprintf(log, "Compiled phonemes: %d errors, %d files resampled to %d Hz.\n", error_count, resample_count, samplerate_native);
2656 	} else
2657 		fprintf(log, "Compiled phonemes: %d errors.\n", error_count);
2658 
2659 	if (f_errors != stderr && f_errors != stdout)
2660 		fclose(f_errors);
2661 
2662 	espeak_ng_STATUS status = ReadPhondataManifest(context);
2663 	if (status != ENS_OK)
2664 		return status;
2665 
2666 	return error_count > 0 ? ENS_COMPILE_ERROR : ENS_OK;
2667 }
2668 
2669 #pragma GCC visibility pop
2670 
2671 static const char *preset_tune_names[] = {
2672 	"s1", "c1", "q1", "e1", NULL
2673 };
2674 
2675 static const TUNE default_tune = {
2676 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2677 	{ 0, 0, 0, 0 },
2678 	{ 0, 40, 24, 8, 0, 0, 0, 0 },
2679 	46, 57, PITCHfall, 16, 0, 0,
2680 	255, 78, 50, 255,
2681 	3, 5,
2682 	{ -7, -7, -7 }, { -7, -7, -7 },
2683 	PITCHfall, 64, 8,
2684 	PITCHfall, 70, 18, 24, 12,
2685 	PITCHfall, 70, 18, 24, 12, 0,
2686 	{ 0, 0, 0, 0, 0, 0, 0, 0 }, 0
dsdot_(integer * n,float * sx,integer * incx,float * sy,integer * incy)2687 };
2688 
2689 #define N_TUNE_NAMES  100
2690 
2691 MNEM_TAB envelope_names[] = {
2692 	{ "fall", 0 },
2693 	{ "rise", 2 },
2694 	{ "fall-rise", 4 },
2695 	{ "fall-rise2", 6 },
2696 	{ "rise-fall",  8 },
2697 	{ "fall-rise3", 10 },
2698 	{ "fall-rise4", 12 },
2699 	{ "fall2", 14 },
2700 	{ "rise2", 16 },
2701 	{ "rise-fall-rise", 18 },
2702 	{ NULL, -1 }
2703 };
2704 
2705 static int LookupEnvelopeName(const char *name)
2706 {
2707 	return LookupMnem(envelope_names, name);
2708 }
2709 
2710 #pragma GCC visibility push(default)
2711 
2712 espeak_ng_STATUS espeak_ng_CompileIntonation(FILE *log, espeak_ng_ERROR_CONTEXT *context)
2713 {
2714 	if (!log) log = stderr;
2715 
2716 	int ix;
2717 	char *p;
2718 	char c;
2719 	int keyword;
2720 	int n_tune_names = 0;
2721 	int done_split = 0;
2722 	int done_onset = 0;
2723 	int done_last = 0;
2724 	int n_preset_tunes = 0;
2725 	int found = 0;
2726 	int tune_number = 0;
2727 	FILE *f_out;
2728 	TUNE *tune_data;
2729 	TUNE new_tune;
2730 
2731 	char name[12];
2732 	char tune_names[N_TUNE_NAMES][12];
2733 	char buf[sizeof(path_home)+150];
2734 
2735 	error_count = 0;
2736 	f_errors = log;
2737 
2738 	sprintf(buf, "%s/../phsource/intonation.txt", path_home);
2739 	if ((f_in = fopen(buf, "r")) == NULL) {
2740 		sprintf(buf, "%s/../phsource/intonation", path_home);
2741 		if ((f_in = fopen(buf, "r")) == NULL) {
2742 			int error = errno;
2743 			fclose(f_errors);
2744 			return create_file_error_context(context, static_cast<espeak_ng_STATUS> (error), buf);
2745 		}
2746 	}
2747 
2748 	for (ix = 0; preset_tune_names[ix] != NULL; ix++)
2749 		strcpy(tune_names[ix], preset_tune_names[ix]);
2750 	n_tune_names = ix;
2751 	n_preset_tunes = ix;
2752 
2753 	// make a list of the tune names
2754 	while (!feof(f_in)) {
2755 		if (fgets(buf, sizeof(buf), f_in) == NULL)
2756 			break;
2757 
2758 		if ((memcmp(buf, "tune", 4) == 0) && isspace(buf[4])) {
2759 			p = &buf[5];
2760 			while (isspace(*p)) p++;
2761 
2762 			ix = 0;
2763 			while ((ix < (int)(sizeof(name) - 1)) && !isspace(*p))
2764 				name[ix++] = *p++;
2765 			name[ix] = 0;
2766 
2767 			found = 0;
2768 			for (ix = 0; ix < n_tune_names; ix++) {
2769 				if (strcmp(name, tune_names[ix]) == 0) {
2770 					found = 1;
2771 					break;
2772 				}
2773 			}
2774 
2775 			if (found == 0) {
2776 				strncpy0(tune_names[n_tune_names++], name, sizeof(name));
2777 
2778 				if (n_tune_names >= N_TUNE_NAMES)
2779 					break;
2780 			}
2781 		}
2782 	}
2783 	rewind(f_in);
2784 	linenum = 1;
2785 
2786 	tune_data = (n_tune_names == 0) ? NULL : (TUNE *)calloc(n_tune_names, sizeof(TUNE));
2787 	if (tune_data == NULL) {
2788 		fclose(f_in);
2789 		fclose(f_errors);
2790 		return static_cast<espeak_ng_STATUS> (ENOMEM);
2791 	}
2792 
2793 	sprintf(buf, "%s/intonations", path_home);
2794 	f_out = fopen(buf, "wb");
2795 	if (f_out == NULL) {
2796 		int error = errno;
2797 		fclose(f_in);
2798 		fclose(f_errors);
2799 		free(tune_data);
2800 		return create_file_error_context(context, static_cast<espeak_ng_STATUS> (error), buf);
2801 	}
2802 
2803 	while (!feof(f_in)) {
2804 		keyword = NextItem(tINTONATION);
2805 
2806 		switch (keyword)
2807 		{
dspmv_(const char * uplo,integer * n,double * alpha,double * ap,double * x,integer * incx,double * beta,double * y,integer * incy)2808 		case kTUNE:
2809 			done_split = 0;
2810 
2811 			memcpy(&new_tune, &default_tune, sizeof(TUNE));
2812 			NextItem(tSTRING);
2813 			strncpy0(new_tune.name, item_string, sizeof(new_tune.name));
2814 
2815 			found = 0;
2816 			tune_number = 0;
2817 			for (ix = 0; ix < n_tune_names; ix++) {
2818 				if (strcmp(new_tune.name, tune_names[ix]) == 0) {
2819 					found = 1;
2820 					tune_number = ix;
2821 
2822 					if (tune_data[ix].name[0] != 0)
2823 						found = 2;
2824 					break;
2825 				}
2826 			}
2827 			if (found == 2)
2828 				error("Duplicate tune name: '%s'", new_tune.name);
2829 			if (found == 0)
2830 				error("Bad tune name: '%s;", new_tune.name);
2831 			break;
2832 		case kENDTUNE:
2833 			if (!found) continue;
2834 			if (done_onset == 0) {
2835 				new_tune.unstr_start[0] = new_tune.unstr_start[1];
2836 				new_tune.unstr_end[0] = new_tune.unstr_end[1];
2837 			}
2838 			if (done_last == 0) {
2839 				new_tune.unstr_start[2] = new_tune.unstr_start[1];
2840 				new_tune.unstr_end[2] = new_tune.unstr_end[1];
2841 			}
2842 			memcpy(&tune_data[tune_number], &new_tune, sizeof(TUNE));
2843 			break;
2844 		case kTUNE_PREHEAD:
2845 			new_tune.prehead_start = NextItem(tNUMBER);
2846 			new_tune.prehead_end = NextItem(tNUMBER);
2847 			break;
2848 		case kTUNE_ONSET:
2849 			new_tune.onset = NextItem(tNUMBER);
2850 			new_tune.unstr_start[0] = NextItem(tSIGNEDNUMBER);
2851 			new_tune.unstr_end[0] = NextItem(tSIGNEDNUMBER);
2852 			done_onset = 1;
2853 			break;
2854 		case kTUNE_HEADLAST:
2855 			new_tune.head_last = NextItem(tNUMBER);
2856 			new_tune.unstr_start[2] = NextItem(tSIGNEDNUMBER);
2857 			new_tune.unstr_end[2] = NextItem(tSIGNEDNUMBER);
2858 			done_last = 1;
2859 			break;
2860 		case kTUNE_HEADENV:
2861 			NextItem(tSTRING);
2862 			if ((ix = LookupEnvelopeName(item_string)) < 0)
2863 				error("Bad envelope name: '%s'", item_string);
2864 			else
2865 				new_tune.stressed_env = ix;
2866 			new_tune.stressed_drop = NextItem(tNUMBER);
2867 			break;
2868 		case kTUNE_HEAD:
2869 			new_tune.head_max_steps = NextItem(tNUMBER);
2870 			new_tune.head_start = NextItem(tNUMBER);
2871 			new_tune.head_end = NextItem(tNUMBER);
2872 			new_tune.unstr_start[1] = NextItem(tSIGNEDNUMBER);
2873 			new_tune.unstr_end[1] = NextItem(tSIGNEDNUMBER);
2874 			break;
2875 		case kTUNE_HEADEXTEND:
2876 			// up to 8 numbers
2877 			for (ix = 0; ix < (int)(sizeof(new_tune.head_extend)); ix++) {
2878 				if (!isdigit(c = CheckNextChar()) && (c != '-'))
2879 					break;
2880 
2881 				new_tune.head_extend[ix] = (NextItem(tSIGNEDNUMBER) * 64) / 100; // convert from percentage to 64ths
2882 			}
2883 			new_tune.n_head_extend = ix; // number of values
2884 			break;
2885 		case kTUNE_NUCLEUS0:
2886 			NextItem(tSTRING);
2887 			if ((ix = LookupEnvelopeName(item_string)) < 0) {
2888 				error("Bad envelope name: '%s'", item_string);
2889 				break;
2890 			}
2891 			new_tune.nucleus0_env = ix;
2892 			new_tune.nucleus0_max = NextItem(tNUMBER);
2893 			new_tune.nucleus0_min = NextItem(tNUMBER);
2894 			break;
2895 		case kTUNE_NUCLEUS1:
2896 			NextItem(tSTRING);
2897 			if ((ix = LookupEnvelopeName(item_string)) < 0) {
2898 				error("Bad envelope name: '%s'", item_string);
2899 				break;
2900 			}
2901 			new_tune.nucleus1_env = ix;
2902 			new_tune.nucleus1_max = NextItem(tNUMBER);
2903 			new_tune.nucleus1_min = NextItem(tNUMBER);
2904 			new_tune.tail_start = NextItem(tNUMBER);
2905 			new_tune.tail_end = NextItem(tNUMBER);
2906 
2907 			if (!done_split) {
2908 				// also this as the default setting for 'split'
2909 				new_tune.split_nucleus_env = ix;
2910 				new_tune.split_nucleus_max = new_tune.nucleus1_max;
2911 				new_tune.split_nucleus_min = new_tune.nucleus1_min;
2912 				new_tune.split_tail_start = new_tune.tail_start;
2913 				new_tune.split_tail_end = new_tune.tail_end;
2914 			}
2915 			break;
2916 		case kTUNE_SPLIT:
2917 			NextItem(tSTRING);
2918 			if ((ix = LookupEnvelopeName(item_string)) < 0) {
2919 				error("Bad envelope name: '%s'", item_string);
2920 				break;
2921 			}
2922 			done_split = 1;
2923 			new_tune.split_nucleus_env = ix;
2924 			new_tune.split_nucleus_max = NextItem(tNUMBER);
2925 			new_tune.split_nucleus_min = NextItem(tNUMBER);
2926 			new_tune.split_tail_start = NextItem(tNUMBER);
2927 			new_tune.split_tail_end = NextItem(tNUMBER);
2928 			NextItem(tSTRING);
2929 			item_string[12] = 0;
2930 			for (ix = 0; ix < n_tune_names; ix++) {
2931 				if (strcmp(item_string, tune_names[ix]) == 0)
2932 					break;
2933 			}
2934 
2935 			if (ix == n_tune_names)
2936 				error("Tune '%s' not found", item_string);
2937 			else
2938 				new_tune.split_tune = ix;
2939 			break;
2940 		default:
2941 			error("Unexpected: '%s'", item_string);
2942 			break;
2943 		}
2944 	}
2945 
2946 	for (ix = 0; ix < n_preset_tunes; ix++) {
2947 		if (tune_data[ix].name[0] == 0)
2948 			error("Tune '%s' not defined", preset_tune_names[ix]);
2949 	}
2950 	fwrite(tune_data, n_tune_names, sizeof(TUNE), f_out);
2951 	free(tune_data);
2952 	fclose(f_in);
2953 	fclose(f_out);
2954 
2955 	fprintf(log, "Compiled %d intonation tunes: %d errors.\n", n_tune_names, error_count);
2956 
2957 	LoadPhData(NULL, NULL);
2958 
2959 	return error_count > 0 ? ENS_COMPILE_ERROR : ENS_OK;
2960 }
2961 #pragma GCC visibility pop
2962 #endif
2963 
2964