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