static int compile_line(char *linebuf, char *dict_line, int *hash) {//=============================================================== // Compile a line in the language_list file unsigned char c; char *p; char *word; char *phonetic; unsigned int ix; int step; unsigned int n_flag_codes = 0; int flag_offset; int length; int multiple_words = 0; char *multiple_string = NULL; char *multiple_string_end = NULL; int len_word; int len_phonetic; int text_not_phonemes; // this word specifies replacement text, not phonemes unsigned int wc; int all_upper_case; char *mnemptr; char *comment; unsigned char flag_codes[100]; char encoded_ph[200]; unsigned char bad_phoneme[4]; static char nullstring[] = {0}; comment = NULL; text_not_phonemes = 0; phonetic = word = nullstring; if(memcmp(linebuf,"_-",2)==0) { step=1; // TEST } p = linebuf; // while(isspace2(*p)) p++; #ifdef deleted if(*p == '$') { if(memcmp(p,"$textmode",9) == 0) { text_mode = 1; return(0); } if(memcmp(p,"$phonememode",12) == 0) { text_mode = 0; return(0); } } #endif step = 0; c = 0; while(c != '\n') { c = *p; if((c == '?') && (step==0)) { // conditional rule, allow only if the numbered condition is set for the voice flag_offset = 100; p++; if(*p == '!') { // allow only if the numbered condition is NOT set flag_offset = 132; p++; } ix = 0; if(isdigit(*p)) { ix += (*p-'0'); p++; } if(isdigit(*p)) { ix = ix*10 + (*p-'0'); p++; } flag_codes[n_flag_codes++] = ix + flag_offset; c = *p; } if((c == '$') && isalnum(p[1])) { /* read keyword parameter */ mnemptr = p; while(!isspace2(c = *p)) p++; *p = 0; ix = LookupMnem(mnem_flags,mnemptr); if(ix > 0) { if(ix == 200) { text_mode = 1; } else if(ix == 201) { text_mode = 0; } else if(ix == BITNUM_FLAG_TEXTMODE) { text_not_phonemes = 1; } else { flag_codes[n_flag_codes++] = ix; } } else { fprintf(f_log,"%5d: Unknown keyword: %s\n",linenum,mnemptr); error_count++; } } if((c == '/') && (p[1] == '/') && (multiple_words==0)) { c = '\n'; /* "//" treat comment as end of line */ comment = p; } switch(step) { case 0: if(c == '(') { multiple_words = 1; word = p+1; step = 1; } else if(!isspace2(c)) { word = p; step = 1; } break; case 1: if((c == '-') && (word[0] != '_')) { flag_codes[n_flag_codes++] = BITNUM_FLAG_HYPHENATED; c = ' '; } if(isspace2(c)) { p[0] = 0; /* terminate english word */ if(multiple_words) { multiple_string = multiple_string_end = p+1; step = 2; } else { step = 3; } } else if((c == ')') && multiple_words) { p[0] = 0; step = 3; multiple_words = 0; } break; case 2: if(isspace2(c)) { multiple_words++; } else if(c == ')') { p[0] = ' '; // terminate extra string multiple_string_end = p+1; step = 3; } break; case 3: if(!isspace2(c)) { phonetic = p; step = 4; } break; case 4: if(isspace2(c)) { p[0] = 0; /* terminate phonetic */ step = 5; } break; case 5: break; } p++; } if(word[0] == 0) { #ifdef OPT_FORMAT if(comment != NULL) fprintf(f_log,"%s",comment); else fputc('\n',f_log); #endif return(0); /* blank line */ } if(text_mode) text_not_phonemes = 1; if(text_not_phonemes != translator->langopts.textmode) { flag_codes[n_flag_codes++] = BITNUM_FLAG_TEXTMODE; } if(text_not_phonemes) { // this is replacement text, so don't encode as phonemes. Restrict the length of the replacement word strncpy0(encoded_ph,phonetic,N_WORD_BYTES-4); } else { EncodePhonemes(phonetic,encoded_ph,bad_phoneme); if(strchr(encoded_ph,phonSWITCH) != 0) { flag_codes[n_flag_codes++] = BITNUM_FLAG_ONLY_S; // don't match on suffixes (except 's') when switching languages } // check for errors in the phonemes codes for(ix=0; ix<sizeof(encoded_ph); ix++) { c = encoded_ph[ix]; if(c == 0) break; if(c == 255) { /* unrecognised phoneme, report error */ fprintf(f_log,"%5d: Bad phoneme [%c] (0x%x) in: %s %s\n",linenum,bad_phoneme[0],bad_phoneme[0],word,phonetic); error_count++; } } } if(sscanf(word,"U+%x",&wc) == 1) { // Character code ix = utf8_out(wc, word); word[ix] = 0; } else if(word[0] != '_') { // convert to lower case, and note if the word is all-capitals int c2; all_upper_case = 1; p = word; for(p=word;;) { // this assumes that the lower case char is the same length as the upper case char // OK, except for Turkish "I", but use towlower() rather than towlower2() ix = utf8_in(&c2,p); if(c2 == 0) break; if(iswupper(c2)) { utf8_out(towlower(c2),p); } else { all_upper_case = 0; } p += ix; } if(all_upper_case) { flag_codes[n_flag_codes++] = BITNUM_FLAG_ALLCAPS; } } len_word = strlen(word); if(transpose_offset > 0) { len_word = TransposeAlphabet(word, transpose_offset, transpose_min, transpose_max); } *hash = HashDictionary(word); len_phonetic = strlen(encoded_ph); dict_line[1] = len_word; // bit 6 indicates whether the word has been compressed len_word &= 0x3f; memcpy(&dict_line[2],word,len_word); if(len_phonetic == 0) { // no phonemes specified. set bit 7 dict_line[1] |= 0x80; length = len_word + 2; } else { length = len_word + len_phonetic + 3; strcpy(&dict_line[(len_word)+2],encoded_ph); } for(ix=0; ix<n_flag_codes; ix++) { dict_line[ix+length] = flag_codes[ix]; } length += n_flag_codes; if((multiple_string != NULL) && (multiple_words > 0)) { if(multiple_words > 10) { fprintf(f_log,"%5d: Two many parts in a multi-word entry: %d\n",linenum,multiple_words); } else { dict_line[length++] = 80 + multiple_words; ix = multiple_string_end - multiple_string; memcpy(&dict_line[length],multiple_string,ix); length += ix; } } dict_line[0] = length; #ifdef OPT_FORMAT spaces = 16; for(ix=0; ix<n_flag_codes; ix++) { if(flag_codes[ix] >= 100) { fprintf(f_log,"?%d ",flag_codes[ix]-100); spaces -= 3; } } fprintf(f_log,"%s",word); spaces -= strlen(word); DecodePhonemes(encoded_ph,decoded_ph); while(spaces-- > 0) fputc(' ',f_log); spaces += (14 - strlen(decoded_ph)); fprintf(f_log," %s",decoded_ph); while(spaces-- > 0) fputc(' ',f_log); for(ix=0; ix<n_flag_codes; ix++) { if(flag_codes[ix] < 100) fprintf(f_log," %s",lookup_mnem(mnem_flags,flag_codes[ix])); } if(comment != NULL) fprintf(f_log," %s",comment); else fputc('\n',f_log); #endif return(length); } /* end of compile_line */
static espeak_VOICE *ReadVoiceFile(FILE *f_in, const char *fname, const char*leafname) {//=================================================================================== // Read a Voice file, allocate a VOICE_DATA and set data from the // file's language, gender, name lines char linebuf[120]; char vname[80]; char vgender[80]; char vlanguage[80]; char languages[300]; // allow space for several alternate language names and priorities unsigned int len; int langix = 0; int n_languages = 0; char *p; espeak_VOICE *voice_data; int priority; int age; int n_variants = 4; // default, number of variants of this voice before using another voice int gender; #ifdef PLATFORM_WINDOWS char fname_buf[sizeof(path_home)+15]; if(memcmp(leafname,"mb-",3) == 0) { // check whether the mbrola speech data is present for this voice memcpy(vname,&leafname[3],3); vname[3] = 0; sprintf(fname_buf,"%s/mbrola/%s",path_home,vname); if(GetFileLength(fname_buf) <= 0) return(0); } #endif vname[0] = 0; vgender[0] = 0; age = 0; while(fgets_strip(linebuf,sizeof(linebuf),f_in) != NULL) { if(memcmp(linebuf,"name",4)==0) { p = &linebuf[4]; while(isspace(*p)) p++; strncpy0(vname,p,sizeof(vname)); } else if(memcmp(linebuf,"language",8)==0) { priority = DEFAULT_LANGUAGE_PRIORITY; vlanguage[0] = 0; sscanf(&linebuf[8],"%s %d",vlanguage,&priority); len = strlen(vlanguage) + 2; // check for space in languages[] if(len < (sizeof(languages)-langix-1)) { languages[langix] = priority; strcpy(&languages[langix+1],vlanguage); langix += len; n_languages++; } } else if(memcmp(linebuf,"gender",6)==0) { sscanf(&linebuf[6],"%s %d",vgender,&age); } else if(memcmp(linebuf,"variants",8)==0) { sscanf(&linebuf[8],"%d",&n_variants); } } languages[langix++] = 0; gender = LookupMnem(genders,vgender); if(n_languages == 0) { return(NULL); // no language lines in the voice file } p = (char *)calloc(sizeof(espeak_VOICE) + langix + strlen(fname) + strlen(vname) + 3, 1); voice_data = (espeak_VOICE *)p; p = &p[sizeof(espeak_VOICE)]; memcpy(p,languages,langix); voice_data->languages = p; strcpy(&p[langix],fname); voice_data->identifier = &p[langix]; voice_data->name = &p[langix]; if(vname[0] != 0) { langix += strlen(fname)+1; strcpy(&p[langix],vname); voice_data->name = &p[langix]; } voice_data->age = age; voice_data->gender = gender; voice_data->variant = 0; voice_data->xx1 = n_variants; return(voice_data); } // end of ReadVoiceFile
voice_t *LoadVoice(const char *vname, int control) {//=============================================== // control, bit 0 1= no_default // bit 1 1 = change tone only, not language // bit 2 1 = don't report error on LoadDictionary // bit 4 1 = vname = full path FILE *f_voice = NULL; char *p; int key; int ix; int n; int value; int value2; int langix = 0; int tone_only = control & 2; int language_set = 0; int phonemes_set = 0; int stress_amps_set = 0; int stress_lengths_set = 0; int stress_add_set = 0; int conditional_rules = 0; LANGUAGE_OPTIONS *langopts = NULL; Translator *new_translator = NULL; char voicename[40]; char language_name[40]; char translator_name[40]; char new_dictionary[40]; char phonemes_name[40]; char option_name[40]; const char *language_type; char buf[sizeof(path_home)+30]; char path_voices[sizeof(path_home)+12]; int dict_min = 0; int stress_amps[8]; int stress_lengths[8]; int stress_add[8]; char names[8][40]; char name1[40]; char name2[80]; const char *voice_dir; int pitch1; int pitch2; static char voice_identifier[40]; // file name for current_voice_selected static char voice_name[40]; // voice name for current_voice_selected static char voice_languages[100]; // list of languages and priorities for current_voice_selected // which directory to look for a named voice. List of voice names, must end in a space. static const char *voices_asia = "az bn fa fa-pin gu hi hy hy-west id ka kn ku ml ms ne pa ta te tr vi vi-hue vi-sgn zh zh-yue "; static const char *voices_europe = "an bg bs ca cs cy da de el en en-us es et eu fi fr fr-be ga hr hu is it lt lv mk nl no pl pt-pt ro ru sk sq sr sv "; strncpy0(voicename, vname, sizeof(voicename)); if(control & 0x10) { strcpy(buf,vname); if(GetFileLength(buf) <= 0) return(NULL); } else { if(voicename[0]==0) strcpy(voicename,"default"); sprintf(path_voices,"%s%cvoices%c",path_home,PATHSEP,PATHSEP); sprintf(buf,"%s%s",path_voices,voicename); // first, look in the main voices directory if(GetFileLength(buf) <= 0) { // then look in the appropriate subdirectory if((voicename[0]=='m') && (voicename[1]=='b')) { voice_dir = "mb"; // mbrola voices } else { sprintf(name2, "%s ", voicename); if(strstr(voices_europe, voicename) != NULL) voice_dir = "europe"; else if(strstr(voices_asia, voicename) != NULL) voice_dir = "asia"; else voice_dir = "other"; sprintf(buf,"%s%s%c%s", path_voices,voice_dir,PATHSEP,voicename); if(GetFileLength(buf) <= 0) { // if not found, look in "test" sub-directory sprintf(buf,"%stest%c%s",path_voices,PATHSEP,voicename); } } } } f_voice = fopen(buf,"r"); language_type = "en"; // default if(f_voice == NULL) { if(control & 3) return(NULL); // can't open file if(SelectPhonemeTableName(voicename) >= 0) language_type = voicename; } if(!tone_only && (translator != NULL)) { DeleteTranslator(translator); translator = NULL; } strcpy(translator_name,language_type); strcpy(new_dictionary,language_type); strcpy(phonemes_name,language_type); if(!tone_only) { voice = &voicedata; strncpy0(voice_identifier,vname,sizeof(voice_identifier)); voice_name[0] = 0; voice_languages[0] = 0; current_voice_selected.identifier = voice_identifier; current_voice_selected.name = voice_name; current_voice_selected.languages = voice_languages; } else { // append the variant file name to the voice identifier if((p = strchr(voice_identifier,'+')) != NULL) *p = 0; // remove previous variant name sprintf(buf,"+%s",&vname[3]); // omit !v/ from the variant filename strcat(voice_identifier,buf); langopts = &translator->langopts; } VoiceReset(tone_only); if(!tone_only) SelectPhonemeTableName(phonemes_name); // set up phoneme_tab while((f_voice != NULL) && (fgets_strip(buf,sizeof(buf),f_voice) != NULL)) { // isolate the attribute name for(p=buf; (*p != 0) && !isspace(*p); p++); *p++ = 0; if(buf[0] == 0) continue; key = LookupMnem(keyword_tab, buf); switch(key) { case V_LANGUAGE: { unsigned int len; int priority; if(tone_only) break; priority = DEFAULT_LANGUAGE_PRIORITY; language_name[0] = 0; sscanf(p,"%s %d",language_name,&priority); if(strcmp(language_name,"variant") == 0) break; len = strlen(language_name) + 2; // check for space in languages[] if(len < (sizeof(voice_languages)-langix-1)) { voice_languages[langix] = priority; strcpy(&voice_languages[langix+1],language_name); langix += len; } // only act on the first language line if(language_set == 0) { language_type = strtok(language_name,"-"); language_set = 1; strcpy(translator_name,language_type); strcpy(new_dictionary,language_type); strcpy(phonemes_name,language_type); SelectPhonemeTableName(phonemes_name); if(new_translator != NULL) DeleteTranslator(new_translator); new_translator = SelectTranslator(translator_name); langopts = &new_translator->langopts; strncpy0(voice->language_name, language_name, sizeof(voice->language_name)); } } break; case V_NAME: if(tone_only == 0) { while(isspace(*p)) p++; strncpy0(voice_name,p,sizeof(voice_name)); } break; case V_GENDER: { int age = 0; char vgender[80]; sscanf(p,"%s %d",vgender,&age); current_voice_selected.gender = LookupMnem(genders,vgender); current_voice_selected.age = age; } break; case V_TRANSLATOR: if(tone_only) break; sscanf(p,"%s",translator_name); if(new_translator != NULL) DeleteTranslator(new_translator); new_translator = SelectTranslator(translator_name); langopts = &new_translator->langopts; break; case V_DICTIONARY: // dictionary sscanf(p,"%s",new_dictionary); break; case V_PHONEMES: // phoneme table sscanf(p,"%s",phonemes_name); break; case V_FORMANT: VoiceFormant(p); break; case V_PITCH: { double factor; // default is pitch 82 118 n = sscanf(p,"%d %d",&pitch1,&pitch2); voice->pitch_base = (pitch1 - 9) << 12; voice->pitch_range = (pitch2 - pitch1) * 108; factor = (double)(pitch1 - 82)/82; voice->formant_factor = (int)((1+factor/4) * 256); // nominal formant shift for a different voice pitch } break; case V_STRESSLENGTH: // stressLength stress_lengths_set = Read8Numbers(p,stress_lengths); break; case V_STRESSAMP: // stressAmp stress_amps_set = Read8Numbers(p,stress_amps); break; case V_STRESSADD: // stressAdd stress_add_set = Read8Numbers(p,stress_add); break; case V_INTONATION: // intonation sscanf(p,"%d %d",&option_tone_flags,&option_tone2); if((option_tone_flags & 0xff) != 0) langopts->intonation_group = option_tone_flags & 0xff; break; case V_TUNES: n = sscanf(p,"%s %s %s %s %s %s",names[0],names[1],names[2],names[3],names[4],names[5]); langopts->intonation_group = 0; for(ix=0; ix<n; ix++) { if(strcmp(names[ix],"NULL")==0) continue; if((value = LookupTune(names[ix])) < 0) fprintf(stderr,"Unknown tune '%s'\n",names[ix]); else langopts->tunes[ix] = value; } break; case V_DICTRULES: // conditional dictionary rules and list entries case V_NUMBERS: case V_STRESSOPT: // expect a list of numbers while(*p != 0) { while(isspace(*p)) p++; n = -1; if((n = atoi(p)) > 0) { p++; if(n < 32) { if(key==V_DICTRULES) conditional_rules |= (1 << n); else if(key==V_NUMBERS) langopts->numbers |= (1 << n); else if(key==V_STRESSOPT) langopts->stress_flags |= (1 << n); } else { if((key==V_NUMBERS) && (n < 64)) langopts->numbers |= (1 << (n-32)); else fprintf(stderr,"Bad option number %d\n", n); } } while(isalnum(*p)) p++; } ProcessLanguageOptions(langopts); break; case V_REPLACE: if(phonemes_set == 0) { // must set up a phoneme table before we can lookup phoneme mnemonics SelectPhonemeTableName(phonemes_name); phonemes_set = 1; } PhonemeReplacement(key,p); break; case V_WORDGAP: // words sscanf(p,"%d %d",&langopts->word_gap, &langopts->vowel_pause); break; case V_STRESSRULE: sscanf(p,"%d %d %d %d",&langopts->stress_rule, &langopts->stress_flags, &langopts->unstressed_wd1, &langopts->unstressed_wd2); break; case V_CHARSET: if((sscanf(p,"%d",&value)==1) && (value < N_CHARSETS)) new_translator->charset_a0 = charsets[value]; break; case V_OPTION: value2 = 0; if(((sscanf(p,"%s %d %d",option_name,&value,&value2) >= 2) && ((ix = LookupMnem(options_tab, option_name)) >= 0)) || ((sscanf(p,"%d %d %d",&ix,&value,&value2) >= 2) && (ix < N_LOPTS))) { langopts->param[ix] = value; langopts->param2[ix] = value2; } else { fprintf(stderr,"Bad voice option: %s %s\n",buf,p); } break; case V_ECHO: // echo. suggest: 135mS 11% value = 0; voice->echo_amp = 0; sscanf(p,"%d %d",&voice->echo_delay,&voice->echo_amp); break; case V_FLUTTER: // flutter if(sscanf(p,"%d",&value)==1) voice->flutter = value * 32; break; case V_ROUGHNESS: // roughness if(sscanf(p,"%d",&value)==1) voice->roughness = value; break; case V_CLARITY: // formantshape if(sscanf(p,"%d",&value)==1) { if(value > 4) { voice->peak_shape = 1; // squarer formant peaks value = 4; } voice->n_harmonic_peaks = 1+value; } break; case V_TONE: { int tone_data[12]; ReadTonePoints(p,tone_data); SetToneAdjust(voice,tone_data); } break; case V_VOICING: if(sscanf(p,"%d",&value)==1) voice->voicing = (value * 64)/100; break; case V_BREATH: voice->breath[0] = Read8Numbers(p,&voice->breath[1]); for(ix=1; ix<8; ix++) { if(ix % 2) voice->breath[ix] = -voice->breath[ix]; } break; case V_BREATHW: voice->breathw[0] = Read8Numbers(p,&voice->breathw[1]); break; case V_CONSONANTS: value = sscanf(p,"%d %d",&voice->consonant_amp, &voice->consonant_ampv); break; case V_SPEED: sscanf(p,"%d",&voice->speed_percent); break; case V_MBROLA: { int srate = 16000; name2[0] = 0; sscanf(p,"%s %s %d",name1,name2,&srate); if(LoadMbrolaTable(name1,name2,srate) != EE_OK) { fprintf(stderr,"mbrola voice not found\n"); } voice->samplerate = srate; } break; case V_KLATT: voice->klattv[0] = 1; // default source: IMPULSIVE Read8Numbers(p,voice->klattv); voice->klattv[KLATT_Kopen] -= 40; break; case V_FAST: Read8Numbers(p,speed.fast_settings); SetSpeed(3); break; case V_DICTMIN: sscanf(p,"%d",&dict_min); break; case V_ALPHABET2: { ALPHABET *alphabet; name1[0] = name2[0] = 0; sscanf(p, "%s %s", name1, name2); if(strcmp(name1, "latin") == 0) { strncpy0(langopts->ascii_language,name2,sizeof(langopts->ascii_language)); } else if((alphabet = AlphabetFromName(name1)) != 0) { langopts->alt_alphabet = alphabet->offset; langopts->alt_alphabet_lang = StringToWord2(name2); } else { fprintf(stderr,"alphabet name '%s' not found\n", name1); } } break; case V_DICTDIALECT: // specify a dialect to use for foreign words, eg, en-us for _^_EN if(sscanf(p, "%s", name1) == 1) { if((ix = LookupMnem(dict_dialects, name1)) > 0) { langopts->dict_dialect |= (1 << ix); } else { fprintf(stderr, "dictdialect name '%s' not recognized\n", name1); } } break; default: if((key & 0xff00) == 0x100) { sscanf(p,"%d",&langopts->param[key &0xff]); } else { fprintf(stderr,"Bad voice attribute: %s\n",buf); } break; } } if(f_voice != NULL) fclose(f_voice); if((new_translator == NULL) && (!tone_only)) { // not set by language attribute new_translator = SelectTranslator(translator_name); } SetSpeed(3); // for speed_percent for(ix=0; ix<N_PEAKS; ix++) { voice->freq2[ix] = voice->freq[ix]; voice->height2[ix] = voice->height[ix]; voice->width2[ix] = voice->width[ix]; } if(tone_only) { new_translator = translator; } else { if((ix = SelectPhonemeTableName(phonemes_name)) < 0) { fprintf(stderr,"Unknown phoneme table: '%s'\n",phonemes_name); ix = 0; } voice->phoneme_tab_ix = ix; new_translator->phoneme_tab_ix = ix; new_translator->dict_min_size = dict_min; LoadDictionary(new_translator, new_dictionary, control & 4); if(dictionary_name[0]==0) return(NULL); // no dictionary loaded new_translator->dict_condition = conditional_rules; voice_languages[langix] = 0; } langopts = &new_translator->langopts; if((value = langopts->param[LOPT_LENGTH_MODS]) != 0) { SetLengthMods(new_translator,value); } voice->width[0] = (voice->width[0] * 105)/100; if(!tone_only) { translator = new_translator; } // relative lengths of different stress syllables for(ix=0; ix<stress_lengths_set; ix++) { translator->stress_lengths[ix] = stress_lengths[ix]; } for(ix=0; ix<stress_add_set; ix++) { translator->stress_lengths[ix] += stress_add[ix]; } for(ix=0; ix<stress_amps_set; ix++) { translator->stress_amps[ix] = stress_amps[ix]; translator->stress_amps_r[ix] = stress_amps[ix] -1; } return(voice); } // end of LoadVoice
voice_t *LoadVoice(const char *vname, int control) {//=============================================== // control, bit 0 1= no_default // bit 1 1 = change tone only, not language // bit 2 1 = don't report error on LoadDictionary // bit 4 1 = vname = full path FILE *f_voice = NULL; keywtab_t *k; char *p; int key; int ix; int n; int value; int error = 0; int langix = 0; int tone_only = control & 2; int language_set = 0; int phonemes_set = 0; int stress_amps_set = 0; int stress_lengths_set = 0; int stress_add_set = 0; int conditional_rules = 0; LANGUAGE_OPTIONS *langopts = NULL; Translator *new_translator = NULL; char voicename[40]; char language_name[40]; char translator_name[40]; char new_dictionary[40]; char phonemes_name[40]; const char *language_type; char buf[200]; char path_voices[sizeof(path_home)+12]; char langname[4]; int stress_amps[8]; int stress_lengths[8]; int stress_add[8]; int pitch1; int pitch2; static char voice_identifier[40]; // file name for voice_selected static char voice_name[40]; // voice name for voice_selected static char voice_languages[100]; // list of languages and priorities for voice_selected strcpy(voicename,vname); if(voicename[0]==0) strcpy(voicename,"default"); if(control & 0x10) { strcpy(buf,vname); if(GetFileLength(buf) <= 0) return(NULL); } else { sprintf(path_voices,"%s%cvoices%c",path_home,PATHSEP,PATHSEP); sprintf(buf,"%s%s",path_voices,voicename); if(GetFileLength(buf) <= 0) { // look for the voice in a sub-directory of the language name langname[0] = voicename[0]; langname[1] = voicename[1]; langname[2] = 0; sprintf(buf,"%s%s%c%s",path_voices,langname,PATHSEP,voicename); if(GetFileLength(buf) <= 0) { // look in "test" sub-directory sprintf(buf,"%stest%c%s",path_voices,PATHSEP,voicename); } } } f_voice = fopen(buf,"r"); language_type = "en"; // default if(f_voice == NULL) { if(control & 3) return(NULL); // can't open file if(SelectPhonemeTableName(voicename) >= 0) language_type = voicename; } if(!tone_only && (translator != NULL)) { DeleteTranslator(translator); translator = NULL; } strcpy(translator_name,language_type); strcpy(new_dictionary,language_type); strcpy(phonemes_name,language_type); if(!tone_only) { voice = &voicedata; strncpy0(voice_identifier,vname,sizeof(voice_identifier)); voice_name[0] = 0; voice_languages[0] = 0; voice_selected.identifier = voice_identifier; voice_selected.name = voice_name; voice_selected.languages = voice_languages; } else { // append the variant file name to the voice identifier if((p = strchr(voice_identifier,'+')) != NULL) *p = 0; // remove previous variant name sprintf(buf,"+%s",&vname[3]); // omit !v/ from the variant filename strcat(voice_identifier,buf); langopts = &translator->langopts; } VoiceReset(tone_only); if(!tone_only) SelectPhonemeTableName(phonemes_name); // set up phoneme_tab while((f_voice != NULL) && (fgets_strip(buf,sizeof(buf),f_voice) != NULL)) { // isolate the attribute name for(p=buf; (*p != 0) && !isspace(*p); p++); *p++ = 0; if(buf[0] == 0) continue; key = 0; for(k=keyword_tab; k->mnem != NULL; k++) { if(strcmp(buf,k->mnem)==0) { key = k->data; break; } } switch(key) { case V_LANGUAGE: { unsigned int len; int priority; if(tone_only) break; priority = DEFAULT_LANGUAGE_PRIORITY; language_name[0] = 0; sscanf(p,"%s %d",language_name,&priority); if(strcmp(language_name,"variant") == 0) break; len = strlen(language_name) + 2; // check for space in languages[] if(len < (sizeof(voice_languages)-langix-1)) { voice_languages[langix] = priority; strcpy(&voice_languages[langix+1],language_name); langix += len; } // only act on the first language line if(language_set == 0) { language_type = strtok(language_name,"-"); language_set = 1; strcpy(translator_name,language_type); strcpy(new_dictionary,language_type); strcpy(phonemes_name,language_type); SelectPhonemeTableName(phonemes_name); if(new_translator != NULL) DeleteTranslator(new_translator); new_translator = SelectTranslator(translator_name); langopts = &new_translator->langopts; } } break; case V_NAME: if(tone_only == 0) { while(isspace(*p)) p++; strncpy0(voice_name,p,sizeof(voice_name)); } break; case V_GENDER: { int age; char vgender[80]; sscanf(p,"%s %d",vgender,&age); voice_selected.gender = LookupMnem(genders,vgender); voice_selected.age = age; } break; case V_TRANSLATOR: if(tone_only) break; sscanf(p,"%s",translator_name); if(new_translator != NULL) DeleteTranslator(new_translator); new_translator = SelectTranslator(translator_name); langopts = &new_translator->langopts; break; case V_DICTIONARY: // dictionary sscanf(p,"%s",new_dictionary); break; case V_PHONEMES: // phoneme table sscanf(p,"%s",phonemes_name); break; case V_FORMANT: VoiceFormant(p); break; case V_PITCH: { double factor; // default is pitch 82 118 n = sscanf(p,"%d %d",&pitch1,&pitch2); voice->pitch_base = (pitch1 - 9) << 12; voice->pitch_range = (pitch2 - pitch1) * 108; factor = double(pitch1 - 82)/82; voice->formant_factor = (int)((1+factor/4) * 256); // nominal formant shift for a different voice pitch } break; case V_STRESSLENGTH: // stressLength stress_lengths_set = Read8Numbers(p,stress_lengths); break; case V_STRESSAMP: // stressAmp stress_amps_set = Read8Numbers(p,stress_amps); break; case V_STRESSADD: // stressAdd stress_add_set = Read8Numbers(p,stress_add); break; case V_INTONATION: // intonation sscanf(p,"%d %d",&option_tone_flags,&option_tone2); if((option_tone_flags & 0xff) != 0) langopts->intonation_group = option_tone_flags & 0xff; break; case V_DICTRULES: // conditional dictionary rules and list entries while(*p != 0) { while(isspace(*p)) p++; n = -1; if(((n = atoi(p)) > 0) && (n < 32)) { p++; conditional_rules |= (1 << n); } while(isalnum(*p)) p++; } break; case V_REPLACE: if(phonemes_set == 0) { // must set up a phoneme table before we can lookup phoneme mnemonics SelectPhonemeTableName(phonemes_name); phonemes_set = 1; } PhonemeReplacement(key,p); break; case V_WORDGAP: // words sscanf(p,"%d %d",&langopts->word_gap, &langopts->vowel_pause); break; case V_STRESSRULE: sscanf(p,"%d %d %d %d",&langopts->stress_rule, &langopts->stress_flags, &langopts->unstressed_wd1, &langopts->unstressed_wd2); break; case V_CHARSET: if((sscanf(p,"%d",&value)==1) && (value < N_CHARSETS)) new_translator->charset_a0 = charsets[value]; break; case V_NUMBERS: sscanf(p,"%d %d",&langopts->numbers,&langopts->numbers2); break; case V_OPTION: if(sscanf(p,"%d %d",&ix,&value) == 2) { if((ix >= 0) && (ix < N_LOPTS)) langopts->param[ix] = value; } break; case V_ECHO: // echo. suggest: 135mS 11% value = 0; voice->echo_amp = 0; sscanf(p,"%d %d",&voice->echo_delay,&voice->echo_amp); break; case V_FLUTTER: // flutter if(sscanf(p,"%d",&value)==1) voice->flutter = value * 32; break; case V_ROUGHNESS: // roughness if(sscanf(p,"%d",&value)==1) voice->roughness = value; break; case V_CLARITY: // formantshape if(sscanf(p,"%d",&value)==1) { if(value > 4) { voice->peak_shape = 1; // squarer formant peaks value = 4; } voice->n_harmonic_peaks = 1+value; } break; case V_TONE: { int tone_data[12]; ReadTonePoints(p,tone_data); SetToneAdjust(voice,tone_data); } break; case V_VOICING: if(sscanf(p,"%d",&value)==1) voice->voicing = (value * 64)/100; break; case V_BREATH: voice->breath[0] = Read8Numbers(p,&voice->breath[1]); for(ix=1; ix<8; ix++) { if(ix % 2) voice->breath[ix] = -voice->breath[ix]; } break; case V_BREATHW: voice->breathw[0] = Read8Numbers(p,&voice->breathw[1]); break; case V_CONSONANTS: value = sscanf(p,"%d %d",&voice->consonant_amp, &voice->consonant_ampv); break; case V_MBROLA: { int srate = 16000; char name[40]; char phtrans[40]; phtrans[0] = 0; sscanf(p,"%s %s %d",name,phtrans,&srate); LoadMbrolaTable(name,phtrans,srate); } break; case V_KLATT: voice->klattv[0] = 1; // default source: IMPULSIVE Read8Numbers(p,voice->klattv); voice->klattv[KLATT_Kopen] -= 40; break; case V_FAST: Read8Numbers(p,speed.fast_settings); SetSpeed(2); break; default: if((key & 0xff00) == 0x100) { sscanf(p,"%d",&langopts->param[key &0xff]); } else { fprintf(stderr,"Bad voice attribute: %s\n",buf); } break; } } if(f_voice != NULL) fclose(f_voice); if((new_translator == NULL) && (!tone_only)) { // not set by language attribute new_translator = SelectTranslator(translator_name); } for(ix=0; ix<N_PEAKS; ix++) { voice->freq2[ix] = voice->freq[ix]; voice->height2[ix] = voice->height[ix]; voice->width2[ix] = voice->width[ix]; } if(tone_only) { new_translator = translator; } else { if((ix = SelectPhonemeTableName(phonemes_name)) < 0) { fprintf(stderr,"Unknown phoneme table: '%s'\n",phonemes_name); } voice->phoneme_tab_ix = ix; error = LoadDictionary(new_translator, new_dictionary, control & 4); if(dictionary_name[0]==0) return(NULL); // no dictionary loaded new_translator->dict_condition = conditional_rules; voice_languages[langix] = 0; } langopts = &new_translator->langopts; if((value = langopts->param[LOPT_LENGTH_MODS]) != 0) { SetLengthMods(new_translator,value); } voice->width[0] = (voice->width[0] * 105)/100; if(!tone_only) { translator = new_translator; } // relative lengths of different stress syllables for(ix=0; ix<stress_lengths_set; ix++) { translator->stress_lengths[ix] = stress_lengths[ix]; } for(ix=0; ix<stress_add_set; ix++) { translator->stress_lengths[ix] += stress_add[ix]; } for(ix=0; ix<stress_amps_set; ix++) { translator->stress_amps[ix] = stress_amps[ix]; translator->stress_amps_r[ix] = stress_amps[ix] -1; } return(voice); } // end of LoadVoice