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, &_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