OSDN Git Service

7f4ea60834f45b41422f0b881ffd8e065996b369
[nvdajp/nvdajpmiscdep.git] / include / espeak / src / voices.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005 to 2013 by Jonathan Duddington                     *
3  *   email: jonsd@users.sourceforge.net                                    *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 3 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write see:                           *
17  *               <http://www.gnu.org/licenses/>.                           *
18  ***************************************************************************/
19
20 #include "StdAfx.h"
21
22 #include "stdio.h"
23 #include "ctype.h"
24 #include "wctype.h"
25 #include "string.h"
26 #include "stdlib.h"
27 #include "speech.h"
28
29 #ifdef PLATFORM_WINDOWS
30 #include "windows.h"
31 #else
32 #ifdef PLATFORM_RISCOS
33 #include "kernel.h"
34 #else
35 #include "dirent.h"
36 #endif
37 #endif
38
39 #include "speak_lib.h"
40 #include "phoneme.h"
41 #include "synthesize.h"
42 #include "voice.h"
43 #include "translate.h"
44
45
46 MNEM_TAB genders [] = {
47         {"unknown", 0},
48         {"male", 1},
49         {"female", 2},
50         {NULL, 0 }
51 };
52
53 int tone_points[12] = {600,170, 1200,135, 2000,110, 3000,110, -1,0};
54 //int tone_points[12] = {250,200,  400,170, 600,170, 1200,135, 2000,110, -1,0};
55
56 // limit the rate of change for each formant number
57 //static int formant_rate_22050[9] = {50, 104, 165, 230, 220, 220, 220, 220, 220};  // values for 22kHz sample rate
58 //static int formant_rate_22050[9] = {240, 180, 180, 180, 180, 180, 180, 180, 180};  // values for 22kHz sample rate
59 static int formant_rate_22050[9] = {240, 170, 170, 170, 170, 170, 170, 170, 170};  // values for 22kHz sample rate
60 int formant_rate[9];         // values adjusted for actual sample rate
61
62
63
64 #define DEFAULT_LANGUAGE_PRIORITY  5
65 #define N_VOICES_LIST  250
66 static int n_voices_list = 0;
67 static espeak_VOICE *voices_list[N_VOICES_LIST];
68 static int len_path_voices;
69
70 espeak_VOICE current_voice_selected;
71
72
73 enum {
74         V_NAME = 1,
75         V_LANGUAGE,
76         V_GENDER,
77         V_TRANSLATOR,
78         V_PHONEMES,
79         V_DICTIONARY,
80
81 // these affect voice quality, are independent of language
82         V_FORMANT,
83         V_PITCH,
84         V_ECHO,
85         V_FLUTTER,
86         V_ROUGHNESS,
87         V_CLARITY,
88         V_TONE,
89         V_VOICING,
90         V_BREATH,
91         V_BREATHW,
92
93 // these override defaults set by the translator
94         V_WORDGAP,
95         V_INTONATION,
96         V_TUNES,
97         V_STRESSLENGTH,
98         V_STRESSAMP,
99         V_STRESSADD,
100         V_DICTRULES,
101         V_STRESSRULE,
102         V_STRESSOPT,
103         V_CHARSET,
104         V_NUMBERS,
105         V_OPTION,
106
107         V_MBROLA,
108         V_KLATT,
109         V_FAST,
110         V_SPEED,
111         V_DICTMIN,
112         V_ALPHABET2,
113
114 // these need a phoneme table to have been specified
115         V_REPLACE,
116         V_CONSONANTS
117 };
118
119
120
121 static MNEM_TAB options_tab[] = {
122         {"reduce_t",  LOPT_REDUCE_T},
123         {"bracket", LOPT_BRACKET_PAUSE},
124         {NULL,   -1}
125 };
126
127 static MNEM_TAB keyword_tab[] = {
128         {"name",       V_NAME},
129         {"language",   V_LANGUAGE},
130         {"gender",     V_GENDER},
131
132         {"formant",    V_FORMANT},
133         {"pitch",      V_PITCH},
134         {"phonemes",   V_PHONEMES},
135         {"translator", V_TRANSLATOR},
136         {"dictionary", V_DICTIONARY},
137         {"stressLength", V_STRESSLENGTH},
138         {"stressAmp",  V_STRESSAMP},
139         {"stressAdd",  V_STRESSADD},
140         {"intonation", V_INTONATION},
141         {"tunes",      V_TUNES},
142         {"dictrules",   V_DICTRULES},
143         {"stressrule", V_STRESSRULE},
144         {"stressopt",  V_STRESSOPT},
145         {"charset",    V_CHARSET},
146         {"replace",    V_REPLACE},
147         {"words",      V_WORDGAP},
148         {"echo",       V_ECHO},
149         {"flutter",    V_FLUTTER},
150         {"roughness",  V_ROUGHNESS},
151         {"clarity",    V_CLARITY},
152         {"tone",       V_TONE},
153         {"voicing",    V_VOICING},
154         {"breath",     V_BREATH},
155         {"breathw",    V_BREATHW},
156         {"numbers",    V_NUMBERS},
157         {"option",     V_OPTION},
158         {"mbrola",     V_MBROLA},
159         {"consonants", V_CONSONANTS},
160         {"klatt",      V_KLATT},
161         {"fast_test2",  V_FAST},
162         {"speed",      V_SPEED},
163         {"dict_min",   V_DICTMIN},
164         {"alphabet2",  V_ALPHABET2},
165
166         // these just set a value in langopts.param[]
167         {"l_dieresis", 0x100+LOPT_DIERESES},
168 //      {"l_lengthen", 0x100+LOPT_IT_LENGTHEN},
169         {"l_prefix",   0x100+LOPT_PREFIXES},
170         {"l_regressive_v", 0x100+LOPT_REGRESSIVE_VOICING},
171         {"l_unpronouncable", 0x100+LOPT_UNPRONOUNCABLE},
172         {"l_sonorant_min", 0x100+LOPT_SONORANT_MIN},
173         {"l_length_mods", 0x100+LOPT_LENGTH_MODS},
174         {"apostrophe", 0x100+LOPT_APOSTROPHE},
175         {NULL,   0}
176 };
177
178 #define N_VOICE_VARIANTS   12
179 const char variants_either[N_VOICE_VARIANTS] = {1,2,12,3,13,4,14,5,11,0};
180 const char variants_male[N_VOICE_VARIANTS] = {1,2,3,4,5,0};
181 const char variants_female[N_VOICE_VARIANTS] = {11,12,13,14,0};
182 const char *variant_lists[3] = {variants_either, variants_male, variants_female};
183
184 static voice_t voicedata;
185 voice_t *voice = &voicedata;
186
187
188 static char *fgets_strip(char *buf, int size, FILE *f_in)
189 {//======================================================
190 // strip trailing spaces, and truncate lines at // comment
191         int len;
192         char *p;
193
194         if(fgets(buf,size,f_in) == NULL)
195                 return(NULL);
196
197         if(buf[0] == '#')
198         {
199                 buf[0] = 0;
200                 return(buf);
201         }
202
203         len = strlen(buf);
204         while((--len > 0) && isspace(buf[len]))
205                 buf[len] = 0;
206
207         if((p = strstr(buf,"//")) != NULL)
208                 *p = 0;
209
210         return(buf);
211 }
212
213
214 static int LookupTune(const char *name)
215 {//====================================
216         int ix;
217
218         for(ix=0; ix<n_tunes; ix++)
219         {
220                 if(strcmp(name, tunes[ix].name) == 0)
221                         return(ix);
222         }
223         return(-1);
224 }  // end of LookupTune
225
226
227
228 static void SetToneAdjust(voice_t *voice, int *tone_pts)
229 {//=====================================================
230         int ix;
231         int pt;
232         int y;
233         int freq1=0;
234         int freq2;
235         int height1 = tone_pts[1];
236         int height2;
237         double rate;
238
239         for(pt=0; pt<12; pt+=2)
240         {
241                 if(tone_pts[pt] == -1)
242                 {
243                         tone_pts[pt] = N_TONE_ADJUST*8;
244                         if(pt > 0)
245                                 tone_pts[pt+1] = tone_pts[pt-1];
246                 }
247                 freq2 = tone_pts[pt] / 8;   // 8Hz steps
248                 height2 = tone_pts[pt+1];
249                 if((freq2 - freq1) > 0)
250                 {
251                         rate = double(height2-height1)/(freq2-freq1);
252
253                         for(ix=freq1; ix<freq2; ix++)
254                         {
255                                 y = height1 + int(rate * (ix-freq1));
256                                 if(y > 255)
257                                         y = 255;
258                                 voice->tone_adjust[ix] = y;
259                         }
260                 }
261                 freq1 = freq2;
262                 height1 = height2;
263         }
264 }
265
266
267 void ReadTonePoints(char *string, int *tone_pts)
268 {//=============================================
269 // tone_pts[] is int[12]
270         int ix;
271
272         for(ix=0; ix<12; ix++)
273                 tone_pts[ix] = -1;
274
275         sscanf(string,"%d %d %d %d %d %d %d %d %d %d",
276                    &tone_pts[0],&tone_pts[1],&tone_pts[2],&tone_pts[3],
277                    &tone_pts[4],&tone_pts[5],&tone_pts[6],&tone_pts[7],
278                    &tone_pts[8],&tone_pts[9]);
279 }
280
281
282
283
284 static espeak_VOICE *ReadVoiceFile(FILE *f_in, const char *fname, const char*leafname)
285 {//===================================================================================
286 // Read a Voice file, allocate a VOICE_DATA and set data from the
287 // file's  language, gender, name  lines
288
289         char linebuf[120];
290         char vname[80];
291         char vgender[80];
292         char vlanguage[80];
293         char languages[300];  // allow space for several alternate language names and priorities
294
295
296         unsigned int len;
297         int langix = 0;
298         int n_languages = 0;
299         char *p;
300         espeak_VOICE *voice_data;
301         int priority;
302         int age;
303         int n_variants = 3;    // default, number of variants of this voice before using another voice
304         int gender;
305
306 #ifdef PLATFORM_WINDOWS
307         char fname_buf[sizeof(path_home)+15];
308         if(memcmp(leafname,"mb-",3) == 0)
309         {
310                 // check whether the mbrola speech data is present for this voice
311                 memcpy(vname,&leafname[3],3);
312                 vname[3] = 0;
313                 sprintf(fname_buf,"%s/mbrola/%s",path_home,vname);
314
315                 if(GetFileLength(fname_buf) <= 0)
316                         return(0);
317         }
318 #endif
319
320         vname[0] = 0;
321         vgender[0] = 0;
322         age = 0;
323
324         while(fgets_strip(linebuf,sizeof(linebuf),f_in) != NULL)
325         {
326                 if(memcmp(linebuf,"name",4)==0)
327                 {
328                         p = &linebuf[4];
329                         while(isspace(*p)) p++;
330                         strncpy0(vname,p,sizeof(vname));
331                 }
332                 else if(memcmp(linebuf,"language",8)==0)
333                 {
334                         priority = DEFAULT_LANGUAGE_PRIORITY;
335                         vlanguage[0] = 0;
336
337                         sscanf(&linebuf[8],"%s %d",vlanguage,&priority);
338                         len = strlen(vlanguage) + 2;
339                         // check for space in languages[]
340                         if(len < (sizeof(languages)-langix-1))
341                         {
342                                 languages[langix] = priority;
343
344                                 strcpy(&languages[langix+1],vlanguage);
345                                 langix += len;
346                                 n_languages++;
347                         }
348                 }
349                 else if(memcmp(linebuf,"gender",6)==0)
350                 {
351                         sscanf(&linebuf[6],"%s %d",vgender,&age);
352                 }
353                 else if(memcmp(linebuf,"variants",8)==0)
354                 {
355                         sscanf(&linebuf[8],"%d",&n_variants);
356                 }
357         }
358         languages[langix++] = 0;
359
360         gender = LookupMnem(genders,vgender);
361
362         if(n_languages == 0)
363         {
364                 return(NULL);    // no language lines in the voice file
365         }
366
367         p = (char *)calloc(sizeof(espeak_VOICE) + langix + strlen(fname) + strlen(vname) + 3, 1);
368         voice_data = (espeak_VOICE *)p;
369         p = &p[sizeof(espeak_VOICE)];
370
371         memcpy(p,languages,langix);
372         voice_data->languages = p;
373
374         strcpy(&p[langix],fname);
375         voice_data->identifier = &p[langix];
376         voice_data->name = &p[langix];
377
378         if(vname[0] != 0)
379         {
380                 langix += strlen(fname)+1;
381                 strcpy(&p[langix],vname);
382                 voice_data->name = &p[langix];
383         }
384
385         voice_data->age = age;
386         voice_data->gender = gender;
387         voice_data->variant = 0;
388         voice_data->xx1 = n_variants;
389         return(voice_data);
390 }  // end of ReadVoiceFile
391
392
393
394
395 void VoiceReset(int tone_only)
396 {//===========================
397 // Set voice to the default values
398
399         int  pk;
400         static unsigned char default_heights[N_PEAKS] = {130,128,120,116,100,100,128,128,128};  // changed for v.1.47
401         static unsigned char default_widths[N_PEAKS] = {140,128,128,160,171,171,128,128,128};
402 //      static unsigned char default_heights[N_PEAKS] = {128,128,120,120,110,110,128,128,128};  // previous version
403 //      static unsigned char default_widths[N_PEAKS] = {128,128,128,160,171,171,128,128,128};
404
405         static int breath_widths[N_PEAKS] = {0,200,200,400,400,400,600,600,600};
406
407         // default is:  pitch 80,118
408         voice->pitch_base = 0x47000;
409         voice->pitch_range = 4104;
410
411 //      default is:  pitch 80,117
412 //      voice->pitch_base = 0x47000;
413 //      voice->pitch_range = 3996;
414
415         voice->formant_factor = 256;
416
417         voice->speed_percent = 100;
418         voice->echo_delay = 0;
419         voice->echo_amp = 0;
420         voice->flutter = 64;
421         voice->n_harmonic_peaks = 5;
422         voice->peak_shape = 0;
423         voice->voicing = 64;
424         voice->consonant_amp = 90;  // change from 100 to 90 for v.1.47
425         voice->consonant_ampv = 100;
426         voice->samplerate = samplerate_native;
427         memset(voice->klattv,0,sizeof(voice->klattv));
428
429         speed.fast_settings[0] = 450;
430         speed.fast_settings[1] = 800;
431         speed.fast_settings[2] = 175;
432
433 #ifdef PLATFORM_RISCOS
434         voice->roughness = 1;
435 #else
436         voice->roughness = 2;
437 #endif
438
439         InitBreath();
440         for(pk=0; pk<N_PEAKS; pk++)
441         {
442                 voice->freq[pk] = 256;
443                 voice->height[pk] = default_heights[pk]*2;
444                 voice->width[pk] = default_widths[pk]*2;
445                 voice->breath[pk] = 0;
446                 voice->breathw[pk] = breath_widths[pk];  // default breath formant woidths
447                 voice->freqadd[pk] = 0;
448
449                 // adjust formant smoothing depending on sample rate
450                 formant_rate[pk] = (formant_rate_22050[pk] * 22050)/samplerate;
451         }
452
453         // This table provides the opportunity for tone control.
454         // Adjustment of harmonic amplitudes, steps of 8Hz
455         // value of 128 means no change
456 //      memset(voice->tone_adjust,128,sizeof(voice->tone_adjust));
457         SetToneAdjust(voice,tone_points);
458
459         // default values of speed factors
460         voice->speedf1 = 256;
461         voice->speedf2 = 238;
462         voice->speedf3 = 232;
463
464         if(tone_only == 0)
465         {
466                 n_replace_phonemes = 0;
467                 option_quiet = 0;
468                 LoadMbrolaTable(NULL,NULL,0);
469         }
470 }  // end of VoiceReset
471
472
473 static void VoiceFormant(char *p)
474 {//==============================
475         // Set parameters for a formant
476         int ix;
477         int formant;
478         int freq = 100;
479         int height = 100;
480         int width = 100;
481         int freqadd = 0;
482
483         ix = sscanf(p,"%d %d %d %d %d",&formant,&freq,&height,&width,&freqadd);
484         if(ix < 2)
485                 return;
486
487         if((formant < 0) || (formant > 8))
488                 return;
489
490         if(freq >= 0)
491                 voice->freq[formant] = int(freq * 2.56001);
492         if(height >= 0)
493                 voice->height[formant] = int(height * 2.56001);
494         if(width >= 0)
495                 voice->width[formant] = int(width * 2.56001);
496         voice->freqadd[formant] = freqadd;
497 }
498
499
500
501
502
503 static void PhonemeReplacement(int type, char *p)
504 {//==============================================
505         int n;
506         int  phon;
507         int flags = 0;
508         char phon_string1[12];
509         char phon_string2[12];
510
511         strcpy(phon_string2,"NULL");
512         n = sscanf(p,"%d %s %s",&flags,phon_string1,phon_string2);
513         if((n < 2) || (n_replace_phonemes >= N_REPLACE_PHONEMES))
514                 return;
515
516         if((phon = LookupPhonemeString(phon_string1)) == 0)
517                 return;  // not recognised
518
519         replace_phonemes[n_replace_phonemes].old_ph = phon;
520         replace_phonemes[n_replace_phonemes].new_ph = LookupPhonemeString(phon_string2);
521         replace_phonemes[n_replace_phonemes++].type = flags;
522 }  //  end of PhonemeReplacement
523
524
525
526 static int Read8Numbers(char *data_in,int *data)
527 {//=============================================
528 // Read 8 integer numbers
529         memset(data, 0, 8+sizeof(int));
530         return(sscanf(data_in,"%d %d %d %d %d %d %d %d",
531                                   &data[0],&data[1],&data[2],&data[3],&data[4],&data[5],&data[6],&data[7]));
532 }
533
534
535 static unsigned int StringToWord2(const char *string)
536 {//======================================================
537 // Convert a language name string to a word such as L('e','n')
538         int ix;
539         int c;
540         unsigned int value = 0;
541
542         for(ix=0; (ix<4) && ((c = string[ix]) != 0); ix++)
543         {
544                 value = (value << 8) | (c & 0xff);
545         }
546         return(value);
547 }
548
549
550 voice_t *LoadVoice(const char *vname, int control)
551 {//===============================================
552 // control, bit 0  1= no_default
553 //          bit 1  1 = change tone only, not language
554 //          bit 2  1 = don't report error on LoadDictionary
555 //          bit 4  1 = vname = full path
556
557         FILE *f_voice = NULL;
558         char *p;
559         int  key;
560         int  ix;
561         int  n;
562         int  value;
563         int  value2;
564         int  langix = 0;
565         int  tone_only = control & 2;
566         int  language_set = 0;
567         int  phonemes_set = 0;
568         int  stress_amps_set = 0;
569         int  stress_lengths_set = 0;
570         int  stress_add_set = 0;
571         int  conditional_rules = 0;
572         LANGUAGE_OPTIONS *langopts = NULL;
573
574         Translator *new_translator = NULL;
575
576         char voicename[40];
577         char language_name[40];
578         char translator_name[40];
579         char new_dictionary[40];
580         char phonemes_name[40];
581         char option_name[40];
582         const char *language_type;
583         char buf[sizeof(path_home)+30];
584         char path_voices[sizeof(path_home)+12];
585
586         int dict_min = 0;
587         int stress_amps[8];
588         int stress_lengths[8];
589         int stress_add[8];
590         char names[8][40];
591         char name1[40];
592         char name2[80];
593         const char *voice_dir;
594
595         int pitch1;
596         int pitch2;
597
598         static char voice_identifier[40];  // file name for  current_voice_selected
599         static char voice_name[40];        // voice name for current_voice_selected
600         static char voice_languages[100];  // list of languages and priorities for current_voice_selected
601
602         // which directory to look for a named voice. List of voice names, must end in a space.
603         static const char *voices_asia =
604                 "bn fa fa-pin hi hy hy-west id ka kn ku ml ms ne pa ta te tr vi vi-hue zh zh-yue ";
605         static const char *voices_europe =
606                 "an bg bs ca cs cy da de el en en-us es et fi fr fr-be ga hr hu is it lt lv mk nl no pl pt-pt ro ru sk sq sr sv ";
607
608
609         strncpy0(voicename, vname, sizeof(voicename));
610         if(control & 0x10)
611         {
612                 strcpy(buf,vname);
613                 if(GetFileLength(buf) <= 0)
614                         return(NULL);
615         }
616         else
617         {
618                 if(voicename[0]==0)
619                         strcpy(voicename,"default");
620
621                 sprintf(path_voices,"%s%cvoices%c",path_home,PATHSEP,PATHSEP);
622                 sprintf(buf,"%s%s",path_voices,voicename);  // first, look in the main voices directory
623
624                 if(GetFileLength(buf) <= 0)
625                 {
626                         // then look in the appropriate subdirectory
627                         if((voicename[0]=='m') && (voicename[1]=='b'))
628                         {
629                                 voice_dir = "mb";   // mbrola voices
630                         }
631                         else
632                         {
633                                 sprintf(name2, "%s ", voicename);
634                                 if(strstr(voices_europe, voicename) != NULL)
635                                         voice_dir = "europe";
636                                 else if(strstr(voices_asia, voicename) != NULL)
637                                         voice_dir = "asia";
638                                 else
639                                         voice_dir = "other";
640
641                                 sprintf(buf,"%s%s%c%s", path_voices,voice_dir,PATHSEP,voicename);
642
643                                 if(GetFileLength(buf) <= 0)
644                                 {
645                                         // if not found, look in "test" sub-directory
646                                         sprintf(buf,"%stest%c%s",path_voices,PATHSEP,voicename);
647                                 }
648                         }
649                 }
650         }
651
652         f_voice = fopen(buf,"r");
653
654         language_type = "en";    // default
655         if(f_voice == NULL)
656         {
657                 if(control & 3)
658                         return(NULL);  // can't open file
659
660                 if(SelectPhonemeTableName(voicename) >= 0)
661                         language_type = voicename;
662         }
663
664         if(!tone_only && (translator != NULL))
665         {
666                 DeleteTranslator(translator);
667                 translator = NULL;
668         }
669
670         strcpy(translator_name,language_type);
671         strcpy(new_dictionary,language_type);
672         strcpy(phonemes_name,language_type);
673
674
675         if(!tone_only)
676         {
677                 voice = &voicedata;
678                 strncpy0(voice_identifier,vname,sizeof(voice_identifier));
679                 voice_name[0] = 0;
680                 voice_languages[0] = 0;
681
682                 current_voice_selected.identifier = voice_identifier;
683                 current_voice_selected.name = voice_name;
684                 current_voice_selected.languages = voice_languages;
685         }
686         else
687         {
688                 // append the variant file name to the voice identifier
689                 if((p = strchr(voice_identifier,'+')) != NULL)
690                         *p = 0;    // remove previous variant name
691                 sprintf(buf,"+%s",&vname[3]);    // omit  !v/  from the variant filename
692                 strcat(voice_identifier,buf);
693                 langopts = &translator->langopts;
694         }
695         VoiceReset(tone_only);
696
697         if(!tone_only)
698                 SelectPhonemeTableName(phonemes_name);  // set up phoneme_tab
699
700
701         while((f_voice != NULL) && (fgets_strip(buf,sizeof(buf),f_voice) != NULL))
702         {
703                 // isolate the attribute name
704                 for(p=buf; (*p != 0) && !isspace(*p); p++);
705                 *p++ = 0;
706
707                 if(buf[0] == 0) continue;
708
709                 key = LookupMnem(keyword_tab, buf);
710
711                 switch(key)
712                 {
713                 case V_LANGUAGE:
714                 {
715                         unsigned int len;
716                         int priority;
717
718                         if(tone_only)
719                                 break;
720
721                         priority = DEFAULT_LANGUAGE_PRIORITY;
722                         language_name[0] = 0;
723
724                         sscanf(p,"%s %d",language_name,&priority);
725                         if(strcmp(language_name,"variant") == 0)
726                                 break;
727
728                         len = strlen(language_name) + 2;
729                         // check for space in languages[]
730                         if(len < (sizeof(voice_languages)-langix-1))
731                         {
732                                 voice_languages[langix] = priority;
733
734                                 strcpy(&voice_languages[langix+1],language_name);
735                                 langix += len;
736                         }
737
738                         // only act on the first language line
739                         if(language_set == 0)
740                         {
741                                 language_type = strtok(language_name,"-");
742                                 language_set = 1;
743                                 strcpy(translator_name,language_type);
744                                 strcpy(new_dictionary,language_type);
745                                 strcpy(phonemes_name,language_type);
746                                 SelectPhonemeTableName(phonemes_name);
747
748                                 if(new_translator != NULL)
749                                         DeleteTranslator(new_translator);
750
751                                 new_translator = SelectTranslator(translator_name);
752                                 langopts = &new_translator->langopts;
753                                 strncpy0(voice->language_name, language_name, sizeof(voice->language_name));
754                         }
755                 }
756                 break;
757
758                 case V_NAME:
759                         if(tone_only == 0)
760                         {
761                                 while(isspace(*p)) p++;
762                                 strncpy0(voice_name,p,sizeof(voice_name));
763                         }
764                         break;
765
766                 case V_GENDER:
767                 {
768                         int age = 0;
769                         char vgender[80];
770                         sscanf(p,"%s %d",vgender,&age);
771                         current_voice_selected.gender = LookupMnem(genders,vgender);
772                         current_voice_selected.age = age;
773                 }
774                 break;
775
776                 case V_TRANSLATOR:
777                         if(tone_only) break;
778
779                         sscanf(p,"%s",translator_name);
780
781                         if(new_translator != NULL)
782                                 DeleteTranslator(new_translator);
783
784                         new_translator = SelectTranslator(translator_name);
785                         langopts = &new_translator->langopts;
786                         break;
787
788                 case V_DICTIONARY:        // dictionary
789                         sscanf(p,"%s",new_dictionary);
790                         break;
791
792                 case V_PHONEMES:        // phoneme table
793                         sscanf(p,"%s",phonemes_name);
794                         break;
795
796                 case V_FORMANT:
797                         VoiceFormant(p);
798                         break;
799
800                 case V_PITCH:
801                 {
802                         double factor;
803                         // default is  pitch 82 118
804                         n = sscanf(p,"%d %d",&pitch1,&pitch2);
805                         voice->pitch_base = (pitch1 - 9) << 12;
806                         voice->pitch_range = (pitch2 - pitch1) * 108;
807                         factor = double(pitch1 - 82)/82;
808                         voice->formant_factor = (int)((1+factor/4) * 256);  // nominal formant shift for a different voice pitch
809                 }
810                 break;
811
812                 case V_STRESSLENGTH:   // stressLength
813                         stress_lengths_set = Read8Numbers(p,stress_lengths);
814                         break;
815
816                 case V_STRESSAMP:   // stressAmp
817                         stress_amps_set = Read8Numbers(p,stress_amps);
818                         break;
819
820                 case V_STRESSADD:   // stressAdd
821                         stress_add_set = Read8Numbers(p,stress_add);
822                         break;
823
824                 case V_INTONATION:   // intonation
825                         sscanf(p,"%d %d",&option_tone_flags,&option_tone2);
826                         if((option_tone_flags & 0xff) != 0)
827                                 langopts->intonation_group = option_tone_flags & 0xff;
828                         break;
829
830                 case V_TUNES:
831                         n = sscanf(p,"%s %s %s %s %s %s",names[0],names[1],names[2],names[3],names[4],names[5]);
832                         langopts->intonation_group = 0;
833                         for(ix=0; ix<n; ix++)
834                         {
835                                 if(strcmp(names[ix],"NULL")==0)
836                                         continue;
837
838                                 if((value = LookupTune(names[ix])) < 0)
839                                         fprintf(stderr,"Unknown tune '%s'\n",names[ix]);
840                                 else
841                                         langopts->tunes[ix] = value;
842                         }
843                         break;
844
845                 case V_DICTRULES:   // conditional dictionary rules and list entries
846                 case V_NUMBERS:
847                 case V_STRESSOPT:
848                         // expect a list of numbers
849                         while(*p != 0)
850                         {
851                                 while(isspace(*p)) p++;
852                                 n = -1;
853                                 if((n = atoi(p)) > 0)
854                                 {
855                                         p++;
856                                         if(n < 32)
857                                         {
858                                                 if(key==V_DICTRULES)
859                                                         conditional_rules |= (1 << n);
860                                                 else if(key==V_NUMBERS)
861                                                         langopts->numbers |= (1 << n);
862                                                 else if(key==V_STRESSOPT)
863                                                         langopts->stress_flags |= (1 << n);
864                                         }
865                                         else
866                                         {
867                                                 if((key==V_NUMBERS) && (n < 64))
868                                                         langopts->numbers |= (1 << (n-32));
869                                                 else
870                                                         fprintf(stderr,"Bad option number %d\n", n);
871                                         }
872                                 }
873                                 while(isalnum(*p)) p++;
874                         }
875                         break;
876
877                 case V_REPLACE:
878                         if(phonemes_set == 0)
879                         {
880                                 // must set up a phoneme table before we can lookup phoneme mnemonics
881                                 SelectPhonemeTableName(phonemes_name);
882                                 phonemes_set = 1;
883                         }
884                         PhonemeReplacement(key,p);
885                         break;
886
887                 case V_WORDGAP:   // words
888                         sscanf(p,"%d %d",&langopts->word_gap, &langopts->vowel_pause);
889                         break;
890
891                 case V_STRESSRULE:
892                         sscanf(p,"%d %d %d %d",&langopts->stress_rule,
893                                    &langopts->stress_flags,
894                                    &langopts->unstressed_wd1,
895                                    &langopts->unstressed_wd2);
896                         break;
897
898                 case V_CHARSET:
899                         if((sscanf(p,"%d",&value)==1) && (value < N_CHARSETS))
900                                 new_translator->charset_a0 = charsets[value];
901                         break;
902
903                 case V_OPTION:
904                         value2 = 0;
905                         if((sscanf(p,"%s %d %d",option_name,&value,&value2) >= 2) && ((ix = LookupMnem(options_tab, option_name)) >= 0))
906                         {
907                                 langopts->param[ix] = value;
908                                 langopts->param2[ix] = value2;
909                         }
910                         else
911                         {
912                                 fprintf(stderr,"Bad voice option: %s %s\n",buf,p);
913                         }
914                         break;
915
916                 case V_ECHO:
917                         // echo.  suggest: 135mS  11%
918                         value = 0;
919                         voice->echo_amp = 0;
920                         sscanf(p,"%d %d",&voice->echo_delay,&voice->echo_amp);
921                         break;
922
923                 case V_FLUTTER:   // flutter
924                         if(sscanf(p,"%d",&value)==1)
925                                 voice->flutter = value * 32;
926                         break;
927
928                 case V_ROUGHNESS:   // roughness
929                         if(sscanf(p,"%d",&value)==1)
930                                 voice->roughness = value;
931                         break;
932
933                 case V_CLARITY:  // formantshape
934                         if(sscanf(p,"%d",&value)==1)
935                         {
936                                 if(value > 4)
937                                 {
938                                         voice->peak_shape = 1;  // squarer formant peaks
939                                         value = 4;
940                                 }
941                                 voice->n_harmonic_peaks = 1+value;
942                         }
943                         break;
944
945                 case V_TONE:
946                 {
947                         int tone_data[12];
948                         ReadTonePoints(p,tone_data);
949                         SetToneAdjust(voice,tone_data);
950                 }
951                 break;
952
953                 case V_VOICING:
954                         if(sscanf(p,"%d",&value)==1)
955                                 voice->voicing = (value * 64)/100;
956                         break;
957
958                 case V_BREATH:
959                         voice->breath[0] = Read8Numbers(p,&voice->breath[1]);
960                         for(ix=1; ix<8; ix++)
961                         {
962                                 if(ix % 2)
963                                         voice->breath[ix] = -voice->breath[ix];
964                         }
965                         break;
966
967                 case V_BREATHW:
968                         voice->breathw[0] = Read8Numbers(p,&voice->breathw[1]);
969                         break;
970
971                 case V_CONSONANTS:
972                         value = sscanf(p,"%d %d",&voice->consonant_amp, &voice->consonant_ampv);
973                         break;
974
975                 case V_SPEED:
976                         sscanf(p,"%d",&voice->speed_percent);
977                         break;
978
979                 case V_MBROLA:
980                 {
981                         int srate = 16000;
982
983                         name2[0] = 0;
984                         sscanf(p,"%s %s %d",name1,name2,&srate);
985                         if(LoadMbrolaTable(name1,name2,srate) != EE_OK)
986                         {
987                                 fprintf(stderr,"mbrola voice not found\n");
988                         }
989                         voice->samplerate = srate;
990                 }
991                 break;
992
993                 case V_KLATT:
994                         voice->klattv[0] = 1;  // default source: IMPULSIVE
995                         Read8Numbers(p,voice->klattv);
996                         voice->klattv[KLATT_Kopen] -= 40;
997                         break;
998
999                 case V_FAST:
1000                         Read8Numbers(p,speed.fast_settings);
1001                         SetSpeed(3);
1002                         break;
1003
1004                 case V_DICTMIN:
1005                         sscanf(p,"%d",&dict_min);
1006                         break;
1007
1008                 case V_ALPHABET2:
1009                         {
1010                                 ALPHABET *alphabet;
1011                                 name1[0] = name2[0] = 0;
1012                                 sscanf(p, "%s %s", name1, name2);
1013
1014                                 if(strcmp(name1, "latin") == 0)
1015                                 {
1016                                         strncpy0(langopts->ascii_language,name2,sizeof(langopts->ascii_language));
1017                                 }
1018                                 else if((alphabet = AlphabetFromName(name1)) != 0)
1019                                 {
1020                                         langopts->alt_alphabet = alphabet->offset;
1021                                         langopts->alt_alphabet_lang = StringToWord2(name2);
1022                                 }
1023                                 else
1024                                 {
1025                                         fprintf(stderr,"alphabet name '%s' not found\n", name1);
1026                                 }
1027                         }
1028                         break;
1029
1030                 default:
1031                         if((key & 0xff00) == 0x100)
1032                         {
1033                                 sscanf(p,"%d",&langopts->param[key &0xff]);
1034                         }
1035                         else
1036                         {
1037                                 fprintf(stderr,"Bad voice attribute: %s\n",buf);
1038                         }
1039                         break;
1040                 }
1041         }
1042         if(f_voice != NULL)
1043                 fclose(f_voice);
1044
1045         if((new_translator == NULL) && (!tone_only))
1046         {
1047                 // not set by language attribute
1048                 new_translator = SelectTranslator(translator_name);
1049         }
1050
1051         SetSpeed(3);   // for speed_percent
1052
1053         for(ix=0; ix<N_PEAKS; ix++)
1054         {
1055                 voice->freq2[ix] = voice->freq[ix];
1056                 voice->height2[ix] = voice->height[ix];
1057                 voice->width2[ix] = voice->width[ix];
1058         }
1059
1060         if(tone_only)
1061         {
1062                 new_translator = translator;
1063         }
1064         else
1065         {
1066                 if((ix = SelectPhonemeTableName(phonemes_name)) < 0)
1067                 {
1068                         fprintf(stderr,"Unknown phoneme table: '%s'\n",phonemes_name);
1069                 }
1070                 voice->phoneme_tab_ix = ix;
1071                 new_translator->phoneme_tab_ix = ix;
1072                 new_translator->dict_min_size = dict_min;
1073                 LoadDictionary(new_translator, new_dictionary, control & 4);
1074                 if(dictionary_name[0]==0)
1075                         return(NULL);   // no dictionary loaded
1076
1077                 new_translator->dict_condition = conditional_rules;
1078
1079                 voice_languages[langix] = 0;
1080         }
1081
1082         langopts = &new_translator->langopts;
1083
1084
1085         if((value = langopts->param[LOPT_LENGTH_MODS]) != 0)
1086         {
1087                 SetLengthMods(new_translator,value);
1088         }
1089
1090         voice->width[0] = (voice->width[0] * 105)/100;
1091
1092         if(!tone_only)
1093         {
1094                 translator = new_translator;
1095         }
1096
1097
1098         // relative lengths of different stress syllables
1099         for(ix=0; ix<stress_lengths_set; ix++)
1100         {
1101                 translator->stress_lengths[ix] = stress_lengths[ix];
1102         }
1103         for(ix=0; ix<stress_add_set; ix++)
1104         {
1105                 translator->stress_lengths[ix] += stress_add[ix];
1106         }
1107         for(ix=0; ix<stress_amps_set; ix++)
1108         {
1109                 translator->stress_amps[ix] = stress_amps[ix];
1110                 translator->stress_amps_r[ix] = stress_amps[ix] -1;
1111         }
1112
1113         return(voice);
1114 }  //  end of LoadVoice
1115
1116
1117 static char *ExtractVoiceVariantName(char *vname, int variant_num, int add_dir)
1118 {//===========================================================================
1119 // Remove any voice variant suffix (name or number) from a voice name
1120 // Returns the voice variant name
1121
1122         char *p;
1123         static char variant_name[40];
1124         char variant_prefix[5];
1125
1126         variant_name[0] = 0;
1127         sprintf(variant_prefix,"!v%c",PATHSEP);
1128         if(add_dir == 0)
1129                 variant_prefix[0] = 0;
1130
1131         if(vname != NULL)
1132         {
1133                 if((p = strchr(vname,'+')) != NULL)
1134                 {
1135                         // The voice name has a +variant suffix
1136                         variant_num = 0;
1137                         *p++ = 0;   // delete the suffix from the voice name
1138                         if(IsDigit09(*p))
1139                         {
1140                                 variant_num = atoi(p);  // variant number
1141                         }
1142                         else
1143                         {
1144                                 // voice variant name, not number
1145                                 sprintf(variant_name, "%s%s", variant_prefix, p);
1146                         }
1147                 }
1148         }
1149
1150         if(variant_num > 0)
1151         {
1152                 if(variant_num < 10)
1153                         sprintf(variant_name,"%sm%d",variant_prefix, variant_num);  // male
1154                 else
1155                         sprintf(variant_name,"%sf%d",variant_prefix, variant_num-10);  // female
1156         }
1157
1158         return(variant_name);
1159 }  //  end of ExtractVoiceVariantName
1160
1161
1162
1163 voice_t *LoadVoiceVariant(const char *vname, int variant_num)
1164 {//==========================================================
1165 // Load a voice file.
1166 // Also apply a voice variant if specified by "variant", or by "+number" or "+name" in the "vname"
1167
1168         voice_t *v;
1169         char *variant_name;
1170         char buf[60];
1171
1172         strncpy0(buf,vname,sizeof(buf));
1173         variant_name = ExtractVoiceVariantName(buf,variant_num, 1);
1174
1175         if((v = LoadVoice(buf,0)) == NULL)
1176                 return(NULL);
1177
1178         if(variant_name[0] != 0)
1179         {
1180                 v = LoadVoice(variant_name,2);
1181         }
1182         return(v);
1183 }
1184
1185
1186
1187 static int __cdecl VoiceNameSorter(const void *p1, const void *p2)
1188 {//=======================================================
1189         int ix;
1190         espeak_VOICE *v1 = *(espeak_VOICE **)p1;
1191         espeak_VOICE *v2 = *(espeak_VOICE **)p2;
1192
1193
1194         if((ix = strcmp(&v1->languages[1],&v2->languages[1])) != 0)  // primary language name
1195                 return(ix);
1196         if((ix = v1->languages[0] - v2->languages[0]) != 0)  // priority number
1197                 return(ix);
1198         return(strcmp(v1->name,v2->name));
1199 }
1200
1201
1202 static int __cdecl VoiceScoreSorter(const void *p1, const void *p2)
1203 {//========================================================
1204         int ix;
1205         espeak_VOICE *v1 = *(espeak_VOICE **)p1;
1206         espeak_VOICE *v2 = *(espeak_VOICE **)p2;
1207
1208         if((ix = v2->score - v1->score) != 0)
1209                 return(ix);
1210         return(strcmp(v1->name,v2->name));
1211 }
1212
1213
1214 static int ScoreVoice(espeak_VOICE *voice_spec, const char *spec_language, int spec_n_parts, int spec_lang_len, espeak_VOICE *voice)
1215 {//=========================================================================================================================
1216         int ix;
1217         const char *p;
1218         int c1, c2;
1219         int language_priority;
1220         int n_parts;
1221         int matching;
1222         int matching_parts;
1223         int score = 0;
1224         int x;
1225         int ratio;
1226         int required_age;
1227         int diff;
1228
1229         p = voice->languages;  // list of languages+dialects for which this voice is suitable
1230
1231         if(spec_n_parts < 0)
1232         {
1233                 // match on the subdirectory
1234                 if(memcmp(voice->identifier, spec_language, spec_lang_len) == 0)
1235                         return(100);
1236                 return(0);
1237         }
1238
1239         if(spec_n_parts == 0)
1240         {
1241                 score = 100;
1242         }
1243         else
1244         {
1245                 if((*p == 0) && (strcmp(spec_language,"variants")==0))
1246                 {
1247                         // match on a voice with no languages if the required language is "variants"
1248                         score = 100;
1249                 }
1250
1251                 // compare the required language with each of the languages of this voice
1252                 while(*p != 0)
1253                 {
1254                         language_priority = *p++;
1255
1256                         matching = 1;
1257                         matching_parts = 0;
1258                         n_parts = 1;
1259
1260                         for(ix=0; ; ix++)
1261                         {
1262                                 if((ix >= spec_lang_len) || ((c1 = spec_language[ix]) == '-'))
1263                                         c1 = 0;
1264                                 if((c2 = p[ix]) == '-')
1265                                         c2 = 0;
1266
1267                                 if(c1 != c2)
1268                                 {
1269                                         matching = 0;
1270                                 }
1271
1272                                 if(p[ix] == '-')
1273                                 {
1274                                         n_parts++;
1275                                         if(matching)
1276                                                 matching_parts++;
1277                                 }
1278                                 if(p[ix] == 0)
1279                                         break;
1280                         }
1281                         p += (ix+1);
1282                         matching_parts += matching;  // number of parts which match
1283
1284                         if(matching_parts == 0)
1285                                 continue;   // no matching parts for this language
1286
1287                         x = 5;
1288                         // reduce the score if not all parts of the required language match
1289                         if((diff = (spec_n_parts - matching_parts)) > 0)
1290                                 x -= diff;
1291
1292                         // reduce score if the language is more specific than required
1293                         if((diff = (n_parts - matching_parts)) > 0)
1294                                 x -= diff;
1295
1296                         x = x*100 - (language_priority * 2);
1297
1298                         if(x > score)
1299                                 score = x;
1300                 }
1301         }
1302         if(score == 0)
1303                 return(0);
1304
1305         if(voice_spec->name != NULL)
1306         {
1307                 if(strcmp(voice_spec->name,voice->name)==0)
1308                 {
1309                         // match on voice name
1310                         score += 500;
1311                 }
1312                 else if(strcmp(voice_spec->name,voice->identifier)==0)
1313                 {
1314                         score += 400;
1315                 }
1316         }
1317
1318         if(((voice_spec->gender == 1) || (voice_spec->gender == 2)) &&
1319                         ((voice->gender == 1) || (voice->gender == 2)))
1320         {
1321                 if(voice_spec->gender == voice->gender)
1322                         score += 50;
1323                 else
1324                         score -= 50;
1325         }
1326
1327         if((voice_spec->age <= 12) && (voice->gender == 2) && (voice->age > 12))
1328         {
1329                 score += 5;  // give some preference for non-child female voice if a child is requested
1330         }
1331
1332         if(voice->age != 0)
1333         {
1334                 if(voice_spec->age == 0)
1335                         required_age = 30;
1336                 else
1337                         required_age = voice_spec->age;
1338
1339                 ratio = (required_age*100)/voice->age;
1340                 if(ratio < 100)
1341                         ratio = 10000/ratio;
1342                 ratio = (ratio - 100)/10;    // 0=exact match, 10=out by factor of 2
1343                 x = 5 - ratio;
1344                 if(x > 0) x = 0;
1345
1346                 score = score + x;
1347
1348                 if(voice_spec->age > 0)
1349                         score += 10;    // required age specified, favour voices with a specified age (near it)
1350         }
1351         if(score < 1)
1352                 score = 1;
1353         return(score);
1354 }  // end of ScoreVoice
1355
1356
1357 static int SetVoiceScores(espeak_VOICE *voice_select, espeak_VOICE **voices, int control)
1358 {//======================================================================================
1359 // control: bit0=1  include mbrola voices
1360         int ix;
1361         int score;
1362         int nv;           // number of candidates
1363         int n_parts=0;
1364         int lang_len=0;
1365         espeak_VOICE *vp;
1366         char language[80];
1367         char buf[sizeof(path_home)+80];
1368
1369         // count number of parts in the specified language
1370         if((voice_select->languages != NULL) && (voice_select->languages[0] != 0))
1371         {
1372                 n_parts = 1;
1373                 lang_len = strlen(voice_select->languages);
1374                 for(ix=0; (ix<=lang_len) && ((unsigned)ix < sizeof(language)); ix++)
1375                 {
1376                         if((language[ix] = tolower(voice_select->languages[ix])) == '-')
1377                                 n_parts++;
1378                 }
1379         }
1380
1381         if((n_parts == 1) && (control & 1))
1382         {
1383                 if(strcmp(language, "mbrola") == 0)
1384                 {
1385                         language[2] = 0;  // truncate to "mb"
1386                         lang_len = 2;
1387                 }
1388
1389                 sprintf(buf, "%s/voices/%s", path_home, language);
1390                 if(GetFileLength(buf) == -2)
1391                 {
1392                         // A subdirectory name has been specified.  List all the voices in that subdirectory
1393                         language[lang_len++] = PATHSEP;
1394                         language[lang_len] = 0;
1395                         n_parts = -1;
1396                 }
1397
1398         }
1399
1400         // select those voices which match the specified language
1401         nv = 0;
1402         for(ix=0; ix<n_voices_list; ix++)
1403         {
1404                 vp = voices_list[ix];
1405
1406                 if(((control & 1) == 0) && (memcmp(vp->identifier,"mb/",3) == 0))
1407                         continue;
1408
1409                 if((score = ScoreVoice(voice_select, language, n_parts, lang_len, voices_list[ix])) > 0)
1410                 {
1411                         voices[nv++] = vp;
1412                         vp->score = score;
1413                 }
1414         }
1415         voices[nv] = NULL;  // list terminator
1416
1417         if(nv==0)
1418                 return(0);
1419
1420         // sort the selected voices by their score
1421         qsort(voices,nv,sizeof(espeak_VOICE *),(int (__cdecl *)(const void *,const void *))VoiceScoreSorter);
1422
1423         return(nv);
1424 }  // end of SetVoiceScores
1425
1426
1427
1428
1429 espeak_VOICE *SelectVoiceByName(espeak_VOICE **voices, const char *name2)
1430 {//======================================================================
1431         int ix;
1432         int match_fname = -1;
1433         int match_fname2 = -1;
1434         int match_name = -1;
1435         const char *id;   // this is the filename within espeak-data/voices
1436         char *variant_name;
1437         int last_part_len;
1438         char last_part[41];
1439         char name[40];
1440
1441         if(voices == NULL)
1442         {
1443                 if(n_voices_list == 0)
1444                         espeak_ListVoices(NULL);   // create the voices list
1445                 voices = voices_list;
1446         }
1447
1448         strncpy0(name, name2, sizeof(name));
1449         if((variant_name = strchr(name, '+')) != NULL)
1450         {
1451                 *variant_name = 0;
1452                 variant_name++;
1453         }
1454
1455         sprintf(last_part,"%c%s",PATHSEP,name);
1456         last_part_len = strlen(last_part);
1457
1458         for(ix=0; voices[ix] != NULL; ix++)
1459         {
1460                 if(strcmp(name,voices[ix]->name)==0)
1461                 {
1462                         match_name = ix;   // found matching voice name
1463                         break;
1464                 }
1465                 else
1466                 {
1467                         id = voices[ix]->identifier;
1468                         if(strcmp(name, id)==0)
1469                         {
1470                                 match_fname = ix;  // matching identifier, use this if no matching name
1471                         }
1472                         else if(strcmp(last_part,&id[strlen(id)-last_part_len])==0)
1473                         {
1474                                 match_fname2 = ix;
1475                         }
1476                 }
1477         }
1478
1479         if(match_name < 0)
1480         {
1481                 match_name = match_fname;  // no matching name, try matching filename
1482                 if(match_name < 0)
1483                         match_name = match_fname2;  // try matching just the last part of the filename
1484         }
1485
1486         if(match_name < 0)
1487                 return(NULL);
1488
1489         return(voices[match_name]);
1490 }  //  end of SelectVoiceByName
1491
1492
1493
1494
1495 char const *SelectVoice(espeak_VOICE *voice_select, int *found)
1496 {//============================================================
1497 // Returns a path within espeak-voices, with a possible +variant suffix
1498 // variant is an output-only parameter
1499         int nv;           // number of candidates
1500         int ix, ix2;
1501         int j;
1502         int n_variants;
1503         int variant_number;
1504         int gender;
1505         int skip;
1506         int aged=1;
1507         char *variant_name;
1508         const char *p, *p_start;
1509         espeak_VOICE *vp = NULL;
1510         espeak_VOICE *vp2;
1511         espeak_VOICE voice_select2;
1512         espeak_VOICE *voices[N_VOICES_LIST]; // list of candidates
1513         espeak_VOICE *voices2[N_VOICES_LIST+N_VOICE_VARIANTS];
1514         static espeak_VOICE voice_variants[N_VOICE_VARIANTS];
1515         static char voice_id[50];
1516
1517         *found = 1;
1518         memcpy(&voice_select2,voice_select,sizeof(voice_select2));
1519
1520         if(n_voices_list == 0)
1521                 espeak_ListVoices(NULL);   // create the voices list
1522
1523         if((voice_select2.languages == NULL) || (voice_select2.languages[0] == 0))
1524         {
1525                 // no language is specified. Get language from the named voice
1526                 static char buf[60];
1527
1528                 if(voice_select2.name == NULL)
1529                 {
1530                         if((voice_select2.name = voice_select2.identifier) == NULL)
1531                                 voice_select2.name = "default";
1532                 }
1533
1534                 strncpy0(buf,voice_select2.name,sizeof(buf));
1535                 variant_name = ExtractVoiceVariantName(buf,0,0);
1536
1537                 vp = SelectVoiceByName(voices_list,buf);
1538                 if(vp != NULL)
1539                 {
1540                         voice_select2.languages = &(vp->languages[1]);
1541
1542                         if((voice_select2.gender==0) && (voice_select2.age==0) && (voice_select2.variant==0))
1543                         {
1544                                 if(variant_name[0] != 0)
1545                                 {
1546                                         sprintf(voice_id,"%s+%s", vp->identifier, variant_name);
1547                                         return(voice_id);
1548                                 }
1549
1550                                 return(vp->identifier);
1551                         }
1552                 }
1553         }
1554
1555         // select and sort voices for the required language
1556         nv = SetVoiceScores(&voice_select2,voices,0);
1557
1558         if(nv == 0)
1559         {
1560                 // no matching voice, choose the default
1561                 *found = 0;
1562                 if((voices[0] = SelectVoiceByName(voices_list,"default")) != NULL)
1563                         nv = 1;
1564         }
1565
1566         gender = 0;
1567         if((voice_select2.gender == 2) || ((voice_select2.age > 0) && (voice_select2.age < 13)))
1568                 gender = 2;
1569         else if(voice_select2.gender == 1)
1570                 gender = 1;
1571
1572 #define AGE_OLD  60
1573         if(voice_select2.age < AGE_OLD)
1574                 aged = 0;
1575
1576         p = p_start = variant_lists[gender];
1577         if(aged == 0)
1578                 p++;   // the first voice in the variants list is older
1579
1580         // add variants for the top voices
1581         n_variants = 0;
1582         for(ix=0, ix2=0; ix<nv; ix++)
1583         {
1584                 vp = voices[ix];
1585                 // is the main voice the required gender?
1586                 skip=0;
1587                 if((gender != 0) && (vp->gender != gender))
1588                 {
1589                         skip=1;
1590                 }
1591                 if((ix2==0) && aged && (vp->age < AGE_OLD))
1592                 {
1593                         skip=1;
1594                 }
1595                 if(skip==0)
1596                 {
1597                         voices2[ix2++] = vp;
1598                 }
1599
1600                 for(j=0; (j < vp->xx1) && (n_variants < N_VOICE_VARIANTS);)
1601                 {
1602                         if((variant_number = *p) == 0)
1603                         {
1604                                 p = p_start;
1605                                 continue;
1606                         }
1607
1608                         vp2 = &voice_variants[n_variants++];        // allocate space for voice variant
1609                         memcpy(vp2,vp,sizeof(espeak_VOICE));        // copy from the original voice
1610                         vp2->variant = variant_number;
1611                         voices2[ix2++] = vp2;
1612                         p++;
1613                         j++;
1614                 }
1615         }
1616         // add any more variants to the end of the list
1617         while((vp != NULL) && ((variant_number = *p++) != 0) && (n_variants < N_VOICE_VARIANTS))
1618         {
1619                 vp2 = &voice_variants[n_variants++];        // allocate space for voice variant
1620                 memcpy(vp2,vp,sizeof(espeak_VOICE));        // copy from the original voice
1621                 vp2->variant = variant_number;
1622                 voices2[ix2++] = vp2;
1623         }
1624
1625         // index the sorted list by the required variant number
1626         if(ix2 == 0)
1627                 return(NULL);
1628         vp = voices2[voice_select2.variant % ix2];
1629
1630         if(vp->variant != 0)
1631         {
1632                 variant_name = ExtractVoiceVariantName(NULL, vp->variant, 0);
1633                 sprintf(voice_id,"%s+%s", vp->identifier, variant_name);
1634                 return(voice_id);
1635         }
1636
1637         return(vp->identifier);
1638 }  //  end of SelectVoice
1639
1640
1641
1642 static void GetVoices(const char *path)
1643 {//====================================
1644         FILE *f_voice;
1645         espeak_VOICE *voice_data;
1646         int ftype;
1647         char fname[sizeof(path_home)+100];
1648
1649 #ifdef PLATFORM_RISCOS
1650         int len;
1651         int *type;
1652         char *p;
1653         _kernel_swi_regs regs;
1654         _kernel_oserror *error;
1655         char buf[80];
1656         char directory2[sizeof(path_home)+100];
1657
1658         regs.r[0] = 10;
1659         regs.r[1] = (int)path;
1660         regs.r[2] = (int)buf;
1661         regs.r[3] = 1;
1662         regs.r[4] = 0;
1663         regs.r[5] = sizeof(buf);
1664         regs.r[6] = 0;
1665
1666         while(regs.r[3] > 0)
1667         {
1668                 error = _kernel_swi(0x0c+0x20000,&regs,&regs);      /* OS_GBPB 10, read directory entries */
1669                 if((error != NULL) || (regs.r[3] == 0))
1670                 {
1671                         break;
1672                 }
1673                 type = (int *)(&buf[16]);
1674                 len = strlen(&buf[20]);
1675                 sprintf(fname,"%s.%s",path,&buf[20]);
1676
1677                 if(*type == 2)
1678                 {
1679                         // a sub-directory
1680                         GetVoices(fname);
1681                 }
1682                 else
1683                 {
1684                         // a regular line, add it to the voices list
1685                         if((f_voice = fopen(fname,"r")) == NULL)
1686                                 continue;
1687
1688                         // pass voice file name within the voices directory
1689                         voice_data = ReadVoiceFile(f_voice, fname+len_path_voices, &buf[20]);
1690                         fclose(f_voice);
1691
1692                         if(voice_data != NULL)
1693                         {
1694                                 voices_list[n_voices_list++] = voice_data;
1695                         }
1696                 }
1697         }
1698 #else
1699 #ifdef PLATFORM_WINDOWS
1700         WIN32_FIND_DATAA FindFileData;
1701         HANDLE hFind = INVALID_HANDLE_VALUE;
1702
1703 #undef UNICODE         // we need FindFirstFileA() which takes an 8-bit c-string
1704         sprintf(fname,"%s\\*",path);
1705         hFind = FindFirstFileA(fname, &FindFileData);
1706         if(hFind == INVALID_HANDLE_VALUE)
1707                 return;
1708
1709         do {
1710                 if(n_voices_list >= (N_VOICES_LIST-2))
1711                         break;   // voices list is full
1712
1713                 if(FindFileData.cFileName[0] != '.')
1714                 {
1715                         sprintf(fname,"%s%c%s",path,PATHSEP,FindFileData.cFileName);
1716                         ftype = GetFileLength(fname);
1717
1718                         if(ftype == -2)
1719                         {
1720                                 // a sub-sirectory
1721                                 GetVoices(fname);
1722                         }
1723                         else if(ftype > 0)
1724                         {
1725                                 // a regular line, add it to the voices list
1726                                 if((f_voice = fopen(fname,"r")) == NULL)
1727                                         continue;
1728
1729                                 // pass voice file name within the voices directory
1730                                 voice_data = ReadVoiceFile(f_voice, fname+len_path_voices, FindFileData.cFileName);
1731                                 fclose(f_voice);
1732
1733                                 if(voice_data != NULL)
1734                                 {
1735                                         voices_list[n_voices_list++] = voice_data;
1736                                 }
1737                         }
1738                 }
1739         } while(FindNextFileA(hFind, &FindFileData) != 0);
1740         FindClose(hFind);
1741
1742 #else
1743         DIR *dir;
1744         struct dirent *ent;
1745
1746         if((dir = opendir((char *)path)) == NULL)    // note: (char *) is needed for WINCE
1747                 return;
1748
1749         while((ent = readdir(dir)) != NULL)
1750         {
1751                 if(n_voices_list >= (N_VOICES_LIST-2))
1752                         break;   // voices list is full
1753
1754                 if(ent->d_name[0] == '.')
1755                         continue;
1756
1757                 sprintf(fname,"%s%c%s",path,PATHSEP,ent->d_name);
1758
1759                 ftype = GetFileLength(fname);
1760
1761                 if(ftype == -2)
1762                 {
1763                         // a sub-sirectory
1764                         GetVoices(fname);
1765                 }
1766                 else if(ftype > 0)
1767                 {
1768                         // a regular line, add it to the voices list
1769                         if((f_voice = fopen(fname,"r")) == NULL)
1770                                 continue;
1771
1772                         // pass voice file name within the voices directory
1773                         voice_data = ReadVoiceFile(f_voice, fname+len_path_voices, ent->d_name);
1774                         fclose(f_voice);
1775
1776                         if(voice_data != NULL)
1777                         {
1778                                 voices_list[n_voices_list++] = voice_data;
1779                         }
1780                 }
1781         }
1782         closedir(dir);
1783 #endif
1784 #endif
1785 }   // end of GetVoices
1786
1787
1788
1789 espeak_ERROR SetVoiceByName(const char *name)
1790 {//=========================================
1791         espeak_VOICE *v;
1792         int ix;
1793         espeak_VOICE voice_selector;
1794         char *variant_name;
1795         static char buf[60];
1796
1797         strncpy0(buf,name,sizeof(buf));
1798
1799         variant_name = ExtractVoiceVariantName(buf, 0, 1);
1800
1801         for(ix=0; ; ix++)
1802         {
1803                 // convert voice name to lower case  (ascii)
1804                 if((buf[ix] = tolower(buf[ix])) == 0)
1805                         break;
1806         }
1807
1808         memset(&voice_selector,0,sizeof(voice_selector));
1809         voice_selector.name = (char *)name;  // include variant name in voice stack ??
1810
1811         // first check for a voice with this filename
1812         // This may avoid the need to call espeak_ListVoices().
1813
1814         if(LoadVoice(buf,1) != NULL)
1815         {
1816                 if(variant_name[0] != 0)
1817                 {
1818                         LoadVoice(variant_name,2);
1819                 }
1820
1821                 DoVoiceChange(voice);
1822                 voice_selector.languages = voice->language_name;
1823                 SetVoiceStack(&voice_selector, variant_name);
1824                 return(EE_OK);
1825         }
1826
1827         if(n_voices_list == 0)
1828                 espeak_ListVoices(NULL);   // create the voices list
1829
1830         if((v = SelectVoiceByName(voices_list,buf)) != NULL)
1831         {
1832                 if(LoadVoice(v->identifier,0) != NULL)
1833                 {
1834                         if(variant_name[0] != 0)
1835                         {
1836                                 LoadVoice(variant_name,2);
1837                         }
1838                         DoVoiceChange(voice);
1839                         voice_selector.languages = voice->language_name;
1840                         SetVoiceStack(&voice_selector, variant_name);
1841                         return(EE_OK);
1842                 }
1843         }
1844         return(EE_INTERNAL_ERROR);   // voice name not found
1845 }  // end of SetVoiceByName
1846
1847
1848
1849 espeak_ERROR SetVoiceByProperties(espeak_VOICE *voice_selector)
1850 {//============================================================
1851         const char *voice_id;
1852         int voice_found;
1853
1854         voice_id = SelectVoice(voice_selector, &voice_found);
1855
1856         if(voice_found == 0)
1857                 return(EE_NOT_FOUND);
1858
1859         LoadVoiceVariant(voice_id,0);
1860         DoVoiceChange(voice);
1861         SetVoiceStack(voice_selector, "");
1862
1863         return(EE_OK);
1864 }  //  end of SetVoiceByProperties
1865
1866
1867 void FreeVoiceList()
1868 {//=================
1869         for(int ix=0; ix<n_voices_list; ix++)
1870         {
1871                 if(voices_list[ix] != NULL)
1872                 {
1873                         free(voices_list[ix]);
1874                         voices_list[ix] = NULL;
1875                 }
1876         }
1877         n_voices_list = 0;
1878 }
1879
1880
1881 //=======================================================================
1882 //  Library Interface Functions
1883 //=======================================================================
1884 #pragma GCC visibility push(default)
1885
1886
1887 ESPEAK_API const espeak_VOICE **espeak_ListVoices(espeak_VOICE *voice_spec)
1888 {//========================================================================
1889         char path_voices[sizeof(path_home)+12];
1890
1891
1892 #ifdef PLATFORM_RISCOS
1893         if(n_voices_list == 0)
1894         {
1895                 sprintf(path_voices,"%s%cvoices",path_home,PATHSEP);
1896                 len_path_voices = strlen(path_voices)+1;
1897                 GetVoices(path_voices);
1898                 voices_list[n_voices_list] = NULL;  // voices list terminator
1899         }
1900         return((const espeak_VOICE **)voices_list);
1901
1902 #else
1903         int ix;
1904         int j;
1905         espeak_VOICE *v;
1906         static espeak_VOICE **voices = NULL;
1907
1908         // free previous voice list data
1909         FreeVoiceList();
1910
1911         sprintf(path_voices,"%s%cvoices",path_home,PATHSEP);
1912         len_path_voices = strlen(path_voices)+1;
1913
1914         GetVoices(path_voices);
1915         voices_list[n_voices_list] = NULL;  // voices list terminator
1916         voices = (espeak_VOICE **)realloc(voices, sizeof(espeak_VOICE *)*(n_voices_list+1));
1917
1918         // sort the voices list
1919         qsort(voices_list,n_voices_list,sizeof(espeak_VOICE *),
1920                   (int (__cdecl *)(const void *,const void *))VoiceNameSorter);
1921
1922
1923         if(voice_spec)
1924         {
1925                 // select the voices which match the voice_spec, and sort them by preference
1926                 SetVoiceScores(voice_spec,voices,1);
1927         }
1928         else
1929         {
1930                 // list all: omit variant voices and mbrola voices and test voices
1931                 j = 0;
1932                 for(ix=0; (v = voices_list[ix]) != NULL; ix++)
1933                 {
1934                         if((v->languages[0] != 0) && (strcmp(&v->languages[1],"variant") != 0)
1935                                 && (memcmp(v->identifier,"mb/",3) != 0) && (memcmp(v->identifier,"test/",5) != 0))
1936                         {
1937                                 voices[j++] = v;
1938                         }
1939                 }
1940                 voices[j] = NULL;
1941         }
1942         return((const espeak_VOICE **)voices);
1943 #endif
1944 }  //  end of espeak_ListVoices
1945
1946
1947
1948 ESPEAK_API espeak_VOICE *espeak_GetCurrentVoice(void)
1949 {//==================================================
1950         return(&current_voice_selected);
1951 }
1952
1953 #pragma GCC visibility pop
1954
1955