| // This version of the command-line speak program uses the | // This version of the command-line speak program uses the | ||||
| // libespeak.so.1 library | // libespeak.so.1 library | ||||
| static const char *help_text = | static const char *help_text = | ||||
| "\nespeak-ng [options] [\"<words>\"]\n\n" | "\nespeak-ng [options] [\"<words>\"]\n\n" | ||||
| "-f <text file> Text file to speak\n" | "-f <text file> Text file to speak\n" | ||||
| "\t List the available voices for the specified language.\n" | "\t List the available voices for the specified language.\n" | ||||
| "\t If <language> is omitted, then list all voices.\n"; | "\t If <language> is omitted, then list all voices.\n"; | ||||
| int samplerate; | int samplerate; | ||||
| int quiet = 0; | int quiet = 0; | ||||
| unsigned int samples_total = 0; | unsigned int samples_total = 0; | ||||
| char filetype[5]; | char filetype[5]; | ||||
| char wavefile[200]; | char wavefile[200]; | ||||
| void DisplayVoices(FILE *f_out, char *language) | void DisplayVoices(FILE *f_out, char *language) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| } | } | ||||
| } | } | ||||
| static void Write4Bytes(FILE *f, int value) | static void Write4Bytes(FILE *f, int value) | ||||
| { | { | ||||
| // Write 4 bytes to a file, least significant first | // Write 4 bytes to a file, least significant first | ||||
| } | } | ||||
| } | } | ||||
| int OpenWavFile(char *path, int rate) | int OpenWavFile(char *path, int rate) | ||||
| { | { | ||||
| static unsigned char wave_hdr[44] = { | static unsigned char wave_hdr[44] = { | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| fwrite(wave_hdr, 1, 24, f_wavfile); | fwrite(wave_hdr, 1, 24, f_wavfile); | ||||
| Write4Bytes(f_wavfile, rate); | Write4Bytes(f_wavfile, rate); | ||||
| Write4Bytes(f_wavfile, rate * 2); | Write4Bytes(f_wavfile, rate * 2); | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static void CloseWavFile() | static void CloseWavFile() | ||||
| { | { | ||||
| unsigned int pos; | unsigned int pos; | ||||
| } | } | ||||
| static int SynthCallback(short *wav, int numsamples, espeak_EVENT *events) | static int SynthCallback(short *wav, int numsamples, espeak_EVENT *events) | ||||
| { | { | ||||
| char fname[210]; | char fname[210]; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static void PrintVersion() | static void PrintVersion() | ||||
| { | { | ||||
| const char *version; | const char *version; | ||||
| printf("eSpeak text-to-speech: %s Data at: %s\n", version, path_data); | printf("eSpeak text-to-speech: %s Data at: %s\n", version, path_data); | ||||
| } | } | ||||
| #ifdef NEED_GETOPT | #ifdef NEED_GETOPT | ||||
| struct option { | struct option { | ||||
| char *name; | char *name; | ||||
| static const char *err_load = "Failed to read "; | static const char *err_load = "Failed to read "; | ||||
| FILE *f_text = NULL; | FILE *f_text = NULL; | ||||
| char *p_text = NULL; | char *p_text = NULL; | ||||
| FILE *f_phonemes_out = stdout; | FILE *f_phonemes_out = stdout; | ||||
| else | else | ||||
| synth_flags |= espeakCHARS_8BIT; | synth_flags |= espeakCHARS_8BIT; | ||||
| break; | break; | ||||
| case 'h': | case 'h': | ||||
| printf("\n"); | printf("\n"); | ||||
| PrintVersion(); | PrintVersion(); | ||||
| printf("%s", help_text); | printf("%s", help_text); | ||||
| exit(0); | exit(0); | ||||
| break; | break; | ||||
| case 'k': | case 'k': | ||||
| option_capitals = atoi(optarg2); | option_capitals = atoi(optarg2); | ||||
| break; | break; | ||||
| case 'x': | case 'x': | ||||
| phoneme_options |= espeakPHONEMES_SHOW; | phoneme_options |= espeakPHONEMES_SHOW; | ||||
| break; | break; | ||||
| case 'X': | case 'X': | ||||
| phoneme_options |= espeakPHONEMES_TRACE; | phoneme_options |= espeakPHONEMES_TRACE; | ||||
| break; | break; | ||||
| case 'm': | case 'm': | ||||
| synth_flags |= espeakSSML; | synth_flags |= espeakSSML; | ||||
| break; | break; | ||||
| case 'p': | case 'p': | ||||
| pitch = atoi(optarg2); | pitch = atoi(optarg2); | ||||
| break; | break; | ||||
| case 'q': | case 'q': | ||||
| quiet = 1; | quiet = 1; | ||||
| break; | break; | ||||
| case 'f': | case 'f': | ||||
| strncpy0(filename, optarg2, sizeof(filename)); | strncpy0(filename, optarg2, sizeof(filename)); | ||||
| break; | break; | ||||
| case 'l': | case 'l': | ||||
| option_linelength = atoi(optarg2); | option_linelength = atoi(optarg2); | ||||
| break; | break; | ||||
| case 'a': | case 'a': | ||||
| volume = atoi(optarg2); | volume = atoi(optarg2); | ||||
| break; | break; | ||||
| case 's': | case 's': | ||||
| speed = atoi(optarg2); | speed = atoi(optarg2); | ||||
| break; | break; | ||||
| case 'g': | case 'g': | ||||
| wordgap = atoi(optarg2); | wordgap = atoi(optarg2); | ||||
| break; | break; | ||||
| case 'v': | case 'v': | ||||
| strncpy0(voicename, optarg2, sizeof(voicename)); | strncpy0(voicename, optarg2, sizeof(voicename)); | ||||
| break; | break; | ||||
| case 'w': | case 'w': | ||||
| option_waveout = 1; | option_waveout = 1; | ||||
| strncpy0(wavefile, optarg2, sizeof(filename)); | strncpy0(wavefile, optarg2, sizeof(filename)); | ||||
| break; | break; | ||||
| case 'z': // remove pause from the end of a sentence | case 'z': // remove pause from the end of a sentence | ||||
| synth_flags &= ~espeakENDPAUSE; | synth_flags &= ~espeakENDPAUSE; | ||||
| break; | break; | ||||
| case 0x100: // --stdin | case 0x100: // --stdin | ||||
| flag_stdin = 1; | flag_stdin = 1; | ||||
| break; | break; | ||||
| case 0x105: // --stdout | case 0x105: // --stdout | ||||
| option_waveout = 1; | option_waveout = 1; | ||||
| strcpy(wavefile, "stdout"); | strcpy(wavefile, "stdout"); | ||||
| break; | break; | ||||
| case 0x101: // --compile-debug | case 0x101: // --compile-debug | ||||
| case 0x102: // --compile | case 0x102: // --compile | ||||
| strncpy0(voicename, optarg2, sizeof(voicename)); | strncpy0(voicename, optarg2, sizeof(voicename)); | ||||
| flag_compile = c; | flag_compile = c; | ||||
| quiet = 1; | quiet = 1; | ||||
| break; | break; | ||||
| case 0x103: // --punct | case 0x103: // --punct | ||||
| option_punctuation = 1; | option_punctuation = 1; | ||||
| if (optarg2 != NULL) { | if (optarg2 != NULL) { | ||||
| option_punctuation = 2; | option_punctuation = 2; | ||||
| } | } | ||||
| break; | break; | ||||
| case 0x104: // --voices | case 0x104: // --voices | ||||
| espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS, 0, data_path, 0); | espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS, 0, data_path, 0); | ||||
| DisplayVoices(stdout, optarg2); | DisplayVoices(stdout, optarg2); | ||||
| exit(0); | exit(0); | ||||
| case 0x106: // -- split | case 0x106: // -- split | ||||
| if (optarg2 == NULL) | if (optarg2 == NULL) | ||||
| samples_split_seconds = 30 * 60; // default 30 minutes | samples_split_seconds = 30 * 60; // default 30 minutes | ||||
| else | else | ||||
| samples_split_seconds = atoi(optarg2) * 60; | samples_split_seconds = atoi(optarg2) * 60; | ||||
| break; | break; | ||||
| case 0x107: // --path | case 0x107: // --path | ||||
| data_path = optarg2; | data_path = optarg2; | ||||
| break; | break; | ||||
| case 0x108: // --phonout | case 0x108: // --phonout | ||||
| if ((f_phonemes_out = fopen(optarg2, "w")) == NULL) | if ((f_phonemes_out = fopen(optarg2, "w")) == NULL) | ||||
| fprintf(stderr, "Can't write to: %s\n", optarg2); | fprintf(stderr, "Can't write to: %s\n", optarg2); | ||||
| break; | break; | ||||
| case 0x109: // --pho | case 0x109: // --pho | ||||
| phoneme_options |= espeakPHONEMES_MBROLA; | phoneme_options |= espeakPHONEMES_MBROLA; | ||||
| break; | break; | ||||
| case 0x10a: // --ipa | case 0x10a: // --ipa | ||||
| phoneme_options |= espeakPHONEMES_IPA; | phoneme_options |= espeakPHONEMES_IPA; | ||||
| if (optarg2 != NULL) { | if (optarg2 != NULL) { | ||||
| } | } | ||||
| break; | break; | ||||
| case 0x10b: // --version | case 0x10b: // --version | ||||
| PrintVersion(); | PrintVersion(); | ||||
| exit(0); | exit(0); | ||||
| case 0x10c: // --sep | case 0x10c: // --sep | ||||
| phoneme_options |= espeakPHONEMES_SHOW; | phoneme_options |= espeakPHONEMES_SHOW; | ||||
| if (optarg2 == 0) | if (optarg2 == 0) | ||||
| if (phonemes_separator == 'z') | if (phonemes_separator == 'z') | ||||
| phonemes_separator = 0x200c; // ZWNJ | phonemes_separator = 0x200c; // ZWNJ | ||||
| break; | break; | ||||
| case 0x10d: // --tie | case 0x10d: // --tie | ||||
| phoneme_options |= (espeakPHONEMES_SHOW | espeakPHONEMES_TIE); | phoneme_options |= (espeakPHONEMES_SHOW | espeakPHONEMES_TIE); | ||||
| if (optarg2 == 0) | if (optarg2 == 0) | ||||
| if (phonemes_separator == 'z') | if (phonemes_separator == 'z') | ||||
| phonemes_separator = 0x200d; // ZWJ | phonemes_separator = 0x200d; // ZWJ | ||||
| break; | break; | ||||
| case 0x10e: // --compile-mbrola | case 0x10e: // --compile-mbrola | ||||
| samplerate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, data_path, 0); | samplerate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, data_path, 0); | ||||
| espeak_ng_CompileMbrolaVoice(optarg2, stdout); | espeak_ng_CompileMbrolaVoice(optarg2, stdout); | ||||
| exit(0); | exit(0); | ||||
| case 0x10f: // --compile-intonations | case 0x10f: // --compile-intonations | ||||
| samplerate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, data_path, espeakINITIALIZE_PATH_ONLY); | samplerate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, data_path, espeakINITIALIZE_PATH_ONLY); | ||||
| espeak_ng_CompileIntonation(stdout); | espeak_ng_CompileIntonation(stdout); | ||||
| exit(0); | exit(0); | ||||
| case 0x110: // --compile-phonemes | case 0x110: // --compile-phonemes | ||||
| samplerate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, data_path, espeakINITIALIZE_PATH_ONLY); | samplerate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, data_path, espeakINITIALIZE_PATH_ONLY); | ||||
| espeak_ng_CompilePhonemeData(22050, stdout); | espeak_ng_CompilePhonemeData(22050, stdout); | ||||
| exit(0); | exit(0); | ||||
| default: | default: | ||||
| exit(0); | exit(0); | ||||
| } | } | ||||
| } | } | ||||
| if (option_waveout || quiet) { | if (option_waveout || quiet) { | ||||
| // writing to a file (or no output), we can use synchronous mode | // writing to a file (or no output), we can use synchronous mode | ||||
| samplerate = espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS, 0, data_path, 0); | samplerate = espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS, 0, data_path, 0); | ||||
| samplerate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, data_path, 0); | samplerate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, data_path, 0); | ||||
| } | } | ||||
| if (voicename[0] == 0) | if (voicename[0] == 0) | ||||
| strcpy(voicename, "default"); | strcpy(voicename, "default"); | ||||
| if (option_punctuation == 2) | if (option_punctuation == 2) | ||||
| espeak_SetPunctuationList(option_punctlist); | espeak_SetPunctuationList(option_punctlist); | ||||
| espeak_SetPhonemeTrace(phoneme_options | (phonemes_separator << 8), f_phonemes_out); | espeak_SetPhonemeTrace(phoneme_options | (phonemes_separator << 8), f_phonemes_out); | ||||
| if (filename[0] == 0) { | if (filename[0] == 0) { | ||||
| exit(1); | exit(1); | ||||
| } | } | ||||
| if (p_text != NULL) { | if (p_text != NULL) { | ||||
| int size; | int size; | ||||
| size = strlen(p_text); | size = strlen(p_text); |
| { NULL, 0, 0 } | { NULL, 0, 0 } | ||||
| }; | }; | ||||
| static keywtab_t k_properties[] = { | static keywtab_t k_properties[] = { | ||||
| { "isPause", 0, phPAUSE }, | { "isPause", 0, phPAUSE }, | ||||
| { "isVowel", 0, phVOWEL }, | { "isVowel", 0, phVOWEL }, | ||||
| { "afr", tPHONEME_TYPE, phSTOP }, // treat as stop | { "afr", tPHONEME_TYPE, phSTOP }, // treat as stop | ||||
| { "apr", tPHONEME_TYPE, phFRICATIVE }, // [h] voiceless approximant | { "apr", tPHONEME_TYPE, phFRICATIVE }, // [h] voiceless approximant | ||||
| // keywords | // keywords | ||||
| { "phonemenumber", tSTATEMENT, kPHONEMENUMBER }, | { "phonemenumber", tSTATEMENT, kPHONEMENUMBER }, | ||||
| { "phonemetable", tSTATEMENT, kPHONEMETABLE }, | { "phonemetable", tSTATEMENT, kPHONEMETABLE }, | ||||
| { "endtype", tSTATEMENT, kENDTYPE }, | { "endtype", tSTATEMENT, kENDTYPE }, | ||||
| { "voicingswitch", tSTATEMENT, kVOICINGSWITCH }, | { "voicingswitch", tSTATEMENT, kVOICINGSWITCH }, | ||||
| { "IF", tSTATEMENT, kIF }, | { "IF", tSTATEMENT, kIF }, | ||||
| { "ELSE", tSTATEMENT, kELSE }, | { "ELSE", tSTATEMENT, kELSE }, | ||||
| { "ELIF", tSTATEMENT, kELIF }, | { "ELIF", tSTATEMENT, kELIF }, | ||||
| { "VowelEnding", tSTATEMENT, kVOWELENDING }, | { "VowelEnding", tSTATEMENT, kVOWELENDING }, | ||||
| { "addWav", tSTATEMENT, kANDWAV }, | { "addWav", tSTATEMENT, kANDWAV }, | ||||
| { "Vowelin", tSTATEMENT, kVOWELIN }, | { "Vowelin", tSTATEMENT, kVOWELIN }, | ||||
| { "Vowelout", tSTATEMENT, kVOWELOUT }, | { "Vowelout", tSTATEMENT, kVOWELOUT }, | ||||
| { "Continue", tSTATEMENT, kCONTINUE }, | { "Continue", tSTATEMENT, kCONTINUE }, | ||||
| { NULL, 0, -1 } | { NULL, 0, -1 } | ||||
| }; | }; | ||||
| static keywtab_t *keyword_tabs[] = { | static keywtab_t *keyword_tabs[] = { | ||||
| keywords, k_conditions, k_properties, k_intonation | keywords, k_conditions, k_properties, k_intonation | ||||
| }; | }; | ||||
| static PHONEME_TAB *phoneme_out; | static PHONEME_TAB *phoneme_out; | ||||
| static int n_phcodes_list[N_PHONEME_TABS]; | static int n_phcodes_list[N_PHONEME_TABS]; | ||||
| static char *p_equivalence; | static char *p_equivalence; | ||||
| static char equivalence_buf[20000]; | static char equivalence_buf[20000]; | ||||
| #define N_PROCS 50 | #define N_PROCS 50 | ||||
| int n_procs; | int n_procs; | ||||
| int proc_addr[N_PROCS]; | int proc_addr[N_PROCS]; | ||||
| fprintf(f_out, ")"); | fprintf(f_out, ")"); | ||||
| } | } | ||||
| break; | break; | ||||
| case 2: | case 2: | ||||
| case 3: | case 3: | ||||
| if (type2 < 12) { | if (type2 < 12) { | ||||
| } | } | ||||
| fprintf(f_out, ")"); | fprintf(f_out, ")"); | ||||
| break; | break; | ||||
| case 6: | case 6: | ||||
| fprintf(f_out, "%s", instn_jumps[(instn >> 9) & 7]); | fprintf(f_out, "%s", instn_jumps[(instn >> 9) & 7]); | ||||
| fprintf(f_out, " %d", instn & 0x1ff); | fprintf(f_out, " %d", instn & 0x1ff); | ||||
| break; | break; | ||||
| case 9: | case 9: | ||||
| address = ((data1 & 0xf) << 4) + *pc++; | address = ((data1 & 0xf) << 4) + *pc++; | ||||
| fprintf(f_out, "CALL %.5x", address); | fprintf(f_out, "CALL %.5x", address); | ||||
| break; | break; | ||||
| case 10: | case 10: | ||||
| fprintf(f_out, "%s", instn10_string[type2]); | fprintf(f_out, "%s", instn10_string[type2]); | ||||
| switch (type2) | switch (type2) | ||||
| break; | break; | ||||
| } | } | ||||
| break; | break; | ||||
| case 11: | case 11: | ||||
| case 12: | case 12: | ||||
| case 13: | case 13: | ||||
| } | } | ||||
| } | } | ||||
| static int n_phoneme_tabs; | static int n_phoneme_tabs; | ||||
| static int n_phcodes; | static int n_phcodes; | ||||
| static FILE *f_prog_log = NULL; | static FILE *f_prog_log = NULL; | ||||
| static FILE *f_report; | static FILE *f_report; | ||||
| static FILE *f_in; | static FILE *f_in; | ||||
| static int f_in_linenum; | static int f_in_linenum; | ||||
| static int f_in_displ; | static int f_in_displ; | ||||
| static int linenum; | static int linenum; | ||||
| static int count_references = 0; | static int count_references = 0; | ||||
| static int duplicate_references = 0; | static int duplicate_references = 0; | ||||
| static REF_HASH_TAB *ref_hash_tab[256]; | static REF_HASH_TAB *ref_hash_tab[256]; | ||||
| #define N_ENVELOPES 30 | #define N_ENVELOPES 30 | ||||
| int n_envelopes = 0; | int n_envelopes = 0; | ||||
| char envelope_paths[N_ENVELOPES][80]; | char envelope_paths[N_ENVELOPES][80]; | ||||
| unsigned char envelope_dat[N_ENVELOPES][ENV_LEN]; | unsigned char envelope_dat[N_ENVELOPES][ENV_LEN]; | ||||
| typedef struct { | typedef struct { | ||||
| FILE *file; | FILE *file; | ||||
| int linenum; | int linenum; | ||||
| } IF_STACK; | } IF_STACK; | ||||
| IF_STACK if_stack[N_IF_STACK]; | IF_STACK if_stack[N_IF_STACK]; | ||||
| enum { | enum { | ||||
| tENDFILE = 1, | tENDFILE = 1, | ||||
| tSTRING, | tSTRING, | ||||
| #define N_ITEM_STRING 256 | #define N_ITEM_STRING 256 | ||||
| char item_string[N_ITEM_STRING]; | char item_string[N_ITEM_STRING]; | ||||
| static int ref_sorter(char **a, char **b) | static int ref_sorter(char **a, char **b) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| return p1->ph_mnemonic - p2->ph_mnemonic; | return p1->ph_mnemonic - p2->ph_mnemonic; | ||||
| } | } | ||||
| static void CompileReport(void) | static void CompileReport(void) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| fclose(f_report); | fclose(f_report); | ||||
| } | } | ||||
| static void error(const char *format, const char *string) | static void error(const char *format, const char *string) | ||||
| { | { | ||||
| if (string == NULL) | if (string == NULL) | ||||
| return f; | return f; | ||||
| } | } | ||||
| static unsigned int StringToWord(const char *string) | static unsigned int StringToWord(const char *string) | ||||
| { | { | ||||
| // Pack 4 characters into a word | // Pack 4 characters into a word | ||||
| return word; | return word; | ||||
| } | } | ||||
| static MNEM_TAB reserved_phonemes[] = { | static MNEM_TAB reserved_phonemes[] = { | ||||
| { "_\001", phonCONTROL }, // NOT USED | { "_\001", phonCONTROL }, // NOT USED | ||||
| { "%", phonSTRESS_U }, | { "%", phonSTRESS_U }, | ||||
| { NULL, 0 } | { NULL, 0 } | ||||
| }; | }; | ||||
| static void ReservePhCodes() | static void ReservePhCodes() | ||||
| { | { | ||||
| // Reserve phoneme codes which have fixed numbers so that they can be | // Reserve phoneme codes which have fixed numbers so that they can be | ||||
| } | } | ||||
| } | } | ||||
| static int LookupPhoneme(const char *string, int control) | static int LookupPhoneme(const char *string, int control) | ||||
| { | { | ||||
| // control = 0 explicit declaration | // control = 0 explicit declaration | ||||
| return use; | return use; | ||||
| } | } | ||||
| static unsigned int get_char() | static unsigned int get_char() | ||||
| { | { | ||||
| unsigned int c; | unsigned int c; | ||||
| linenum--; | linenum--; | ||||
| } | } | ||||
| int CheckNextChar() | int CheckNextChar() | ||||
| { | { | ||||
| int c; | int c; | ||||
| return c; | return c; | ||||
| } | } | ||||
| static int NextItem(int type) | static int NextItem(int type) | ||||
| { | { | ||||
| int acc; | int acc; | ||||
| return acc * sign; | return acc * sign; | ||||
| } | } | ||||
| if ((type >= tKEYWORD) && (type <= tINTONATION)) { | if ((type >= tKEYWORD) && (type <= tINTONATION)) { | ||||
| pk = keyword_tabs[type-tKEYWORD]; | pk = keyword_tabs[type-tKEYWORD]; | ||||
| while (pk->mnem != NULL) { | while (pk->mnem != NULL) { | ||||
| return -1; | return -1; | ||||
| } | } | ||||
| static int NextItemMax(int max) | static int NextItemMax(int max) | ||||
| { | { | ||||
| // Get a number, but restrict value to max | // Get a number, but restrict value to max | ||||
| return value; | return value; | ||||
| } | } | ||||
| static int NextItemBrackets(int type, int control) | static int NextItemBrackets(int type, int control) | ||||
| { | { | ||||
| // Expect a parameter inside parantheses | // Expect a parameter inside parantheses | ||||
| if (item_terminator != ')') | if (item_terminator != ')') | ||||
| error("Expected ')'", NULL); | error("Expected ')'", NULL); | ||||
| return value; | return value; | ||||
| } | } | ||||
| static void UngetItem() | static void UngetItem() | ||||
| { | { | ||||
| fseek(f_in, f_in_displ, SEEK_SET); | fseek(f_in, f_in_displ, SEEK_SET); | ||||
| linenum = f_in_linenum; | linenum = f_in_linenum; | ||||
| } | } | ||||
| static int Range(int value, int divide, int min, int max) | static int Range(int value, int divide, int min, int max) | ||||
| { | { | ||||
| if (value < 0) | if (value < 0) | ||||
| return value - min; | return value - min; | ||||
| } | } | ||||
| int CompileVowelTransition(int which) | int CompileVowelTransition(int which) | ||||
| { | { | ||||
| // Compile a vowel transition | // Compile a vowel transition | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| int LoadSpect(const char *path, int control) | int LoadSpect(const char *path, int control) | ||||
| { | { | ||||
| SpectSeq *spectseq; | SpectSeq *spectseq; | ||||
| return displ; | return displ; | ||||
| } | } | ||||
| static int LoadWavefile(FILE *f, const char *fname) | static int LoadWavefile(FILE *f, const char *fname) | ||||
| { | { | ||||
| int displ; | int displ; | ||||
| if (system(command) != 0) | if (system(command) != 0) | ||||
| failed = 1; | failed = 1; | ||||
| if (failed || (GetFileLength(fname_temp) <= 0)) { | if (failed || (GetFileLength(fname_temp) <= 0)) { | ||||
| if (resample_fails < 2) | if (resample_fails < 2) | ||||
| error("Resample command failed: %s", command); | error("Resample command failed: %s", command); | ||||
| max = sample; | max = sample; | ||||
| else if (sample < -max) | else if (sample < -max) | ||||
| max = -sample; | max = -sample; | ||||
| } | } | ||||
| scale_factor = (max / 127) + 1; | scale_factor = (max / 127) + 1; | ||||
| return displ | 0x800000; // set bit 23 to indicate a wave file rather than a spectrum | return displ | 0x800000; // set bit 23 to indicate a wave file rather than a spectrum | ||||
| } | } | ||||
| static int LoadEnvelope(FILE *f, const char *fname) | static int LoadEnvelope(FILE *f, const char *fname) | ||||
| { | { | ||||
| int displ; | int displ; | ||||
| return displ; | return displ; | ||||
| } | } | ||||
| /* Generate a hash code from the specified string */ | /* Generate a hash code from the specified string */ | ||||
| static int Hash8(const char *string) | static int Hash8(const char *string) | ||||
| { | { | ||||
| return (hash+chars) & 0xff; | return (hash+chars) & 0xff; | ||||
| } | } | ||||
| static int LoadEnvelope2(FILE *f, const char *fname) | static int LoadEnvelope2(FILE *f, const char *fname) | ||||
| { | { | ||||
| int ix, ix2; | int ix, ix2; | ||||
| env[x] = y; | env[x] = y; | ||||
| } | } | ||||
| if (n_envelopes < N_ENVELOPES) { | if (n_envelopes < N_ENVELOPES) { | ||||
| strncpy0(envelope_paths[n_envelopes], fname, sizeof(envelope_paths[0])); | strncpy0(envelope_paths[n_envelopes], fname, sizeof(envelope_paths[0])); | ||||
| memcpy(envelope_dat[n_envelopes], env, ENV_LEN); | memcpy(envelope_dat[n_envelopes], env, ENV_LEN); | ||||
| return addr; | return addr; | ||||
| } | } | ||||
| static int CompileToneSpec(void) | static int CompileToneSpec(void) | ||||
| { | { | ||||
| int pitch1 = 0; | int pitch1 = 0; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| /* | /* | ||||
| Condition | Condition | ||||
| bits 14,15 1 | bits 14,15 1 | ||||
| =8 data = stress bitmap | =8 data = stress bitmap | ||||
| =9 special tests | =9 special tests | ||||
| */ | */ | ||||
| int CompileIf(int elif) | int CompileIf(int elif) | ||||
| { | { | ||||
| int key; | int key; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| void FillThen(int add) | void FillThen(int add) | ||||
| { | { | ||||
| USHORT *p; | USHORT *p; | ||||
| then_count = 0; | then_count = 0; | ||||
| } | } | ||||
| int CompileElse(void) | int CompileElse(void) | ||||
| { | { | ||||
| USHORT *ref; | USHORT *ref; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| int CompileElif(void) | int CompileElif(void) | ||||
| { | { | ||||
| if (if_level < 1) { | if (if_level < 1) { | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| int CompileEndif(void) | int CompileEndif(void) | ||||
| { | { | ||||
| USHORT *p; | USHORT *p; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static int CompileSwitch(int type) | static int CompileSwitch(int type) | ||||
| { | { | ||||
| // Type 0: EndSwitch | // Type 0: EndSwitch | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static PHONEME_TAB_LIST *FindPhonemeTable(const char *string) | static PHONEME_TAB_LIST *FindPhonemeTable(const char *string) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| static PHONEME_TAB *FindPhoneme(const char *string) | static PHONEME_TAB *FindPhoneme(const char *string) | ||||
| { | { | ||||
| PHONEME_TAB_LIST *phtab = NULL; | PHONEME_TAB_LIST *phtab = NULL; | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| static void ImportPhoneme(void) | static void ImportPhoneme(void) | ||||
| { | { | ||||
| unsigned int ph_mnem; | unsigned int ph_mnem; | ||||
| if (phoneme_out->type != phVOWEL) { | if (phoneme_out->type != phVOWEL) { | ||||
| phoneme_out->end_type = 0; // voicingswitch, this must be set later to refer to a local phoneme | phoneme_out->end_type = 0; // voicingswitch, this must be set later to refer to a local phoneme | ||||
| } | } | ||||
| } | } | ||||
| static void CallPhoneme(void) | static void CallPhoneme(void) | ||||
| { | { | ||||
| PHONEME_TAB *ph; | PHONEME_TAB *ph; | ||||
| *prog_out++ = addr; | *prog_out++ = addr; | ||||
| } | } | ||||
| static void DecThenCount() | static void DecThenCount() | ||||
| { | { | ||||
| if (then_count > 0) | if (then_count > 0) | ||||
| *prog_out++ = instn + phcode; | *prog_out++ = instn + phcode; | ||||
| } | } | ||||
| int CompilePhoneme(int compile_phoneme) | int CompilePhoneme(int compile_phoneme) | ||||
| { | { | ||||
| int endphoneme = 0; | int endphoneme = 0; | ||||
| error("More than one phoneme type: %s", item_string); | error("More than one phoneme type: %s", item_string); | ||||
| phoneme_out->type = keyword; | phoneme_out->type = keyword; | ||||
| break; | break; | ||||
| case tPLACE: | case tPLACE: | ||||
| if (place_articulation > 0) | if (place_articulation > 0) | ||||
| error("Place of articulation has already been given: %s", item_string); | error("Place of articulation has already been given: %s", item_string); | ||||
| place_articulation = keyword; | place_articulation = keyword; | ||||
| break; | break; | ||||
| case tPHONEME_FLAG: | case tPHONEME_FLAG: | ||||
| phoneme_flags |= keyword; | phoneme_flags |= keyword; | ||||
| break; | break; | ||||
| case tINSTRN1: | case tINSTRN1: | ||||
| // instruction group 0, with 8 bit operands which set data in PHONEME_DATA | // instruction group 0, with 8 bit operands which set data in PHONEME_DATA | ||||
| switch (keyword) | switch (keyword) | ||||
| InstnPlusPhoneme(keyword << 8); | InstnPlusPhoneme(keyword << 8); | ||||
| DecThenCount(); | DecThenCount(); | ||||
| break; | break; | ||||
| case i_PAUSE_BEFORE: | case i_PAUSE_BEFORE: | ||||
| value = NextItemMax(255); | value = NextItemMax(255); | ||||
| *prog_out++ = (i_PAUSE_BEFORE << 8) + value; | *prog_out++ = (i_PAUSE_BEFORE << 8) + value; | ||||
| DecThenCount(); | DecThenCount(); | ||||
| break; | break; | ||||
| case i_PAUSE_AFTER: | case i_PAUSE_AFTER: | ||||
| value = NextItemMax(255); | value = NextItemMax(255); | ||||
| *prog_out++ = (i_PAUSE_AFTER << 8) + value; | *prog_out++ = (i_PAUSE_AFTER << 8) + value; | ||||
| DecThenCount(); | DecThenCount(); | ||||
| break; | break; | ||||
| case i_SET_LENGTH: | case i_SET_LENGTH: | ||||
| value = NextItemMax(511); | value = NextItemMax(511); | ||||
| if (phoneme_out->type == phVOWEL) | if (phoneme_out->type == phVOWEL) | ||||
| DecThenCount(); | DecThenCount(); | ||||
| } | } | ||||
| break; | break; | ||||
| case i_ADD_LENGTH: | case i_ADD_LENGTH: | ||||
| value = NextItem(tSIGNEDNUMBER) / 2; | value = NextItem(tSIGNEDNUMBER) / 2; | ||||
| *prog_out++ = (i_ADD_LENGTH << 8) + (value & 0xff); | *prog_out++ = (i_ADD_LENGTH << 8) + (value & 0xff); | ||||
| DecThenCount(); | DecThenCount(); | ||||
| break; | break; | ||||
| case i_LENGTH_MOD: | case i_LENGTH_MOD: | ||||
| value = NextItem(tNUMBER); | value = NextItem(tNUMBER); | ||||
| phoneme_out->length_mod = value; | phoneme_out->length_mod = value; | ||||
| break; | break; | ||||
| case i_IPA_NAME: | case i_IPA_NAME: | ||||
| NextItem(tSTRING); | NextItem(tSTRING); | ||||
| break; | break; | ||||
| } | } | ||||
| break; | break; | ||||
| case tSTATEMENT: | case tSTATEMENT: | ||||
| switch (keyword) | switch (keyword) | ||||
| { | { | ||||
| ImportPhoneme(); | ImportPhoneme(); | ||||
| phoneme_flags = phoneme_out->phflags; | phoneme_flags = phoneme_out->phflags; | ||||
| break; | break; | ||||
| case kSTARTTYPE: | case kSTARTTYPE: | ||||
| phcode = NextItem(tPHONEMEMNEM); | phcode = NextItem(tPHONEMEMNEM); | ||||
| if (phcode == -1) | if (phcode == -1) | ||||
| phcode = LookupPhoneme(item_string, 1); | phcode = LookupPhoneme(item_string, 1); | ||||
| phoneme_out->start_type = phcode; | phoneme_out->start_type = phcode; | ||||
| break; | break; | ||||
| case kENDTYPE: | case kENDTYPE: | ||||
| phcode = NextItem(tPHONEMEMNEM); | phcode = NextItem(tPHONEMEMNEM); | ||||
| if (phcode == -1) | if (phcode == -1) | ||||
| else if (phcode != phoneme_out->start_type) | else if (phcode != phoneme_out->start_type) | ||||
| error("endtype must equal starttype for consonants", NULL); | error("endtype must equal starttype for consonants", NULL); | ||||
| break; | break; | ||||
| case kVOICINGSWITCH: | case kVOICINGSWITCH: | ||||
| phcode = NextItem(tPHONEMEMNEM); | phcode = NextItem(tPHONEMEMNEM); | ||||
| if (phcode == -1) | if (phcode == -1) | ||||
| phcode = LookupPhoneme(item_string, 1); | phcode = LookupPhoneme(item_string, 1); | ||||
| phoneme_out->end_type = phcode; // use end_type field for consonants as voicing_switch | phoneme_out->end_type = phcode; // use end_type field for consonants as voicing_switch | ||||
| break; | break; | ||||
| case kSTRESSTYPE: | case kSTRESSTYPE: | ||||
| value = NextItem(tNUMBER); | value = NextItem(tNUMBER); | ||||
| phoneme_out->std_length = value; | phoneme_out->std_length = value; | ||||
| prog_out = prog_buf; | prog_out = prog_buf; | ||||
| } | } | ||||
| break; | break; | ||||
| case kIF: | case kIF: | ||||
| endphoneme = CompileIf(0); | endphoneme = CompileIf(0); | ||||
| break; | break; | ||||
| case kELSE: | case kELSE: | ||||
| endphoneme = CompileElse(); | endphoneme = CompileElse(); | ||||
| break; | break; | ||||
| case kELIF: | case kELIF: | ||||
| endphoneme = CompileElif(); | endphoneme = CompileElif(); | ||||
| break; | break; | ||||
| case kENDIF: | case kENDIF: | ||||
| endphoneme = CompileEndif(); | endphoneme = CompileEndif(); | ||||
| break; | break; | ||||
| case kENDSWITCH: | case kENDSWITCH: | ||||
| break; | break; | ||||
| case kSWITCH_PREVVOWEL: | case kSWITCH_PREVVOWEL: | ||||
| endphoneme = CompileSwitch(1); | endphoneme = CompileSwitch(1); | ||||
| break; | break; | ||||
| case kSWITCH_NEXTVOWEL: | case kSWITCH_NEXTVOWEL: | ||||
| endphoneme = CompileSwitch(2); | endphoneme = CompileSwitch(2); | ||||
| break; | break; | ||||
| case kCALLPH: | case kCALLPH: | ||||
| CallPhoneme(); | CallPhoneme(); | ||||
| DecThenCount(); | DecThenCount(); | ||||
| break; | break; | ||||
| case kFMT: | case kFMT: | ||||
| if_stack[if_level].returned = 1; | if_stack[if_level].returned = 1; | ||||
| DecThenCount(); | DecThenCount(); | ||||
| else | else | ||||
| endphoneme = CompileSound(keyword, 0); | endphoneme = CompileSound(keyword, 0); | ||||
| break; | break; | ||||
| case kWAV: | case kWAV: | ||||
| if_stack[if_level].returned = 1; | if_stack[if_level].returned = 1; | ||||
| // fallthrough: | |||||
| case kVOWELSTART: | case kVOWELSTART: | ||||
| case kVOWELENDING: | case kVOWELENDING: | ||||
| case kANDWAV: | case kANDWAV: | ||||
| DecThenCount(); | DecThenCount(); | ||||
| endphoneme = CompileSound(keyword, 0); | endphoneme = CompileSound(keyword, 0); | ||||
| break; | break; | ||||
| case kVOWELIN: | case kVOWELIN: | ||||
| DecThenCount(); | DecThenCount(); | ||||
| endphoneme = CompileVowelTransition(1); | endphoneme = CompileVowelTransition(1); | ||||
| break; | break; | ||||
| case kVOWELOUT: | case kVOWELOUT: | ||||
| DecThenCount(); | DecThenCount(); | ||||
| endphoneme = CompileVowelTransition(2); | endphoneme = CompileVowelTransition(2); | ||||
| break; | break; | ||||
| case kTONESPEC: | case kTONESPEC: | ||||
| DecThenCount(); | DecThenCount(); | ||||
| endphoneme = CompileToneSpec(); | endphoneme = CompileToneSpec(); | ||||
| break; | break; | ||||
| case kCONTINUE: | case kCONTINUE: | ||||
| *prog_out++ = i_CONTINUE; | *prog_out++ = i_CONTINUE; | ||||
| DecThenCount(); | DecThenCount(); | ||||
| break; | break; | ||||
| case kRETURN: | case kRETURN: | ||||
| *prog_out++ = i_RETURN; | *prog_out++ = i_RETURN; | ||||
| DecThenCount(); | DecThenCount(); | ||||
| break; | break; | ||||
| case kINCLUDE: | case kINCLUDE: | ||||
| case kPHONEMENUMBER: | case kPHONEMENUMBER: | ||||
| case kPHONEMETABLE: | case kPHONEMETABLE: | ||||
| error("Missing 'endphoneme' before '%s'", item_string); // drop through to endphoneme | error("Missing 'endphoneme' before '%s'", item_string); // drop through to endphoneme | ||||
| // fallthrough: | |||||
| case kENDPHONEME: | case kENDPHONEME: | ||||
| case kENDPROCEDURE: | case kENDPROCEDURE: | ||||
| endphoneme = 1; | endphoneme = 1; | ||||
| *prog_out++ = i_RETURN; | *prog_out++ = i_RETURN; | ||||
| break; | break; | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static void WritePhonemeTables() | static void WritePhonemeTables() | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| } | } | ||||
| } | } | ||||
| static void EndPhonemeTable() | static void EndPhonemeTable() | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| } | } | ||||
| } | } | ||||
| static void StartPhonemeTable(const char *name) | static void StartPhonemeTable(const char *name) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| n_phoneme_tabs++; | n_phoneme_tabs++; | ||||
| } | } | ||||
| static void CompileEquivalents() | static void CompileEquivalents() | ||||
| { | { | ||||
| // a list of phonemes in another language and the equivalent phoneme strings in this language | // a list of phonemes in another language and the equivalent phoneme strings in this language | ||||
| p_start[3] = n_bytes; | p_start[3] = n_bytes; | ||||
| } | } | ||||
| static void CompilePhonemeFiles() | static void CompilePhonemeFiles() | ||||
| { | { | ||||
| int item; | int item; | ||||
| { | { | ||||
| case kUTF8_BOM: | case kUTF8_BOM: | ||||
| break; // ignore bytes 0xef 0xbb 0xbf | break; // ignore bytes 0xef 0xbb 0xbf | ||||
| case kINCLUDE: | case kINCLUDE: | ||||
| NextItem(tSTRING); | NextItem(tSTRING); | ||||
| sprintf(buf, "%s/../phsource/%s", path_home, item_string); | sprintf(buf, "%s/../phsource/%s", path_home, item_string); | ||||
| f_in = f; | f_in = f; | ||||
| strncpy0(current_fname, item_string, sizeof(current_fname)); | strncpy0(current_fname, item_string, sizeof(current_fname)); | ||||
| linenum = 1; | linenum = 1; | ||||
| } else | } else | ||||
| error("Missing file: %s", item_string); | error("Missing file: %s", item_string); | ||||
| break; | break; | ||||
| case kPHONEMETABLE: | case kPHONEMETABLE: | ||||
| EndPhonemeTable(); | EndPhonemeTable(); | ||||
| NextItem(tSTRING); // name of the new phoneme table | NextItem(tSTRING); // name of the new phoneme table | ||||
| StartPhonemeTable(item_string); | StartPhonemeTable(item_string); | ||||
| break; | break; | ||||
| case kPHONEMESTART: | case kPHONEMESTART: | ||||
| if (n_phoneme_tabs == 0) { | if (n_phoneme_tabs == 0) { | ||||
| Error("phonemetable is missing"); | Error("phonemetable is missing"); | ||||
| } | } | ||||
| CompilePhoneme(1); | CompilePhoneme(1); | ||||
| break; | break; | ||||
| case kPROCEDURE: | case kPROCEDURE: | ||||
| CompilePhoneme(0); | CompilePhoneme(0); | ||||
| break; | break; | ||||
| case kEQUIVALENTS: | case kEQUIVALENTS: | ||||
| CompileEquivalents(); | CompileEquivalents(); | ||||
| break; | break; | ||||
| default: | default: | ||||
| if (!feof(f_in)) | if (!feof(f_in)) | ||||
| Error("Keyword 'phoneme' expected"); | Error("Keyword 'phoneme' expected"); | ||||
| phoneme_tab2[n_phcodes+1].mnemonic = 0; // terminator | phoneme_tab2[n_phcodes+1].mnemonic = 0; // terminator | ||||
| } | } | ||||
| static espeak_ng_STATUS CompilePhonemeData2(const char *source, FILE *log) | static espeak_ng_STATUS CompilePhonemeData2(const char *source, FILE *log) | ||||
| { | { | ||||
| char fname[sizeof(path_home)+40]; | char fname[sizeof(path_home)+40]; | ||||
| "# Address Data file\n" | "# Address Data file\n" | ||||
| "# ------- ---------\n"); | "# ------- ---------\n"); | ||||
| fprintf(f_errors, "Source data path = '%s/../phsource'\n", path_home); | fprintf(f_errors, "Source data path = '%s/../phsource'\n", path_home); | ||||
| fprintf(f_errors, "Master phonemes file = '%s/../phsource/phonemes'\n", path_home); | fprintf(f_errors, "Master phonemes file = '%s/../phsource/phonemes'\n", path_home); | ||||
| fprintf(f_errors, "Output to '%s/'\n\n", path_home); | fprintf(f_errors, "Output to '%s/'\n\n", path_home); | ||||
| return ENS_OK; | return ENS_OK; | ||||
| } | } | ||||
| static const char *preset_tune_names[] = { | static const char *preset_tune_names[] = { | ||||
| "s1", "c1", "q1", "e1", NULL | "s1", "c1", "q1", "e1", NULL | ||||
| }; | }; | ||||
| return LookupMnem(envelope_names, name); | return LookupMnem(envelope_names, name); | ||||
| } | } | ||||
| #pragma GCC visibility push(default) | #pragma GCC visibility push(default) | ||||
| espeak_ng_STATUS espeak_ng_CompileIntonation(FILE *log) | espeak_ng_STATUS espeak_ng_CompileIntonation(FILE *log) | ||||
| TUNE *tune_data; | TUNE *tune_data; | ||||
| TUNE new_tune; | TUNE new_tune; | ||||
| char name[12]; | char name[12]; | ||||
| char tune_names[N_TUNE_NAMES][12]; | char tune_names[N_TUNE_NAMES][12]; | ||||
| char buf[sizeof(path_home)+150]; | char buf[sizeof(path_home)+150]; | ||||
| } | } | ||||
| } | } | ||||
| for (ix = 0; preset_tune_names[ix] != NULL; ix++) { | for (ix = 0; preset_tune_names[ix] != NULL; ix++) { | ||||
| strcpy(tune_names[ix], preset_tune_names[ix]); | strcpy(tune_names[ix], preset_tune_names[ix]); | ||||
| } | } | ||||
| if (found == 0) | if (found == 0) | ||||
| error("Bad tune name: '%s;", new_tune.name); | error("Bad tune name: '%s;", new_tune.name); | ||||
| break; | break; | ||||
| case kENDTUNE: | case kENDTUNE: | ||||
| compiling_tune = 0; | compiling_tune = 0; | ||||
| } | } | ||||
| memcpy(&tune_data[tune_number], &new_tune, sizeof(TUNE)); | memcpy(&tune_data[tune_number], &new_tune, sizeof(TUNE)); | ||||
| break; | break; | ||||
| case kTUNE_PREHEAD: | case kTUNE_PREHEAD: | ||||
| new_tune.prehead_start = NextItem(tNUMBER); | new_tune.prehead_start = NextItem(tNUMBER); | ||||
| new_tune.prehead_end = NextItem(tNUMBER); | new_tune.prehead_end = NextItem(tNUMBER); | ||||
| break; | break; | ||||
| case kTUNE_ONSET: | case kTUNE_ONSET: | ||||
| new_tune.onset = NextItem(tNUMBER); | new_tune.onset = NextItem(tNUMBER); | ||||
| new_tune.unstr_start[0] = NextItem(tSIGNEDNUMBER); | new_tune.unstr_start[0] = NextItem(tSIGNEDNUMBER); | ||||
| new_tune.unstr_end[0] = NextItem(tSIGNEDNUMBER); | new_tune.unstr_end[0] = NextItem(tSIGNEDNUMBER); | ||||
| done_onset = 1; | done_onset = 1; | ||||
| break; | break; | ||||
| case kTUNE_HEADLAST: | case kTUNE_HEADLAST: | ||||
| new_tune.head_last = NextItem(tNUMBER); | new_tune.head_last = NextItem(tNUMBER); | ||||
| new_tune.unstr_start[2] = NextItem(tSIGNEDNUMBER); | new_tune.unstr_start[2] = NextItem(tSIGNEDNUMBER); | ||||
| new_tune.unstr_end[2] = NextItem(tSIGNEDNUMBER); | new_tune.unstr_end[2] = NextItem(tSIGNEDNUMBER); | ||||
| done_last = 1; | done_last = 1; | ||||
| break; | break; | ||||
| case kTUNE_HEADENV: | case kTUNE_HEADENV: | ||||
| NextItem(tSTRING); | NextItem(tSTRING); | ||||
| if ((ix = LookupEnvelopeName(item_string)) < 0) | if ((ix = LookupEnvelopeName(item_string)) < 0) | ||||
| new_tune.stressed_env = ix; | new_tune.stressed_env = ix; | ||||
| new_tune.stressed_drop = NextItem(tNUMBER); | new_tune.stressed_drop = NextItem(tNUMBER); | ||||
| break; | break; | ||||
| case kTUNE_HEAD: | case kTUNE_HEAD: | ||||
| new_tune.head_max_steps = NextItem(tNUMBER); | new_tune.head_max_steps = NextItem(tNUMBER); | ||||
| new_tune.head_start = NextItem(tNUMBER); | new_tune.head_start = NextItem(tNUMBER); | ||||
| new_tune.unstr_start[1] = NextItem(tSIGNEDNUMBER); | new_tune.unstr_start[1] = NextItem(tSIGNEDNUMBER); | ||||
| new_tune.unstr_end[1] = NextItem(tSIGNEDNUMBER); | new_tune.unstr_end[1] = NextItem(tSIGNEDNUMBER); | ||||
| break; | break; | ||||
| case kTUNE_HEADEXTEND: | case kTUNE_HEADEXTEND: | ||||
| // up to 8 numbers | // up to 8 numbers | ||||
| for (ix = 0; ix < (int)(sizeof(new_tune.head_extend)); ix++) { | for (ix = 0; ix < (int)(sizeof(new_tune.head_extend)); ix++) { | ||||
| } | } | ||||
| new_tune.n_head_extend = ix; // number of values | new_tune.n_head_extend = ix; // number of values | ||||
| break; | break; | ||||
| case kTUNE_NUCLEUS0: | case kTUNE_NUCLEUS0: | ||||
| NextItem(tSTRING); | NextItem(tSTRING); | ||||
| if ((ix = LookupEnvelopeName(item_string)) < 0) { | if ((ix = LookupEnvelopeName(item_string)) < 0) { | ||||
| new_tune.nucleus0_max = NextItem(tNUMBER); | new_tune.nucleus0_max = NextItem(tNUMBER); | ||||
| new_tune.nucleus0_min = NextItem(tNUMBER); | new_tune.nucleus0_min = NextItem(tNUMBER); | ||||
| break; | break; | ||||
| case kTUNE_NUCLEUS1: | case kTUNE_NUCLEUS1: | ||||
| NextItem(tSTRING); | NextItem(tSTRING); | ||||
| if ((ix = LookupEnvelopeName(item_string)) < 0) { | if ((ix = LookupEnvelopeName(item_string)) < 0) { | ||||
| new_tune.split_tail_end = new_tune.tail_end; | new_tune.split_tail_end = new_tune.tail_end; | ||||
| } | } | ||||
| break; | break; | ||||
| case kTUNE_SPLIT: | case kTUNE_SPLIT: | ||||
| NextItem(tSTRING); | NextItem(tSTRING); | ||||
| if ((ix = LookupEnvelopeName(item_string)) < 0) { | if ((ix = LookupEnvelopeName(item_string)) < 0) { | ||||
| else | else | ||||
| new_tune.split_tune = ix; | new_tune.split_tune = ix; | ||||
| break; | break; | ||||
| default: | default: | ||||
| error("Unexpected: '%s'", item_string); | error("Unexpected: '%s'", item_string); | ||||
| break; | break; | ||||
| return error_count > 0 ? ENE_COMPILE_ERRORS : ENS_OK; | return error_count > 0 ? ENE_COMPILE_ERRORS : ENS_OK; | ||||
| } | } | ||||
| espeak_ng_STATUS espeak_ng_CompilePhonemeData(long rate, FILE *log) | espeak_ng_STATUS espeak_ng_CompilePhonemeData(long rate, FILE *log) | ||||
| { | { | ||||
| if (!log) log = stderr; | if (!log) log = stderr; |
| { "$u2+", 0x4e }, | { "$u2+", 0x4e }, | ||||
| { "$u3+", 0x4f }, | { "$u3+", 0x4f }, | ||||
| // these set the corresponding numbered bit if dictionary_flags | // these set the corresponding numbered bit if dictionary_flags | ||||
| { "$pause", 8 }, // ensure pause before this word | { "$pause", 8 }, // ensure pause before this word | ||||
| { "$strend", 9 }, // full stress if at end of clause | { "$strend", 9 }, // full stress if at end of clause | ||||
| { NULL, -1 } | { NULL, -1 } | ||||
| }; | }; | ||||
| #define LEN_GROUP_NAME 12 | #define LEN_GROUP_NAME 12 | ||||
| typedef struct { | typedef struct { | ||||
| int group3_ix; | int group3_ix; | ||||
| } RGROUP; | } RGROUP; | ||||
| int isspace2(unsigned int c) | int isspace2(unsigned int c) | ||||
| { | { | ||||
| // can't use isspace() because on Windows, isspace(0xe1) gives TRUE ! | // can't use isspace() because on Windows, isspace(0xe1) gives TRUE ! | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| static FILE *fopen_log(const char *fname, const char *access) | static FILE *fopen_log(const char *fname, const char *access) | ||||
| { | { | ||||
| // performs fopen, but produces error message to f_log if it fails | // performs fopen, but produces error message to f_log if it fails | ||||
| return f; | return f; | ||||
| } | } | ||||
| /* Lookup a mnemonic string in a table, return its name */ | /* Lookup a mnemonic string in a table, return its name */ | ||||
| const char *LookupMnemName(MNEM_TAB *table, const int value) | const char *LookupMnemName(MNEM_TAB *table, const int value) | ||||
| { | { | ||||
| return ""; /* not found */ | return ""; /* not found */ | ||||
| } | } | ||||
| void print_dictionary_flags(unsigned int *flags, char *buf, int buf_len) | void print_dictionary_flags(unsigned int *flags, char *buf, int buf_len) | ||||
| { | { | ||||
| int stress; | int stress; | ||||
| } | } | ||||
| } | } | ||||
| char *DecodeRule(const char *group_chars, int group_length, char *rule, int control) | char *DecodeRule(const char *group_chars, int group_length, char *rule, int control) | ||||
| { | { | ||||
| /* Convert compiled match template to ascii */ | /* Convert compiled match template to ascii */ | ||||
| return output; | return output; | ||||
| } | } | ||||
| static int compile_line(char *linebuf, char *dict_line, int *hash) | static int compile_line(char *linebuf, char *dict_line, int *hash) | ||||
| { | { | ||||
| // Compile a line in the language_list file | // Compile a line in the language_list file | ||||
| step = 1; | step = 1; | ||||
| } | } | ||||
| break; | break; | ||||
| case 1: | case 1: | ||||
| if ((c == '-') && multiple_words) { | if ((c == '-') && multiple_words) { | ||||
| if (IsDigit09(word[0])) | if (IsDigit09(word[0])) | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| case 2: | case 2: | ||||
| if (isspace2(c)) | if (isspace2(c)) | ||||
| multiple_words++; | multiple_words++; | ||||
| step = 3; | step = 3; | ||||
| } | } | ||||
| break; | break; | ||||
| case 3: | case 3: | ||||
| if (!isspace2(c)) { | if (!isspace2(c)) { | ||||
| phonetic = p; | phonetic = p; | ||||
| step = 4; | step = 4; | ||||
| } | } | ||||
| break; | break; | ||||
| case 4: | case 4: | ||||
| if (isspace2(c)) { | if (isspace2(c)) { | ||||
| p[0] = 0; /* terminate phonetic */ | p[0] = 0; /* terminate phonetic */ | ||||
| step = 5; | step = 5; | ||||
| } | } | ||||
| break; | break; | ||||
| case 5: | case 5: | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| dict_line[0] = length; | dict_line[0] = length; | ||||
| return length; | return length; | ||||
| } | } | ||||
| static void compile_dictlist_start(void) | static void compile_dictlist_start(void) | ||||
| { | { | ||||
| // initialise dictionary list | // initialise dictionary list | ||||
| } | } | ||||
| } | } | ||||
| static void compile_dictlist_end(FILE *f_out) | static void compile_dictlist_end(FILE *f_out) | ||||
| { | { | ||||
| // Write out the compiled dictionary list | // Write out the compiled dictionary list | ||||
| } | } | ||||
| } | } | ||||
| static int compile_dictlist_file(const char *path, const char *filename) | static int compile_dictlist_file(const char *path, const char *filename) | ||||
| { | { | ||||
| int length; | int length; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static char rule_cond[80]; | static char rule_cond[80]; | ||||
| static char rule_pre[80]; | static char rule_pre[80]; | ||||
| static char rule_post[80]; | static char rule_post[80]; | ||||
| #define N_RULES 3000 // max rules for each group | #define N_RULES 3000 // max rules for each group | ||||
| int isHexDigit(int c) | int isHexDigit(int c) | ||||
| { | { | ||||
| if ((c >= '0') && (c <= '9')) | if ((c >= '0') && (c <= '9')) | ||||
| return -1; | return -1; | ||||
| } | } | ||||
| static void copy_rule_string(char *string, int *state_out) | static void copy_rule_string(char *string, int *state_out) | ||||
| { | { | ||||
| // state 0: conditional, 1=pre, 2=match, 3=post, 4=phonemes | // state 0: conditional, 1=pre, 2=match, 3=post, 4=phonemes | ||||
| } else | } else | ||||
| output[ix++] = RULE_LETTERGP2; | output[ix++] = RULE_LETTERGP2; | ||||
| break; | break; | ||||
| case '$': | case '$': | ||||
| value = 0; | value = 0; | ||||
| mr = mnem_rules; | mr = mnem_rules; | ||||
| error_count++; | error_count++; | ||||
| } | } | ||||
| break; | break; | ||||
| case 'P': | case 'P': | ||||
| sxflags |= SUFX_P; // Prefix, now drop through to Suffix | sxflags |= SUFX_P; // Prefix, now drop through to Suffix | ||||
| // fallthrough | |||||
| case 'S': | case 'S': | ||||
| output[ix++] = RULE_ENDING; | output[ix++] = RULE_ENDING; | ||||
| value = 0; | value = 0; | ||||
| *state_out = next_state[state]; | *state_out = next_state[state]; | ||||
| } | } | ||||
| static char *compile_rule(char *input) | static char *compile_rule(char *input) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| copy_rule_string(buf, &state); | copy_rule_string(buf, &state); | ||||
| p = buf; | p = buf; | ||||
| break; | break; | ||||
| case '(': // start of suffix section | case '(': // start of suffix section | ||||
| *p = 0; | *p = 0; | ||||
| state = 2; | state = 2; | ||||
| error_count++; | error_count++; | ||||
| } | } | ||||
| break; | break; | ||||
| case '\n': // end of line | case '\n': // end of line | ||||
| case '\r': | case '\r': | ||||
| case 0: // end of line | case 0: // end of line | ||||
| copy_rule_string(buf, &state); | copy_rule_string(buf, &state); | ||||
| finish = 1; | finish = 1; | ||||
| break; | break; | ||||
| case '\t': // end of section section | case '\t': // end of section section | ||||
| case ' ': | case ' ': | ||||
| *p = 0; | *p = 0; | ||||
| copy_rule_string(buf, &state); | copy_rule_string(buf, &state); | ||||
| p = buf; | p = buf; | ||||
| break; | break; | ||||
| case '?': | case '?': | ||||
| if (state == 2) | if (state == 2) | ||||
| state = 0; | state = 0; | ||||
| else | else | ||||
| *p++ = c; | *p++ = c; | ||||
| break; | break; | ||||
| default: | default: | ||||
| *p++ = c; | *p++ = c; | ||||
| break; | break; | ||||
| return prule; | return prule; | ||||
| } | } | ||||
| int __cdecl string_sorter(char **a, char **b) | int __cdecl string_sorter(char **a, char **b) | ||||
| { | { | ||||
| char *pa, *pb; | char *pa, *pb; | ||||
| return strcmp(pa, pb); | return strcmp(pa, pb); | ||||
| } | } | ||||
| static int __cdecl rgroup_sorter(RGROUP *a, RGROUP *b) | static int __cdecl rgroup_sorter(RGROUP *a, RGROUP *b) | ||||
| { | { | ||||
| // Sort long names before short names | // Sort long names before short names | ||||
| return a->start-b->start; | return a->start-b->start; | ||||
| } | } | ||||
| #ifdef OUTPUT_FORMAT | #ifdef OUTPUT_FORMAT | ||||
| static void print_rule_group(FILE *f_out, int n_rules, char **rules, char *name) | static void print_rule_group(FILE *f_out, int n_rules, char **rules, char *name) | ||||
| { | { | ||||
| } | } | ||||
| #endif | #endif | ||||
| static void output_rule_group(FILE *f_out, int n_rules, char **rules, char *name) | static void output_rule_group(FILE *f_out, int n_rules, char **rules, char *name) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| #endif | #endif | ||||
| } | } | ||||
| static int compile_lettergroup(char *input, FILE *f_out) | static int compile_lettergroup(char *input, FILE *f_out) | ||||
| { | { | ||||
| char *p; | char *p; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static int compile_dictrules(FILE *f_in, FILE *f_out, char *fname_temp) | static int compile_dictrules(FILE *f_in, FILE *f_out, char *fname_temp) | ||||
| { | { | ||||
| char *prule; | char *prule; | ||||
| } | } | ||||
| break; | break; | ||||
| case 2: // .replace | case 2: // .replace | ||||
| { | { | ||||
| int replace1; | int replace1; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| int CompileDictionary(const char *dsource, const char *dict_name, FILE *log, char *fname_err, int flags) | int CompileDictionary(const char *dsource, const char *dict_name, FILE *log, char *fname_err, int flags) | ||||
| { | { | ||||
| // fname: space to write the filename in case of error | // fname: space to write the filename in case of error |
| #include "synthesize.h" | #include "synthesize.h" | ||||
| #include "translate.h" | #include "translate.h" | ||||
| int dictionary_skipwords; | int dictionary_skipwords; | ||||
| char dictionary_name[40]; | char dictionary_name[40]; | ||||
| 'a', 'a', 'a', 'b', 'o', 'c', 'd', 'd', 'e', 'e', 'e', 'e', 'e', 'e' | 'a', 'a', 'a', 'b', 'o', 'c', 'd', 'd', 'e', 'e', 'e', 'e', 'e', 'e' | ||||
| }; | }; | ||||
| #pragma GCC visibility push(default) | #pragma GCC visibility push(default) | ||||
| void strncpy0(char *to, const char *from, int size) | void strncpy0(char *to, const char *from, int size) | ||||
| { | { | ||||
| } | } | ||||
| #pragma GCC visibility pop | #pragma GCC visibility pop | ||||
| int Reverse4Bytes(int word) | int Reverse4Bytes(int word) | ||||
| { | { | ||||
| // reverse the order of bytes from little-endian to big-endian | // reverse the order of bytes from little-endian to big-endian | ||||
| #endif | #endif | ||||
| } | } | ||||
| int LookupMnem(MNEM_TAB *table, const char *string) | int LookupMnem(MNEM_TAB *table, const char *string) | ||||
| { | { | ||||
| while (table->mnem != NULL) { | while (table->mnem != NULL) { | ||||
| return table->value; | return table->value; | ||||
| } | } | ||||
| static void InitGroups(Translator *tr) | static void InitGroups(Translator *tr) | ||||
| { | { | ||||
| // Called after dictionary 1 is loaded, to set up table of entry points for translation rule chains | // Called after dictionary 1 is loaded, to set up table of entry points for translation rule chains | ||||
| // for single-letters and two-letter combinations | // for single-letters and two-letter combinations | ||||
| int ix; | int ix; | ||||
| char *p; | char *p; | ||||
| char *p_name; | char *p_name; | ||||
| p += (strlen(p) + 1); | p += (strlen(p) + 1); | ||||
| p++; | p++; | ||||
| } | } | ||||
| } | } | ||||
| int LoadDictionary(Translator *tr, const char *name, int no_error) | int LoadDictionary(Translator *tr, const char *name, int no_error) | ||||
| { | { | ||||
| int hash; | int hash; | ||||
| size = fread(tr->data_dictlist, 1, size, f); | size = fread(tr->data_dictlist, 1, size, f); | ||||
| fclose(f); | fclose(f); | ||||
| pw = (int *)(tr->data_dictlist); | pw = (int *)(tr->data_dictlist); | ||||
| length = Reverse4Bytes(pw[1]); | length = Reverse4Bytes(pw[1]); | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| /* Generate a hash code from the specified string | /* Generate a hash code from the specified string | ||||
| This is used to access the dictionary_2 word-lookup dictionary | This is used to access the dictionary_2 word-lookup dictionary | ||||
| */ | */ | ||||
| return (hash+chars) & 0x3ff; // a 10 bit hash code | return (hash+chars) & 0x3ff; // a 10 bit hash code | ||||
| } | } | ||||
| /* Translate a phoneme string from ascii mnemonics to internal phoneme numbers, | /* Translate a phoneme string from ascii mnemonics to internal phoneme numbers, | ||||
| from 'p' up to next blank . | from 'p' up to next blank . | ||||
| Returns advanced 'p' | Returns advanced 'p' | ||||
| p++; | p++; | ||||
| break; | break; | ||||
| } | } | ||||
| default: | default: | ||||
| // lookup the phoneme mnemonic, find the phoneme with the highest number of | // lookup the phoneme mnemonic, find the phoneme with the highest number of | ||||
| // matching characters | // matching characters | ||||
| return p; | return p; | ||||
| } | } | ||||
| void DecodePhonemes(const char *inptr, char *outptr) | void DecodePhonemes(const char *inptr, char *outptr) | ||||
| { | { | ||||
| // Translate from internal phoneme codes into phoneme mnemonics | // Translate from internal phoneme codes into phoneme mnemonics | ||||
| *outptr = 0; /* string terminator */ | *outptr = 0; /* string terminator */ | ||||
| } | } | ||||
| // using Kirschenbaum to IPA translation, ascii 0x20 to 0x7f | // using Kirschenbaum to IPA translation, ascii 0x20 to 0x7f | ||||
| unsigned short ipa1[96] = { | unsigned short ipa1[96] = { | ||||
| 0x20, 0x21, 0x22, 0x2b0, 0x24, 0x25, 0x0e6, 0x2c8, 0x28, 0x29, 0x27e, 0x2b, 0x2cc, 0x2d, 0x2e, 0x2f, | 0x20, 0x21, 0x22, 0x2b0, 0x24, 0x25, 0x0e6, 0x2c8, 0x28, 0x29, 0x27e, 0x2b, 0x2cc, 0x2d, 0x2e, 0x2f, | ||||
| static char *phon_out_buf = NULL; // passes the result of GetTranslatedPhonemeString() | static char *phon_out_buf = NULL; // passes the result of GetTranslatedPhonemeString() | ||||
| static unsigned int phon_out_size = 0; | static unsigned int phon_out_size = 0; | ||||
| char *WritePhMnemonic(char *phon_out, PHONEME_TAB *ph, PHONEME_LIST *plist, int use_ipa, int *flags) | char *WritePhMnemonic(char *phon_out, PHONEME_TAB *ph, PHONEME_LIST *plist, int use_ipa, int *flags) | ||||
| { | { | ||||
| int c; | int c; | ||||
| return phon_out; | return phon_out; | ||||
| } | } | ||||
| const char *GetTranslatedPhonemeString(int phoneme_mode) | const char *GetTranslatedPhonemeString(int phoneme_mode) | ||||
| { | { | ||||
| /* Called after a clause has been translated into phonemes, in order | /* Called after a clause has been translated into phonemes, in order | ||||
| use_tie = 0; | use_tie = 0; | ||||
| } | } | ||||
| for (ix = 1; ix < (n_phoneme_list-2); ix++) { | for (ix = 1; ix < (n_phoneme_list-2); ix++) { | ||||
| buf = phon_buf; | buf = phon_buf; | ||||
| return phon_out_buf; | return phon_out_buf; | ||||
| } | } | ||||
| static int IsLetterGroup(Translator *tr, char *word, int group, int pre) | static int IsLetterGroup(Translator *tr, char *word, int group, int pre) | ||||
| { | { | ||||
| // match the word against a list of utf-8 strings | // match the word against a list of utf-8 strings | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static int IsLetter(Translator *tr, int letter, int group) | static int IsLetter(Translator *tr, int letter, int group) | ||||
| { | { | ||||
| int letter2; | int letter2; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| int IsVowel(Translator *tr, int letter) | int IsVowel(Translator *tr, int letter) | ||||
| { | { | ||||
| return IsLetter(tr, letter, LETTERGP_VOWEL2); | return IsLetter(tr, letter, LETTERGP_VOWEL2); | ||||
| } | } | ||||
| static int Unpronouncable2(Translator *tr, char *word) | static int Unpronouncable2(Translator *tr, char *word) | ||||
| { | { | ||||
| int c; | int c; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| int Unpronouncable(Translator *tr, char *word, int posn) | int Unpronouncable(Translator *tr, char *word, int posn) | ||||
| { | { | ||||
| /* Determines whether a word in 'unpronouncable', i.e. whether it should | /* Determines whether a word in 'unpronouncable', i.e. whether it should | ||||
| return 1; // no vowel, or no vowel in first few letters | return 1; // no vowel, or no vowel in first few letters | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static int GetVowelStress(Translator *tr, unsigned char *phonemes, signed char *vowel_stress, int *vowel_count, int *stressed_syllable, int control) | static int GetVowelStress(Translator *tr, unsigned char *phonemes, signed char *vowel_stress, int *vowel_count, int *stressed_syllable, int control) | ||||
| { | { | ||||
| // control = 1, set stress to 1 for forced unstressed vowels | // control = 1, set stress to 1 for forced unstressed vowels | ||||
| return max_stress; | return max_stress; | ||||
| } | } | ||||
| static char stress_phonemes[] = { phonSTRESS_D, phonSTRESS_U, phonSTRESS_2, phonSTRESS_3, | static char stress_phonemes[] = { phonSTRESS_D, phonSTRESS_U, phonSTRESS_2, phonSTRESS_3, | ||||
| phonSTRESS_P, phonSTRESS_P2, phonSTRESS_TONIC }; | phonSTRESS_P, phonSTRESS_P2, phonSTRESS_TONIC }; | ||||
| void ChangeWordStress(Translator *tr, char *word, int new_stress) | void ChangeWordStress(Translator *tr, char *word, int new_stress) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| *word = 0; | *word = 0; | ||||
| } | } | ||||
| void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags, int tonic, int control) | void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags, int tonic, int control) | ||||
| { | { | ||||
| /* Guess stress pattern of word. This is language specific | /* Guess stress pattern of word. This is language specific | ||||
| static char consonant_types[16] = { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 }; | static char consonant_types[16] = { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 }; | ||||
| /* stress numbers STRESS_BASE + | /* stress numbers STRESS_BASE + | ||||
| 0 diminished, unstressed within a word | 0 diminished, unstressed within a word | ||||
| 1 unstressed, weak | 1 unstressed, weak | ||||
| } | } | ||||
| } | } | ||||
| switch (tr->langopts.stress_rule) | switch (tr->langopts.stress_rule) | ||||
| { | { | ||||
| case 8: | case 8: | ||||
| // stress on first syllable, unless it is a light syllable followed by a heavy syllable | // stress on first syllable, unless it is a light syllable followed by a heavy syllable | ||||
| if ((syllable_weight[1] > 0) || (syllable_weight[2] == 0)) | if ((syllable_weight[1] > 0) || (syllable_weight[2] == 0)) | ||||
| break; | break; | ||||
| // else drop through to case 1 | |||||
| // fallthrough | |||||
| case 1: | case 1: | ||||
| // stress on second syllable | // stress on second syllable | ||||
| if ((stressed_syllable == 0) && (vowel_count > 2)) { | if ((stressed_syllable == 0) && (vowel_count > 2)) { | ||||
| max_stress = 4; | max_stress = 4; | ||||
| } | } | ||||
| break; | break; | ||||
| case 10: // penultimate, but final if only 1 or 2 syllables | case 10: // penultimate, but final if only 1 or 2 syllables | ||||
| if (stressed_syllable == 0) { | if (stressed_syllable == 0) { | ||||
| if (vowel_count < 4) { | if (vowel_count < 4) { | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| // drop through to next case | |||||
| // fallthrough | |||||
| case 2: | case 2: | ||||
| // a language with stress on penultimate vowel | // a language with stress on penultimate vowel | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| case 3: | case 3: | ||||
| // stress on last vowel | // stress on last vowel | ||||
| if (stressed_syllable == 0) { | if (stressed_syllable == 0) { | ||||
| max_stress = 4; | max_stress = 4; | ||||
| } | } | ||||
| break; | break; | ||||
| case 4: // stress on antipenultimate vowel | case 4: // stress on antipenultimate vowel | ||||
| if (stressed_syllable == 0) { | if (stressed_syllable == 0) { | ||||
| stressed_syllable = vowel_count - 3; | stressed_syllable = vowel_count - 3; | ||||
| max_stress = 4; | max_stress = 4; | ||||
| } | } | ||||
| break; | break; | ||||
| case 5: | case 5: | ||||
| // LANG=Russian | // LANG=Russian | ||||
| if (stressed_syllable == 0) { | if (stressed_syllable == 0) { | ||||
| max_stress = 4; | max_stress = 4; | ||||
| } | } | ||||
| break; | break; | ||||
| case 6: // LANG=hi stress on the last heaviest syllable | case 6: // LANG=hi stress on the last heaviest syllable | ||||
| if (stressed_syllable == 0) { | if (stressed_syllable == 0) { | ||||
| int wt; | int wt; | ||||
| max_stress = 4; | max_stress = 4; | ||||
| } | } | ||||
| break; | break; | ||||
| case 7: // LANG=tr, the last syllable for any vowel marked explicitly as unstressed | case 7: // LANG=tr, the last syllable for any vowel marked explicitly as unstressed | ||||
| if (stressed_syllable == 0) { | if (stressed_syllable == 0) { | ||||
| stressed_syllable = vowel_count - 1; | stressed_syllable = vowel_count - 1; | ||||
| max_stress = 4; | max_stress = 4; | ||||
| } | } | ||||
| break; | break; | ||||
| case 9: // mark all as stressed | case 9: // mark all as stressed | ||||
| for (ix = 1; ix < vowel_count; ix++) { | for (ix = 1; ix < vowel_count; ix++) { | ||||
| if (vowel_stress[ix] < 0) | if (vowel_stress[ix] < 0) | ||||
| vowel_stress[ix] = 4; | vowel_stress[ix] = 4; | ||||
| } | } | ||||
| break; | break; | ||||
| case 12: // LANG=kl (Greenlandic) | case 12: // LANG=kl (Greenlandic) | ||||
| long_vowel = 0; | long_vowel = 0; | ||||
| for (ix = 1; ix < vowel_count; ix++) { | for (ix = 1; ix < vowel_count; ix++) { | ||||
| vowel_stress[stressed_syllable] = 4; | vowel_stress[stressed_syllable] = 4; | ||||
| max_stress = 4; | max_stress = 4; | ||||
| break; | break; | ||||
| case 13: // LANG=ml, 1st unless 1st vowel is short and 2nd is long | case 13: // LANG=ml, 1st unless 1st vowel is short and 2nd is long | ||||
| if (stressed_syllable == 0) { | if (stressed_syllable == 0) { | ||||
| stressed_syllable = 1; | stressed_syllable = 1; | ||||
| else | else | ||||
| stress = 3; | stress = 3; | ||||
| if (unstressed_word == 0) { | if (unstressed_word == 0) { | ||||
| if ((stressflags & S_2_SYL_2) && (vowel_count == 3)) { | if ((stressflags & S_2_SYL_2) && (vowel_count == 3)) { | ||||
| // Two syllable word, if one syllable has primary stress, then give the other secondary stress | // Two syllable word, if one syllable has primary stress, then give the other secondary stress | ||||
| max_stress = tonic; | max_stress = tonic; | ||||
| } | } | ||||
| /* produce output phoneme string */ | /* produce output phoneme string */ | ||||
| p = phonetic; | p = phonetic; | ||||
| v = 1; | v = 1; | ||||
| if (!(control & 1) && ((ph = phoneme_tab[*p]) != NULL)) { | if (!(control & 1) && ((ph = phoneme_tab[*p]) != NULL)) { | ||||
| while ((ph->type == phSTRESS) || (*p == phonEND_WORD)) { | while ((ph->type == phSTRESS) || (*p == phonEND_WORD)) { | ||||
| p++; | p++; | ||||
| ph = phoneme_tab[p[0]]; | ph = phoneme_tab[p[0]]; | ||||
| return; | return; | ||||
| } | } | ||||
| void AppendPhonemes(Translator *tr, char *string, int size, const char *ph) | void AppendPhonemes(Translator *tr, char *string, int size, const char *ph) | ||||
| { | { | ||||
| /* Add new phoneme string "ph" to "string" | /* Add new phoneme string "ph" to "string" | ||||
| strcat(string, ph); | strcat(string, ph); | ||||
| } | } | ||||
| static void MatchRule(Translator *tr, char *word[], char *word_start, int group_length, char *rule, MatchRecord *match_out, int word_flags, int dict_flags) | static void MatchRule(Translator *tr, char *word[], char *word_start, int group_length, char *rule, MatchRecord *match_out, int word_flags, int dict_flags) | ||||
| { | { | ||||
| /* Checks a specified word against dictionary rules. | /* Checks a specified word against dictionary rules. | ||||
| return; | return; | ||||
| } | } | ||||
| total_consumed = 0; | total_consumed = 0; | ||||
| common_phonemes = NULL; | common_phonemes = NULL; | ||||
| match_type = 0; | match_type = 0; | ||||
| rule--; // so we are still pointing at the 0 | rule--; // so we are still pointing at the 0 | ||||
| failed = 2; // matched OK | failed = 2; // matched OK | ||||
| break; | break; | ||||
| case RULE_PRE_ATSTART: // pre rule with implied 'start of word' | case RULE_PRE_ATSTART: // pre rule with implied 'start of word' | ||||
| check_atstart = 1; | check_atstart = 1; | ||||
| unpron_ignore = 0; | unpron_ignore = 0; | ||||
| match_type = RULE_PRE; | match_type = RULE_PRE; | ||||
| break; | break; | ||||
| case RULE_PRE: | case RULE_PRE: | ||||
| match_type = RULE_PRE; | match_type = RULE_PRE; | ||||
| if (word_flags & FLAG_UNPRON_TEST) { | if (word_flags & FLAG_UNPRON_TEST) { | ||||
| failed = 1; | failed = 1; | ||||
| } | } | ||||
| break; | break; | ||||
| case RULE_POST: | case RULE_POST: | ||||
| match_type = RULE_POST; | match_type = RULE_POST; | ||||
| break; | break; | ||||
| } else | } else | ||||
| failed = 1; | failed = 1; | ||||
| break; | break; | ||||
| case RULE_POST: | case RULE_POST: | ||||
| /* continue moving fowards */ | /* continue moving fowards */ | ||||
| distance_right += 6; | distance_right += 6; | ||||
| } else | } else | ||||
| failed = 1; | failed = 1; | ||||
| break; | break; | ||||
| case RULE_LETTERGP2: // match against a list of utf-8 strings | case RULE_LETTERGP2: // match against a list of utf-8 strings | ||||
| letter_group = *rule++ - 'A'; | letter_group = *rule++ - 'A'; | ||||
| if ((n_bytes = IsLetterGroup(tr, post_ptr-1, letter_group, 0)) > 0) { | if ((n_bytes = IsLetterGroup(tr, post_ptr-1, letter_group, 0)) > 0) { | ||||
| } else | } else | ||||
| failed = 1; | failed = 1; | ||||
| break; | break; | ||||
| case RULE_NOTVOWEL: | case RULE_NOTVOWEL: | ||||
| if (IsLetter(tr, letter_w, 0) || ((letter_w == ' ') && (word_flags & FLAG_SUFFIX_VOWEL))) | if (IsLetter(tr, letter_w, 0) || ((letter_w == ' ') && (word_flags & FLAG_SUFFIX_VOWEL))) | ||||
| failed = 1; | failed = 1; | ||||
| post_ptr += letter_xbytes; | post_ptr += letter_xbytes; | ||||
| } | } | ||||
| break; | break; | ||||
| case RULE_DIGIT: | case RULE_DIGIT: | ||||
| if (IsDigit(letter_w)) { | if (IsDigit(letter_w)) { | ||||
| add_points = (20-distance_right); | add_points = (20-distance_right); | ||||
| } else | } else | ||||
| failed = 1; | failed = 1; | ||||
| break; | break; | ||||
| case RULE_NONALPHA: | case RULE_NONALPHA: | ||||
| if (!iswalpha2(letter_w)) { | if (!iswalpha2(letter_w)) { | ||||
| add_points = (21-distance_right); | add_points = (21-distance_right); | ||||
| } else | } else | ||||
| failed = 1; | failed = 1; | ||||
| break; | break; | ||||
| case RULE_DOUBLE: | case RULE_DOUBLE: | ||||
| if (letter == last_letter) | if (letter == last_letter) | ||||
| add_points = (21-distance_right); | add_points = (21-distance_right); | ||||
| else | else | ||||
| failed = 1; | failed = 1; | ||||
| break; | break; | ||||
| case RULE_DOLLAR: | case RULE_DOLLAR: | ||||
| command = *rule++; | command = *rule++; | ||||
| if (command == DOLLAR_UNPR) | if (command == DOLLAR_UNPR) | ||||
| failed = 1; | failed = 1; | ||||
| } | } | ||||
| break; | break; | ||||
| case '-': | case '-': | ||||
| if ((letter == '-') || ((letter == ' ') && (word_flags & FLAG_HYPHEN_AFTER))) | if ((letter == '-') || ((letter == ' ') && (word_flags & FLAG_HYPHEN_AFTER))) | ||||
| add_points = (22-distance_right); // one point more than match against space | add_points = (22-distance_right); // one point more than match against space | ||||
| else | else | ||||
| failed = 1; | failed = 1; | ||||
| break; | break; | ||||
| case RULE_SYLLABLE: | case RULE_SYLLABLE: | ||||
| { | { | ||||
| /* more than specified number of vowel letters to the right */ | /* more than specified number of vowel letters to the right */ | ||||
| else | else | ||||
| failed = 1; | failed = 1; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case RULE_NOVOWELS: | case RULE_NOVOWELS: | ||||
| { | { | ||||
| char *p = post_ptr + letter_xbytes; | char *p = post_ptr + letter_xbytes; | ||||
| if (!failed) | if (!failed) | ||||
| add_points = (19-distance_right); | add_points = (19-distance_right); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case RULE_SKIPCHARS: | case RULE_SKIPCHARS: | ||||
| { | { | ||||
| // Used for lang=Tamil, used to match on the next word after an unknown word ending | // Used for lang=Tamil, used to match on the next word after an unknown word ending | ||||
| if (letter_w == rule_w) | if (letter_w == rule_w) | ||||
| post_ptr = p2; | post_ptr = p2; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case RULE_INC_SCORE: | case RULE_INC_SCORE: | ||||
| add_points = 20; // force an increase in points | add_points = 20; // force an increase in points | ||||
| break; | break; | ||||
| case RULE_DEL_FWD: | case RULE_DEL_FWD: | ||||
| // find the next 'e' in the word and replace by 'E' | // find the next 'e' in the word and replace by 'E' | ||||
| for (p = *word + group_length; p < post_ptr; p++) { | for (p = *word + group_length; p < post_ptr; p++) { | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| case RULE_ENDING: | case RULE_ENDING: | ||||
| { | { | ||||
| int end_type; | int end_type; | ||||
| rule += 3; | rule += 3; | ||||
| } | } | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case RULE_NO_SUFFIX: | case RULE_NO_SUFFIX: | ||||
| if (word_flags & FLAG_SUFFIX_REMOVED) | if (word_flags & FLAG_SUFFIX_REMOVED) | ||||
| failed = 1; // a suffix has been removed | failed = 1; // a suffix has been removed | ||||
| else | else | ||||
| add_points = 1; | add_points = 1; | ||||
| break; | break; | ||||
| default: | default: | ||||
| if (letter == rb) { | if (letter == rb) { | ||||
| if ((letter & 0xc0) != 0x80) { | if ((letter & 0xc0) != 0x80) { | ||||
| break; | break; | ||||
| } | } | ||||
| break; | break; | ||||
| case RULE_PRE: | case RULE_PRE: | ||||
| /* match backwards from start of current group */ | /* match backwards from start of current group */ | ||||
| distance_left += 2; | distance_left += 2; | ||||
| } else | } else | ||||
| failed = 1; | failed = 1; | ||||
| break; | break; | ||||
| case RULE_LETTERGP2: // match against a list of utf-8 strings | case RULE_LETTERGP2: // match against a list of utf-8 strings | ||||
| letter_group = *rule++ - 'A'; | letter_group = *rule++ - 'A'; | ||||
| if ((n_bytes = IsLetterGroup(tr, pre_ptr, letter_group, 1)) > 0) { | if ((n_bytes = IsLetterGroup(tr, pre_ptr, letter_group, 1)) > 0) { | ||||
| } else | } else | ||||
| failed = 1; | failed = 1; | ||||
| break; | break; | ||||
| case RULE_NOTVOWEL: | case RULE_NOTVOWEL: | ||||
| if (!IsLetter(tr, letter_w, 0)) { | if (!IsLetter(tr, letter_w, 0)) { | ||||
| add_points = (20-distance_left); | add_points = (20-distance_left); | ||||
| } else | } else | ||||
| failed = 1; | failed = 1; | ||||
| break; | break; | ||||
| case RULE_DOUBLE: | case RULE_DOUBLE: | ||||
| if (letter == last_letter) | if (letter == last_letter) | ||||
| add_points = (21-distance_left); | add_points = (21-distance_left); | ||||
| else | else | ||||
| failed = 1; | failed = 1; | ||||
| break; | break; | ||||
| case RULE_DIGIT: | case RULE_DIGIT: | ||||
| if (IsDigit(letter_w)) { | if (IsDigit(letter_w)) { | ||||
| add_points = (21-distance_left); | add_points = (21-distance_left); | ||||
| } else | } else | ||||
| failed = 1; | failed = 1; | ||||
| break; | break; | ||||
| case RULE_NONALPHA: | case RULE_NONALPHA: | ||||
| if (!iswalpha2(letter_w)) { | if (!iswalpha2(letter_w)) { | ||||
| add_points = (21-distance_right); | add_points = (21-distance_right); | ||||
| } else | } else | ||||
| failed = 1; | failed = 1; | ||||
| break; | break; | ||||
| case RULE_DOLLAR: | case RULE_DOLLAR: | ||||
| command = *rule++; | command = *rule++; | ||||
| if ((command == DOLLAR_LIST) || ((command & 0xf0) == 0x20)) { | if ((command == DOLLAR_LIST) || ((command & 0xf0) == 0x20)) { | ||||
| failed = 1; | failed = 1; | ||||
| } | } | ||||
| break; | break; | ||||
| case RULE_SYLLABLE: | case RULE_SYLLABLE: | ||||
| /* more than specified number of vowels to the left */ | /* more than specified number of vowels to the left */ | ||||
| syllable_count = 1; | syllable_count = 1; | ||||
| else | else | ||||
| failed = 1; | failed = 1; | ||||
| break; | break; | ||||
| case RULE_STRESSED: | case RULE_STRESSED: | ||||
| if (tr->word_stressed_count > 0) | if (tr->word_stressed_count > 0) | ||||
| add_points = 19; | add_points = 19; | ||||
| else | else | ||||
| failed = 1; | failed = 1; | ||||
| break; | break; | ||||
| case RULE_NOVOWELS: | case RULE_NOVOWELS: | ||||
| { | { | ||||
| char *p = pre_ptr - letter_xbytes - 1; | char *p = pre_ptr - letter_xbytes - 1; | ||||
| if (!failed) | if (!failed) | ||||
| add_points = 3; | add_points = 3; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case RULE_IFVERB: | case RULE_IFVERB: | ||||
| if (tr->expect_verb) | if (tr->expect_verb) | ||||
| add_points = 1; | add_points = 1; | ||||
| else | else | ||||
| failed = 1; | failed = 1; | ||||
| break; | break; | ||||
| case RULE_CAPITAL: | case RULE_CAPITAL: | ||||
| if (word_flags & FLAG_FIRST_UPPER) | if (word_flags & FLAG_FIRST_UPPER) | ||||
| add_points = 1; | add_points = 1; | ||||
| else | else | ||||
| failed = 1; | failed = 1; | ||||
| break; | break; | ||||
| case '.': | case '.': | ||||
| // dot in pre- section, match on any dot before this point in the word | // dot in pre- section, match on any dot before this point in the word | ||||
| for (p = pre_ptr; *p != ' '; p--) { | for (p = pre_ptr; *p != ' '; p--) { | ||||
| if (*p == ' ') | if (*p == ' ') | ||||
| failed = 1; | failed = 1; | ||||
| break; | break; | ||||
| case '-': | case '-': | ||||
| if ((letter == '-') || ((letter == ' ') && (word_flags & FLAG_HYPHEN))) | if ((letter == '-') || ((letter == ' ') && (word_flags & FLAG_HYPHEN))) | ||||
| add_points = (22-distance_right); // one point more than match against space | add_points = (22-distance_right); // one point more than match against space | ||||
| else | else | ||||
| failed = 1; | failed = 1; | ||||
| break; | break; | ||||
| default: | default: | ||||
| if (letter == rb) { | if (letter == rb) { | ||||
| if (letter == RULE_SPACE) | if (letter == RULE_SPACE) | ||||
| memcpy(match_out, &best, sizeof(MatchRecord)); | memcpy(match_out, &best, sizeof(MatchRecord)); | ||||
| } | } | ||||
| int TranslateRules(Translator *tr, char *p_start, char *phonemes, int ph_size, char *end_phonemes, int word_flags, unsigned int *dict_flags) | int TranslateRules(Translator *tr, char *p_start, char *phonemes, int ph_size, char *end_phonemes, int word_flags, unsigned int *dict_flags) | ||||
| { | { | ||||
| /* Translate a word bounded by space characters | /* Translate a word bounded by space characters | ||||
| } | } | ||||
| word_copy[ix] = 0; | word_copy[ix] = 0; | ||||
| if ((option_phonemes & espeakPHONEMES_TRACE) && ((word_flags & FLAG_NO_TRACE) == 0)) { | if ((option_phonemes & espeakPHONEMES_TRACE) && ((word_flags & FLAG_NO_TRACE) == 0)) { | ||||
| char wordbuf[120]; | char wordbuf[120]; | ||||
| unsigned int ix; | unsigned int ix; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| void ApplySpecialAttribute2(Translator *tr, char *phonemes, int dict_flags) | void ApplySpecialAttribute2(Translator *tr, char *phonemes, int dict_flags) | ||||
| { | { | ||||
| // apply after the translation is complete | // apply after the translation is complete | ||||
| } | } | ||||
| } | } | ||||
| int TransposeAlphabet(Translator *tr, char *text) | int TransposeAlphabet(Translator *tr, char *text) | ||||
| { | { | ||||
| // transpose cyrillic alphabet (for example) into ascii (single byte) character codes | // transpose cyrillic alphabet (for example) into ascii (single byte) character codes | ||||
| int bufix; | int bufix; | ||||
| char buf[N_WORD_BYTES+1]; | char buf[N_WORD_BYTES+1]; | ||||
| offset = tr->transpose_min - 1; | offset = tr->transpose_min - 1; | ||||
| min = tr->transpose_min; | min = tr->transpose_min; | ||||
| max = tr->transpose_max; | max = tr->transpose_max; | ||||
| return strlen(text); | return strlen(text); | ||||
| } | } | ||||
| /* Find an entry in the word_dict file for a specified word. | /* Find an entry in the word_dict file for a specified word. | ||||
| Returns NULL if no match, else returns 'word_end' | Returns NULL if no match, else returns 'word_end' | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| /* Lookup a specified word in the word dictionary. | /* Lookup a specified word in the word dictionary. | ||||
| Returns phonetic data in 'phonetic' and bits in 'flags' | Returns phonetic data in 'phonetic' and bits in 'flags' | ||||
| found = LookupDict2(tr, word, word1, ph_out, flags, end_flags, wtab); | found = LookupDict2(tr, word, word1, ph_out, flags, end_flags, wtab); | ||||
| if (flags[0] & FLAG_MAX3) { | if (flags[0] & FLAG_MAX3) { | ||||
| if (strcmp(ph_out, tr->phonemes_repeat) == 0) { | if (strcmp(ph_out, tr->phonemes_repeat) == 0) { | ||||
| tr->phonemes_repeat_count++; | tr->phonemes_repeat_count++; | ||||
| } else | } else | ||||
| tr->phonemes_repeat_count = 0; | tr->phonemes_repeat_count = 0; | ||||
| if ((found == 0) && (flags[1] & FLAG_ACCENT)) { | if ((found == 0) && (flags[1] & FLAG_ACCENT)) { | ||||
| int letter; | int letter; | ||||
| word2 = word; | word2 = word; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| extern char word_phonemes[N_WORD_PHONEMES]; // a word translated into phoneme codes | extern char word_phonemes[N_WORD_PHONEMES]; // a word translated into phoneme codes | ||||
| int Lookup(Translator *tr, const char *word, char *ph_out) | int Lookup(Translator *tr, const char *word, char *ph_out) | ||||
| return flags0; | return flags0; | ||||
| } | } | ||||
| int LookupFlags(Translator *tr, const char *word, unsigned int **flags_out) | int LookupFlags(Translator *tr, const char *word, unsigned int **flags_out) | ||||
| { | { | ||||
| char buf[100]; | char buf[100]; | ||||
| return flags[0]; | return flags[0]; | ||||
| } | } | ||||
| int RemoveEnding(Translator *tr, char *word, int end_type, char *word_copy) | int RemoveEnding(Translator *tr, char *word, int end_type, char *word_copy) | ||||
| { | { | ||||
| /* Removes a standard suffix from a word, once it has been indicated by the dictionary rules. | /* Removes a standard suffix from a word, once it has been indicated by the dictionary rules. |
| free(the_command->u.my_text.text); | free(the_command->u.my_text.text); | ||||
| } | } | ||||
| break; | break; | ||||
| case ET_MARK: | case ET_MARK: | ||||
| if (the_command->u.my_mark.text) | if (the_command->u.my_mark.text) | ||||
| free(the_command->u.my_mark.text); | free(the_command->u.my_mark.text); | ||||
| if (the_command->u.my_mark.index_mark) | if (the_command->u.my_mark.index_mark) | ||||
| free((void *)(the_command->u.my_mark.index_mark)); | free((void *)(the_command->u.my_mark.index_mark)); | ||||
| break; | break; | ||||
| case ET_TERMINATED_MSG: | case ET_TERMINATED_MSG: | ||||
| { | { | ||||
| // if the terminated msg is pending, | // if the terminated msg is pending, | ||||
| sync_espeak_terminated_msg(data->unique_identifier, data->user_data); | sync_espeak_terminated_msg(data->unique_identifier, data->user_data); | ||||
| } | } | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case ET_KEY: | case ET_KEY: | ||||
| if (the_command->u.my_key.key_name) | if (the_command->u.my_key.key_name) | ||||
| free((void *)(the_command->u.my_key.key_name)); | free((void *)(the_command->u.my_key.key_name)); | ||||
| break; | break; | ||||
| case ET_CHAR: | case ET_CHAR: | ||||
| case ET_PARAMETER: | case ET_PARAMETER: | ||||
| // No allocation | // No allocation | ||||
| break; | break; | ||||
| case ET_PUNCTUATION_LIST: | case ET_PUNCTUATION_LIST: | ||||
| if (the_command->u.my_punctuation_list) | if (the_command->u.my_punctuation_list) | ||||
| free((void *)(the_command->u.my_punctuation_list)); | free((void *)(the_command->u.my_punctuation_list)); | ||||
| break; | break; | ||||
| case ET_VOICE_NAME: | case ET_VOICE_NAME: | ||||
| if (the_command->u.my_voice_name) | if (the_command->u.my_voice_name) | ||||
| free((void *)(the_command->u.my_voice_name)); | free((void *)(the_command->u.my_voice_name)); | ||||
| break; | break; | ||||
| case ET_VOICE_SPEC: | case ET_VOICE_SPEC: | ||||
| { | { | ||||
| espeak_VOICE *data = &(the_command->u.my_voice_spec); | espeak_VOICE *data = &(the_command->u.my_voice_spec); | ||||
| if (data->identifier) | if (data->identifier) | ||||
| free((void *)data->identifier); | free((void *)data->identifier); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| default: | default: | ||||
| assert(0); | assert(0); | ||||
| } | } | ||||
| data->position, data->position_type, | data->position, data->position_type, | ||||
| data->end_position, data->flags, data->user_data); | data->end_position, data->flags, data->user_data); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case ET_MARK: | case ET_MARK: | ||||
| { | { | ||||
| t_espeak_mark *data = &(the_command->u.my_mark); | t_espeak_mark *data = &(the_command->u.my_mark); | ||||
| data->index_mark, data->end_position, data->flags, | data->index_mark, data->end_position, data->flags, | ||||
| data->user_data); | data->user_data); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case ET_TERMINATED_MSG: | case ET_TERMINATED_MSG: | ||||
| { | { | ||||
| t_espeak_terminated_msg *data = &(the_command->u.my_terminated_msg); | t_espeak_terminated_msg *data = &(the_command->u.my_terminated_msg); | ||||
| sync_espeak_terminated_msg(data->unique_identifier, data->user_data); | sync_espeak_terminated_msg(data->unique_identifier, data->user_data); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case ET_KEY: | case ET_KEY: | ||||
| { | { | ||||
| const char *data = the_command->u.my_key.key_name; | const char *data = the_command->u.my_key.key_name; | ||||
| sync_espeak_Key(data); | sync_espeak_Key(data); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case ET_CHAR: | case ET_CHAR: | ||||
| { | { | ||||
| const wchar_t data = the_command->u.my_char.character; | const wchar_t data = the_command->u.my_char.character; | ||||
| sync_espeak_Char(data); | sync_espeak_Char(data); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case ET_PARAMETER: | case ET_PARAMETER: | ||||
| { | { | ||||
| t_espeak_parameter *data = &(the_command->u.my_param); | t_espeak_parameter *data = &(the_command->u.my_param); | ||||
| SetParameter(data->parameter, data->value, data->relative); | SetParameter(data->parameter, data->value, data->relative); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case ET_PUNCTUATION_LIST: | case ET_PUNCTUATION_LIST: | ||||
| { | { | ||||
| const wchar_t *data = the_command->u.my_punctuation_list; | const wchar_t *data = the_command->u.my_punctuation_list; | ||||
| sync_espeak_SetPunctuationList(data); | sync_espeak_SetPunctuationList(data); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case ET_VOICE_NAME: | case ET_VOICE_NAME: | ||||
| { | { | ||||
| const char *data = the_command->u.my_voice_name; | const char *data = the_command->u.my_voice_name; | ||||
| SetVoiceByName(data); | SetVoiceByName(data); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case ET_VOICE_SPEC: | case ET_VOICE_SPEC: | ||||
| { | { | ||||
| espeak_VOICE *data = &(the_command->u.my_voice_spec); | espeak_VOICE *data = &(the_command->u.my_voice_spec); | ||||
| SetVoiceByProperties(data); | SetVoiceByProperties(data); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| default: | default: | ||||
| assert(0); | assert(0); | ||||
| break; | break; | ||||
| t_espeak_text *data = &(the_command->u.my_text); | t_espeak_text *data = &(the_command->u.my_text); | ||||
| SHOW("display_espeak_command > (0x%x) uid=%d, TEXT=%s, user_data=0x%x\n", the_command, data->unique_identifier, (char *)data->text, (int)(data->user_data)); | SHOW("display_espeak_command > (0x%x) uid=%d, TEXT=%s, user_data=0x%x\n", the_command, data->unique_identifier, (char *)data->text, (int)(data->user_data)); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case ET_MARK: | case ET_MARK: | ||||
| { | { | ||||
| t_espeak_mark *data = &(the_command->u.my_mark); | t_espeak_mark *data = &(the_command->u.my_mark); | ||||
| SHOW("display_espeak_command > (0x%x) uid=%d, MARK=%s, user_data=0x%x\n", the_command, data->unique_identifier, (char *)data->text, (int)(data->user_data)); | SHOW("display_espeak_command > (0x%x) uid=%d, MARK=%s, user_data=0x%x\n", the_command, data->unique_identifier, (char *)data->text, (int)(data->user_data)); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case ET_KEY: | case ET_KEY: | ||||
| { | { | ||||
| const char *data = the_command->u.my_key.key_name; | const char *data = the_command->u.my_key.key_name; | ||||
| SHOW("display_espeak_command > (0x%x) KEY=%c\n", the_command, data); | SHOW("display_espeak_command > (0x%x) KEY=%c\n", the_command, data); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case ET_TERMINATED_MSG: | case ET_TERMINATED_MSG: | ||||
| { | { | ||||
| t_espeak_terminated_msg *data = &(the_command->u.my_terminated_msg); | t_espeak_terminated_msg *data = &(the_command->u.my_terminated_msg); | ||||
| the_command, data->unique_identifier, data->user_data, | the_command, data->unique_identifier, data->user_data, | ||||
| the_command->state); | the_command->state); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case ET_CHAR: | case ET_CHAR: | ||||
| { | { | ||||
| const wchar_t data = the_command->u.my_char.character; | const wchar_t data = the_command->u.my_char.character; | ||||
| SHOW("display_espeak_command > (0x%x) CHAR=%c\n", the_command, (char)data); | SHOW("display_espeak_command > (0x%x) CHAR=%c\n", the_command, (char)data); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case ET_PARAMETER: | case ET_PARAMETER: | ||||
| { | { | ||||
| t_espeak_parameter *data = &(the_command->u.my_param); | t_espeak_parameter *data = &(the_command->u.my_param); | ||||
| SHOW("display_espeak_command > (0x%x) PARAMETER=%d, value=%d, relative=%d\n", | SHOW("display_espeak_command > (0x%x) PARAMETER=%d, value=%d, relative=%d\n", | ||||
| the_command, data->parameter, data->value, data->relative); | the_command, data->parameter, data->value, data->relative); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case ET_PUNCTUATION_LIST: | case ET_PUNCTUATION_LIST: | ||||
| { | { | ||||
| const wchar_t *data = the_command->u.my_punctuation_list; | const wchar_t *data = the_command->u.my_punctuation_list; | ||||
| sync_espeak_SetPunctuationList(data); | sync_espeak_SetPunctuationList(data); | ||||
| SHOW("display_espeak_command > (0x%x) PUNCTLIST=%s\n", the_command, (char *)data); | SHOW("display_espeak_command > (0x%x) PUNCTLIST=%s\n", the_command, (char *)data); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case ET_VOICE_NAME: | case ET_VOICE_NAME: | ||||
| { | { | ||||
| const char *data = the_command->u.my_voice_name; | const char *data = the_command->u.my_voice_name; | ||||
| SHOW("display_espeak_command > (0x%x) VOICE_NAME=%s\n", the_command, data); | SHOW("display_espeak_command > (0x%x) VOICE_NAME=%s\n", the_command, data); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case ET_VOICE_SPEC: | case ET_VOICE_SPEC: | ||||
| { | { | ||||
| SHOW("display_espeak_command > (0x%x) VOICE_SPEC", the_command); | SHOW("display_espeak_command > (0x%x) VOICE_SPEC", the_command); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| default: | default: | ||||
| assert(0); | assert(0); | ||||
| break; | break; |
| ACTIVITY_TIMEOUT = 50, // in ms, check that the stream is active | ACTIVITY_TIMEOUT = 50, // in ms, check that the stream is active | ||||
| MAX_ACTIVITY_CHECK = 6 }; | MAX_ACTIVITY_CHECK = 6 }; | ||||
| typedef struct t_node { | typedef struct t_node { | ||||
| void *data; | void *data; | ||||
| struct t_node *next; | struct t_node *next; | ||||
| my_callback(NULL, 0, events); | my_callback(NULL, 0, events); | ||||
| a_old_uid = event->unique_identifier; | a_old_uid = event->unique_identifier; | ||||
| break; | break; | ||||
| case espeakEVENT_MSG_TERMINATED: | case espeakEVENT_MSG_TERMINATED: | ||||
| case espeakEVENT_MARK: | case espeakEVENT_MARK: | ||||
| case espeakEVENT_WORD: | case espeakEVENT_WORD: | ||||
| my_callback(NULL, 0, events); | my_callback(NULL, 0, events); | ||||
| a_old_uid = event->unique_identifier; | a_old_uid = event->unique_identifier; | ||||
| } | } | ||||
| break; | |||||
| default: | |||||
| break; | |||||
| case espeakEVENT_LIST_TERMINATED: | case espeakEVENT_LIST_TERMINATED: | ||||
| case espeakEVENT_PLAY: | case espeakEVENT_PLAY: | ||||
| default: | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| case espeakEVENT_MSG_TERMINATED: | case espeakEVENT_MSG_TERMINATED: | ||||
| event_notify(event); | event_notify(event); | ||||
| break; | break; | ||||
| case espeakEVENT_MARK: | case espeakEVENT_MARK: | ||||
| case espeakEVENT_PLAY: | case espeakEVENT_PLAY: | ||||
| if (event->id.name) | if (event->id.name) | ||||
| free((void *)(event->id.name)); | free((void *)(event->id.name)); | ||||
| break; | break; | ||||
| default: | default: | ||||
| break; | break; | ||||
| } | } |
| #include "wave.h" | #include "wave.h" | ||||
| #include "debug.h" | #include "debug.h" | ||||
| // my_mutex: protects my_thread_is_talking, | // my_mutex: protects my_thread_is_talking, | ||||
| // my_stop_is_required, and the command fifo | // my_stop_is_required, and the command fifo | ||||
| static pthread_mutex_t my_mutex; | static pthread_mutex_t my_mutex; |
| #include "voice.h" | #include "voice.h" | ||||
| #include "translate.h" | #include "translate.h" | ||||
| /* Note this module is mostly old code that needs to be rewritten to | /* Note this module is mostly old code that needs to be rewritten to | ||||
| provide a more flexible intonation system. | provide a more flexible intonation system. | ||||
| */ | */ | ||||
| static SYLLABLE *syllable_tab; | static SYLLABLE *syllable_tab; | ||||
| static int tone_pitch_env; /* used to return pitch envelope */ | static int tone_pitch_env; /* used to return pitch envelope */ | ||||
| /* Pitch data for tone types */ | /* Pitch data for tone types */ | ||||
| /*****************************/ | /*****************************/ | ||||
| #define PITCHfall 0 | #define PITCHfall 0 | ||||
| #define PITCHrise 2 | #define PITCHrise 2 | ||||
| #define PITCHfrise 4 // and 3 must be for the variant preceded by 'r' | #define PITCHfrise 4 // and 3 must be for the variant preceded by 'r' | ||||
| 0x50, 0x5a, 0x64, 0x70, 0x7c, 0x83, 0x85, 0x88, 0x8a, 0x8c, 0x8e, 0x8f, 0x91, 0x92, 0x93, 0x93 | 0x50, 0x5a, 0x64, 0x70, 0x7c, 0x83, 0x85, 0x88, 0x8a, 0x8c, 0x8e, 0x8f, 0x91, 0x92, 0x93, 0x93 | ||||
| }; | }; | ||||
| unsigned char *envelope_data[N_ENVELOPE_DATA] = { | unsigned char *envelope_data[N_ENVELOPE_DATA] = { | ||||
| env_fall, env_fall, | env_fall, env_fall, | ||||
| env_rise, env_rise, | env_rise, env_rise, | ||||
| env_risefallrise, env_risefallrise | env_risefallrise, env_risefallrise | ||||
| }; | }; | ||||
| /* indexed by stress */ | /* indexed by stress */ | ||||
| static int min_drop[] = { 6, 7, 9, 9, 20, 20, 20, 25 }; | static int min_drop[] = { 6, 7, 9, 9, 20, 20, 20, 25 }; | ||||
| static signed char oflow_emf[] = { 10, 52, 32, 20, 10 }; | static signed char oflow_emf[] = { 10, 52, 32, 20, 10 }; | ||||
| static signed char oflow_less[] = { 6, 38, 24, 14, 4 }; | static signed char oflow_less[] = { 6, 38, 24, 14, 4 }; | ||||
| #define N_TONE_HEAD_TABLE 13 | #define N_TONE_HEAD_TABLE 13 | ||||
| #define N_TONE_NUCLEUS_TABLE 13 | #define N_TONE_NUCLEUS_TABLE 13 | ||||
| typedef struct { | typedef struct { | ||||
| unsigned char pre_start; | unsigned char pre_start; | ||||
| unsigned char pre_end; | unsigned char pre_end; | ||||
| signed char *overflow; | signed char *overflow; | ||||
| } TONE_HEAD; | } TONE_HEAD; | ||||
| typedef struct { | typedef struct { | ||||
| unsigned char pitch_env0; /* pitch envelope, tonic syllable at end */ | unsigned char pitch_env0; /* pitch envelope, tonic syllable at end */ | ||||
| unsigned char tonic_max0; | unsigned char tonic_max0; | ||||
| { PITCHfall, 70, 18, PITCHfall, 70, 24, NULL, 32, 20, 0 }, // 12 test | { PITCHfall, 70, 18, PITCHfall, 70, 24, NULL, 32, 20, 0 }, // 12 test | ||||
| }; | }; | ||||
| /* index by 0=. 1=, 2=?, 3=! 4=none, 5=emphasized */ | /* index by 0=. 1=, 2=?, 3=! 4=none, 5=emphasized */ | ||||
| unsigned char punctuation_to_tone[INTONATION_TYPES][PUNCT_INTONATIONS] = { | unsigned char punctuation_to_tone[INTONATION_TYPES][PUNCT_INTONATIONS] = { | ||||
| { 0, 1, 2, 3, 0, 4 }, | { 0, 1, 2, 3, 0, 4 }, | ||||
| { 12, 12, 12, 12, 0, 0 } | { 12, 12, 12, 12, 0, 0 } | ||||
| }; | }; | ||||
| int n_tunes = 0; | int n_tunes = 0; | ||||
| TUNE *tunes = NULL; | TUNE *tunes = NULL; | ||||
| #define SECONDARY 3 | #define SECONDARY 3 | ||||
| #define PRIMARY 4 | #define PRIMARY 4 | ||||
| #define PRIMARY_STRESSED 6 | #define PRIMARY_STRESSED 6 | ||||
| #define PRIMARY_LAST 7 | #define PRIMARY_LAST 7 | ||||
| static int number_pre; | static int number_pre; | ||||
| static int number_body; | static int number_body; | ||||
| static int number_tail; | static int number_tail; | ||||
| static int tone_posn2; | static int tone_posn2; | ||||
| static int no_tonic; | static int no_tonic; | ||||
| static void count_pitch_vowels(int start, int end, int clause_end) | static void count_pitch_vowels(int start, int end, int clause_end) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| last_primary = ix; | last_primary = ix; | ||||
| } | } | ||||
| } | } | ||||
| if (number_pre < 0) | if (number_pre < 0) | ||||
| } | } | ||||
| } | } | ||||
| /* Count number of primary stresses up to tonic syllable or body_reset */ | /* Count number of primary stresses up to tonic syllable or body_reset */ | ||||
| static int count_increments(int ix, int end_ix, int min_stress) | static int count_increments(int ix, int end_ix, int min_stress) | ||||
| { | { | ||||
| return count; | return count; | ||||
| } | } | ||||
| // Set the pitch of a vowel in syllable_tab | // Set the pitch of a vowel in syllable_tab | ||||
| static void set_pitch(SYLLABLE *syl, int base, int drop) | static void set_pitch(SYLLABLE *syl, int base, int drop) | ||||
| { | { | ||||
| syl->flags |= flags; | syl->flags |= flags; | ||||
| } | } | ||||
| static int CountUnstressed(int start, int end, int limit) | static int CountUnstressed(int start, int end, int limit) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| increment = pitch_range / (n_steps -1); | increment = pitch_range / (n_steps -1); | ||||
| else | else | ||||
| increment = 0; | increment = 0; | ||||
| } else if (syl_ix == head_final) { | } else if (syl_ix == head_final) { | ||||
| // a pitch has been specified for the last primary stress before the nucleus | // a pitch has been specified for the last primary stress before the nucleus | ||||
| pitch = tune->head_last << 8; | pitch = tune->head_last << 8; | ||||
| syl_ix++; | syl_ix++; | ||||
| } | } | ||||
| return syl_ix; | return syl_ix; | ||||
| } | } | ||||
| /* Calculate pitches until next RESET or tonic syllable, or end. | /* Calculate pitches until next RESET or tonic syllable, or end. | ||||
| Increment pitch if stress is >= min_stress. | Increment pitch if stress is >= min_stress. | ||||
| Used for tonic segment */ | Used for tonic segment */ | ||||
| return ix; | return ix; | ||||
| } | } | ||||
| static void SetPitchGradient(int start_ix, int end_ix, int start_pitch, int end_pitch) | static void SetPitchGradient(int start_ix, int end_ix, int start_pitch, int end_pitch) | ||||
| { | { | ||||
| // Set a linear pitch change over a number of syllables. | // Set a linear pitch change over a number of syllables. | ||||
| } | } | ||||
| } | } | ||||
| // Calculate pitch values for the vowels in this tone group | // Calculate pitch values for the vowels in this tone group | ||||
| static int calc_pitches2(int start, int end, int tune_number) | static int calc_pitches2(int start, int end, int tune_number) | ||||
| { | { | ||||
| return tone_pitch_env; | return tone_pitch_env; | ||||
| } | } | ||||
| // Calculate pitch values for the vowels in this tone group | // Calculate pitch values for the vowels in this tone group | ||||
| static int calc_pitches(int control, int start, int end, int tune_number) | static int calc_pitches(int control, int start, int end, int tune_number) | ||||
| { | { | ||||
| return tone_pitch_env; | return tone_pitch_env; | ||||
| } | } | ||||
| static void CalcPitches_Tone(Translator *tr, int clause_tone) | static void CalcPitches_Tone(Translator *tr, int clause_tone) | ||||
| { | { | ||||
| // clause_tone: 0=. 1=, 2=?, 3=! 4=none | // clause_tone: 0=. 1=, 2=?, 3=! 4=none | ||||
| p->tone_ph = PhonemeCode('7'); // change default tone (tone 1) to falling tone at end of clause | p->tone_ph = PhonemeCode('7'); // change default tone (tone 1) to falling tone at end of clause | ||||
| } | } | ||||
| pause = 1; | pause = 1; | ||||
| tone_promoted = 0; | tone_promoted = 0; | ||||
| p->tone_ph = tone_ph; | p->tone_ph = tone_ph; | ||||
| tph = phoneme_tab[tone_ph]; | tph = phoneme_tab[tone_ph]; | ||||
| } else | } else | ||||
| tone_promoted = 0; | tone_promoted = 0; | ||||
| p->pitch2 = pitch_adjust + phoneme_tab[tone_ph]->end_type; | p->pitch2 = pitch_adjust + phoneme_tab[tone_ph]->end_type; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| void CalcPitches(Translator *tr, int clause_type) | void CalcPitches(Translator *tr, int clause_type) | ||||
| { | { | ||||
| // clause_type: 0=. 1=, 2=?, 3=! 4=none | // clause_type: 0=. 1=, 2=?, 3=! 4=none | ||||
| if (n_st == 0) | if (n_st == 0) | ||||
| return; // nothing to do | return; // nothing to do | ||||
| if (tr->langopts.tone_language == 1) { | if (tr->langopts.tone_language == 1) { | ||||
| CalcPitches_Tone(tr, clause_type); | CalcPitches_Tone(tr, clause_type); | ||||
| return; | return; | ||||
| } | } | ||||
| option = tr->langopts.intonation_group; | option = tr->langopts.intonation_group; | ||||
| if (option >= INTONATION_TYPES) | if (option >= INTONATION_TYPES) | ||||
| option = 1; | option = 1; | ||||
| calc_pitches(option, st_start, st_ix, group_tone); | calc_pitches(option, st_start, st_ix, group_tone); | ||||
| } | } | ||||
| // unpack pitch data | // unpack pitch data | ||||
| st_ix = 0; | st_ix = 0; | ||||
| for (ix = ph_start; ix < ph_end; ix++) { | for (ix = ph_start; ix < ph_end; ix++) { | ||||
| st_ix++; | st_ix++; | ||||
| } | } | ||||
| } | } | ||||
| } | } |
| static int nsamples; | static int nsamples; | ||||
| static int sample_count; | static int sample_count; | ||||
| #ifdef _MSC_VER | #ifdef _MSC_VER | ||||
| #define getrandom(min, max) ((rand()%(int)(((max)+1)-(min)))+(min)) | #define getrandom(min, max) ((rand()%(int)(((max)+1)-(min)))+(min)) | ||||
| #else | #else | ||||
| #define getrandom(min, max) ((rand()%(long)(((max)+1)-(min)))+(min)) | #define getrandom(min, max) ((rand()%(long)(((max)+1)-(min)))+(min)) | ||||
| #endif | #endif | ||||
| /* function prototypes for functions private to this file */ | /* function prototypes for functions private to this file */ | ||||
| static void flutter(klatt_frame_ptr); | static void flutter(klatt_frame_ptr); | ||||
| return (double)x; | return (double)x; | ||||
| } | } | ||||
| static double antiresonator2(resonator_ptr r, double input) | static double antiresonator2(resonator_ptr r, double input) | ||||
| { | { | ||||
| register double x = (double)r->a * (double)input + (double)r->b * (double)r->p1 + (double)r->c * (double)r->p2; | register double x = (double)r->a * (double)input + (double)r->b * (double)r->p1 + (double)r->c * (double)r->p2; | ||||
| return (double)x; | return (double)x; | ||||
| } | } | ||||
| /* | /* | ||||
| function FLUTTER | function FLUTTER | ||||
| time_count++; | time_count++; | ||||
| } | } | ||||
| /* | /* | ||||
| function SAMPLED_SOURCE | function SAMPLED_SOURCE | ||||
| return result; | return result; | ||||
| } | } | ||||
| /* | /* | ||||
| function PARWAVE | function PARWAVE | ||||
| Converts synthesis parameters to a waveform. | Converts synthesis parameters to a waveform. | ||||
| */ | */ | ||||
| static int parwave(klatt_frame_ptr frame) | static int parwave(klatt_frame_ptr frame) | ||||
| { | { | ||||
| double temp; | double temp; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| void KlattReset(int control) | void KlattReset(int control) | ||||
| { | { | ||||
| int r_ix; | int r_ix; | ||||
| kt_globals.rsn[r_ix].p1 = 0; | kt_globals.rsn[r_ix].p1 = 0; | ||||
| kt_globals.rsn[r_ix].p2 = 0; | kt_globals.rsn[r_ix].p2 = 0; | ||||
| } | } | ||||
| } | } | ||||
| for (r_ix = 0; r_ix <= R6p; r_ix++) { | for (r_ix = 0; r_ix <= R6p; r_ix++) { | ||||
| } | } | ||||
| } | } | ||||
| /* | /* | ||||
| function FRAME_INIT | function FRAME_INIT | ||||
| kt_globals.rsn[F_NZ].b_inc = (kt_globals.rsn_next[F_NZ].b - kt_globals.rsn[F_NZ].b) / 64.0; | kt_globals.rsn[F_NZ].b_inc = (kt_globals.rsn_next[F_NZ].b - kt_globals.rsn[F_NZ].b) / 64.0; | ||||
| kt_globals.rsn[F_NZ].c_inc = (kt_globals.rsn_next[F_NZ].c - kt_globals.rsn[F_NZ].c) / 64.0; | kt_globals.rsn[F_NZ].c_inc = (kt_globals.rsn_next[F_NZ].c - kt_globals.rsn[F_NZ].c) / 64.0; | ||||
| /* Set coefficients of parallel resonators, and amplitude of outputs */ | /* Set coefficients of parallel resonators, and amplitude of outputs */ | ||||
| for (ix = 0; ix <= 6; ix++) { | for (ix = 0; ix <= 6; ix++) { | ||||
| /* output low-pass filter */ | /* output low-pass filter */ | ||||
| setabc((long)0.0, (long)(kt_globals.samrate/2), &(kt_globals.rsn[Rout])); | setabc((long)0.0, (long)(kt_globals.samrate/2), &(kt_globals.rsn[Rout])); | ||||
| } | } | ||||
| /* | /* | ||||
| function IMPULSIVE_SOURCE | function IMPULSIVE_SOURCE | ||||
| to Kopen. | to Kopen. | ||||
| */ | */ | ||||
| static double impulsive_source() | static double impulsive_source() | ||||
| { | { | ||||
| static double doublet[] = { 0.0, 13000000.0, -13000000.0 }; | static double doublet[] = { 0.0, 13000000.0, -13000000.0 }; | ||||
| return resonator(&(kt_globals.rsn[RGL]), vwave); | return resonator(&(kt_globals.rsn[RGL]), vwave); | ||||
| } | } | ||||
| /* | /* | ||||
| function NATURAL_SOURCE | function NATURAL_SOURCE | ||||
| return 0.0; | return 0.0; | ||||
| } | } | ||||
| /* | /* | ||||
| function PITCH_SYNC_PAR_RESET | function PITCH_SYNC_PAR_RESET | ||||
| kt_globals.nopen = 40; | kt_globals.nopen = 40; | ||||
| } | } | ||||
| /* Reset a & b, which determine shape of "natural" glottal waveform */ | /* Reset a & b, which determine shape of "natural" glottal waveform */ | ||||
| kt_globals.pulse_shape_b = B0[kt_globals.nopen-40]; | kt_globals.pulse_shape_b = B0[kt_globals.nopen-40]; | ||||
| } | } | ||||
| } | } | ||||
| /* | /* | ||||
| function SETABC | function SETABC | ||||
| equation constants. | equation constants. | ||||
| */ | */ | ||||
| static void setabc(long int f, long int bw, resonator_ptr rp) | static void setabc(long int f, long int bw, resonator_ptr rp) | ||||
| { | { | ||||
| double r; | double r; | ||||
| rp->a = 1.0 - rp->b - rp->c; | rp->a = 1.0 - rp->b - rp->c; | ||||
| } | } | ||||
| /* | /* | ||||
| function SETZEROABC | function SETZEROABC | ||||
| } | } | ||||
| } | } | ||||
| /* | /* | ||||
| function GEN_NOISE | function GEN_NOISE | ||||
| the origin in the z-plane, i.e. output = input + (0.75 * lastoutput) | the origin in the z-plane, i.e. output = input + (0.75 * lastoutput) | ||||
| */ | */ | ||||
| static double gen_noise(double noise) | static double gen_noise(double noise) | ||||
| { | { | ||||
| long temp; | long temp; | ||||
| return noise; | return noise; | ||||
| } | } | ||||
| /* | /* | ||||
| function DBTOLIN | function DBTOLIN | ||||
| steps. | steps. | ||||
| */ | */ | ||||
| static double DBtoLIN(long dB) | static double DBtoLIN(long dB) | ||||
| { | { | ||||
| static short amptable[88] = { | static short amptable[88] = { | ||||
| return (double)(amptable[dB]) * 0.001; | return (double)(amptable[dB]) * 0.001; | ||||
| } | } | ||||
| extern voice_t *wvoice; | extern voice_t *wvoice; | ||||
| static klatt_peaks_t peaks[N_PEAKS]; | static klatt_peaks_t peaks[N_PEAKS]; | ||||
| static int end_wave; | static int end_wave; | ||||
| static double klattp1[N_KLATTP]; | static double klattp1[N_KLATTP]; | ||||
| static double klattp_inc[N_KLATTP]; | static double klattp_inc[N_KLATTP]; | ||||
| int Wavegen_Klatt(int resume) | int Wavegen_Klatt(int resume) | ||||
| { | { | ||||
| int pk; | int pk; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| void SetSynth_Klatt(int length, int modn, frame_t *fr1, frame_t *fr2, voice_t *v, int control) | void SetSynth_Klatt(int length, int modn, frame_t *fr1, frame_t *fr2, voice_t *v, int control) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| } | } | ||||
| } | } | ||||
| int Wavegen_Klatt2(int length, int modulation, int resume, frame_t *fr1, frame_t *fr2) | int Wavegen_Klatt2(int length, int modulation, int resume, frame_t *fr1, frame_t *fr2) | ||||
| { | { | ||||
| if (resume == 0) | if (resume == 0) | ||||
| return Wavegen_Klatt(resume); | return Wavegen_Klatt(resume); | ||||
| } | } | ||||
| void KlattInit() | void KlattInit() | ||||
| { | { | ||||
| #include "mbrowrap.h" | #include "mbrowrap.h" | ||||
| /* | /* | ||||
| * mbrola instance parameters | * mbrola instance parameters | ||||
| */ | */ |
| #include "voice.h" | #include "voice.h" | ||||
| #include "translate.h" | #include "translate.h" | ||||
| #define M_LIGATURE 0x8000 | #define M_LIGATURE 0x8000 | ||||
| #define M_NAME 0 | #define M_NAME 0 | ||||
| #define M_SMALLCAP 1 | #define M_SMALLCAP 1 | ||||
| #define M_RETROFLEX 20 | #define M_RETROFLEX 20 | ||||
| #define M_HOOK 21 | #define M_HOOK 21 | ||||
| #define M_MIDDLE_DOT M_DOT_ABOVE // duplicate of M_DOT_ABOVE | #define M_MIDDLE_DOT M_DOT_ABOVE // duplicate of M_DOT_ABOVE | ||||
| #define M_IMPLOSIVE M_HOOK | #define M_IMPLOSIVE M_HOOK | ||||
| static int speak_missing_thousands; | static int speak_missing_thousands; | ||||
| static int number_control; | static int number_control; | ||||
| typedef struct { | typedef struct { | ||||
| const char *name; | const char *name; | ||||
| int accent_flags; // bit 0, say before the letter name | int accent_flags; // bit 0, say before the letter name | ||||
| { "_hok", 0 }, // hook | { "_hok", 0 }, // hook | ||||
| }; | }; | ||||
| #define CAPITAL 0 | #define CAPITAL 0 | ||||
| #define LETTER(ch, mod1, mod2) (ch-59)+(mod1 << 6)+(mod2 << 11) | #define LETTER(ch, mod1, mod2) (ch-59)+(mod1 << 6)+(mod2 << 11) | ||||
| #define LIGATURE(ch1, ch2, mod1) (ch1-59)+((ch2-59) << 6)+(mod1 << 12)+M_LIGATURE | #define LIGATURE(ch1, ch2, mod1) (ch1-59)+((ch2-59) << 6)+(mod1 << 12)+M_LIGATURE | ||||
| #define L_RTAP 72 // U+27E | #define L_RTAP 72 // U+27E | ||||
| #define L_RLONG 73 // U+27C | #define L_RLONG 73 // U+27C | ||||
| static const short non_ascii_tab[] = { | static const short non_ascii_tab[] = { | ||||
| 0, 0x3b1, 0x259, 0x25b, 0x3b3, 0x3b9, 0x153, 0x3c9, | 0, 0x3b1, 0x259, 0x25b, 0x3b3, 0x3b9, 0x153, 0x3c9, | ||||
| 0x3c6, 0x283, 0x3c5, 0x292, 0x294, 0x27e, 0x27c | 0x3c6, 0x283, 0x3c5, 0x292, 0x294, 0x27e, 0x27c | ||||
| }; | }; | ||||
| // characters U+00e0 to U+017f | // characters U+00e0 to U+017f | ||||
| static const unsigned short letter_accents_0e0[] = { | static const unsigned short letter_accents_0e0[] = { | ||||
| LETTER('a', M_GRAVE, 0), // U+00e0 | LETTER('a', M_GRAVE, 0), // U+00e0 | ||||
| CAPITAL, | CAPITAL, | ||||
| LETTER('z', M_CARON, 0), | LETTER('z', M_CARON, 0), | ||||
| LETTER('s', M_NAME, 0), // long-s // U+17f | LETTER('s', M_NAME, 0), // long-s // U+17f | ||||
| }; | }; | ||||
| // characters U+0250 to U+029F | // characters U+0250 to U+029F | ||||
| static const unsigned short letter_accents_250[] = { | static const unsigned short letter_accents_250[] = { | ||||
| LETTER('a', M_TURNED, 0), // U+250 | LETTER('a', M_TURNED, 0), // U+250 | ||||
| return ph_buf[0]; | return ph_buf[0]; | ||||
| } | } | ||||
| void LookupAccentedLetter(Translator *tr, unsigned int letter, char *ph_buf) | void LookupAccentedLetter(Translator *tr, unsigned int letter, char *ph_buf) | ||||
| { | { | ||||
| // lookup the character in the accents table | // lookup the character in the accents table | ||||
| accent2 = (accent_data >> 11) & 0xf; | accent2 = (accent_data >> 11) & 0xf; | ||||
| } | } | ||||
| if ((accent1 == 0) && !(accent_data & M_LIGATURE)) { | if ((accent1 == 0) && !(accent_data & M_LIGATURE)) { | ||||
| // just a letter name, not an accented character or ligature | // just a letter name, not an accented character or ligature | ||||
| return; | return; | ||||
| } | } | ||||
| if ((flags1 = Lookup(tr, accents_tab[accent1].name, ph_accent1)) != 0) { | if ((flags1 = Lookup(tr, accents_tab[accent1].name, ph_accent1)) != 0) { | ||||
| if (LookupLetter2(tr, basic_letter, ph_letter1) != 0) { | if (LookupLetter2(tr, basic_letter, ph_letter1) != 0) { | ||||
| if (accent2 != 0) { | if (accent2 != 0) { | ||||
| flags2 = Lookup(tr, accents_tab[accent2].name, ph_accent2); | flags2 = Lookup(tr, accents_tab[accent2].name, ph_accent2); | ||||
| } | } | ||||
| } | } | ||||
| void LookupLetter(Translator *tr, unsigned int letter, int next_byte, char *ph_buf1, int control) | void LookupLetter(Translator *tr, unsigned int letter, int next_byte, char *ph_buf1, int control) | ||||
| { | { | ||||
| // control, bit 0: not the first letter of a word | // control, bit 0: not the first letter of a word | ||||
| dict_flags[0] = 0; | dict_flags[0] = 0; | ||||
| dict_flags[1] = 0; | dict_flags[1] = 0; | ||||
| SetWordStress(tr, ph_buf1, dict_flags, -1, control & 1); | SetWordStress(tr, ph_buf1, dict_flags, -1, control & 1); | ||||
| } | } | ||||
| // unicode ranges for non-ascii digits 0-9 | // unicode ranges for non-ascii digits 0-9 | ||||
| static const int number_ranges[] = { | static const int number_ranges[] = { | ||||
| 0x660, 0x6f0, // arabic | 0x660, 0x6f0, // arabic | ||||
| 0 | 0 | ||||
| }; // these must be in ascending order | }; // these must be in ascending order | ||||
| int NonAsciiNumber(int letter) | int NonAsciiNumber(int letter) | ||||
| { | { | ||||
| // Change non-ascii digit into ascii digit '0' to '9', (or -1 if not) | // Change non-ascii digit into ascii digit '0' to '9', (or -1 if not) | ||||
| 0, 0 | 0, 0 | ||||
| }; | }; | ||||
| static const char *hex_letters[] = { "'e:j", "b'i:", "s'i:", "d'i:", "'i:", "'ef" }; // names, using phonemes available to all languages | static const char *hex_letters[] = { "'e:j", "b'i:", "s'i:", "d'i:", "'i:", "'ef" }; // names, using phonemes available to all languages | ||||
| int IsSuperscript(int letter) | int IsSuperscript(int letter) | ||||
| { | { | ||||
| // is this a subscript or superscript letter ? | // is this a subscript or superscript letter ? | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| int TranslateLetter(Translator *tr, char *word, char *phonemes, int control) | int TranslateLetter(Translator *tr, char *word, char *phonemes, int control) | ||||
| { | { | ||||
| // get pronunciation for an isolated letter | // get pronunciation for an isolated letter | ||||
| } | } | ||||
| } | } | ||||
| // caution: SetWordStress() etc don't expect phonSWITCH + phoneme table number | // caution: SetWordStress() etc don't expect phonSWITCH + phoneme table number | ||||
| if (ph_buf[0] == 0) { | if (ph_buf[0] == 0) { | ||||
| return n_bytes; | return n_bytes; | ||||
| } | } | ||||
| void SetSpellingStress(Translator *tr, char *phonemes, int control, int n_chars) | void SetSpellingStress(Translator *tr, char *phonemes, int control, int n_chars) | ||||
| { | { | ||||
| // Individual letter names, reduce the stress of some. | // Individual letter names, reduce the stress of some. | ||||
| *phonemes = 0; | *phonemes = 0; | ||||
| } | } | ||||
| // Numbers | // Numbers | ||||
| static char ph_ordinal2[12]; | static char ph_ordinal2[12]; | ||||
| static char ph_ordinal2x[12]; | static char ph_ordinal2x[12]; | ||||
| static int CheckDotOrdinal(Translator *tr, char *word, char *word_end, WORD_TAB *wtab, int roman) | static int CheckDotOrdinal(Translator *tr, char *word, char *word_end, WORD_TAB *wtab, int roman) | ||||
| { | { | ||||
| int ordinal = 0; | int ordinal = 0; | ||||
| int c2; | int c2; | ||||
| int nextflags; | int nextflags; | ||||
| return ordinal; | return ordinal; | ||||
| } | } | ||||
| static int hu_number_e(const char *word, int thousandplex, int value) | static int hu_number_e(const char *word, int thousandplex, int value) | ||||
| { | { | ||||
| // lang-hu: variant form of numbers when followed by hyphen and a suffix starting with 'a' or 'e' (but not a, e, az, ez, azt, ezt, att. ett | // lang-hu: variant form of numbers when followed by hyphen and a suffix starting with 'a' or 'e' (but not a, e, az, ez, azt, ezt, att. ett | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| int TranslateRoman(Translator *tr, char *word, char *ph_out, WORD_TAB *wtab) | int TranslateRoman(Translator *tr, char *word, char *ph_out, WORD_TAB *wtab) | ||||
| { | { | ||||
| int c; | int c; | ||||
| if (acc > tr->langopts.max_roman) | if (acc > tr->langopts.max_roman) | ||||
| return 0; | return 0; | ||||
| Lookup(tr, "_roman", ph_roman); // precede by "roman" if _rom is defined in *_list | Lookup(tr, "_roman", ph_roman); // precede by "roman" if _rom is defined in *_list | ||||
| p = &ph_out[0]; | p = &ph_out[0]; | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| static const char *M_Variant(int value) | static const char *M_Variant(int value) | ||||
| { | { | ||||
| // returns M, or perhaps MA or MB for some cases | // returns M, or perhaps MA or MB for some cases | ||||
| if ((teens == 0) && ((value % 10) == 1)) | if ((teens == 0) && ((value % 10) == 1)) | ||||
| return "1M"; | return "1M"; | ||||
| break; | break; | ||||
| case 2: // lang=cs,sk | case 2: // lang=cs,sk | ||||
| if ((value >= 2) && (value <= 4)) | if ((value >= 2) && (value <= 4)) | ||||
| return "0MA"; | return "0MA"; | ||||
| break; | break; | ||||
| case 3: // lang=pl | case 3: // lang=pl | ||||
| if ((teens == 0) && (((value % 10) >= 2) && ((value % 10) <= 4))) | if ((teens == 0) && (((value % 10) >= 2) && ((value % 10) <= 4))) | ||||
| return "0MA"; | return "0MA"; | ||||
| break; | break; | ||||
| case 4: // lang=lt | case 4: // lang=lt | ||||
| if ((teens == 1) || ((value % 10) == 0)) | if ((teens == 1) || ((value % 10) == 0)) | ||||
| return "0MB"; | return "0MB"; | ||||
| if ((value % 10) == 1) | if ((value % 10) == 1) | ||||
| return "0MA"; | return "0MA"; | ||||
| break; | break; | ||||
| case 5: // lang=bs,hr,sr | case 5: // lang=bs,hr,sr | ||||
| if (teens == 0) { | if (teens == 0) { | ||||
| if ((value % 10) == 1) | if ((value % 10) == 1) | ||||
| return "0M"; | return "0M"; | ||||
| } | } | ||||
| static int LookupThousands(Translator *tr, int value, int thousandplex, int thousands_exact, char *ph_out) | static int LookupThousands(Translator *tr, int value, int thousandplex, int thousands_exact, char *ph_out) | ||||
| { | { | ||||
| // thousands_exact: bit 0 no hundreds,tens,or units, bit 1 ordinal numberr | // thousands_exact: bit 0 no hundreds,tens,or units, bit 1 ordinal numberr | ||||
| return found_value; | return found_value; | ||||
| } | } | ||||
| static int LookupNum2(Translator *tr, int value, int thousandplex, const int control, char *ph_out) | static int LookupNum2(Translator *tr, int value, int thousandplex, const int control, char *ph_out) | ||||
| { | { | ||||
| // Lookup a 2 digit number | // Lookup a 2 digit number | ||||
| if (found) { | if (found) { | ||||
| ph_tens[0] = 0; | ph_tens[0] = 0; | ||||
| } else { | } else { | ||||
| if (is_ordinal) { | if (is_ordinal) { | ||||
| sprintf(string, "_%dX%c", tens, ord_type); | sprintf(string, "_%dX%c", tens, ord_type); | ||||
| if (Lookup(tr, string, ph_tens) != 0) { | if (Lookup(tr, string, ph_tens) != 0) { | ||||
| return used_and; | return used_and; | ||||
| } | } | ||||
| static int LookupNum3(Translator *tr, int value, char *ph_out, int suppress_null, int thousandplex, int control) | static int LookupNum3(Translator *tr, int value, char *ph_out, int suppress_null, int thousandplex, int control) | ||||
| { | { | ||||
| // Translate a 3 digit number | // Translate a 3 digit number | ||||
| } | } | ||||
| } | } | ||||
| buf2[0] = 0; | buf2[0] = 0; | ||||
| if ((tensunits != 0) || (suppress_null == 0)) { | if ((tensunits != 0) || (suppress_null == 0)) { | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| bool CheckThousandsGroup(char *word, int group_len) | bool CheckThousandsGroup(char *word, int group_len) | ||||
| { | { | ||||
| // Is this a group of 3 digits which looks like a thousands group? | // Is this a group of 3 digits which looks like a thousands group? | ||||
| return true; | return true; | ||||
| } | } | ||||
| static int TranslateNumber_1(Translator *tr, char *word, char *ph_out, unsigned int *flags, WORD_TAB *wtab, int control) | static int TranslateNumber_1(Translator *tr, char *word, char *ph_out, unsigned int *flags, WORD_TAB *wtab, int control) | ||||
| { | { | ||||
| // Number translation with various options | // Number translation with various options | ||||
| ph_append[0] = 0; | ph_append[0] = 0; | ||||
| ph_buf2[0] = 0; | ph_buf2[0] = 0; | ||||
| if ((word[0] == '0') && (prev_thousands == 0) && (word[1] != ' ') && (word[1] != tr->langopts.decimal_sep)) { | if ((word[0] == '0') && (prev_thousands == 0) && (word[1] != ' ') && (word[1] != tr->langopts.decimal_sep)) { | ||||
| if ((n_digits == 2) && (word[3] == ':') && IsDigit09(word[5]) && isspace(word[7])) { | if ((n_digits == 2) && (word[3] == ':') && IsDigit09(word[5]) && isspace(word[7])) { | ||||
| // looks like a time 02:30, omit the leading zero | // looks like a time 02:30, omit the leading zero | ||||
| { | { | ||||
| case NUM_DFRACTION_4: | case NUM_DFRACTION_4: | ||||
| max_decimal_count = 5; | max_decimal_count = 5; | ||||
| // fallthrough | |||||
| case NUM_DFRACTION_2: | case NUM_DFRACTION_2: | ||||
| // French/Polish decimal fraction | // French/Polish decimal fraction | ||||
| while (word[n_digits] == '0') { | while (word[n_digits] == '0') { | ||||
| n_digits += decimal_count; | n_digits += decimal_count; | ||||
| } | } | ||||
| break; | break; | ||||
| case NUM_DFRACTION_1: // italian, say "hundredths" if leading zero | case NUM_DFRACTION_1: // italian, say "hundredths" if leading zero | ||||
| case NUM_DFRACTION_5: // hungarian, always say "tenths" etc. | case NUM_DFRACTION_5: // hungarian, always say "tenths" etc. | ||||
| case NUM_DFRACTION_6: // kazakh, always say "tenths" etc, before the decimal fraction | case NUM_DFRACTION_6: // kazakh, always say "tenths" etc, before the decimal fraction | ||||
| strcat(ph_out, ph_buf); | strcat(ph_out, ph_buf); | ||||
| n_digits += decimal_count; | n_digits += decimal_count; | ||||
| break; | break; | ||||
| case NUM_DFRACTION_3: | case NUM_DFRACTION_3: | ||||
| // Romanian decimal fractions | // Romanian decimal fractions | ||||
| if ((decimal_count <= 4) && (word[n_digits] != '0')) { | if ((decimal_count <= 4) && (word[n_digits] != '0')) { | ||||
| n_digits += decimal_count; | n_digits += decimal_count; | ||||
| } | } | ||||
| break; | break; | ||||
| case NUM_DFRACTION_7: | case NUM_DFRACTION_7: | ||||
| // alternative form of decimal fraction digits, except the final digit | // alternative form of decimal fraction digits, except the final digit | ||||
| while (decimal_count-- > 1) { | while (decimal_count-- > 1) { | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| int TranslateNumber(Translator *tr, char *word1, char *ph_out, unsigned int *flags, WORD_TAB *wtab, int control) | int TranslateNumber(Translator *tr, char *word1, char *ph_out, unsigned int *flags, WORD_TAB *wtab, int control) | ||||
| { | { | ||||
| if ((option_sayas == SAYAS_DIGITS1) || (wtab[0].flags & FLAG_INDIVIDUAL_DIGITS)) | if ((option_sayas == SAYAS_DIGITS1) || (wtab[0].flags & FLAG_INDIVIDUAL_DIGITS)) |
| #include "synthesize.h" | #include "synthesize.h" | ||||
| #include "translate.h" | #include "translate.h" | ||||
| const unsigned char pause_phonemes[8] = { 0, phonPAUSE_VSHORT, phonPAUSE_SHORT, phonPAUSE, phonPAUSE_LONG, phonGLOTTALSTOP, phonPAUSE_LONG, phonPAUSE_LONG }; | const unsigned char pause_phonemes[8] = { 0, phonPAUSE_VSHORT, phonPAUSE_SHORT, phonPAUSE, phonPAUSE_LONG, phonGLOTTALSTOP, phonPAUSE_LONG, phonPAUSE_LONG }; | ||||
| extern int n_ph_list2; | extern int n_ph_list2; | ||||
| extern PHONEME_LIST2 ph_list2[N_PHONEME_LIST]; // first stage of text->phonemes | extern PHONEME_LIST2 ph_list2[N_PHONEME_LIST]; // first stage of text->phonemes | ||||
| static int SubstitutePhonemes(Translator *tr, PHONEME_LIST *plist_out) | static int SubstitutePhonemes(Translator *tr, PHONEME_LIST *plist_out) | ||||
| { | { | ||||
| // Copy the phonemes list and perform any substitutions that are required for the | // Copy the phonemes list and perform any substitutions that are required for the | ||||
| return n_plist_out; | return n_plist_out; | ||||
| } | } | ||||
| void MakePhonemeList(Translator *tr, int post_pause, int start_sentence) | void MakePhonemeList(Translator *tr, int post_pause, int start_sentence) | ||||
| { | { | ||||
| int ix = 0; | int ix = 0; | ||||
| int j; | int j; | ||||
| int insert_ph = 0; | int insert_ph = 0; |
| #include <locale.h> | #include <locale.h> | ||||
| #define N_XML_BUF 500 | #define N_XML_BUF 500 | ||||
| static const char *xmlbase = ""; // base URL from <speak> | static const char *xmlbase = ""; // base URL from <speak> | ||||
| static int namedata_ix = 0; | static int namedata_ix = 0; | ||||
| static int n_namedata = 0; | static int n_namedata = 0; | ||||
| char *namedata = NULL; | char *namedata = NULL; | ||||
| static FILE *f_input = NULL; | static FILE *f_input = NULL; | ||||
| static int ungot_char2 = 0; | static int ungot_char2 = 0; | ||||
| unsigned char *p_textinput; | unsigned char *p_textinput; | ||||
| 0 }; | 0 }; | ||||
| // indexed by (entry num. in punct_chars) + 1 | // indexed by (entry num. in punct_chars) + 1 | ||||
| // bits 0-7 pause x 10mS, bits 12-14 intonation type, bit 15 don't need following space or bracket | // bits 0-7 pause x 10mS, bits 12-14 intonation type, bit 15 don't need following space or bracket | ||||
| static const unsigned int punct_attributes[] = { 0, | static const unsigned int punct_attributes[] = { 0, | ||||
| CLAUSE_SEMICOLON, // spare | CLAUSE_SEMICOLON, // spare | ||||
| 0 }; | 0 }; | ||||
| // stack for language and voice properties | // stack for language and voice properties | ||||
| // frame 0 is for the defaults, before any ssml tags. | // frame 0 is for the defaults, before any ssml tags. | ||||
| typedef struct { | typedef struct { | ||||
| static char base_voice_variant_name[40] = { 0 }; | static char base_voice_variant_name[40] = { 0 }; | ||||
| static char current_voice_id[40] = { 0 }; | static char current_voice_id[40] = { 0 }; | ||||
| #define N_PARAM_STACK 20 | #define N_PARAM_STACK 20 | ||||
| static int n_param_stack; | static int n_param_stack; | ||||
| PARAM_STACK param_stack[N_PARAM_STACK]; | PARAM_STACK param_stack[N_PARAM_STACK]; | ||||
| 0, // voice type | 0, // voice type | ||||
| }; | }; | ||||
| // additional Latin characters beyond the ascii character set | // additional Latin characters beyond the ascii character set | ||||
| #define MAX_WALPHA 0x24f | #define MAX_WALPHA 0x24f | ||||
| // indexed by character - 0x80 | // indexed by character - 0x80 | ||||
| 0, 0 | 0, 0 | ||||
| }; | }; | ||||
| #ifdef NEED_WCHAR_FUNCTIONS | #ifdef NEED_WCHAR_FUNCTIONS | ||||
| // use ctype.h functions for Latin1 (character < 0x100) | // use ctype.h functions for Latin1 (character < 0x100) | ||||
| } | } | ||||
| #endif | #endif | ||||
| // use internal data for iswalpha up to U+024F | // use internal data for iswalpha up to U+024F | ||||
| // iswalpha() on Windows is unreliable (U+AA, U+BA). | // iswalpha() on Windows is unreliable (U+AA, U+BA). | ||||
| int iswalpha2(int c) | int iswalpha2(int c) | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static void GetC_unget(int c) | static void GetC_unget(int c) | ||||
| { | { | ||||
| // This is only called with UTF8 input, not wchar input | // This is only called with UTF8 input, not wchar input | ||||
| return end_of_input; | return end_of_input; | ||||
| } | } | ||||
| static int GetC_get(void) | static int GetC_get(void) | ||||
| { | { | ||||
| unsigned int c; | unsigned int c; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static int GetC(void) | static int GetC(void) | ||||
| { | { | ||||
| // Returns a unicode wide character | // Returns a unicode wide character | ||||
| return c1; | return c1; | ||||
| } | } | ||||
| static void UngetC(int c) | static void UngetC(int c) | ||||
| { | { | ||||
| ungot_char = c; | ungot_char = c; | ||||
| } | } | ||||
| const char *WordToString2(unsigned int word) | const char *WordToString2(unsigned int word) | ||||
| { | { | ||||
| // Convert a language mnemonic word into a string | // Convert a language mnemonic word into a string | ||||
| return buf; | return buf; | ||||
| } | } | ||||
| static const char *LookupSpecial(Translator *tr, const char *string, char *text_out) | static const char *LookupSpecial(Translator *tr, const char *string, char *text_out) | ||||
| { | { | ||||
| unsigned int flags[2]; | unsigned int flags[2]; | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| static const char *LookupCharName(Translator *tr, int c, int only) | static const char *LookupCharName(Translator *tr, int c, int only) | ||||
| { | { | ||||
| // Find the phoneme string (in ascii) to speak the name of character c | // Find the phoneme string (in ascii) to speak the name of character c | ||||
| return acc; | return acc; | ||||
| } | } | ||||
| static int LoadSoundFile(const char *fname, int index) | static int LoadSoundFile(const char *fname, int index) | ||||
| { | { | ||||
| FILE *f; | FILE *f; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static int LookupSoundicon(int c) | static int LookupSoundicon(int c) | ||||
| { | { | ||||
| // Find the sound icon number for a punctuation chatacter | // Find the sound icon number for a punctuation chatacter | ||||
| return -1; | return -1; | ||||
| } | } | ||||
| static int LoadSoundFile2(const char *fname) | static int LoadSoundFile2(const char *fname) | ||||
| { | { | ||||
| // Load a sound file into one of the reserved slots in the sound icon table | // Load a sound file into one of the reserved slots in the sound icon table | ||||
| return slot; | return slot; | ||||
| } | } | ||||
| static int AnnouncePunctuation(Translator *tr, int c1, int *c2_ptr, char *output, int *bufix, int end_clause) | static int AnnouncePunctuation(Translator *tr, int c1, int *c2_ptr, char *output, int *bufix, int end_clause) | ||||
| { | { | ||||
| // announce punctuation names | // announce punctuation names | ||||
| // these tags have no effect if they are self-closing, eg. <voice /> | // these tags have no effect if they are self-closing, eg. <voice /> | ||||
| static char ignore_if_self_closing[] = { 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0 }; | static char ignore_if_self_closing[] = { 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0 }; | ||||
| static MNEM_TAB ssmltags[] = { | static MNEM_TAB ssmltags[] = { | ||||
| { "speak", SSML_SPEAK }, | { "speak", SSML_SPEAK }, | ||||
| { "voice", SSML_VOICE }, | { "voice", SSML_VOICE }, | ||||
| { NULL, 0 } | { NULL, 0 } | ||||
| }; | }; | ||||
| static const char *VoiceFromStack() | static const char *VoiceFromStack() | ||||
| { | { | ||||
| // Use the voice properties from the SSML stack to choose a voice, and switch | // Use the voice properties from the SSML stack to choose a voice, and switch | ||||
| return v_id; | return v_id; | ||||
| } | } | ||||
| static void ProcessParamStack(char *outbuf, int *outix) | static void ProcessParamStack(char *outbuf, int *outix) | ||||
| { | { | ||||
| // Set the speech parameters from the parameter stack | // Set the speech parameters from the parameter stack | ||||
| int new_parameters[N_SPEECH_PARAM]; | int new_parameters[N_SPEECH_PARAM]; | ||||
| static char cmd_letter[N_SPEECH_PARAM] = { 0, 'S', 'A', 'P', 'R', 0, 'C', 0, 0, 0, 0, 0, 'F' }; // embedded command letters | static char cmd_letter[N_SPEECH_PARAM] = { 0, 'S', 'A', 'P', 'R', 0, 'C', 0, 0, 0, 0, 0, 'F' }; // embedded command letters | ||||
| for (param = 0; param < N_SPEECH_PARAM; param++) | for (param = 0; param < N_SPEECH_PARAM; param++) | ||||
| new_parameters[param] = -1; | new_parameters[param] = -1; | ||||
| case espeakPUNCTUATION: | case espeakPUNCTUATION: | ||||
| option_punctuation = value-1; | option_punctuation = value-1; | ||||
| break; | break; | ||||
| case espeakCAPITALS: | case espeakCAPITALS: | ||||
| option_capitals = value; | option_capitals = value; | ||||
| break; | break; | ||||
| case espeakRATE: | case espeakRATE: | ||||
| case espeakVOLUME: | case espeakVOLUME: | ||||
| case espeakPITCH: | case espeakPITCH: | ||||
| } | } | ||||
| } | } | ||||
| static PARAM_STACK *PushParamStack(int tag_type) | static PARAM_STACK *PushParamStack(int tag_type) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| return sp; | return sp; | ||||
| } | } | ||||
| static void PopParamStack(int tag_type, char *outbuf, int *outix) | static void PopParamStack(int tag_type, char *outbuf, int *outix) | ||||
| { | { | ||||
| // unwind the stack up to and including the previous tag of this type | // unwind the stack up to and including the previous tag of this type | ||||
| ProcessParamStack(outbuf, outix); | ProcessParamStack(outbuf, outix); | ||||
| } | } | ||||
| static wchar_t *GetSsmlAttribute(wchar_t *pw, const char *name) | static wchar_t *GetSsmlAttribute(wchar_t *pw, const char *name) | ||||
| { | { | ||||
| // Gets the value string for an attribute. | // Gets the value string for an attribute. | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| static int attrcmp(const wchar_t *string1, const char *string2) | static int attrcmp(const wchar_t *string1, const char *string2) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| static int attrlookup(const wchar_t *string1, const MNEM_TAB *mtab) | static int attrlookup(const wchar_t *string1, const MNEM_TAB *mtab) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| return mtab[ix].value; | return mtab[ix].value; | ||||
| } | } | ||||
| static int attrnumber(const wchar_t *pw, int default_value, int type) | static int attrnumber(const wchar_t *pw, int default_value, int type) | ||||
| { | { | ||||
| int value = 0; | int value = 0; | ||||
| return value; | return value; | ||||
| } | } | ||||
| static int attrcopy_utf8(char *buf, const wchar_t *pw, int len) | static int attrcopy_utf8(char *buf, const wchar_t *pw, int len) | ||||
| { | { | ||||
| // Convert attribute string into utf8, write to buf, and return its utf8 length | // Convert attribute string into utf8, write to buf, and return its utf8 length | ||||
| return ix; | return ix; | ||||
| } | } | ||||
| static int attr_prosody_value(int param_type, const wchar_t *pw, int *value_out) | static int attr_prosody_value(int param_type, const wchar_t *pw, int *value_out) | ||||
| { | { | ||||
| int sign = 0; | int sign = 0; | ||||
| return sign; // -1, 0, or 1 | return sign; // -1, 0, or 1 | ||||
| } | } | ||||
| int AddNameData(const char *name, int wide) | int AddNameData(const char *name, int wide) | ||||
| { | { | ||||
| // Add the name to the namedata and return its position | // Add the name to the namedata and return its position | ||||
| return ix; | return ix; | ||||
| } | } | ||||
| void SetVoiceStack(espeak_VOICE *v, const char *variant_name) | void SetVoiceStack(espeak_VOICE *v, const char *variant_name) | ||||
| { | { | ||||
| SSML_STACK *sp; | SSML_STACK *sp; | ||||
| memcpy(&base_voice, ¤t_voice_selected, sizeof(base_voice)); | memcpy(&base_voice, ¤t_voice_selected, sizeof(base_voice)); | ||||
| } | } | ||||
| static int GetVoiceAttributes(wchar_t *pw, int tag_type) | static int GetVoiceAttributes(wchar_t *pw, int tag_type) | ||||
| { | { | ||||
| // Determines whether voice attribute are specified in this tag, and if so, whether this means | // Determines whether voice attribute are specified in this tag, and if so, whether this means | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static void SetProsodyParameter(int param_type, wchar_t *attr1, PARAM_STACK *sp) | static void SetProsodyParameter(int param_type, wchar_t *attr1, PARAM_STACK *sp) | ||||
| { | { | ||||
| int value; | int value; | ||||
| NULL, mnem_rate, mnem_volume, mnem_pitch, mnem_range | NULL, mnem_rate, mnem_volume, mnem_pitch, mnem_range | ||||
| }; | }; | ||||
| if ((value = attrlookup(attr1, mnem_tabs[param_type])) >= 0) { | if ((value = attrlookup(attr1, mnem_tabs[param_type])) >= 0) { | ||||
| // mnemonic specifies a value as a percentage of the base pitch/range/rate/volume | // mnemonic specifies a value as a percentage of the base pitch/range/rate/volume | ||||
| sp->parameter[param_type] = (param_stack[0].parameter[param_type] * value)/100; | sp->parameter[param_type] = (param_stack[0].parameter[param_type] * value)/100; | ||||
| } | } | ||||
| } | } | ||||
| static int ReplaceKeyName(char *outbuf, int index, int *outix) | static int ReplaceKeyName(char *outbuf, int index, int *outix) | ||||
| { | { | ||||
| // Replace some key-names by single characters, so they can be pronounced in different languages | // Replace some key-names by single characters, so they can be pronounced in different languages | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int *outix, int n_outbuf, int self_closing) | static int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int *outix, int n_outbuf, int self_closing) | ||||
| { | { | ||||
| // xml_buf is the tag and attributes with a zero terminator in place of the original '>' | // xml_buf is the tag and attributes with a zero terminator in place of the original '>' | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| voice_change_flag = 0; | voice_change_flag = 0; | ||||
| terminator = CLAUSE_NONE; | terminator = CLAUSE_NONE; | ||||
| ssml_sp = &ssml_stack[n_ssml_stack-1]; | ssml_sp = &ssml_stack[n_ssml_stack-1]; | ||||
| } | } | ||||
| ProcessParamStack(outbuf, outix); | ProcessParamStack(outbuf, outix); | ||||
| break; | break; | ||||
| case SSML_PROSODY: | case SSML_PROSODY: | ||||
| sp = PushParamStack(tag_type); | sp = PushParamStack(tag_type); | ||||
| ProcessParamStack(outbuf, outix); | ProcessParamStack(outbuf, outix); | ||||
| break; | break; | ||||
| case SSML_EMPHASIS: | case SSML_EMPHASIS: | ||||
| sp = PushParamStack(tag_type); | sp = PushParamStack(tag_type); | ||||
| value = 3; // default is "moderate" | value = 3; // default is "moderate" | ||||
| } | } | ||||
| ProcessParamStack(outbuf, outix); | ProcessParamStack(outbuf, outix); | ||||
| break; | break; | ||||
| case SSML_STYLE + SSML_CLOSE: | case SSML_STYLE + SSML_CLOSE: | ||||
| case SSML_PROSODY + SSML_CLOSE: | case SSML_PROSODY + SSML_CLOSE: | ||||
| case SSML_EMPHASIS + SSML_CLOSE: | case SSML_EMPHASIS + SSML_CLOSE: | ||||
| PopParamStack(tag_type, outbuf, outix); | PopParamStack(tag_type, outbuf, outix); | ||||
| break; | break; | ||||
| case SSML_PHONEME: | case SSML_PHONEME: | ||||
| attr1 = GetSsmlAttribute(px, "alphabet"); | attr1 = GetSsmlAttribute(px, "alphabet"); | ||||
| attr2 = GetSsmlAttribute(px, "ph"); | attr2 = GetSsmlAttribute(px, "ph"); | ||||
| outbuf[(*outix)++] = ']'; | outbuf[(*outix)++] = ']'; | ||||
| } | } | ||||
| break; | break; | ||||
| case SSML_SAYAS: | case SSML_SAYAS: | ||||
| attr1 = GetSsmlAttribute(px, "interpret-as"); | attr1 = GetSsmlAttribute(px, "interpret-as"); | ||||
| attr2 = GetSsmlAttribute(px, "format"); | attr2 = GetSsmlAttribute(px, "format"); | ||||
| sayas_start = *outix; | sayas_start = *outix; | ||||
| sayas_mode = value; // punctuation doesn't end clause during SAY-AS | sayas_mode = value; // punctuation doesn't end clause during SAY-AS | ||||
| break; | break; | ||||
| case SSML_SAYAS + SSML_CLOSE: | case SSML_SAYAS + SSML_CLOSE: | ||||
| if (sayas_mode == SAYAS_KEY) { | if (sayas_mode == SAYAS_KEY) { | ||||
| outbuf[*outix] = 0; | outbuf[*outix] = 0; | ||||
| outbuf[(*outix)++] = 'Y'; | outbuf[(*outix)++] = 'Y'; | ||||
| sayas_mode = 0; | sayas_mode = 0; | ||||
| break; | break; | ||||
| case SSML_SUB: | case SSML_SUB: | ||||
| if ((attr1 = GetSsmlAttribute(px, "alias")) != NULL) { | if ((attr1 = GetSsmlAttribute(px, "alias")) != NULL) { | ||||
| // use the alias rather than the text | // use the alias rather than the text | ||||
| *outix += attrcopy_utf8(&outbuf[*outix], attr1, n_outbuf-*outix); | *outix += attrcopy_utf8(&outbuf[*outix], attr1, n_outbuf-*outix); | ||||
| } | } | ||||
| break; | break; | ||||
| case SSML_IGNORE_TEXT: | case SSML_IGNORE_TEXT: | ||||
| ignore_text = 1; | ignore_text = 1; | ||||
| break; | break; | ||||
| case SSML_SUB + SSML_CLOSE: | case SSML_SUB + SSML_CLOSE: | ||||
| case SSML_IGNORE_TEXT + SSML_CLOSE: | case SSML_IGNORE_TEXT + SSML_CLOSE: | ||||
| ignore_text = 0; | ignore_text = 0; | ||||
| break; | break; | ||||
| case SSML_MARK: | case SSML_MARK: | ||||
| if ((attr1 = GetSsmlAttribute(px, "name")) != NULL) { | if ((attr1 = GetSsmlAttribute(px, "name")) != NULL) { | ||||
| // add name to circular buffer of marker names | // add name to circular buffer of marker names | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| case SSML_AUDIO: | case SSML_AUDIO: | ||||
| sp = PushParamStack(tag_type); | sp = PushParamStack(tag_type); | ||||
| else | else | ||||
| audio_text = 1; | audio_text = 1; | ||||
| return CLAUSE_NONE; | return CLAUSE_NONE; | ||||
| case SSML_AUDIO + SSML_CLOSE: | case SSML_AUDIO + SSML_CLOSE: | ||||
| PopParamStack(tag_type, outbuf, outix); | PopParamStack(tag_type, outbuf, outix); | ||||
| audio_text = 0; | audio_text = 0; | ||||
| return CLAUSE_NONE; | return CLAUSE_NONE; | ||||
| case SSML_BREAK: | case SSML_BREAK: | ||||
| value = 21; | value = 21; | ||||
| terminator = CLAUSE_NONE; | terminator = CLAUSE_NONE; | ||||
| return terminator + value; | return terminator + value; | ||||
| } | } | ||||
| break; | break; | ||||
| case SSML_SPEAK: | case SSML_SPEAK: | ||||
| if ((attr1 = GetSsmlAttribute(px, "xml:base")) != NULL) { | if ((attr1 = GetSsmlAttribute(px, "xml:base")) != NULL) { | ||||
| attrcopy_utf8(buf, attr1, sizeof(buf)); | attrcopy_utf8(buf, attr1, sizeof(buf)); | ||||
| if (GetVoiceAttributes(px, tag_type) == 0) | if (GetVoiceAttributes(px, tag_type) == 0) | ||||
| return 0; // no voice change | return 0; // no voice change | ||||
| return CLAUSE_VOICE; | return CLAUSE_VOICE; | ||||
| case SSML_VOICE: | case SSML_VOICE: | ||||
| if (GetVoiceAttributes(px, tag_type) == 0) | if (GetVoiceAttributes(px, tag_type) == 0) | ||||
| return 0; // no voice change | return 0; // no voice change | ||||
| return CLAUSE_VOICE; | return CLAUSE_VOICE; | ||||
| case SSML_SPEAK + SSML_CLOSE: | case SSML_SPEAK + SSML_CLOSE: | ||||
| // unwind stack until the previous <voice> or <speak> tag | // unwind stack until the previous <voice> or <speak> tag | ||||
| while ((n_ssml_stack > 1) && (ssml_stack[n_ssml_stack-1].tag_type != SSML_SPEAK)) | while ((n_ssml_stack > 1) && (ssml_stack[n_ssml_stack-1].tag_type != SSML_SPEAK)) | ||||
| n_ssml_stack--; | n_ssml_stack--; | ||||
| return CLAUSE_PERIOD + GetVoiceAttributes(px, tag_type); | return CLAUSE_PERIOD + GetVoiceAttributes(px, tag_type); | ||||
| case SSML_VOICE + SSML_CLOSE: | case SSML_VOICE + SSML_CLOSE: | ||||
| // unwind stack until the previous <voice> or <speak> tag | // unwind stack until the previous <voice> or <speak> tag | ||||
| while ((n_ssml_stack > 1) && (ssml_stack[n_ssml_stack-1].tag_type != SSML_VOICE)) | while ((n_ssml_stack > 1) && (ssml_stack[n_ssml_stack-1].tag_type != SSML_VOICE)) | ||||
| terminator = 0; // ?? Sentence intonation, but no pause ?? | terminator = 0; // ?? Sentence intonation, but no pause ?? | ||||
| return terminator + GetVoiceAttributes(px, tag_type); | return terminator + GetVoiceAttributes(px, tag_type); | ||||
| case HTML_BREAK: | case HTML_BREAK: | ||||
| case HTML_BREAK + SSML_CLOSE: | case HTML_BREAK + SSML_CLOSE: | ||||
| return CLAUSE_COLON; | return CLAUSE_COLON; | ||||
| case SSML_SENTENCE: | case SSML_SENTENCE: | ||||
| if (ssml_sp->tag_type == SSML_SENTENCE) { | if (ssml_sp->tag_type == SSML_SENTENCE) { | ||||
| // new sentence implies end-of-sentence | // new sentence implies end-of-sentence | ||||
| } | } | ||||
| voice_change_flag |= GetVoiceAttributes(px, tag_type); | voice_change_flag |= GetVoiceAttributes(px, tag_type); | ||||
| return CLAUSE_PARAGRAPH + voice_change_flag; | return CLAUSE_PARAGRAPH + voice_change_flag; | ||||
| case SSML_PARAGRAPH: | case SSML_PARAGRAPH: | ||||
| if (ssml_sp->tag_type == SSML_SENTENCE) { | if (ssml_sp->tag_type == SSML_SENTENCE) { | ||||
| // new paragraph implies end-of-sentence or end-of-paragraph | // new paragraph implies end-of-sentence or end-of-paragraph | ||||
| } | } | ||||
| voice_change_flag |= GetVoiceAttributes(px, tag_type); | voice_change_flag |= GetVoiceAttributes(px, tag_type); | ||||
| return CLAUSE_PARAGRAPH + voice_change_flag; | return CLAUSE_PARAGRAPH + voice_change_flag; | ||||
| case SSML_SENTENCE + SSML_CLOSE: | case SSML_SENTENCE + SSML_CLOSE: | ||||
| if (ssml_sp->tag_type == SSML_SENTENCE) { | if (ssml_sp->tag_type == SSML_SENTENCE) { | ||||
| // end of a sentence which specified a language | // end of a sentence which specified a language | ||||
| voice_change_flag = GetVoiceAttributes(px, tag_type); | voice_change_flag = GetVoiceAttributes(px, tag_type); | ||||
| } | } | ||||
| return CLAUSE_PERIOD + voice_change_flag; | return CLAUSE_PERIOD + voice_change_flag; | ||||
| case SSML_PARAGRAPH + SSML_CLOSE: | case SSML_PARAGRAPH + SSML_CLOSE: | ||||
| if ((ssml_sp->tag_type == SSML_SENTENCE) || (ssml_sp->tag_type == SSML_PARAGRAPH)) { | if ((ssml_sp->tag_type == SSML_SENTENCE) || (ssml_sp->tag_type == SSML_PARAGRAPH)) { | ||||
| // End of a paragraph which specified a language. | // End of a paragraph which specified a language. | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static void RemoveChar(char *p) | static void RemoveChar(char *p) | ||||
| { | { | ||||
| // Replace a UTF-8 character by spaces | // Replace a UTF-8 character by spaces | ||||
| memset(p, ' ', utf8_in(&c, p)); | memset(p, ' ', utf8_in(&c, p)); | ||||
| } | } | ||||
| static MNEM_TAB xml_char_mnemonics[] = { | static MNEM_TAB xml_char_mnemonics[] = { | ||||
| { "gt", '>' }, | { "gt", '>' }, | ||||
| { "lt", 0xe000 + '<' }, // private usage area, to avoid confusion with XML tag | { "lt", 0xe000 + '<' }, // private usage area, to avoid confusion with XML tag | ||||
| { NULL, -1 } | { NULL, -1 } | ||||
| }; | }; | ||||
| int ReadClause(Translator *tr, FILE *f_in, char *buf, short *charix, int *charix_top, int n_buf, int *tone_type, char *voice_change) | int ReadClause(Translator *tr, FILE *f_in, char *buf, short *charix, int *charix_top, int n_buf, int *tone_type, char *voice_change) | ||||
| { | { | ||||
| /* Find the end of the current clause. | /* Find the end of the current clause. | ||||
| return CLAUSE_EOF; // end of file | return CLAUSE_EOF; // end of file | ||||
| } | } | ||||
| void InitNamedata(void) | void InitNamedata(void) | ||||
| { | { | ||||
| namedata_ix = 0; | namedata_ix = 0; | ||||
| } | } | ||||
| } | } | ||||
| void InitText2(void) | void InitText2(void) | ||||
| { | { | ||||
| int param; | int param; |
| extern void DoSonicSpeed(int value); | extern void DoSonicSpeed(int value); | ||||
| extern int saved_parameters[]; | extern int saved_parameters[]; | ||||
| // convert from words-per-minute to internal speed factor | // convert from words-per-minute to internal speed factor | ||||
| // Use this to calibrate speed for wpm 80-350 | // Use this to calibrate speed for wpm 80-350 | ||||
| static unsigned char speed_lookup[] = { | static unsigned char speed_lookup[] = { | ||||
| 9, 9, 8, 8, 8, // 355 | 9, 9, 8, 8, 8, // 355 | ||||
| }; | }; | ||||
| // speed_factor1 adjustments for speeds 350 to 374: pauses | // speed_factor1 adjustments for speeds 350 to 374: pauses | ||||
| static unsigned char pause_factor_350[] = { | static unsigned char pause_factor_350[] = { | ||||
| 22, 22, 22, 22, 22, 22, 22, 21, 21, 21, // 350 | 22, 22, 22, 22, 22, 22, 22, 21, 21, 21, // 350 | ||||
| static int speed2 = 121; | static int speed2 = 121; | ||||
| static int speed3 = 118; | static int speed3 = 118; | ||||
| #if HAVE_SONIC_H | #if HAVE_SONIC_H | ||||
| void SetSpeed(int control) | void SetSpeed(int control) | ||||
| embedded_value[EMBED_S2] = new_value; | embedded_value[EMBED_S2] = new_value; | ||||
| SetSpeed(3); | SetSpeed(3); | ||||
| break; | break; | ||||
| case espeakVOLUME: | case espeakVOLUME: | ||||
| embedded_value[EMBED_A] = new_value; | embedded_value[EMBED_A] = new_value; | ||||
| GetAmplitude(); | GetAmplitude(); | ||||
| break; | break; | ||||
| case espeakPITCH: | case espeakPITCH: | ||||
| if (new_value > 99) new_value = 99; | if (new_value > 99) new_value = 99; | ||||
| if (new_value < 0) new_value = 0; | if (new_value < 0) new_value = 0; | ||||
| embedded_value[EMBED_P] = new_value; | embedded_value[EMBED_P] = new_value; | ||||
| break; | break; | ||||
| case espeakRANGE: | case espeakRANGE: | ||||
| if (new_value > 99) new_value = 99; | if (new_value > 99) new_value = 99; | ||||
| embedded_value[EMBED_R] = new_value; | embedded_value[EMBED_R] = new_value; | ||||
| break; | break; | ||||
| case espeakLINELENGTH: | case espeakLINELENGTH: | ||||
| option_linelength = new_value; | option_linelength = new_value; | ||||
| break; | break; | ||||
| case espeakWORDGAP: | case espeakWORDGAP: | ||||
| option_wordgap = new_value; | option_wordgap = new_value; | ||||
| break; | break; | ||||
| case espeakINTONATION: | case espeakINTONATION: | ||||
| if ((new_value & 0xff) != 0) | if ((new_value & 0xff) != 0) | ||||
| translator->langopts.intonation_group = new_value & 0xff; | translator->langopts.intonation_group = new_value & 0xff; | ||||
| option_tone_flags = new_value; | option_tone_flags = new_value; | ||||
| break; | break; | ||||
| default: | default: | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| static void DoEmbedded2(int *embix) | static void DoEmbedded2(int *embix) | ||||
| { | { | ||||
| // There were embedded commands in the text at this point | // There were embedded commands in the text at this point | ||||
| } while ((word & 0x80) == 0); | } while ((word & 0x80) == 0); | ||||
| } | } | ||||
| void CalcLengths(Translator *tr) | void CalcLengths(Translator *tr) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| case phPAUSE: | case phPAUSE: | ||||
| last_pitch = 0; | last_pitch = 0; | ||||
| break; | break; | ||||
| case phSTOP: | case phSTOP: | ||||
| last_pitch = 0; | last_pitch = 0; | ||||
| if (prev->type == phFRICATIVE) | if (prev->type == phFRICATIVE) | ||||
| if (p->synthflags & SFLAG_LENGTHEN) | if (p->synthflags & SFLAG_LENGTHEN) | ||||
| p->prepause += tr->langopts.long_stop; | p->prepause += tr->langopts.long_stop; | ||||
| break; | break; | ||||
| case phVFRICATIVE: | case phVFRICATIVE: | ||||
| case phFRICATIVE: | case phFRICATIVE: | ||||
| if (p->newword) { | if (p->newword) { | ||||
| p->length = (255 + prev->length)/2; | p->length = (255 + prev->length)/2; | ||||
| } | } | ||||
| break; | break; | ||||
| case phVSTOP: | case phVSTOP: | ||||
| if (prev->type == phVFRICATIVE || prev->type == phFRICATIVE || (prev->ph->phflags & phSIBILANT) || (prev->type == phLIQUID)) | if (prev->type == phVFRICATIVE || prev->type == phFRICATIVE || (prev->ph->phflags & phSIBILANT) || (prev->type == phLIQUID)) | ||||
| p->prepause = 30; | p->prepause = 30; | ||||
| } | } | ||||
| if ((tr->langopts.word_gap & 0x10) && (p->newword) && (p->prepause < 20)) | if ((tr->langopts.word_gap & 0x10) && (p->newword) && (p->prepause < 20)) | ||||
| p->prepause = 20; | p->prepause = 20; | ||||
| break; | break; | ||||
| case phLIQUID: | case phLIQUID: | ||||
| case phNASAL: | case phNASAL: | ||||
| p->amp = tr->stress_amps[0]; // unless changed later | p->amp = tr->stress_amps[0]; // unless changed later | ||||
| pre_voiced = 0; | pre_voiced = 0; | ||||
| } | } | ||||
| break; | break; | ||||
| case phVOWEL: | case phVOWEL: | ||||
| min_drop = 0; | min_drop = 0; | ||||
| next2 = &phoneme_list[ix+2]; | next2 = &phoneme_list[ix+2]; | ||||
| } | } | ||||
| } | } | ||||
| if ((end_of_clause == 2) && !(tr->langopts.stress_flags & S_NO_EOC_LENGTHEN)) { | if ((end_of_clause == 2) && !(tr->langopts.stress_flags & S_NO_EOC_LENGTHEN)) { | ||||
| // this is the last syllable in the clause, lengthen it - more for short vowels | // this is the last syllable in the clause, lengthen it - more for short vowels | ||||
| len = (p->ph->std_length * 2); | len = (p->ph->std_length * 2); |
| char path_home[N_PATH_HOME]; // this is the espeak-data directory | char path_home[N_PATH_HOME]; // this is the espeak-data directory | ||||
| extern int saved_parameters[N_SPEECH_PARAM]; // Parameters saved on synthesis start | extern int saved_parameters[N_SPEECH_PARAM]; // Parameters saved on synthesis start | ||||
| void WVoiceChanged(voice_t *wvoice) | void WVoiceChanged(voice_t *wvoice) | ||||
| { | { | ||||
| // Voice change in wavegen | // Voice change in wavegen | ||||
| voice_samplerate = wvoice->samplerate; | voice_samplerate = wvoice->samplerate; | ||||
| } | } | ||||
| #ifdef USE_ASYNC | #ifdef USE_ASYNC | ||||
| static int dispatch_audio(short *outbuf, int length, espeak_EVENT *event) | static int dispatch_audio(short *outbuf, int length, espeak_EVENT *event) | ||||
| a_wave_can_be_played = fifo_is_command_enabled(); | a_wave_can_be_played = fifo_is_command_enabled(); | ||||
| } | } | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case AUDIO_OUTPUT_RETRIEVAL: | case AUDIO_OUTPUT_RETRIEVAL: | ||||
| if (synth_callback) | if (synth_callback) | ||||
| synth_callback(outbuf, length, event); | synth_callback(outbuf, length, event); | ||||
| break; | break; | ||||
| case AUDIO_OUTPUT_SYNCHRONOUS: | case AUDIO_OUTPUT_SYNCHRONOUS: | ||||
| case AUDIO_OUTPUT_SYNCH_PLAYBACK: | case AUDIO_OUTPUT_SYNCH_PLAYBACK: | ||||
| break; | break; | ||||
| return a_wave_can_be_played == 0; // 1 = stop synthesis, -1 = error | return a_wave_can_be_played == 0; // 1 = stop synthesis, -1 = error | ||||
| } | } | ||||
| static int create_events(short *outbuf, int length, espeak_EVENT *event, uint32_t the_write_pos) | static int create_events(short *outbuf, int length, espeak_EVENT *event, uint32_t the_write_pos) | ||||
| { | { | ||||
| int finished; | int finished; | ||||
| return finished; | return finished; | ||||
| } | } | ||||
| int sync_espeak_terminated_msg(uint32_t unique_identifier, void *user_data) | int sync_espeak_terminated_msg(uint32_t unique_identifier, void *user_data) | ||||
| { | { | ||||
| ENTER("sync_espeak_terminated_msg"); | ENTER("sync_espeak_terminated_msg"); | ||||
| #endif | #endif | ||||
| static void select_output(espeak_AUDIO_OUTPUT output_type) | static void select_output(espeak_AUDIO_OUTPUT output_type) | ||||
| { | { | ||||
| my_mode = output_type; | my_mode = output_type; | ||||
| // wave_init() is now called just before the first wave_write() | // wave_init() is now called just before the first wave_write() | ||||
| synchronous_mode = 0; | synchronous_mode = 0; | ||||
| break; | break; | ||||
| case AUDIO_OUTPUT_RETRIEVAL: | case AUDIO_OUTPUT_RETRIEVAL: | ||||
| synchronous_mode = 0; | synchronous_mode = 0; | ||||
| break; | break; | ||||
| case AUDIO_OUTPUT_SYNCHRONOUS: | case AUDIO_OUTPUT_SYNCHRONOUS: | ||||
| break; | break; | ||||
| case AUDIO_OUTPUT_SYNCH_PLAYBACK: | case AUDIO_OUTPUT_SYNCH_PLAYBACK: | ||||
| option_waveout = 0; | option_waveout = 0; | ||||
| WavegenInitSound(); | WavegenInitSound(); | ||||
| } | } | ||||
| } | } | ||||
| #pragma GCC visibility push(default) | #pragma GCC visibility push(default) | ||||
| int GetFileLength(const char *filename) | int GetFileLength(const char *filename) | ||||
| { | { | ||||
| } | } | ||||
| #pragma GCC visibility pop | #pragma GCC visibility pop | ||||
| char *Alloc(int size) | char *Alloc(int size) | ||||
| { | { | ||||
| char *p; | char *p; | ||||
| free(ptr); | free(ptr); | ||||
| } | } | ||||
| static void init_path(const char *path) | static void init_path(const char *path) | ||||
| { | { | ||||
| #ifdef PLATFORM_WINDOWS | #ifdef PLATFORM_WINDOWS | ||||
| RegQueryValueExA(RegKey, "path", 0, &var_type, buf, &size); | RegQueryValueExA(RegKey, "path", 0, &var_type, buf, &size); | ||||
| sprintf(path_home, "%s\\espeak-data", buf); | sprintf(path_home, "%s\\espeak-data", buf); | ||||
| #else | #else | ||||
| char *env; | char *env; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static espeak_ERROR Synthesize(unsigned int unique_identifier, const void *text, int flags) | static espeak_ERROR Synthesize(unsigned int unique_identifier, const void *text, int flags) | ||||
| { | { | ||||
| // Fill the buffer with output sound | // Fill the buffer with output sound | ||||
| }; | }; | ||||
| #endif | #endif | ||||
| void MarkerEvent(int type, unsigned int char_position, int value, int value2, unsigned char *out_ptr) | void MarkerEvent(int type, unsigned int char_position, int value, int value2, unsigned char *out_ptr) | ||||
| { | { | ||||
| // type: 1=word, 2=sentence, 3=named mark, 4=play audio, 5=end, 7=phoneme | // type: 1=word, 2=sentence, 3=named mark, 4=play audio, 5=end, 7=phoneme | ||||
| ep->id.number = value; | ep->id.number = value; | ||||
| } | } | ||||
| espeak_ERROR sync_espeak_Synth(unsigned int unique_identifier, const void *text, size_t size, | espeak_ERROR sync_espeak_Synth(unsigned int unique_identifier, const void *text, size_t size, | ||||
| unsigned int position, espeak_POSITION_TYPE position_type, | unsigned int position, espeak_POSITION_TYPE position_type, | ||||
| unsigned int end_position, unsigned int flags, void *user_data) | unsigned int end_position, unsigned int flags, void *user_data) | ||||
| { | { | ||||
| #ifdef DEBUG_ENABLED | #ifdef DEBUG_ENABLED | ||||
| ENTER("sync_espeak_Synth"); | ENTER("sync_espeak_Synth"); | ||||
| SHOW("sync_espeak_Synth > position=%d, position_type=%d, end_position=%d, flags=%d, user_data=0x%x, text=%s\n", position, position_type, end_position, flags, user_data, text); | SHOW("sync_espeak_Synth > position=%d, position_type=%d, end_position=%d, flags=%d, user_data=0x%x, text=%s\n", position, position_type, end_position, flags, user_data, text); | ||||
| case POS_CHARACTER: | case POS_CHARACTER: | ||||
| skip_characters = position; | skip_characters = position; | ||||
| break; | break; | ||||
| case POS_WORD: | case POS_WORD: | ||||
| skip_words = position; | skip_words = position; | ||||
| break; | break; | ||||
| case POS_SENTENCE: | case POS_SENTENCE: | ||||
| skip_sentences = position; | skip_sentences = position; | ||||
| break; | break; | ||||
| return aStatus; | return aStatus; | ||||
| } | } | ||||
| espeak_ERROR sync_espeak_Synth_Mark(unsigned int unique_identifier, const void *text, size_t size, | espeak_ERROR sync_espeak_Synth_Mark(unsigned int unique_identifier, const void *text, size_t size, | ||||
| const char *index_mark, unsigned int end_position, | const char *index_mark, unsigned int end_position, | ||||
| unsigned int flags, void *user_data) | unsigned int flags, void *user_data) | ||||
| end_character_position = end_position; | end_character_position = end_position; | ||||
| aStatus = Synthesize(unique_identifier, text, flags | espeakSSML); | aStatus = Synthesize(unique_identifier, text, flags | espeakSSML); | ||||
| SHOW_TIME("LEAVE sync_espeak_Synth_Mark"); | SHOW_TIME("LEAVE sync_espeak_Synth_Mark"); | ||||
| return aStatus; | return aStatus; | ||||
| } | } | ||||
| void sync_espeak_Key(const char *key) | void sync_espeak_Key(const char *key) | ||||
| { | { | ||||
| // symbolic name, symbolicname_character - is there a system resource of symbolic names per language? | // symbolic name, symbolicname_character - is there a system resource of symbolic names per language? | ||||
| Synthesize(0, key, 0); // speak key as a text string | Synthesize(0, key, 0); // speak key as a text string | ||||
| } | } | ||||
| void sync_espeak_Char(wchar_t character) | void sync_espeak_Char(wchar_t character) | ||||
| { | { | ||||
| // is there a system resource of character names per language? | // is there a system resource of character names per language? | ||||
| Synthesize(0, buf, espeakSSML); | Synthesize(0, buf, espeakSSML); | ||||
| } | } | ||||
| void sync_espeak_SetPunctuationList(const wchar_t *punctlist) | void sync_espeak_SetPunctuationList(const wchar_t *punctlist) | ||||
| { | { | ||||
| // Set the list of punctuation which are spoken for "some". | // Set the list of punctuation which are spoken for "some". | ||||
| } | } | ||||
| } | } | ||||
| #pragma GCC visibility push(default) | #pragma GCC visibility push(default) | ||||
| ESPEAK_API void espeak_SetSynthCallback(t_espeak_callback *SynthCallback) | ESPEAK_API void espeak_SetSynthCallback(t_espeak_callback *SynthCallback) | ||||
| { | { | ||||
| ENTER("espeak_SetSynthCallback"); | ENTER("espeak_SetSynthCallback"); | ||||
| uri_callback = UriCallback; | uri_callback = UriCallback; | ||||
| } | } | ||||
| ESPEAK_API void espeak_SetPhonemeCallback(int (*PhonemeCallback)(const char *)) | ESPEAK_API void espeak_SetPhonemeCallback(int (*PhonemeCallback)(const char *)) | ||||
| { | { | ||||
| phoneme_callback = PhonemeCallback; | phoneme_callback = PhonemeCallback; | ||||
| return samplerate; | return samplerate; | ||||
| } | } | ||||
| ESPEAK_API espeak_ERROR espeak_Synth(const void *text, size_t size, | ESPEAK_API espeak_ERROR espeak_Synth(const void *text, size_t size, | ||||
| unsigned int position, | unsigned int position, | ||||
| espeak_POSITION_TYPE position_type, | espeak_POSITION_TYPE position_type, | ||||
| delete_espeak_command(c1); | delete_espeak_command(c1); | ||||
| delete_espeak_command(c2); | delete_espeak_command(c2); | ||||
| } | } | ||||
| #endif | #endif | ||||
| return a_error; | return a_error; | ||||
| } | } | ||||
| ESPEAK_API espeak_ERROR espeak_Synth_Mark(const void *text, size_t size, | ESPEAK_API espeak_ERROR espeak_Synth_Mark(const void *text, size_t size, | ||||
| const char *index_mark, | const char *index_mark, | ||||
| unsigned int end_position, | unsigned int end_position, | ||||
| if (f_logespeak) | if (f_logespeak) | ||||
| fprintf(f_logespeak, "\nSYNTH MARK %s posn %d flags 0x%x\n%s\n", index_mark, end_position, flags, (const char *)text); | fprintf(f_logespeak, "\nSYNTH MARK %s posn %d flags 0x%x\n%s\n", index_mark, end_position, flags, (const char *)text); | ||||
| if (unique_identifier == NULL) | if (unique_identifier == NULL) | ||||
| unique_identifier = &temp_identifier; | unique_identifier = &temp_identifier; | ||||
| *unique_identifier = 0; | *unique_identifier = 0; | ||||
| delete_espeak_command(c1); | delete_espeak_command(c1); | ||||
| delete_espeak_command(c2); | delete_espeak_command(c2); | ||||
| } | } | ||||
| #endif | #endif | ||||
| return a_error; | return a_error; | ||||
| } | } | ||||
| ESPEAK_API espeak_ERROR espeak_Key(const char *key) | ESPEAK_API espeak_ERROR espeak_Key(const char *key) | ||||
| { | { | ||||
| ENTER("espeak_Key"); | ENTER("espeak_Key"); | ||||
| a_error = fifo_add_command(c); | a_error = fifo_add_command(c); | ||||
| if (a_error != EE_OK) | if (a_error != EE_OK) | ||||
| delete_espeak_command(c); | delete_espeak_command(c); | ||||
| #endif | #endif | ||||
| return a_error; | return a_error; | ||||
| } | } | ||||
| ESPEAK_API espeak_ERROR espeak_Char(wchar_t character) | ESPEAK_API espeak_ERROR espeak_Char(wchar_t character) | ||||
| { | { | ||||
| ENTER("espeak_Char"); | ENTER("espeak_Char"); | ||||
| #endif | #endif | ||||
| } | } | ||||
| ESPEAK_API espeak_ERROR espeak_SetVoiceByName(const char *name) | ESPEAK_API espeak_ERROR espeak_SetVoiceByName(const char *name) | ||||
| { | { | ||||
| ENTER("espeak_SetVoiceByName"); | ENTER("espeak_SetVoiceByName"); | ||||
| return SetVoiceByName(name); | return SetVoiceByName(name); | ||||
| } | } | ||||
| ESPEAK_API espeak_ERROR espeak_SetVoiceByProperties(espeak_VOICE *voice_selector) | ESPEAK_API espeak_ERROR espeak_SetVoiceByProperties(espeak_VOICE *voice_selector) | ||||
| { | { | ||||
| ENTER("espeak_SetVoiceByProperties"); | ENTER("espeak_SetVoiceByProperties"); | ||||
| return SetVoiceByProperties(voice_selector); | return SetVoiceByProperties(voice_selector); | ||||
| } | } | ||||
| ESPEAK_API int espeak_GetParameter(espeak_PARAMETER parameter, int current) | ESPEAK_API int espeak_GetParameter(espeak_PARAMETER parameter, int current) | ||||
| { | { | ||||
| ENTER("espeak_GetParameter"); | ENTER("espeak_GetParameter"); | ||||
| return param_defaults[parameter]; | return param_defaults[parameter]; | ||||
| } | } | ||||
| ESPEAK_API espeak_ERROR espeak_SetParameter(espeak_PARAMETER parameter, int value, int relative) | ESPEAK_API espeak_ERROR espeak_SetParameter(espeak_PARAMETER parameter, int value, int relative) | ||||
| { | { | ||||
| ENTER("espeak_SetParameter"); | ENTER("espeak_SetParameter"); | ||||
| #endif | #endif | ||||
| } | } | ||||
| ESPEAK_API espeak_ERROR espeak_SetPunctuationList(const wchar_t *punctlist) | ESPEAK_API espeak_ERROR espeak_SetPunctuationList(const wchar_t *punctlist) | ||||
| { | { | ||||
| ENTER("espeak_SetPunctuationList"); | ENTER("espeak_SetPunctuationList"); | ||||
| #endif | #endif | ||||
| } | } | ||||
| ESPEAK_API void espeak_SetPhonemeTrace(int phonememode, FILE *stream) | ESPEAK_API void espeak_SetPhonemeTrace(int phonememode, FILE *stream) | ||||
| { | { | ||||
| ENTER("espeak_SetPhonemes"); | ENTER("espeak_SetPhonemes"); | ||||
| f_trans = stream; | f_trans = stream; | ||||
| if (stream == NULL) | if (stream == NULL) | ||||
| f_trans = stderr; | f_trans = stderr; | ||||
| } | } | ||||
| ESPEAK_API const char *espeak_TextToPhonemes(const void **textptr, int textmode, int phonememode) | ESPEAK_API const char *espeak_TextToPhonemes(const void **textptr, int textmode, int phonememode) | ||||
| { | { | ||||
| /* phoneme_mode | /* phoneme_mode | ||||
| return GetTranslatedPhonemeString(phonememode); | return GetTranslatedPhonemeString(phonememode); | ||||
| } | } | ||||
| ESPEAK_API void espeak_CompileDictionary(const char *path, FILE *log, int flags) | ESPEAK_API void espeak_CompileDictionary(const char *path, FILE *log, int flags) | ||||
| { | { | ||||
| ENTER("espeak_CompileDictionary"); | ENTER("espeak_CompileDictionary"); | ||||
| CompileDictionary(path, dictionary_name, log, NULL, flags); | CompileDictionary(path, dictionary_name, log, NULL, flags); | ||||
| } | } | ||||
| ESPEAK_API espeak_ERROR espeak_Cancel(void) | ESPEAK_API espeak_ERROR espeak_Cancel(void) | ||||
| { | { | ||||
| #ifdef USE_ASYNC | #ifdef USE_ASYNC | ||||
| return EE_OK; | return EE_OK; | ||||
| } | } | ||||
| ESPEAK_API int espeak_IsPlaying(void) | ESPEAK_API int espeak_IsPlaying(void) | ||||
| { | { | ||||
| #ifdef USE_ASYNC | #ifdef USE_ASYNC | ||||
| #endif | #endif | ||||
| } | } | ||||
| ESPEAK_API espeak_ERROR espeak_Synchronize(void) | ESPEAK_API espeak_ERROR espeak_Synchronize(void) | ||||
| { | { | ||||
| espeak_ERROR berr = err; | espeak_ERROR berr = err; | ||||
| return berr; | return berr; | ||||
| } | } | ||||
| extern void FreePhData(void); | extern void FreePhData(void); | ||||
| extern void FreeVoiceList(void); | extern void FreeVoiceList(void); | ||||
| wave_terminate(); | wave_terminate(); | ||||
| out_samplerate = 0; | out_samplerate = 0; | ||||
| } | } | ||||
| #endif | #endif | ||||
| Free(event_list); | Free(event_list); | ||||
| event_list = NULL; | event_list = NULL; |
| return y; | return y; | ||||
| } | } | ||||
| static SpectFrame *SpectFrameCreate() | static SpectFrame *SpectFrameCreate() | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| free(frame); | free(frame); | ||||
| } | } | ||||
| int LoadFrame(SpectFrame *frame, FILE *stream, int file_format_type) | int LoadFrame(SpectFrame *frame, FILE *stream, int file_format_type) | ||||
| { | { | ||||
| short ix; | short ix; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| double GetFrameRms(SpectFrame *frame, int seq_amplitude) | double GetFrameRms(SpectFrame *frame, int seq_amplitude) | ||||
| { | { | ||||
| int h; | int h; | ||||
| return frame->rms; | return frame->rms; | ||||
| } | } | ||||
| SpectSeq *SpectSeqCreate() | SpectSeq *SpectSeqCreate() | ||||
| { | { | ||||
| SpectSeq *spect = malloc(sizeof(SpectSeq)); | SpectSeq *spect = malloc(sizeof(SpectSeq)); | ||||
| free(spect); | free(spect); | ||||
| } | } | ||||
| static float GetFrameLength(SpectSeq *spect, int frame) | static float GetFrameLength(SpectSeq *spect, int frame) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| return (spect->frames[ix]->time - spect->frames[frame]->time) * 1000.0 + adjust; | return (spect->frames[ix]->time - spect->frames[frame]->time) * 1000.0 + adjust; | ||||
| } | } | ||||
| int LoadSpectSeq(SpectSeq *spect, const char *filename) | int LoadSpectSeq(SpectSeq *spect, const char *filename) | ||||
| { | { | ||||
| short n, temp; | short n, temp; | ||||
| frame_width = (int)((FRAME_WIDTH*spect->max_x)/MAX_DISPLAY_FREQ); | frame_width = (int)((FRAME_WIDTH*spect->max_x)/MAX_DISPLAY_FREQ); | ||||
| if (frame_width > FRAME_WIDTH) frame_width = FRAME_WIDTH; | if (frame_width > FRAME_WIDTH) frame_width = FRAME_WIDTH; | ||||
| // start times from zero | // start times from zero | ||||
| time_offset = spect->frames[0]->time; | time_offset = spect->frames[0]->time; | ||||
| for (ix = 0; ix < spect->numframes; ix++) | for (ix = 0; ix < spect->numframes; ix++) |
| #include "translate.h" | #include "translate.h" | ||||
| #include "voice.h" | #include "voice.h" | ||||
| #ifdef INCLUDE_MBROLA | #ifdef INCLUDE_MBROLA | ||||
| extern int Read4Bytes(FILE *f); | extern int Read4Bytes(FILE *f); | ||||
| PROCIV getFreq_MBR; | PROCIV getFreq_MBR; | ||||
| PROCVF setVolumeRatio_MBR; | PROCVF setVolumeRatio_MBR; | ||||
| HINSTANCE hinstDllMBR = NULL; | HINSTANCE hinstDllMBR = NULL; | ||||
| BOOL load_MBR() | BOOL load_MBR() | ||||
| { | { | ||||
| if (hinstDllMBR != NULL) | if (hinstDllMBR != NULL) | ||||
| return TRUE; | return TRUE; | ||||
| } | } | ||||
| void unload_MBR() | void unload_MBR() | ||||
| { | { | ||||
| if (hinstDllMBR) { | if (hinstDllMBR) { | ||||
| #endif // windows | #endif // windows | ||||
| static MBROLA_TAB *mbrola_tab = NULL; | static MBROLA_TAB *mbrola_tab = NULL; | ||||
| static int mbrola_control = 0; | static int mbrola_control = 0; | ||||
| static int mbr_name_prefix = 0; | static int mbr_name_prefix = 0; | ||||
| return EE_OK; | return EE_OK; | ||||
| } | } | ||||
| static int GetMbrName(PHONEME_LIST *plist, PHONEME_TAB *ph, PHONEME_TAB *ph_prev, PHONEME_TAB *ph_next, int *name2, int *split, int *control) | static int GetMbrName(PHONEME_LIST *plist, PHONEME_TAB *ph, PHONEME_TAB *ph_prev, PHONEME_TAB *ph_next, int *name2, int *split, int *control) | ||||
| { | { | ||||
| // Look up a phoneme in the mbrola phoneme name translation table | // Look up a phoneme in the mbrola phoneme name translation table | ||||
| return mnem; | return mnem; | ||||
| } | } | ||||
| static char *WritePitch(int env, int pitch1, int pitch2, int split, int final) | static char *WritePitch(int env, int pitch1, int pitch2, int split, int final) | ||||
| { | { | ||||
| // final=1: only give the final pitch value. | // final=1: only give the final pitch value. | ||||
| output[0] = 0; | output[0] = 0; | ||||
| pitch_env = envelope_data[env]; | pitch_env = envelope_data[env]; | ||||
| SetPitch2(voice, pitch1, pitch2, &pitch_base, &pitch_range); | SetPitch2(voice, pitch1, pitch2, &pitch_base, &pitch_range); | ||||
| env_split = (split * 128)/100; | env_split = (split * 128)/100; | ||||
| if (env_split < 0) | if (env_split < 0) | ||||
| env_split = 0-env_split; | env_split = 0-env_split; | ||||
| p1 = ((pitch_env[0]*pitch_range)>>8) + pitch_base; // Hz << 12 | p1 = ((pitch_env[0]*pitch_range)>>8) + pitch_base; // Hz << 12 | ||||
| p_end = ((pitch_env[127]*pitch_range)>>8) + pitch_base; | p_end = ((pitch_env[127]*pitch_range)>>8) + pitch_base; | ||||
| if (split >= 0) { | if (split >= 0) { | ||||
| sprintf(buf, " 0 %d", p1/4096); | sprintf(buf, " 0 %d", p1/4096); | ||||
| strcat(output, buf); | strcat(output, buf); | ||||
| return output; | return output; | ||||
| } | } | ||||
| int MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, int resume, FILE *f_mbrola) | int MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, int resume, FILE *f_mbrola) | ||||
| { | { | ||||
| // Generate a mbrola pho file | // Generate a mbrola pho file | ||||
| } | } | ||||
| done = 1; | done = 1; | ||||
| break; | break; | ||||
| case phSTOP: | case phSTOP: | ||||
| released = 0; | released = 0; | ||||
| if (next->type == phVOWEL) released = 1; | if (next->type == phVOWEL) released = 1; | ||||
| len = (len * 1000)/samplerate; // convert to mS | len = (len * 1000)/samplerate; // convert to mS | ||||
| len += PauseLength(p->prepause, 1); | len += PauseLength(p->prepause, 1); | ||||
| break; | break; | ||||
| case phVSTOP: | case phVSTOP: | ||||
| len = (80 * speed.wav_factor)/256; | len = (80 * speed.wav_factor)/256; | ||||
| break; | break; | ||||
| case phFRICATIVE: | case phFRICATIVE: | ||||
| len = 0; | len = 0; | ||||
| InterpretPhoneme(NULL, 0, p, &phdata, NULL); | InterpretPhoneme(NULL, 0, p, &phdata, NULL); | ||||
| len = (len * 1000)/samplerate; // convert to mS | len = (len * 1000)/samplerate; // convert to mS | ||||
| break; | break; | ||||
| case phNASAL: | case phNASAL: | ||||
| if (next->type != phVOWEL) { | if (next->type != phVOWEL) { | ||||
| memset(&fmtp, 0, sizeof(fmtp)); | memset(&fmtp, 0, sizeof(fmtp)); | ||||
| final_pitch = WritePitch(p->env, p->pitch1, p->pitch2, 0, 1); | final_pitch = WritePitch(p->env, p->pitch1, p->pitch2, 0, 1); | ||||
| } | } | ||||
| break; | break; | ||||
| case phLIQUID: | case phLIQUID: | ||||
| if (next->type == phPAUSE) { | if (next->type == phPAUSE) { | ||||
| len += 50; | len += 50; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| int MbrolaGenerate(PHONEME_LIST *phoneme_list, int *n_ph, int resume) | int MbrolaGenerate(PHONEME_LIST *phoneme_list, int *n_ph, int resume) | ||||
| { | { | ||||
| FILE *f_mbrola = NULL; | FILE *f_mbrola = NULL; | ||||
| return again; | return again; | ||||
| } | } | ||||
| int MbrolaFill(int length, int resume, int amplitude) | int MbrolaFill(int length, int resume, int amplitude) | ||||
| { | { | ||||
| // Read audio data from Mbrola (length is in millisecs) | // Read audio data from Mbrola (length is in millisecs) | ||||
| return n_samples ? 1 : 0; | return n_samples ? 1 : 0; | ||||
| } | } | ||||
| void MbrolaReset(void) | void MbrolaReset(void) | ||||
| { | { | ||||
| // Reset the Mbrola engine and flush the pending audio | // Reset the Mbrola engine and flush the pending audio | ||||
| { | { | ||||
| } | } | ||||
| #endif // INCLUDE_MBROLA | #endif // INCLUDE_MBROLA |
| #include <stdint.h> | #include <stdint.h> | ||||
| #endif | #endif | ||||
| #include "speak_lib.h" | #include "speak_lib.h" | ||||
| #include "speech.h" | #include "speech.h" | ||||
| #include "phoneme.h" | #include "phoneme.h" | ||||
| int FormantTransition2(frameref_t *seq, int *n_frames, unsigned int data1, unsigned int data2, PHONEME_TAB *other_ph, int which); | int FormantTransition2(frameref_t *seq, int *n_frames, unsigned int data1, unsigned int data2, PHONEME_TAB *other_ph, int which); | ||||
| static char *ReadPhFile(void *ptr, const char *fname, int *size) | static char *ReadPhFile(void *ptr, const char *fname, int *size) | ||||
| { | { | ||||
| FILE *f_in; | FILE *f_in; | ||||
| return p; | return p; | ||||
| } | } | ||||
| int LoadPhData(int *srate) | int LoadPhData(int *srate) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| return result; | return result; | ||||
| } | } | ||||
| void FreePhData(void) | void FreePhData(void) | ||||
| { | { | ||||
| Free(phoneme_tab_data); | Free(phoneme_tab_data); | ||||
| tunes = NULL; | tunes = NULL; | ||||
| } | } | ||||
| int PhonemeCode(unsigned int mnem) | int PhonemeCode(unsigned int mnem) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| int LookupPhonemeString(const char *string) | int LookupPhonemeString(const char *string) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| return PhonemeCode(mnem); | return PhonemeCode(mnem); | ||||
| } | } | ||||
| frameref_t *LookupSpect(PHONEME_TAB *this_ph, int which, FMT_PARAMS *fmt_params, int *n_frames, PHONEME_LIST *plist) | frameref_t *LookupSpect(PHONEME_TAB *this_ph, int which, FMT_PARAMS *fmt_params, int *n_frames, PHONEME_LIST *plist) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| seqk = (SPECT_SEQK *)seq; | seqk = (SPECT_SEQK *)seq; | ||||
| nf = seq->n_frames; | nf = seq->n_frames; | ||||
| if (nf >= N_SEQ_FRAMES) | if (nf >= N_SEQ_FRAMES) | ||||
| nf = N_SEQ_FRAMES - 1; | nf = N_SEQ_FRAMES - 1; | ||||
| return frames; | return frames; | ||||
| } | } | ||||
| unsigned char *GetEnvelope(int index) | unsigned char *GetEnvelope(int index) | ||||
| { | { | ||||
| if (index == 0) { | if (index == 0) { | ||||
| return (unsigned char *)&phondata_ptr[index]; | return (unsigned char *)&phondata_ptr[index]; | ||||
| } | } | ||||
| static void SetUpPhonemeTable(int number, int recursing) | static void SetUpPhonemeTable(int number, int recursing) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| } | } | ||||
| } | } | ||||
| void SelectPhonemeTable(int number) | void SelectPhonemeTable(int number) | ||||
| { | { | ||||
| n_phoneme_tab = 0; | n_phoneme_tab = 0; | ||||
| current_phoneme_table = number; | current_phoneme_table = number; | ||||
| } | } | ||||
| int LookupPhonemeTable(const char *name) | int LookupPhonemeTable(const char *name) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| return ix; | return ix; | ||||
| } | } | ||||
| int SelectPhonemeTableName(const char *name) | int SelectPhonemeTableName(const char *name) | ||||
| { | { | ||||
| // Look up a phoneme set by name, and select it if it exists | // Look up a phoneme set by name, and select it if it exists | ||||
| return ix; | return ix; | ||||
| } | } | ||||
| void LoadConfig(void) | void LoadConfig(void) | ||||
| { | { | ||||
| // Load configuration file, if one exists | // Load configuration file, if one exists | ||||
| fclose(f); | fclose(f); | ||||
| } | } | ||||
| PHONEME_DATA this_ph_data; | PHONEME_DATA this_ph_data; | ||||
| static void InvalidInstn(PHONEME_TAB *ph, int instn) | static void InvalidInstn(PHONEME_TAB *ph, int instn) | ||||
| { | { | ||||
| fprintf(stderr, "Invalid instruction %.4x for phoneme '%s'\n", instn, WordToString(ph->mnemonic)); | fprintf(stderr, "Invalid instruction %.4x for phoneme '%s'\n", instn, WordToString(ph->mnemonic)); | ||||
| } | } | ||||
| static bool StressCondition(Translator *tr, PHONEME_LIST *plist, int condition, int control) | static bool StressCondition(Translator *tr, PHONEME_LIST *plist, int condition, int control) | ||||
| { | { | ||||
| // condition: | // condition: | ||||
| } | } | ||||
| static int CountVowelPosition(PHONEME_LIST *plist) | static int CountVowelPosition(PHONEME_LIST *plist) | ||||
| { | { | ||||
| int count = 0; | int count = 0; | ||||
| return count; | return count; | ||||
| } | } | ||||
| static bool InterpretCondition(Translator *tr, int control, PHONEME_LIST *plist, USHORT *p_prog, WORD_PH_DATA *worddata) | static bool InterpretCondition(Translator *tr, int control, PHONEME_LIST *plist, USHORT *p_prog, WORD_PH_DATA *worddata) | ||||
| { | { | ||||
| int which; | int which; | ||||
| return false; | return false; | ||||
| } | } | ||||
| switch (which) | switch (which) | ||||
| { | { | ||||
| case 0: // prevPh | case 0: // prevPh | ||||
| plist--; | plist--; | ||||
| check_endtype = 1; | check_endtype = 1; | ||||
| break; | break; | ||||
| case 1: // thisPh | case 1: // thisPh | ||||
| break; | break; | ||||
| case 2: // nextPh | case 2: // nextPh | ||||
| case 4: // nextPhW | case 4: // nextPhW | ||||
| plist++; | plist++; | ||||
| break; | break; | ||||
| case 3: // next2Ph | case 3: // next2Ph | ||||
| case 6: // next2PhW | case 6: // next2PhW | ||||
| plist += 2; | plist += 2; | ||||
| break; | break; | ||||
| case 7: | case 7: | ||||
| // nextVowel, not word boundary | // nextVowel, not word boundary | ||||
| for (which = 1;; which++) { | for (which = 1;; which++) { | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| case 8: // prevVowel in this word | case 8: // prevVowel in this word | ||||
| if ((worddata == NULL) || (worddata->prev_vowel.ph == NULL)) | if ((worddata == NULL) || (worddata->prev_vowel.ph == NULL)) | ||||
| return false; // no previous vowel | return false; // no previous vowel | ||||
| plist = &(worddata->prev_vowel); | plist = &(worddata->prev_vowel); | ||||
| check_endtype = 1; | check_endtype = 1; | ||||
| break; | break; | ||||
| case 9: // next3PhW | case 9: // next3PhW | ||||
| for (ix = 1; ix <= 3; ix++) { | for (ix = 1; ix <= 3; ix++) { | ||||
| if (plist[ix].sourceix) | if (plist[ix].sourceix) | ||||
| } | } | ||||
| plist = &plist[3]; | plist = &plist[3]; | ||||
| break; | break; | ||||
| case 10: // prev2PhW | case 10: // prev2PhW | ||||
| if ((plist[0].sourceix) || (plist[-1].sourceix)) | if ((plist[0].sourceix) || (plist[-1].sourceix)) | ||||
| return false; | return false; | ||||
| // phoneme type, vowel, nasal, fricative, etc | // phoneme type, vowel, nasal, fricative, etc | ||||
| return ph->type == data; | return ph->type == data; | ||||
| break; | break; | ||||
| case 0x20: | case 0x20: | ||||
| // place of articulation | // place of articulation | ||||
| return ((ph->phflags >> 16) & 0xf) == data; | return ((ph->phflags >> 16) & 0xf) == data; | ||||
| break; | break; | ||||
| case 0x40: | case 0x40: | ||||
| // is a bit set in phoneme flags | // is a bit set in phoneme flags | ||||
| return (ph->phflags & (1 << data)) != 0; | return (ph->phflags & (1 << data)) != 0; | ||||
| break; | break; | ||||
| case 0x80: | case 0x80: | ||||
| switch (data) | switch (data) | ||||
| { | { | ||||
| case 3: | case 3: | ||||
| case 4: | case 4: | ||||
| return StressCondition(tr, plist, data, 0); | return StressCondition(tr, plist, data, 0); | ||||
| case 5: // isBreak, Either pause phoneme, or (stop/vstop/vfric not followed by vowel or (liquid in same word)) | case 5: // isBreak, Either pause phoneme, or (stop/vstop/vfric not followed by vowel or (liquid in same word)) | ||||
| return (ph->type == phPAUSE) || (plist_this->synthflags & SFLAG_NEXT_PAUSE); | return (ph->type == phPAUSE) || (plist_this->synthflags & SFLAG_NEXT_PAUSE); | ||||
| case 6: // isWordStart | case 6: // isWordStart | ||||
| return plist->sourceix != 0; | return plist->sourceix != 0; | ||||
| case 7: // notWordStart | case 7: // notWordStart | ||||
| return plist->sourceix == 0; | return plist->sourceix == 0; | ||||
| case 8: // isWordEnd | case 8: // isWordEnd | ||||
| return plist[1].sourceix || (plist[1].ph->type == phPAUSE); | return plist[1].sourceix || (plist[1].ph->type == phPAUSE); | ||||
| break; | break; | ||||
| case 9: // isAfterStress | case 9: // isAfterStress | ||||
| if (plist->sourceix != 0) | if (plist->sourceix != 0) | ||||
| return false; | return false; | ||||
| } while (plist->sourceix == 0); | } while (plist->sourceix == 0); | ||||
| break; | break; | ||||
| case 10: // isNotVowel | case 10: // isNotVowel | ||||
| return ph->type != phVOWEL; | return ph->type != phVOWEL; | ||||
| case 11: // isFinalVowel | case 11: // isFinalVowel | ||||
| for (;;) { | for (;;) { | ||||
| plist++; | plist++; | ||||
| return false; | return false; | ||||
| } | } | ||||
| break; | break; | ||||
| case 12: // isVoiced | case 12: // isVoiced | ||||
| return (ph->type == phVOWEL) || (ph->type == phLIQUID) || (ph->phflags & phVOICED); | return (ph->type == phVOWEL) || (ph->type == phLIQUID) || (ph->phflags & phVOICED); | ||||
| case 13: // isFirstVowel | case 13: // isFirstVowel | ||||
| return CountVowelPosition(plist) == 1; | return CountVowelPosition(plist) == 1; | ||||
| case 14: // isSecondVowel | case 14: // isSecondVowel | ||||
| return CountVowelPosition(plist) == 2; | return CountVowelPosition(plist) == 2; | ||||
| case 15: // isSeqFlag1 | case 15: // isSeqFlag1 | ||||
| // is this preceded by a sequence if 1 or more vowels which have 'flag1' ? (lang=hi) | // is this preceded by a sequence if 1 or more vowels which have 'flag1' ? (lang=hi) | ||||
| if (plist->sourceix != 0) | if (plist->sourceix != 0) | ||||
| break; | break; | ||||
| } | } | ||||
| return count > 0; | return count > 0; | ||||
| case 0x10: // isTranslationGiven | case 0x10: // isTranslationGiven | ||||
| return (plist->synthflags & SFLAG_DICTIONARY) != 0; | return (plist->synthflags & SFLAG_DICTIONARY) != 0; | ||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| static void SwitchOnVowelType(PHONEME_LIST *plist, PHONEME_DATA *phdata, USHORT **p_prog, int instn_type) | static void SwitchOnVowelType(PHONEME_LIST *plist, PHONEME_DATA *phdata, USHORT **p_prog, int instn_type) | ||||
| { | { | ||||
| USHORT *prog; | USHORT *prog; | ||||
| *p_prog += 12; | *p_prog += 12; | ||||
| } | } | ||||
| int NumInstnWords(USHORT *prog) | int NumInstnWords(USHORT *prog) | ||||
| { | { | ||||
| int instn; | int instn; | ||||
| return n+1; | return n+1; | ||||
| } | } | ||||
| return 1; | return 1; | ||||
| case 6: | case 6: | ||||
| type2 = (instn & 0xf00) >> 9; | type2 = (instn & 0xf00) >> 9; | ||||
| if ((type2 == 5) || (type2 == 6)) | if ((type2 == 5) || (type2 == 6)) | ||||
| return 12; // switch on vowel type | return 12; // switch on vowel type | ||||
| return 1; | return 1; | ||||
| case 2: | case 2: | ||||
| case 3: | case 3: | ||||
| // a condition, check for a 2-word instruction | // a condition, check for a 2-word instruction | ||||
| if (((n = instn & 0x0f00) == 0x600) || (n == 0x0d00)) | if (((n = instn & 0x0f00) == 0x600) || (n == 0x0d00)) | ||||
| return 2; | return 2; | ||||
| return 1; | return 1; | ||||
| default: | default: | ||||
| // instn_type 11 to 15, 2 words | // instn_type 11 to 15, 2 words | ||||
| instn2 = prog[2]; | instn2 = prog[2]; | ||||
| } | } | ||||
| } | } | ||||
| void InterpretPhoneme(Translator *tr, int control, PHONEME_LIST *plist, PHONEME_DATA *phdata, WORD_PH_DATA *worddata) | void InterpretPhoneme(Translator *tr, int control, PHONEME_LIST *plist, PHONEME_DATA *phdata, WORD_PH_DATA *worddata) | ||||
| { | { | ||||
| // control: | // control: | ||||
| } else | } else | ||||
| InvalidInstn(ph, instn); | InvalidInstn(ph, instn); | ||||
| break; | break; | ||||
| case 1: | case 1: | ||||
| if (tr == NULL) | if (tr == NULL) | ||||
| break; // ignore if in synthesis stage | break; // ignore if in synthesis stage | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| case 2: | case 2: | ||||
| case 3: | case 3: | ||||
| // conditions | // conditions | ||||
| } | } | ||||
| prog--; | prog--; | ||||
| break; | break; | ||||
| case 6: | case 6: | ||||
| // JUMP | // JUMP | ||||
| switch (instn2 >> 1) | switch (instn2 >> 1) | ||||
| break; | break; | ||||
| } | } | ||||
| break; | break; | ||||
| case 9: | case 9: | ||||
| data = ((instn & 0xf) << 16) + prog[1]; | data = ((instn & 0xf) << 16) + prog[1]; | ||||
| prog++; | prog++; | ||||
| prog = &phoneme_index[data] - 1; | prog = &phoneme_index[data] - 1; | ||||
| } | } | ||||
| break; | break; | ||||
| case 2: | case 2: | ||||
| // pitch envelope | // pitch envelope | ||||
| phdata->pitch_env = data; | phdata->pitch_env = data; | ||||
| break; | break; | ||||
| case 3: | case 3: | ||||
| // amplitude envelope | // amplitude envelope | ||||
| phdata->amp_env = data; | phdata->amp_env = data; | ||||
| break; | break; | ||||
| } | } | ||||
| break; | break; | ||||
| case 10: // Vowelin, Vowelout | case 10: // Vowelin, Vowelout | ||||
| if (instn2 == 1) | if (instn2 == 1) | ||||
| ix = 0; | ix = 0; | ||||
| phdata->vowel_transition[ix+1] = (prog[2] << 16) + prog[3]; | phdata->vowel_transition[ix+1] = (prog[2] << 16) + prog[3]; | ||||
| prog += 3; | prog += 3; | ||||
| break; | break; | ||||
| case 11: // FMT | case 11: // FMT | ||||
| case 12: // WAV | case 12: // WAV | ||||
| case 13: // VowelStart | case 13: // VowelStart | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| default: | default: | ||||
| InvalidInstn(ph, instn); | InvalidInstn(ph, instn); | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| void InterpretPhoneme2(int phcode, PHONEME_DATA *phdata) | void InterpretPhoneme2(int phcode, PHONEME_DATA *phdata) | ||||
| { | { | ||||
| // Examine the program of a single isolated phoneme | // Examine the program of a single isolated phoneme |
| #include "voice.h" | #include "voice.h" | ||||
| #include "translate.h" | #include "translate.h" | ||||
| extern FILE *f_log; | extern FILE *f_log; | ||||
| static void SmoothSpect(void); | static void SmoothSpect(void); | ||||
| // list of phonemes in a clause | // list of phonemes in a clause | ||||
| int n_phoneme_list = 0; | int n_phoneme_list = 0; | ||||
| PHONEME_LIST phoneme_list[N_PHONEME_LIST+1]; | PHONEME_LIST phoneme_list[N_PHONEME_LIST+1]; | ||||
| #define VOWEL_FRONT_LENGTH 50 | #define VOWEL_FRONT_LENGTH 50 | ||||
| // a dummy phoneme_list entry which looks like a pause | // a dummy phoneme_list entry which looks like a pause | ||||
| static PHONEME_LIST next_pause; | static PHONEME_LIST next_pause; | ||||
| const char *WordToString(unsigned int word) | const char *WordToString(unsigned int word) | ||||
| { | { | ||||
| // Convert a phoneme mnemonic word into a string | // Convert a phoneme mnemonic word into a string | ||||
| return buf; | return buf; | ||||
| } | } | ||||
| void SynthesizeInit() | void SynthesizeInit() | ||||
| { | { | ||||
| last_pitch_cmd = 0; | last_pitch_cmd = 0; | ||||
| next_pause.newword = 0; | next_pause.newword = 0; | ||||
| } | } | ||||
| static void EndAmplitude(void) | static void EndAmplitude(void) | ||||
| { | { | ||||
| if (amp_length > 0) { | if (amp_length > 0) { | ||||
| } | } | ||||
| } | } | ||||
| static void EndPitch(int voice_break) | static void EndPitch(int voice_break) | ||||
| { | { | ||||
| // posssible end of pitch envelope, fill in the length | // posssible end of pitch envelope, fill in the length | ||||
| } | } | ||||
| } | } | ||||
| static void DoAmplitude(int amp, unsigned char *amp_env) | static void DoAmplitude(int amp, unsigned char *amp_env) | ||||
| { | { | ||||
| intptr_t *q; | intptr_t *q; | ||||
| WcmdqInc(); | WcmdqInc(); | ||||
| } | } | ||||
| static void DoPitch(unsigned char *env, int pitch1, int pitch2) | static void DoPitch(unsigned char *env, int pitch1, int pitch2) | ||||
| { | { | ||||
| intptr_t *q; | intptr_t *q; | ||||
| WcmdqInc(); | WcmdqInc(); | ||||
| } | } | ||||
| int PauseLength(int pause, int control) | int PauseLength(int pause, int control) | ||||
| { | { | ||||
| unsigned int len; | unsigned int len; | ||||
| return len; | return len; | ||||
| } | } | ||||
| static void DoPause(int length, int control) | static void DoPause(int length, int control) | ||||
| { | { | ||||
| // length in nominal mS | // length in nominal mS | ||||
| } | } | ||||
| } | } | ||||
| extern int seq_len_adjust; // temporary fix to advance the start point for playing the wav sample | extern int seq_len_adjust; // temporary fix to advance the start point for playing the wav sample | ||||
| static int DoSample2(int index, int which, int std_length, int control, int length_mod, int amp) | static int DoSample2(int index, int which, int std_length, int control, int length_mod, int amp) | ||||
| { | { | ||||
| int length; | int length; | ||||
| q[3] = wav_scale + (amp << 8); | q[3] = wav_scale + (amp << 8); | ||||
| WcmdqInc(); | WcmdqInc(); | ||||
| while (length > len4*3) { | while (length > len4*3) { | ||||
| x = len4; | x = len4; | ||||
| if (wav_scale == 0) | if (wav_scale == 0) | ||||
| return length; | return length; | ||||
| } | } | ||||
| int DoSample3(PHONEME_DATA *phdata, int length_mod, int amp) | int DoSample3(PHONEME_DATA *phdata, int length_mod, int amp) | ||||
| { | { | ||||
| int amp2; | int amp2; | ||||
| return len; | return len; | ||||
| } | } | ||||
| static frame_t *AllocFrame() | static frame_t *AllocFrame() | ||||
| { | { | ||||
| // Allocate a temporary spectrum frame for the wavegen queue. Use a pool which is big | // Allocate a temporary spectrum frame for the wavegen queue. Use a pool which is big | ||||
| return &frame_pool[ix]; | return &frame_pool[ix]; | ||||
| } | } | ||||
| static void set_frame_rms(frame_t *fr, int new_rms) | static void set_frame_rms(frame_t *fr, int new_rms) | ||||
| { | { | ||||
| // Each frame includes its RMS amplitude value, so to set a new | // Each frame includes its RMS amplitude value, so to set a new | ||||
| } | } | ||||
| } | } | ||||
| static void formants_reduce_hf(frame_t *fr, int level) | static void formants_reduce_hf(frame_t *fr, int level) | ||||
| { | { | ||||
| // change height of peaks 2 to 8, percentage | // change height of peaks 2 to 8, percentage | ||||
| } | } | ||||
| } | } | ||||
| static frame_t *CopyFrame(frame_t *frame1, int copy) | static frame_t *CopyFrame(frame_t *frame1, int copy) | ||||
| { | { | ||||
| // create a copy of the specified frame in temporary buffer | // create a copy of the specified frame in temporary buffer | ||||
| return frame2; | return frame2; | ||||
| } | } | ||||
| static frame_t *DuplicateLastFrame(frameref_t *seq, int n_frames, int length) | static frame_t *DuplicateLastFrame(frameref_t *seq, int n_frames, int length) | ||||
| { | { | ||||
| frame_t *fr; | frame_t *fr; | ||||
| return fr; | return fr; | ||||
| } | } | ||||
| static void AdjustFormants(frame_t *fr, int target, int min, int max, int f1_adj, int f3_adj, int hf_reduce, int flags) | static void AdjustFormants(frame_t *fr, int target, int min, int max, int f1_adj, int f3_adj, int hf_reduce, int flags) | ||||
| { | { | ||||
| int x; | int x; | ||||
| formants_reduce_hf(fr, hf_reduce); | formants_reduce_hf(fr, hf_reduce); | ||||
| } | } | ||||
| static int VowelCloseness(frame_t *fr) | static int VowelCloseness(frame_t *fr) | ||||
| { | { | ||||
| // return a value 0-3 depending on the vowel's f1 | // return a value 0-3 depending on the vowel's f1 | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| int FormantTransition2(frameref_t *seq, int *n_frames, unsigned int data1, unsigned int data2, PHONEME_TAB *other_ph, int which) | int FormantTransition2(frameref_t *seq, int *n_frames, unsigned int data1, unsigned int data2, PHONEME_TAB *other_ph, int which) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static void SmoothSpect(void) | static void SmoothSpect(void) | ||||
| { | { | ||||
| // Limit the rate of frequence change of formants, to reduce chirping | // Limit the rate of frequence change of formants, to reduce chirping | ||||
| break; | break; | ||||
| if (q[0] <= WCMD_SPECT2) { | if (q[0] <= WCMD_SPECT2) { | ||||
| len = q[1] & 0xffff; | len = q[1] & 0xffff; | ||||
| frame1 = (frame_t *)q[2]; | frame1 = (frame_t *)q[2]; | ||||
| syllable_start = syllable_end; | syllable_start = syllable_end; | ||||
| } | } | ||||
| static void StartSyllable(void) | static void StartSyllable(void) | ||||
| { | { | ||||
| // start of syllable, if not already started | // start of syllable, if not already started | ||||
| syllable_end = wcmdq_tail; | syllable_end = wcmdq_tail; | ||||
| } | } | ||||
| int DoSpect2(PHONEME_TAB *this_ph, int which, FMT_PARAMS *fmt_params, PHONEME_LIST *plist, int modulation) | int DoSpect2(PHONEME_TAB *this_ph, int which, FMT_PARAMS *fmt_params, PHONEME_LIST *plist, int modulation) | ||||
| { | { | ||||
| // which: 0 not a vowel, 1 start of vowel, 2 body and end of vowel | // which: 0 not a vowel, 1 start of vowel, 2 body and end of vowel | ||||
| WcmdqInc(); | WcmdqInc(); | ||||
| } | } | ||||
| return total_len; | return total_len; | ||||
| } | } | ||||
| void DoMarker(int type, int char_posn, int length, int value) | void DoMarker(int type, int char_posn, int length, int value) | ||||
| { | { | ||||
| // This could be used to return an index to the word currently being spoken | // This could be used to return an index to the word currently being spoken | ||||
| } | } | ||||
| } | } | ||||
| void DoPhonemeMarker(int type, int char_posn, int length, char *name) | void DoPhonemeMarker(int type, int char_posn, int length, char *name) | ||||
| { | { | ||||
| // This could be used to return an index to the word currently being spoken | // This could be used to return an index to the word currently being spoken | ||||
| } | } | ||||
| } | } | ||||
| #if HAVE_SONIC_H | #if HAVE_SONIC_H | ||||
| void DoSonicSpeed(int value) | void DoSonicSpeed(int value) | ||||
| { | { | ||||
| } | } | ||||
| #endif | #endif | ||||
| void DoVoiceChange(voice_t *v) | void DoVoiceChange(voice_t *v) | ||||
| { | { | ||||
| // allocate memory for a copy of the voice data, and free it in wavegenfill() | // allocate memory for a copy of the voice data, and free it in wavegenfill() | ||||
| WcmdqInc(); | WcmdqInc(); | ||||
| } | } | ||||
| void DoEmbedded(int *embix, int sourceix) | void DoEmbedded(int *embix, int sourceix) | ||||
| { | { | ||||
| // There were embedded commands in the text at this point | // There were embedded commands in the text at this point | ||||
| SetEmbedded((command & 0x60) + EMBED_S2, value); // adjusts embedded_value[EMBED_S2] | SetEmbedded((command & 0x60) + EMBED_S2, value); // adjusts embedded_value[EMBED_S2] | ||||
| SetSpeed(2); | SetSpeed(2); | ||||
| break; | break; | ||||
| case EMBED_I: // play dynamically loaded wav data (sound icon) | case EMBED_I: // play dynamically loaded wav data (sound icon) | ||||
| if ((int)value < n_soundicon_tab) { | if ((int)value < n_soundicon_tab) { | ||||
| if (soundicon_tab[value].length != 0) { | if (soundicon_tab[value].length != 0) { | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| case EMBED_M: // named marker | case EMBED_M: // named marker | ||||
| DoMarker(espeakEVENT_MARK, (sourceix & 0x7ff) + clause_start_char, 0, value); | DoMarker(espeakEVENT_MARK, (sourceix & 0x7ff) + clause_start_char, 0, value); | ||||
| break; | break; | ||||
| case EMBED_U: // play sound | case EMBED_U: // play sound | ||||
| DoMarker(espeakEVENT_PLAY, count_characters+1, 0, value); // always occurs at end of clause | DoMarker(espeakEVENT_PLAY, count_characters+1, 0, value); // always occurs at end of clause | ||||
| break; | break; | ||||
| default: | default: | ||||
| DoPause(10, 0); // ensure a break in the speech | DoPause(10, 0); // ensure a break in the speech | ||||
| wcmdq[wcmdq_tail][0] = WCMD_EMBEDDED; | wcmdq[wcmdq_tail][0] = WCMD_EMBEDDED; | ||||
| } while ((word & 0x80) == 0); | } while ((word & 0x80) == 0); | ||||
| } | } | ||||
| int Generate(PHONEME_LIST *phoneme_list, int *n_ph, int resume) | int Generate(PHONEME_LIST *phoneme_list, int *n_ph, int resume) | ||||
| { | { | ||||
| static int ix; | static int ix; | ||||
| DoPause(p->length, 0); | DoPause(p->length, 0); | ||||
| p->std_length = p->ph->std_length; | p->std_length = p->ph->std_length; | ||||
| break; | break; | ||||
| case phSTOP: | case phSTOP: | ||||
| released = 0; | released = 0; | ||||
| ph = p->ph; | ph = p->ph; | ||||
| phdata.pd_control |= pd_DONTLENGTHEN; | phdata.pd_control |= pd_DONTLENGTHEN; | ||||
| DoSample3(&phdata, 0, 0); | DoSample3(&phdata, 0, 0); | ||||
| break; | break; | ||||
| case phFRICATIVE: | case phFRICATIVE: | ||||
| InterpretPhoneme(NULL, 0, p, &phdata, &worddata); | InterpretPhoneme(NULL, 0, p, &phdata, &worddata); | ||||
| DoSample3(&phdata, p->length, 0); // play it twice for [s:] etc. | DoSample3(&phdata, p->length, 0); // play it twice for [s:] etc. | ||||
| DoSample3(&phdata, p->length, 0); | DoSample3(&phdata, p->length, 0); | ||||
| break; | break; | ||||
| case phVSTOP: | case phVSTOP: | ||||
| ph = p->ph; | ph = p->ph; | ||||
| memset(&fmtp, 0, sizeof(fmtp)); | memset(&fmtp, 0, sizeof(fmtp)); | ||||
| DoPause(12, 0); | DoPause(12, 0); | ||||
| } | } | ||||
| break; | break; | ||||
| case phVFRICATIVE: | case phVFRICATIVE: | ||||
| if (next->type == phVOWEL) { | if (next->type == phVOWEL) { | ||||
| DoAmplitude(p->amp, NULL); | DoAmplitude(p->amp, NULL); | ||||
| DoSpect2(p->ph, 0, &fmtp, p, 0); | DoSpect2(p->ph, 0, &fmtp, p, 0); | ||||
| DoSpect2(p->ph, 0, &fmtp, p, 0); | DoSpect2(p->ph, 0, &fmtp, p, 0); | ||||
| break; | break; | ||||
| case phNASAL: | case phNASAL: | ||||
| memset(&fmtp, 0, sizeof(fmtp)); | memset(&fmtp, 0, sizeof(fmtp)); | ||||
| if (!(p->synthflags & SFLAG_SEQCONTINUE)) { | if (!(p->synthflags & SFLAG_SEQCONTINUE)) { | ||||
| } | } | ||||
| break; | break; | ||||
| case phLIQUID: | case phLIQUID: | ||||
| memset(&fmtp, 0, sizeof(fmtp)); | memset(&fmtp, 0, sizeof(fmtp)); | ||||
| modulation = 0; | modulation = 0; | ||||
| fmtp.wav_addr = phdata.sound_addr[pd_ADDWAV]; | fmtp.wav_addr = phdata.sound_addr[pd_ADDWAV]; | ||||
| fmtp.wav_amp = phdata.sound_param[pd_ADDWAV]; | fmtp.wav_amp = phdata.sound_param[pd_ADDWAV]; | ||||
| DoSpect2(p->ph, 0, &fmtp, p, modulation); | DoSpect2(p->ph, 0, &fmtp, p, modulation); | ||||
| break; | break; | ||||
| case phVOWEL: | case phVOWEL: | ||||
| ph = p->ph; | ph = p->ph; | ||||
| stress = p->stresslevel & 0xf; | stress = p->stresslevel & 0xf; | ||||
| } | } | ||||
| DoSpect2(ph, 2, &fmtp, p, modulation); | DoSpect2(ph, 2, &fmtp, p, modulation); | ||||
| break; | break; | ||||
| } | } | ||||
| ix++; | ix++; | ||||
| return 0; // finished the phoneme list | return 0; // finished the phoneme list | ||||
| } | } | ||||
| static int timer_on = 0; | static int timer_on = 0; | ||||
| static int paused = 0; | static int paused = 0; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| int SynthStatus() | int SynthStatus() | ||||
| { | { | ||||
| return timer_on | paused; | return timer_on | paused; | ||||
| } | } | ||||
| int SpeakNextClause(FILE *f_in, const void *text_in, int control) | int SpeakNextClause(FILE *f_in, const void *text_in, int control) | ||||
| { | { | ||||
| // Speak text from file (f_in) or memory (text_in) | // Speak text from file (f_in) or memory (text_in) |
| #include "synthesize.h" | #include "synthesize.h" | ||||
| #include "translate.h" | #include "translate.h" | ||||
| #define L_qa 0x716100 | #define L_qa 0x716100 | ||||
| #define L_grc 0x677263 // grc Ancient Greek | #define L_grc 0x677263 // grc Ancient Greek | ||||
| #define L_jbo 0x6a626f // jbo Lojban | #define L_jbo 0x6a626f // jbo Lojban | ||||
| #define OFFSET_KOREAN 0x1100 | #define OFFSET_KOREAN 0x1100 | ||||
| #define OFFSET_ETHIOPIC 0x1200 | #define OFFSET_ETHIOPIC 0x1200 | ||||
| // character ranges must be listed in ascending unicode order | // character ranges must be listed in ascending unicode order | ||||
| ALPHABET alphabets[] = { | ALPHABET alphabets[] = { | ||||
| { "_el", OFFSET_GREEK, 0x380, 0x3ff, L('e', 'l'), AL_DONT_NAME | AL_NOT_LETTERS | AL_WORDS }, | { "_el", OFFSET_GREEK, 0x380, 0x3ff, L('e', 'l'), AL_DONT_NAME | AL_NOT_LETTERS | AL_WORDS }, | ||||
| { NULL, 0, 0, 0, 0, 0 } | { NULL, 0, 0, 0, 0, 0 } | ||||
| }; | }; | ||||
| ALPHABET *AlphabetFromName(const char *name) | ALPHABET *AlphabetFromName(const char *name) | ||||
| { | { | ||||
| ALPHABET *alphabet; | ALPHABET *alphabet; | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| ALPHABET *AlphabetFromChar(int c) | ALPHABET *AlphabetFromChar(int c) | ||||
| { | { | ||||
| // Find the alphabet from a character. | // Find the alphabet from a character. | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| static void Translator_Russian(Translator *tr); | static void Translator_Russian(Translator *tr); | ||||
| static void SetLetterVowel(Translator *tr, int c) | static void SetLetterVowel(Translator *tr, int c) | ||||
| { | { | ||||
| tr->letter_bits[c] = (tr->letter_bits[c] & 0x40) | 0x81; // keep value for group 6 (front vowels e,i,y) | tr->letter_bits[c] = (tr->letter_bits[c] & 0x40) | 0x81; // keep value for group 6 (front vowels e,i,y) | ||||
| const char string_ordinal[] = { 0xc2, 0xba, 0 }; // masculine ordinal character, UTF-8 | const char string_ordinal[] = { 0xc2, 0xba, 0 }; // masculine ordinal character, UTF-8 | ||||
| static Translator *NewTranslator(void) | static Translator *NewTranslator(void) | ||||
| { | { | ||||
| Translator *tr; | Translator *tr; | ||||
| SetLetterBits(tr, 6, "eiy"); // Letter group Y, front vowels | SetLetterBits(tr, 6, "eiy"); // Letter group Y, front vowels | ||||
| SetLetterBits(tr, 7, "aeiouy"); // vowels, including y | SetLetterBits(tr, 7, "aeiouy"); // vowels, including y | ||||
| tr->char_plus_apostrophe = empty_wstring; | tr->char_plus_apostrophe = empty_wstring; | ||||
| tr->punct_within_word = punct_in_word; | tr->punct_within_word = punct_in_word; | ||||
| tr->chars_ignore = chars_ignore_default; | tr->chars_ignore = chars_ignore_default; | ||||
| 0x7fff | 0x7fff | ||||
| }; | }; | ||||
| static const unsigned int replace_cyrillic_latin[] = | static const unsigned int replace_cyrillic_latin[] = | ||||
| { 0x430, 'a', | { 0x430, 'a', | ||||
| 0x431, 'b', | 0x431, 'b', | ||||
| 0x45c, 0x107, | 0x45c, 0x107, | ||||
| 0 }; // ѓ ѕ ќ | 0 }; // ѓ ѕ ќ | ||||
| static const unsigned char ru_vowels[] = { 0x10, 0x15, 0x31, 0x18, 0x1e, 0x23, 0x2b, 0x2d, 0x2e, 0x2f, 0xb9, 0xc9, 0x91, 0x8f, 0x36, 0 }; // also kazakh | static const unsigned char ru_vowels[] = { 0x10, 0x15, 0x31, 0x18, 0x1e, 0x23, 0x2b, 0x2d, 0x2e, 0x2f, 0xb9, 0xc9, 0x91, 0x8f, 0x36, 0 }; // also kazakh | ||||
| static const unsigned char ru_consonants[] = { 0x11, 0x12, 0x13, 0x14, 0x16, 0x17, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1f, 0x20, 0x21, 0x22, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2c, 0x73, 0x7b, 0x83, 0x9b, 0 }; | static const unsigned char ru_consonants[] = { 0x11, 0x12, 0x13, 0x14, 0x16, 0x17, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1f, 0x20, 0x21, 0x22, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2c, 0x73, 0x7b, 0x83, 0x9b, 0 }; | ||||
| SetLetterBits(tr, LETTERGP_VOWEL2, (char *)ru_vowels); | SetLetterBits(tr, LETTERGP_VOWEL2, (char *)ru_vowels); | ||||
| } | } | ||||
| void SetIndicLetters(Translator *tr) | void SetIndicLetters(Translator *tr) | ||||
| { | { | ||||
| // Set letter types for Indic scripts, Devanagari, Tamill, etc | // Set letter types for Indic scripts, Devanagari, Tamill, etc | ||||
| tr->langopts.suffix_add_e = tr->letter_bits_offset + 0x4d; // virama | tr->langopts.suffix_add_e = tr->letter_bits_offset + 0x4d; // virama | ||||
| } | } | ||||
| void SetupTranslator(Translator *tr, const short *lengths, const unsigned char *amps) | void SetupTranslator(Translator *tr, const short *lengths, const unsigned char *amps) | ||||
| { | { | ||||
| if (lengths != NULL) | if (lengths != NULL) | ||||
| memcpy(tr->stress_amps, amps, sizeof(tr->stress_amps)); | memcpy(tr->stress_amps, amps, sizeof(tr->stress_amps)); | ||||
| } | } | ||||
| Translator *SelectTranslator(const char *name) | Translator *SelectTranslator(const char *name) | ||||
| { | { | ||||
| int name2 = 0; | int name2 = 0; | ||||
| tr->langopts.numbers = NUM_SWAP_TENS | NUM_HUNDRED_AND | NUM_SINGLE_AND | NUM_ROMAN | NUM_1900; | tr->langopts.numbers = NUM_SWAP_TENS | NUM_HUNDRED_AND | NUM_SINGLE_AND | NUM_ROMAN | NUM_1900; | ||||
| tr->langopts.accents = 1; | tr->langopts.accents = 1; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('a', 'm'): // Amharic, Ethiopia | case L('a', 'm'): // Amharic, Ethiopia | ||||
| { | { | ||||
| SetupTranslator(tr, stress_lengths_fr, stress_amps_fr); | SetupTranslator(tr, stress_lengths_fr, stress_amps_fr); | ||||
| tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words | tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words | ||||
| tr->langopts.numbers = NUM_OMIT_1_HUNDRED; | tr->langopts.numbers = NUM_OMIT_1_HUNDRED; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('a', 'r'): // Arabic | case L('a', 'r'): // Arabic | ||||
| tr->transpose_min = 0x620; // for ar_list, use 6-bit character codes | tr->transpose_min = 0x620; // for ar_list, use 6-bit character codes | ||||
| tr->transpose_max = 0x65f; | tr->transpose_max = 0x65f; | ||||
| tr->langopts.numbers = NUM_SWAP_TENS | NUM_AND_UNITS | NUM_HUNDRED_AND | NUM_OMIT_1_HUNDRED | NUM_AND_HUNDRED | NUM_THOUSAND_AND | NUM_OMIT_1_THOUSAND; | tr->langopts.numbers = NUM_SWAP_TENS | NUM_AND_UNITS | NUM_HUNDRED_AND | NUM_OMIT_1_HUNDRED | NUM_AND_HUNDRED | NUM_THOUSAND_AND | NUM_OMIT_1_THOUSAND; | ||||
| tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words | tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words | ||||
| break; | break; | ||||
| case L('b', 'g'): // Bulgarian | case L('b', 'g'): // Bulgarian | ||||
| { | { | ||||
| SetCyrillicLetters(tr); | SetCyrillicLetters(tr); | ||||
| tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_ALLOW_SPACE | NUM_OMIT_1_HUNDRED | NUM_HUNDRED_AND | NUM_AND_UNITS | NUM_SINGLE_AND | NUM_ROMAN | NUM_ROMAN_ORDINAL | NUM_ROMAN_CAPITALS; | tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_ALLOW_SPACE | NUM_OMIT_1_HUNDRED | NUM_HUNDRED_AND | NUM_AND_UNITS | NUM_SINGLE_AND | NUM_ROMAN | NUM_ROMAN_ORDINAL | NUM_ROMAN_CAPITALS; | ||||
| tr->langopts.thousands_sep = ' '; // don't allow dot as thousands separator | tr->langopts.thousands_sep = ' '; // don't allow dot as thousands separator | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('b', 'n'): // Bengali | case L('b', 'n'): // Bengali | ||||
| case L('a', 's'): // Assamese | case L('a', 's'): // Assamese | ||||
| case L_mni: // Manipuri (temporary placement - it's not indo-european) | case L_mni: // Manipuri (temporary placement - it's not indo-european) | ||||
| } | } | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('b', 'o'): // Tibet | case L('b', 'o'): // Tibet | ||||
| { | { | ||||
| tr->langopts.stress_rule = STRESSPOSN_1L; | tr->langopts.stress_rule = STRESSPOSN_1L; | ||||
| tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words | tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words | ||||
| tr->langopts.numbers = 1; | tr->langopts.numbers = 1; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('c', 'y'): // Welsh | case L('c', 'y'): // Welsh | ||||
| { | { | ||||
| static const short stress_lengths_cy[8] = { 170, 220, 180, 180, 0, 0, 250, 270 }; | static const short stress_lengths_cy[8] = { 170, 220, 180, 180, 0, 0, 250, 270 }; | ||||
| SetLetterVowel(tr, 'w'); // add letter to vowels and remove from consonants | SetLetterVowel(tr, 'w'); // add letter to vowels and remove from consonants | ||||
| SetLetterVowel(tr, 'y'); | SetLetterVowel(tr, 'y'); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('d', 'a'): // Danish | case L('d', 'a'): // Danish | ||||
| { | { | ||||
| static const short stress_lengths_da[8] = { 160, 140, 200, 200, 0, 0, 220, 230 }; | static const short stress_lengths_da[8] = { 160, 140, 200, 200, 0, 0, 220, 230 }; | ||||
| SetLetterVowel(tr, 'y'); | SetLetterVowel(tr, 'y'); | ||||
| tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_SWAP_TENS | NUM_HUNDRED_AND | NUM_OMIT_1_HUNDRED | NUM_ORDINAL_DOT | NUM_1900 | NUM_ROMAN | NUM_ROMAN_CAPITALS | NUM_ROMAN_ORDINAL; | tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_SWAP_TENS | NUM_HUNDRED_AND | NUM_OMIT_1_HUNDRED | NUM_ORDINAL_DOT | NUM_1900 | NUM_ROMAN | NUM_ROMAN_CAPITALS | NUM_ROMAN_ORDINAL; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('d', 'e'): | case L('d', 'e'): | ||||
| { | { | ||||
| static const short stress_lengths_de[8] = { 150, 130, 200, 200, 0, 0, 270, 270 }; | static const short stress_lengths_de[8] = { 150, 130, 200, 200, 0, 0, 270, 270 }; | ||||
| SetLetterVowel(tr, 'y'); | SetLetterVowel(tr, 'y'); | ||||
| tr->langopts.param[LOPT_UNPRONOUNCABLE] = 2; // use de_rules for unpronouncable rules | tr->langopts.param[LOPT_UNPRONOUNCABLE] = 2; // use de_rules for unpronouncable rules | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('d', 'v'): // Divehi (Maldives) | case L('d', 'v'): // Divehi (Maldives) | ||||
| { | { | ||||
| SetupTranslator(tr, stress_lengths_ta, stress_amps_ta); | SetupTranslator(tr, stress_lengths_ta, stress_amps_ta); | ||||
| tr->langopts.break_numbers = 0x14a8; // 1000, 100,000 10,000,000 | tr->langopts.break_numbers = 0x14a8; // 1000, 100,000 10,000,000 | ||||
| tr->langopts.numbers = 1; | tr->langopts.numbers = 1; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('e', 'n'): | case L('e', 'n'): | ||||
| { | { | ||||
| static const short stress_lengths_en[8] = { 182, 140, 220, 220, 0, 0, 248, 275 }; | static const short stress_lengths_en[8] = { 182, 140, 220, 220, 0, 0, 248, 275 }; | ||||
| tr->langopts.param[LOPT_UNPRONOUNCABLE] = 2; // use en_rules for unpronouncable rules | tr->langopts.param[LOPT_UNPRONOUNCABLE] = 2; // use en_rules for unpronouncable rules | ||||
| SetLetterBits(tr, 6, "aeiouy"); // Group Y: vowels, including y | SetLetterBits(tr, 6, "aeiouy"); // Group Y: vowels, including y | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('e', 'l'): // Greek | case L('e', 'l'): // Greek | ||||
| case L_grc: // Ancient Greek | case L_grc: // Ancient Greek | ||||
| { | { | ||||
| tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; | tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; | ||||
| } | } | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('e', 'o'): | case L('e', 'o'): | ||||
| { | { | ||||
| static const short stress_lengths_eo[8] = { 150, 140, 180, 180, 0, 0, 200, 200 }; | static const short stress_lengths_eo[8] = { 150, 140, 180, 180, 0, 0, 200, 200 }; | ||||
| tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_OMIT_1_HUNDRED | NUM_ALLOW_SPACE | NUM_ROMAN; | tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_OMIT_1_HUNDRED | NUM_ALLOW_SPACE | NUM_ROMAN; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('e', 's'): // Spanish | case L('e', 's'): // Spanish | ||||
| case L('a', 'n'): // Aragonese | case L('a', 'n'): // Aragonese | ||||
| case L('c', 'a'): // Catalan | case L('c', 'a'): // Catalan | ||||
| } else | } else | ||||
| tr->langopts.param[LOPT_UNPRONOUNCABLE] = 2; // use es_rules for unpronouncable rules | tr->langopts.param[LOPT_UNPRONOUNCABLE] = 2; // use es_rules for unpronouncable rules | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('e', 'u'): // basque | case L('e', 'u'): // basque | ||||
| { | { | ||||
| static const short stress_lengths_eu[8] = { 200, 200, 200, 200, 0, 0, 210, 230 }; // very weak stress | static const short stress_lengths_eu[8] = { 200, 200, 200, 200, 0, 0, 210, 230 }; // very weak stress | ||||
| tr->langopts.param[LOPT_SUFFIX] = 1; | tr->langopts.param[LOPT_SUFFIX] = 1; | ||||
| tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_HUNDRED_AND | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_VIGESIMAL; | tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_HUNDRED_AND | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_VIGESIMAL; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('f', 'a'): // Farsi | case L('f', 'a'): // Farsi | ||||
| { | { | ||||
| // Convert characters in the range 0x620 to 0x6cc to the range 1 to 63. | // Convert characters in the range 0x620 to 0x6cc to the range 1 to 63. | ||||
| tr->chars_ignore = chars_ignore_zwnj_hyphen; // replace ZWNJ by hyphen | tr->chars_ignore = chars_ignore_zwnj_hyphen; // replace ZWNJ by hyphen | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('e', 't'): // Estonian | case L('e', 't'): // Estonian | ||||
| tr->charset_a0 = charsets[4]; // ISO-8859-4 | tr->charset_a0 = charsets[4]; // ISO-8859-4 | ||||
| // drop through to Finnish | |||||
| // drop through to Finnish | |||||
| case L('f', 'i'): // Finnish | case L('f', 'i'): // Finnish | ||||
| { | { | ||||
| static const unsigned char stress_amps_fi[8] = { 18, 16, 22, 22, 20, 22, 22, 22 }; | static const unsigned char stress_amps_fi[8] = { 18, 16, 22, 22, 20, 22, 22, 22 }; | ||||
| tr->langopts.spelling_stress = 1; | tr->langopts.spelling_stress = 1; | ||||
| tr->langopts.intonation_group = 3; // less intonation, don't raise pitch at comma | tr->langopts.intonation_group = 3; // less intonation, don't raise pitch at comma | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('f', 'r'): // french | case L('f', 'r'): // french | ||||
| { | { | ||||
| SetupTranslator(tr, stress_lengths_fr, stress_amps_fr); | SetupTranslator(tr, stress_lengths_fr, stress_amps_fr); | ||||
| tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_ALLOW_SPACE | NUM_OMIT_1_HUNDRED | NUM_NOPAUSE | NUM_ROMAN | NUM_ROMAN_CAPITALS | NUM_ROMAN_AFTER | NUM_VIGESIMAL | NUM_DFRACTION_4; | tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_ALLOW_SPACE | NUM_OMIT_1_HUNDRED | NUM_NOPAUSE | NUM_ROMAN | NUM_ROMAN_CAPITALS | NUM_ROMAN_AFTER | NUM_VIGESIMAL | NUM_DFRACTION_4; | ||||
| SetLetterVowel(tr, 'y'); | SetLetterVowel(tr, 'y'); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('g', 'a'): // irish | case L('g', 'a'): // irish | ||||
| case L('g', 'd'): // scots gaelic | case L('g', 'd'): // scots gaelic | ||||
| { | { | ||||
| tr->langopts.param[LOPT_UNPRONOUNCABLE] = 3; // don't count apostrophe | tr->langopts.param[LOPT_UNPRONOUNCABLE] = 3; // don't count apostrophe | ||||
| tr->langopts.param[LOPT_IT_LENGTHEN] = 1; // remove [:] phoneme from non-stressed syllables (Lang=gd) | tr->langopts.param[LOPT_IT_LENGTHEN] = 1; // remove [:] phoneme from non-stressed syllables (Lang=gd) | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('h', 'i'): // Hindi | case L('h', 'i'): // Hindi | ||||
| case L('n', 'e'): // Nepali | case L('n', 'e'): // Nepali | ||||
| case L('o', 'r'): // Oriya | case L('o', 'r'): // Oriya | ||||
| tr->letter_bits_offset = OFFSET_ORIYA; | tr->letter_bits_offset = OFFSET_ORIYA; | ||||
| SetIndicLetters(tr); | SetIndicLetters(tr); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('h', 'r'): // Croatian | case L('h', 'r'): // Croatian | ||||
| case L('b', 's'): // Bosnian | case L('b', 's'): // Bosnian | ||||
| case L('s', 'r'): // Serbian | case L('s', 'r'): // Serbian | ||||
| SetLetterVowel(tr, 'y'); | SetLetterVowel(tr, 'y'); | ||||
| SetLetterVowel(tr, 'r'); | SetLetterVowel(tr, 'r'); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('h', 't'): // Haitian Creole | case L('h', 't'): // Haitian Creole | ||||
| tr->langopts.stress_rule = STRESSPOSN_1R; // stress on final syllable | tr->langopts.stress_rule = STRESSPOSN_1R; // stress on final syllable | ||||
| tr->langopts.stress_flags = S_NO_AUTO_2 | S_FINAL_DIM; // don't use secondary stress | tr->langopts.stress_flags = S_NO_AUTO_2 | S_FINAL_DIM; // don't use secondary stress | ||||
| tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_OMIT_1_HUNDRED | NUM_NOPAUSE | NUM_ROMAN | NUM_VIGESIMAL | NUM_DFRACTION_4; | tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_OMIT_1_HUNDRED | NUM_NOPAUSE | NUM_ROMAN | NUM_VIGESIMAL | NUM_DFRACTION_4; | ||||
| break; | break; | ||||
| case L('h', 'u'): // Hungarian | case L('h', 'u'): // Hungarian | ||||
| { | { | ||||
| static const unsigned char stress_amps_hu[8] = { 17, 17, 19, 19, 20, 22, 22, 21 }; | static const unsigned char stress_amps_hu[8] = { 17, 17, 19, 19, 20, 22, 22, 21 }; | ||||
| tr->langopts.spelling_stress = 1; | tr->langopts.spelling_stress = 1; | ||||
| SetLengthMods(tr, 3); // all equal | SetLengthMods(tr, 3); // all equal | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('h', 'y'): // Armenian | case L('h', 'y'): // Armenian | ||||
| { | { | ||||
| static const short stress_lengths_hy[8] = { 250, 200, 250, 250, 0, 0, 250, 250 }; | static const short stress_lengths_hy[8] = { 250, 200, 250, 250, 0, 0, 250, 250 }; | ||||
| tr->langopts.max_initial_consonants = 6; | tr->langopts.max_initial_consonants = 6; | ||||
| tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_ALLOW_SPACE | NUM_OMIT_1_HUNDRED; | tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_ALLOW_SPACE | NUM_OMIT_1_HUNDRED; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('i', 'd'): // Indonesian | case L('i', 'd'): // Indonesian | ||||
| case L('m', 's'): // Malay | case L('m', 's'): // Malay | ||||
| { | { | ||||
| tr->langopts.stress_flags = S_FINAL_DIM_ONLY | S_FINAL_NO_2; | tr->langopts.stress_flags = S_FINAL_DIM_ONLY | S_FINAL_NO_2; | ||||
| tr->langopts.accents = 2; // "capital" after letter name | tr->langopts.accents = 2; // "capital" after letter name | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('i', 's'): // Icelandic | case L('i', 's'): // Icelandic | ||||
| { | { | ||||
| static const short stress_lengths_is[8] = { 180, 160, 200, 200, 0, 0, 240, 250 }; | static const short stress_lengths_is[8] = { 180, 160, 200, 200, 0, 0, 240, 250 }; | ||||
| tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_SINGLE_AND | NUM_HUNDRED_AND | NUM_AND_UNITS | NUM_1900; | tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_SINGLE_AND | NUM_HUNDRED_AND | NUM_AND_UNITS | NUM_1900; | ||||
| tr->langopts.numbers2 = 0x2; | tr->langopts.numbers2 = 0x2; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('i', 't'): // Italian | case L('i', 't'): // Italian | ||||
| { | { | ||||
| static const short stress_lengths_it[8] = | static const short stress_lengths_it[8] = | ||||
| tr->langopts.accents = 2; // Say "Capital" after the letter. | tr->langopts.accents = 2; // Say "Capital" after the letter. | ||||
| SetLetterVowel(tr, 'y'); | SetLetterVowel(tr, 'y'); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L_jbo: // Lojban | case L_jbo: // Lojban | ||||
| { | { | ||||
| static const short stress_lengths_jbo[8] = { 145, 145, 170, 160, 0, 0, 330, 350 }; | static const short stress_lengths_jbo[8] = { 145, 145, 170, 160, 0, 0, 330, 350 }; | ||||
| SetLetterVowel(tr, 'y'); | SetLetterVowel(tr, 'y'); | ||||
| tr->langopts.max_lengthmod = 368; | tr->langopts.max_lengthmod = 368; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('k', 'a'): // Georgian | case L('k', 'a'): // Georgian | ||||
| { | { | ||||
| // character codes offset by 0x1080 | // character codes offset by 0x1080 | ||||
| tr->langopts.alt_alphabet = OFFSET_CYRILLIC; | tr->langopts.alt_alphabet = OFFSET_CYRILLIC; | ||||
| tr->langopts.alt_alphabet_lang = L('r', 'u'); | tr->langopts.alt_alphabet_lang = L('r', 'u'); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('k', 'k'): // Kazakh | case L('k', 'k'): // Kazakh | ||||
| { | { | ||||
| static const unsigned char stress_amps_tr[8] = { 18, 16, 20, 21, 20, 21, 21, 20 }; | static const unsigned char stress_amps_tr[8] = { 18, 16, 20, 21, 20, 21, 21, 20 }; | ||||
| tr->langopts.max_initial_consonants = 2; | tr->langopts.max_initial_consonants = 2; | ||||
| SetLengthMods(tr, 3); // all equal | SetLengthMods(tr, 3); // all equal | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('k', 'l'): // Greenlandic | case L('k', 'l'): // Greenlandic | ||||
| { | { | ||||
| SetupTranslator(tr, stress_lengths_equal, stress_amps_equal); | SetupTranslator(tr, stress_lengths_equal, stress_amps_equal); | ||||
| tr->langopts.stress_flags = S_NO_AUTO_2; | tr->langopts.stress_flags = S_NO_AUTO_2; | ||||
| tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_SWAP_TENS | NUM_HUNDRED_AND | NUM_OMIT_1_HUNDRED | NUM_ORDINAL_DOT | NUM_1900 | NUM_ROMAN | NUM_ROMAN_CAPITALS | NUM_ROMAN_ORDINAL; | tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_SWAP_TENS | NUM_HUNDRED_AND | NUM_OMIT_1_HUNDRED | NUM_ORDINAL_DOT | NUM_1900 | NUM_ROMAN | NUM_ROMAN_CAPITALS | NUM_ROMAN_ORDINAL; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('k', 'o'): // Korean, TEST | case L('k', 'o'): // Korean, TEST | ||||
| { | { | ||||
| static const char ko_ivowels[] = { 0x63, 0x64, 0x67, 0x68, 0x6d, 0x72, 0x74, 0x75, 0 }; // y and i vowels | static const char ko_ivowels[] = { 0x63, 0x64, 0x67, 0x68, 0x6d, 0x72, 0x74, 0x75, 0 }; // y and i vowels | ||||
| tr->langopts.break_numbers = 0x1111110; | tr->langopts.break_numbers = 0x1111110; | ||||
| tr->langopts.max_digits = 20; | tr->langopts.max_digits = 20; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('k', 'u'): // Kurdish | case L('k', 'u'): // Kurdish | ||||
| { | { | ||||
| static const unsigned char stress_amps_ku[8] = { 18, 18, 20, 20, 20, 22, 22, 21 }; | static const unsigned char stress_amps_ku[8] = { 18, 18, 20, 20, 20, 22, 22, 21 }; | ||||
| tr->langopts.numbers = NUM_HUNDRED_AND | NUM_AND_UNITS | NUM_OMIT_1_HUNDRED | NUM_AND_HUNDRED; | tr->langopts.numbers = NUM_HUNDRED_AND | NUM_AND_UNITS | NUM_OMIT_1_HUNDRED | NUM_AND_HUNDRED; | ||||
| tr->langopts.max_initial_consonants = 2; | tr->langopts.max_initial_consonants = 2; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('l', 'a'): // Latin | case L('l', 'a'): // Latin | ||||
| { | { | ||||
| tr->charset_a0 = charsets[4]; // ISO-8859-4, includes a,e,i,o,u-macron | tr->charset_a0 = charsets[4]; // ISO-8859-4, includes a,e,i,o,u-macron | ||||
| tr->langopts.numbers = NUM_ROMAN; | tr->langopts.numbers = NUM_ROMAN; | ||||
| tr->langopts.max_roman = 5000; | tr->langopts.max_roman = 5000; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('l', 't'): // Lithuanian | case L('l', 't'): // Lithuanian | ||||
| { | { | ||||
| tr->charset_a0 = charsets[4]; // ISO-8859-4 | tr->charset_a0 = charsets[4]; // ISO-8859-4 | ||||
| tr->langopts.numbers2 = NUM2_THOUSANDS_VAR4; | tr->langopts.numbers2 = NUM2_THOUSANDS_VAR4; | ||||
| tr->langopts.max_roman = 5000; | tr->langopts.max_roman = 5000; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('l', 'v'): // latvian | case L('l', 'v'): // latvian | ||||
| { | { | ||||
| static const unsigned char stress_amps_lv[8] = { 17, 13, 20, 20, 20, 22, 22, 21 }; | static const unsigned char stress_amps_lv[8] = { 17, 13, 20, 20, 20, 22, 22, 21 }; | ||||
| tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_OMIT_1_HUNDRED | NUM_DFRACTION_4 | NUM_ORDINAL_DOT; | tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_OMIT_1_HUNDRED | NUM_DFRACTION_4 | NUM_ORDINAL_DOT; | ||||
| tr->langopts.stress_flags = S_FINAL_DIM_ONLY | S_FINAL_NO_2 | S_EO_CLAUSE1; | tr->langopts.stress_flags = S_FINAL_DIM_ONLY | S_FINAL_NO_2 | S_EO_CLAUSE1; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('m', 'k'): // Macedonian | case L('m', 'k'): // Macedonian | ||||
| { | { | ||||
| static wchar_t vowels_cyrillic[] = { 0x440, // also include 'р' [R] | static wchar_t vowels_cyrillic[] = { 0x440, // also include 'р' [R] | ||||
| tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_AND_UNITS | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_DFRACTION_2; | tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_AND_UNITS | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_DFRACTION_2; | ||||
| tr->langopts.numbers2 = 0x8a; // variant numbers before thousands,milliards | tr->langopts.numbers2 = 0x8a; // variant numbers before thousands,milliards | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('m', 't'): // Maltese | case L('m', 't'): // Maltese | ||||
| { | { | ||||
| tr->charset_a0 = charsets[3]; // ISO-8859-3 | tr->charset_a0 = charsets[3]; // ISO-8859-3 | ||||
| tr->langopts.stress_rule = STRESSPOSN_2R; // penultimate | tr->langopts.stress_rule = STRESSPOSN_2R; // penultimate | ||||
| tr->langopts.numbers = 1; | tr->langopts.numbers = 1; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('n', 'l'): // Dutch | case L('n', 'l'): // Dutch | ||||
| { | { | ||||
| static const short stress_lengths_nl[8] = { 160, 135, 210, 210, 0, 0, 260, 280 }; | static const short stress_lengths_nl[8] = { 160, 135, 210, 210, 0, 0, 260, 280 }; | ||||
| tr->langopts.stress_flags = S_FIRST_PRIMARY; | tr->langopts.stress_flags = S_FIRST_PRIMARY; | ||||
| memcpy(tr->stress_lengths, stress_lengths_nl, sizeof(tr->stress_lengths)); | memcpy(tr->stress_lengths, stress_lengths_nl, sizeof(tr->stress_lengths)); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('n', 'o'): // Norwegian | case L('n', 'o'): // Norwegian | ||||
| { | { | ||||
| static const short stress_lengths_no[8] = { 160, 140, 200, 200, 0, 0, 220, 230 }; | static const short stress_lengths_no[8] = { 160, 140, 200, 200, 0, 0, 220, 230 }; | ||||
| SetLetterVowel(tr, 'y'); | SetLetterVowel(tr, 'y'); | ||||
| tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_HUNDRED_AND | NUM_ALLOW_SPACE | NUM_1900 | NUM_ORDINAL_DOT; | tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_HUNDRED_AND | NUM_ALLOW_SPACE | NUM_1900 | NUM_ORDINAL_DOT; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('o', 'm'): // Oromo | case L('o', 'm'): // Oromo | ||||
| { | { | ||||
| static const unsigned char stress_amps_om[] = { 18, 15, 20, 20, 20, 22, 22, 22 }; | static const unsigned char stress_amps_om[] = { 18, 15, 20, 20, 20, 22, 22, 22 }; | ||||
| tr->langopts.numbers = NUM_OMIT_1_HUNDRED | NUM_HUNDRED_AND; | tr->langopts.numbers = NUM_OMIT_1_HUNDRED | NUM_HUNDRED_AND; | ||||
| tr->langopts.numbers2 = 0x200; // say "thousands" before its number | tr->langopts.numbers2 = 0x200; // say "thousands" before its number | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('p', 'l'): // Polish | case L('p', 'l'): // Polish | ||||
| { | { | ||||
| static const short stress_lengths_pl[8] = { 160, 190, 175, 175, 0, 0, 200, 210 }; | static const short stress_lengths_pl[8] = { 160, 190, 175, 175, 0, 0, 200, 210 }; | ||||
| tr->langopts.param[LOPT_COMBINE_WORDS] = 4 + 0x100; // combine 'nie' (marked with $alt2) with some 1-syllable (and 2-syllable) words (marked with $alt) | tr->langopts.param[LOPT_COMBINE_WORDS] = 4 + 0x100; // combine 'nie' (marked with $alt2) with some 1-syllable (and 2-syllable) words (marked with $alt) | ||||
| SetLetterVowel(tr, 'y'); | SetLetterVowel(tr, 'y'); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('p', 't'): // Portuguese | case L('p', 't'): // Portuguese | ||||
| { | { | ||||
| static const short stress_lengths_pt[8] = { 170, 115, 210, 240, 0, 0, 260, 280 }; | static const short stress_lengths_pt[8] = { 170, 115, 210, 240, 0, 0, 260, 280 }; | ||||
| tr->langopts.param[LOPT_ALT] = 2; // call ApplySpecialAttributes2() if a word has $alt or $alt2 | tr->langopts.param[LOPT_ALT] = 2; // call ApplySpecialAttributes2() if a word has $alt or $alt2 | ||||
| tr->langopts.accents = 2; // 'capital' after letter name | tr->langopts.accents = 2; // 'capital' after letter name | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('r', 'o'): // Romanian | case L('r', 'o'): // Romanian | ||||
| { | { | ||||
| static const short stress_lengths_ro[8] = { 170, 170, 180, 180, 0, 0, 240, 260 }; | static const short stress_lengths_ro[8] = { 170, 170, 180, 180, 0, 0, 240, 260 }; | ||||
| tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_ALLOW_SPACE | NUM_DFRACTION_3 | NUM_AND_UNITS | NUM_ROMAN; | tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_ALLOW_SPACE | NUM_DFRACTION_3 | NUM_AND_UNITS | NUM_ROMAN; | ||||
| tr->langopts.numbers2 = 0x1e; // variant numbers before all thousandplex | tr->langopts.numbers2 = 0x1e; // variant numbers before all thousandplex | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('r', 'u'): // Russian | case L('r', 'u'): // Russian | ||||
| Translator_Russian(tr); | Translator_Russian(tr); | ||||
| break; | break; | ||||
| case L('r', 'w'): // Kiryarwanda | case L('r', 'w'): // Kiryarwanda | ||||
| { | { | ||||
| tr->langopts.stress_rule = STRESSPOSN_2R; | tr->langopts.stress_rule = STRESSPOSN_2R; | ||||
| tr->langopts.numbers = NUM_HUNDRED_AND | NUM_AND_UNITS | NUM_DFRACTION_2 | NUM_AND_HUNDRED; | tr->langopts.numbers = NUM_HUNDRED_AND | NUM_AND_UNITS | NUM_DFRACTION_2 | NUM_AND_HUNDRED; | ||||
| tr->langopts.numbers2 = 0x200; // say "thousands" before its number | tr->langopts.numbers2 = 0x200; // say "thousands" before its number | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('s', 'k'): // Slovak | case L('s', 'k'): // Slovak | ||||
| case L('c', 's'): // Czech | case L('c', 's'): // Czech | ||||
| { | { | ||||
| ResetLetterBits(tr, 0x20); | ResetLetterBits(tr, 0x20); | ||||
| SetLetterBits(tr, 5, sk_voiced); | SetLetterBits(tr, 5, sk_voiced); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('s', 'i'): // Sinhala | case L('s', 'i'): // Sinhala | ||||
| { | { | ||||
| SetupTranslator(tr, stress_lengths_ta, stress_amps_ta); | SetupTranslator(tr, stress_lengths_ta, stress_amps_ta); | ||||
| tr->langopts.numbers2 = NUM2_PERCENT_BEFORE; | tr->langopts.numbers2 = NUM2_PERCENT_BEFORE; | ||||
| tr->langopts.break_numbers = 0x14aa8; // for languages which have numbers for 100,000 and 100,00,000, eg Hindi | tr->langopts.break_numbers = 0x14aa8; // for languages which have numbers for 100,000 and 100,00,000, eg Hindi | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('s', 'l'): // Slovenian | case L('s', 'l'): // Slovenian | ||||
| tr->charset_a0 = charsets[2]; // ISO-8859-2 | tr->charset_a0 = charsets[2]; // ISO-8859-2 | ||||
| tr->langopts.stress_rule = STRESSPOSN_2R; // Temporary | tr->langopts.stress_rule = STRESSPOSN_2R; // Temporary | ||||
| tr->langopts.thousands_sep = ' '; // don't allow dot as thousands separator | tr->langopts.thousands_sep = ' '; // don't allow dot as thousands separator | ||||
| tr->langopts.replace_chars = replace_cyrillic_latin; | tr->langopts.replace_chars = replace_cyrillic_latin; | ||||
| break; | break; | ||||
| case L('s', 'q'): // Albanian | case L('s', 'q'): // Albanian | ||||
| { | { | ||||
| static const short stress_lengths_sq[8] = { 150, 150, 180, 180, 0, 0, 300, 300 }; | static const short stress_lengths_sq[8] = { 150, 150, 180, 180, 0, 0, 300, 300 }; | ||||
| tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_HUNDRED_AND | NUM_AND_UNITS | NUM_DFRACTION_4; | tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_HUNDRED_AND | NUM_AND_UNITS | NUM_DFRACTION_4; | ||||
| tr->langopts.accents = 2; // "capital" after letter name | tr->langopts.accents = 2; // "capital" after letter name | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('s', 'v'): // Swedish | case L('s', 'v'): // Swedish | ||||
| { | { | ||||
| static const unsigned char stress_amps_sv[] = { 16, 16, 20, 20, 20, 22, 22, 21 }; | static const unsigned char stress_amps_sv[] = { 16, 16, 20, 20, 20, 22, 22, 21 }; | ||||
| tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_ALLOW_SPACE | NUM_1900; | tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_ALLOW_SPACE | NUM_1900; | ||||
| tr->langopts.accents = 1; | tr->langopts.accents = 1; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('s', 'w'): // Swahili | case L('s', 'w'): // Swahili | ||||
| case L('t', 'n'): // Setswana | case L('t', 'n'): // Setswana | ||||
| { | { | ||||
| tr->langopts.numbers = NUM_AND_UNITS | NUM_HUNDRED_AND | NUM_SINGLE_AND | NUM_OMIT_1_HUNDRED; | tr->langopts.numbers = NUM_AND_UNITS | NUM_HUNDRED_AND | NUM_SINGLE_AND | NUM_OMIT_1_HUNDRED; | ||||
| tr->langopts.break_numbers = 0x49249268; // for languages which have numbers for 100,000 and 1,000,000 | tr->langopts.break_numbers = 0x49249268; // for languages which have numbers for 100,000 and 1,000,000 | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('t', 'a'): // Tamil | case L('t', 'a'): // Tamil | ||||
| case L('k', 'n'): // Kannada | case L('k', 'n'): // Kannada | ||||
| case L('m', 'l'): // Malayalam | case L('m', 'l'): // Malayalam | ||||
| SetIndicLetters(tr); // call this after setting OFFSET_ | SetIndicLetters(tr); // call this after setting OFFSET_ | ||||
| SetLetterBitsRange(tr, LETTERGP_B, 0x4e, 0x4e); // chillu-virama (unofficial) | SetLetterBitsRange(tr, LETTERGP_B, 0x4e, 0x4e); // chillu-virama (unofficial) | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('t', 'r'): // Turkish | case L('t', 'r'): // Turkish | ||||
| case L('a', 'z'): // Azerbaijan | case L('a', 'z'): // Azerbaijan | ||||
| { | { | ||||
| tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_DFRACTION_2; | tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_DFRACTION_2; | ||||
| tr->langopts.max_initial_consonants = 2; | tr->langopts.max_initial_consonants = 2; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('t', 't'): // Tatar | case L('t', 't'): // Tatar | ||||
| { | { | ||||
| SetCyrillicLetters(tr); | SetCyrillicLetters(tr); | ||||
| tr->langopts.stress_flags = S_NO_AUTO_2; // no automatic secondary stress | tr->langopts.stress_flags = S_NO_AUTO_2; // no automatic secondary stress | ||||
| tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_DFRACTION_4; | tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_DFRACTION_4; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('u', 'k'): // Ukrainian | case L('u', 'k'): // Ukrainian | ||||
| { | { | ||||
| SetCyrillicLetters(tr); | SetCyrillicLetters(tr); | ||||
| tr->langopts.param[LOPT_UNPRONOUNCABLE] = 0x432; // [v] don't count this character at start of word | tr->langopts.param[LOPT_UNPRONOUNCABLE] = 0x432; // [v] don't count this character at start of word | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('u', 'r'): // Urdu | case L('u', 'r'): // Urdu | ||||
| case L('s', 'd'): // Sindhi | case L('s', 'd'): // Sindhi | ||||
| { | { | ||||
| tr->langopts.numbers = NUM_SWAP_TENS; | tr->langopts.numbers = NUM_SWAP_TENS; | ||||
| tr->langopts.break_numbers = 0x52a8; // for languages which have numbers for 100,000 and 100,00,000, eg Hindi | tr->langopts.break_numbers = 0x52a8; // for languages which have numbers for 100,000 and 100,00,000, eg Hindi | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('v', 'i'): // Vietnamese | case L('v', 'i'): // Vietnamese | ||||
| { | { | ||||
| static const short stress_lengths_vi[8] = { 150, 150, 180, 180, 210, 230, 230, 240 }; | static const short stress_lengths_vi[8] = { 150, 150, 180, 180, 210, 230, 230, 240 }; | ||||
| tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_HUNDRED_AND_DIGIT | NUM_DFRACTION_4 | NUM_ZERO_HUNDRED; | tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_HUNDRED_AND_DIGIT | NUM_DFRACTION_4 | NUM_ZERO_HUNDRED; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case L('w', 'o'): | case L('w', 'o'): | ||||
| tr->langopts.stress_rule = STRESSPOSN_1L; | tr->langopts.stress_rule = STRESSPOSN_1L; | ||||
| tr->langopts.numbers = NUM_AND_UNITS | NUM_HUNDRED_AND | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_SINGLE_STRESS; | tr->langopts.numbers = NUM_AND_UNITS | NUM_HUNDRED_AND | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_SINGLE_STRESS; | ||||
| break; | break; | ||||
| case L('z', 'h'): | case L('z', 'h'): | ||||
| case L_zhy: | case L_zhy: | ||||
| { | { | ||||
| tr->langopts.break_numbers = 0x00018; | tr->langopts.break_numbers = 0x00018; | ||||
| } | } | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| default: | default: | ||||
| tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words | tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words | ||||
| break; | break; | ||||
| return tr; | return tr; | ||||
| } | } | ||||
| void ProcessLanguageOptions(LANGUAGE_OPTIONS *langopts) | void ProcessLanguageOptions(LANGUAGE_OPTIONS *langopts) | ||||
| { | { | ||||
| if (langopts->numbers & NUM_DECIMAL_COMMA) { | if (langopts->numbers & NUM_DECIMAL_COMMA) { | ||||
| langopts->thousands_sep = 0; // don't allow thousands separator, except space | langopts->thousands_sep = 0; // don't allow thousands separator, except space | ||||
| } | } | ||||
| static void Translator_Russian(Translator *tr) | static void Translator_Russian(Translator *tr) | ||||
| { | { | ||||
| static const unsigned char stress_amps_ru[] = { 16, 16, 18, 18, 20, 24, 24, 22 }; | static const unsigned char stress_amps_ru[] = { 16, 16, 18, 18, 20, 24, 24, 22 }; | ||||
| tr->langopts.numbers2 = 0x2 + NUM2_THOUSANDS_VAR1; // variant numbers before thousands | tr->langopts.numbers2 = 0x2 + NUM2_THOUSANDS_VAR1; // variant numbers before thousands | ||||
| tr->langopts.phoneme_change = 1; | tr->langopts.phoneme_change = 1; | ||||
| tr->langopts.testing = 2; | tr->langopts.testing = 2; | ||||
| } | } |
| #define WORD_STRESS_CHAR '*' | #define WORD_STRESS_CHAR '*' | ||||
| Translator *translator = NULL; // the main translator | Translator *translator = NULL; // the main translator | ||||
| Translator *translator2 = NULL; // secondary translator for certain words | Translator *translator2 = NULL; // secondary translator for certain words | ||||
| static char translator2_language[20] = { 0 }; | static char translator2_language[20] = { 0 }; | ||||
| int pre_pause; | int pre_pause; | ||||
| ALPHABET *current_alphabet; | ALPHABET *current_alphabet; | ||||
| // these were previously in translator class | // these were previously in translator class | ||||
| #ifdef PLATFORM_WINDOWS | #ifdef PLATFORM_WINDOWS | ||||
| char word_phonemes[N_WORD_PHONEMES*2]; // longer, because snprint() is not available | char word_phonemes[N_WORD_PHONEMES*2]; // longer, because snprint() is not available | ||||
| int n_ph_list2; | int n_ph_list2; | ||||
| PHONEME_LIST2 ph_list2[N_PHONEME_LIST]; // first stage of text->phonemes | PHONEME_LIST2 ph_list2[N_PHONEME_LIST]; // first stage of text->phonemes | ||||
| wchar_t option_punctlist[N_PUNCTLIST] = { 0 }; | wchar_t option_punctlist[N_PUNCTLIST] = { 0 }; | ||||
| char ctrl_embedded = '\001'; // to allow an alternative CTRL for embedded commands | char ctrl_embedded = '\001'; // to allow an alternative CTRL for embedded commands | ||||
| int option_multibyte = espeakCHARS_AUTO; // 0=auto, 1=utf8, 2=8bit, 3=wchar, 4=16bit | int option_multibyte = espeakCHARS_AUTO; // 0=auto, 1=utf8, 2=8bit, 3=wchar, 4=16bit | ||||
| int n_replace_phonemes; | int n_replace_phonemes; | ||||
| REPLACE_PHONEMES replace_phonemes[N_REPLACE_PHONEMES]; | REPLACE_PHONEMES replace_phonemes[N_REPLACE_PHONEMES]; | ||||
| // brackets, also 0x2014 to 0x021f which don't need to be in this list | // brackets, also 0x2014 to 0x021f which don't need to be in this list | ||||
| static const unsigned short brackets[] = { | static const unsigned short brackets[] = { | ||||
| '(', ')', '[', ']', '{', '}', '<', '>', '"', '\'', '`', | '(', ')', '[', ']', '{', '}', '<', '>', '"', '\'', '`', | ||||
| 110, 120, 100, 110, 110, 110, 110, 110, 110, 110 | 110, 120, 100, 110, 110, 110, 110, 110, 110, 110 | ||||
| }; | }; | ||||
| static unsigned char *length_mod_tabs[6] = { | static unsigned char *length_mod_tabs[6] = { | ||||
| length_mods_en, | length_mods_en, | ||||
| length_mods_en, // 1 | length_mods_en, // 1 | ||||
| length_mods_equal // 5 | length_mods_equal // 5 | ||||
| }; | }; | ||||
| void SetLengthMods(Translator *tr, int value) | void SetLengthMods(Translator *tr, int value) | ||||
| { | { | ||||
| int value2; | int value2; | ||||
| tr->langopts.length_mods0 = length_mod_tabs[value2]; | tr->langopts.length_mods0 = length_mod_tabs[value2]; | ||||
| } | } | ||||
| int IsAlpha(unsigned int c) | int IsAlpha(unsigned int c) | ||||
| { | { | ||||
| // Replacement for iswalph() which also checks for some in-word symbols | // Replacement for iswalph() which also checks for some in-word symbols | ||||
| return iswspace(c); | return iswspace(c); | ||||
| } | } | ||||
| void DeleteTranslator(Translator *tr) | void DeleteTranslator(Translator *tr) | ||||
| { | { | ||||
| if (tr->data_dictlist != NULL) | if (tr->data_dictlist != NULL) | ||||
| Free(tr); | Free(tr); | ||||
| } | } | ||||
| int lookupwchar(const unsigned short *list, int c) | int lookupwchar(const unsigned short *list, int c) | ||||
| { | { | ||||
| // Is the character c in the list ? | // Is the character c in the list ? | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| int lookupwchar2(const unsigned short *list, int c) | int lookupwchar2(const unsigned short *list, int c) | ||||
| { | { | ||||
| // Replace character c by another character. | // Replace character c by another character. | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| int IsBracket(int c) | int IsBracket(int c) | ||||
| { | { | ||||
| if ((c >= 0x2014) && (c <= 0x201f)) | if ((c >= 0x2014) && (c <= 0x201f)) | ||||
| return lookupwchar(brackets, c); | return lookupwchar(brackets, c); | ||||
| } | } | ||||
| int utf8_out(unsigned int c, char *buf) | int utf8_out(unsigned int c, char *buf) | ||||
| { | { | ||||
| // write a unicode character into a buffer as utf8 | // write a unicode character into a buffer as utf8 | ||||
| return n_bytes+1; | return n_bytes+1; | ||||
| } | } | ||||
| int utf8_nbytes(const char *buf) | int utf8_nbytes(const char *buf) | ||||
| { | { | ||||
| // Returns the number of bytes for the first UTF-8 character in buf | // Returns the number of bytes for the first UTF-8 character in buf | ||||
| return 4; | return 4; | ||||
| } | } | ||||
| int utf8_in2(int *c, const char *buf, int backwards) | int utf8_in2(int *c, const char *buf, int backwards) | ||||
| { | { | ||||
| // Read a unicode characater from a UTF8 string | // Read a unicode characater from a UTF8 string | ||||
| return n_bytes+1; | return n_bytes+1; | ||||
| } | } | ||||
| #pragma GCC visibility push(default) | #pragma GCC visibility push(default) | ||||
| int utf8_in(int *c, const char *buf) | int utf8_in(int *c, const char *buf) | ||||
| { | { | ||||
| } | } | ||||
| #pragma GCC visibility pop | #pragma GCC visibility pop | ||||
| char *strchr_w(const char *s, int c) | char *strchr_w(const char *s, int c) | ||||
| { | { | ||||
| // return NULL for any non-ascii character | // return NULL for any non-ascii character | ||||
| return strchr((char *)s, c); // (char *) is needed for Borland compiler | return strchr((char *)s, c); // (char *) is needed for Borland compiler | ||||
| } | } | ||||
| int IsAllUpper(const char *word) | int IsAllUpper(const char *word) | ||||
| { | { | ||||
| int c; | int c; | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| static char *SpeakIndividualLetters(Translator *tr, char *word, char *phonemes, int spell_word) | static char *SpeakIndividualLetters(Translator *tr, char *word, char *phonemes, int spell_word) | ||||
| { | { | ||||
| int posn = 0; | int posn = 0; | ||||
| return word; | return word; | ||||
| } | } | ||||
| static int CheckDottedAbbrev(char *word1, WORD_TAB *wtab) | static int CheckDottedAbbrev(char *word1, WORD_TAB *wtab) | ||||
| { | { | ||||
| int wc; | int wc; | ||||
| return count; | return count; | ||||
| } | } | ||||
| extern char *phondata_ptr; | extern char *phondata_ptr; | ||||
| int ChangeEquivalentPhonemes(Translator *tr, int lang2, char *phonemes) | int ChangeEquivalentPhonemes(Translator *tr, int lang2, char *phonemes) | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| int TranslateWord(Translator *tr, char *word_start, int next_pause, WORD_TAB *wtab, char *word_out) | int TranslateWord(Translator *tr, char *word_start, int next_pause, WORD_TAB *wtab, char *word_out) | ||||
| { | { | ||||
| // word1 is terminated by space (0x20) character | // word1 is terminated by space (0x20) character | ||||
| if (!found) | if (!found) | ||||
| found = LookupDictList(tr, &word1, phonemes, dictionary_flags, FLAG_ALLOW_TEXTMODE, wtab); // the original word | found = LookupDictList(tr, &word1, phonemes, dictionary_flags, FLAG_ALLOW_TEXTMODE, wtab); // the original word | ||||
| if ((dictionary_flags[0] & (FLAG_ALLOW_DOT | FLAG_NEEDS_DOT)) && (wordx[1] == '.')) | if ((dictionary_flags[0] & (FLAG_ALLOW_DOT | FLAG_NEEDS_DOT)) && (wordx[1] == '.')) | ||||
| wordx[1] = ' '; // remove a Dot after this word | wordx[1] = ' '; // remove a Dot after this word | ||||
| } | } | ||||
| } | } | ||||
| // if textmode, LookupDictList() replaces word1 by the new text and returns found=0 | // if textmode, LookupDictList() replaces word1 by the new text and returns found=0 | ||||
| if (phonemes[0] == phonSWITCH) { | if (phonemes[0] == phonSWITCH) { | ||||
| } | } | ||||
| } | } | ||||
| if ((end_type != 0) && !(end_type & SUFX_P)) { | if ((end_type != 0) && !(end_type & SUFX_P)) { | ||||
| end_type1 = end_type; | end_type1 = end_type; | ||||
| strcpy(phonemes2, phonemes); | strcpy(phonemes2, phonemes); | ||||
| memcpy(wordx, word_copy, strlen(word_copy)); | memcpy(wordx, word_copy, strlen(word_copy)); | ||||
| } | } | ||||
| wordx[-1] = c_temp; | wordx[-1] = c_temp; | ||||
| } | } | ||||
| } | } | ||||
| wflags |= emphasize_allcaps; | wflags |= emphasize_allcaps; | ||||
| /* determine stress pattern for this word */ | /* determine stress pattern for this word */ | ||||
| /******************************************/ | /******************************************/ | ||||
| add_suffix_phonemes = 0; | add_suffix_phonemes = 0; | ||||
| ChangeWordStress(tr, word_phonemes, 3); | ChangeWordStress(tr, word_phonemes, 3); | ||||
| } | } | ||||
| // dictionary flags for this word give a clue about which alternative pronunciations of | // dictionary flags for this word give a clue about which alternative pronunciations of | ||||
| // following words to use. | // following words to use. | ||||
| if (end_type1 & SUFX_F) { | if (end_type1 & SUFX_F) { | ||||
| return dictionary_flags[0]; | return dictionary_flags[0]; | ||||
| } | } | ||||
| static void SetPlist2(PHONEME_LIST2 *p, unsigned char phcode) | static void SetPlist2(PHONEME_LIST2 *p, unsigned char phcode) | ||||
| { | { | ||||
| p->phcode = phcode; | p->phcode = phcode; | ||||
| return count; | return count; | ||||
| } | } | ||||
| void Word_EmbeddedCmd() | void Word_EmbeddedCmd() | ||||
| { | { | ||||
| // Process embedded commands for emphasis, sayas, and break | // Process embedded commands for emphasis, sayas, and break | ||||
| } while (((embedded_cmd & 0x80) == 0) && (embedded_read < embedded_ix)); | } while (((embedded_cmd & 0x80) == 0) && (embedded_read < embedded_ix)); | ||||
| } | } | ||||
| int SetTranslator2(const char *new_language) | int SetTranslator2(const char *new_language) | ||||
| { | { | ||||
| // Set translator2 to a second language | // Set translator2 to a second language | ||||
| return new_phoneme_tab; | return new_phoneme_tab; | ||||
| } | } | ||||
| static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pause, int next_pause) | static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pause, int next_pause) | ||||
| { | { | ||||
| int flags = 0; | int flags = 0; | ||||
| return flags; | return flags; | ||||
| } | } | ||||
| static int EmbeddedCommand(unsigned int *source_index_out) | static int EmbeddedCommand(unsigned int *source_index_out) | ||||
| { | { | ||||
| // An embedded command to change the pitch, volume, etc. | // An embedded command to change the pitch, volume, etc. | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| static int SubstituteChar(Translator *tr, unsigned int c, unsigned int next_in, int *insert, int *wordflags) | static int SubstituteChar(Translator *tr, unsigned int c, unsigned int next_in, int *insert, int *wordflags) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| *wordflags |= FLAG_CHAR_REPLACED; | *wordflags |= FLAG_CHAR_REPLACED; | ||||
| return new_c; | return new_c; | ||||
| } | } | ||||
| static int TranslateChar(Translator *tr, char *ptr, int prev_in, unsigned int c, unsigned int next_in, int *insert, int *wordflags) | static int TranslateChar(Translator *tr, char *ptr, int prev_in, unsigned int c, unsigned int next_in, int *insert, int *wordflags) | ||||
| { | { | ||||
| // To allow language specific examination and replacement of characters | // To allow language specific examination and replacement of characters | ||||
| case L('n', 'l'): | case L('n', 'l'): | ||||
| // look for 'n and replace by a special character (unicode: schwa) | // look for 'n and replace by a special character (unicode: schwa) | ||||
| if (!iswalpha2(prev_in)) { | if (!iswalpha2(prev_in)) { | ||||
| utf8_in(&next2, &ptr[1]); | utf8_in(&next2, &ptr[1]); | ||||
| return SubstituteChar(tr, c, next_in, insert, wordflags); | return SubstituteChar(tr, c, next_in, insert, wordflags); | ||||
| } | } | ||||
| static const char *UCase_ga[] = { "bp", "bhf", "dt", "gc", "hA", "mb", "nd", "ng", "ts", "tA", "nA", NULL }; | static const char *UCase_ga[] = { "bp", "bhf", "dt", "gc", "hA", "mb", "nd", "ng", "ts", "tA", "nA", NULL }; | ||||
| int UpperCaseInWord(Translator *tr, char *word, int c) | int UpperCaseInWord(Translator *tr, char *word, int c) | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| void *TranslateClause(Translator *tr, FILE *f_text, const void *vp_input, int *tone_out, char **voice_change) | void *TranslateClause(Translator *tr, FILE *f_text, const void *vp_input, int *tone_out, char **voice_change) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| } | } | ||||
| words[0].flags |= FLAG_FIRST_WORD; | words[0].flags |= FLAG_FIRST_WORD; | ||||
| for (ix = 0; ix < word_count; ix++) { | for (ix = 0; ix < word_count; ix++) { | ||||
| int nx; | int nx; | ||||
| int c_temp; | int c_temp; | ||||
| embedded_list[embedded_ix] = 0x80; | embedded_list[embedded_ix] = 0x80; | ||||
| } | } | ||||
| prev_clause_pause = clause_pause; | prev_clause_pause = clause_pause; | ||||
| if (tone_out != NULL) | if (tone_out != NULL) | ||||
| if (terminator & CLAUSE_BIT_SENTENCE) | if (terminator & CLAUSE_BIT_SENTENCE) | ||||
| new_sentence = 1; // next clause is a new sentence | new_sentence = 1; // next clause is a new sentence | ||||
| if (voice_change != NULL) { | if (voice_change != NULL) { | ||||
| // return new voice name if an embedded voice change command terminated the clause | // return new voice name if an embedded voice change command terminated the clause | ||||
| if (terminator & CLAUSE_BIT_VOICE) | if (terminator & CLAUSE_BIT_VOICE) | ||||
| return (void *)p_textinput; | return (void *)p_textinput; | ||||
| } | } | ||||
| void InitText(int control) | void InitText(int control) | ||||
| { | { | ||||
| count_sentences = 0; | count_sentences = 0; |
| #include "voice.h" | #include "voice.h" | ||||
| #include "translate.h" | #include "translate.h" | ||||
| MNEM_TAB genders[] = { | MNEM_TAB genders[] = { | ||||
| { "unknown", 0 }, | { "unknown", 0 }, | ||||
| { "male", 1 }, | { "male", 1 }, | ||||
| static int formant_rate_22050[9] = { 240, 170, 170, 170, 170, 170, 170, 170, 170 }; // values for 22kHz sample rate | static int formant_rate_22050[9] = { 240, 170, 170, 170, 170, 170, 170, 170, 170 }; // values for 22kHz sample rate | ||||
| int formant_rate[9]; // values adjusted for actual sample rate | int formant_rate[9]; // values adjusted for actual sample rate | ||||
| #define DEFAULT_LANGUAGE_PRIORITY 5 | #define DEFAULT_LANGUAGE_PRIORITY 5 | ||||
| #define N_VOICES_LIST 250 | #define N_VOICES_LIST 250 | ||||
| static int n_voices_list = 0; | static int n_voices_list = 0; | ||||
| espeak_VOICE current_voice_selected; | espeak_VOICE current_voice_selected; | ||||
| enum { | enum { | ||||
| V_NAME = 1, | V_NAME = 1, | ||||
| V_LANGUAGE, | V_LANGUAGE, | ||||
| V_CONSONANTS | V_CONSONANTS | ||||
| }; | }; | ||||
| static MNEM_TAB options_tab[] = { | static MNEM_TAB options_tab[] = { | ||||
| { "reduce_t", LOPT_REDUCE_T }, | { "reduce_t", LOPT_REDUCE_T }, | ||||
| { "bracket", LOPT_BRACKET_PAUSE }, | { "bracket", LOPT_BRACKET_PAUSE }, | ||||
| { NULL, 0 } | { NULL, 0 } | ||||
| }; | }; | ||||
| #define N_VOICE_VARIANTS 12 | #define N_VOICE_VARIANTS 12 | ||||
| const char variants_either[N_VOICE_VARIANTS] = { 1, 2, 12, 3, 13, 4, 14, 5, 11, 0 }; | const char variants_either[N_VOICE_VARIANTS] = { 1, 2, 12, 3, 13, 4, 14, 5, 11, 0 }; | ||||
| const char variants_male[N_VOICE_VARIANTS] = { 1, 2, 3, 4, 5, 6, 0 }; | const char variants_male[N_VOICE_VARIANTS] = { 1, 2, 3, 4, 5, 6, 0 }; | ||||
| static voice_t voicedata; | static voice_t voicedata; | ||||
| voice_t *voice = &voicedata; | voice_t *voice = &voicedata; | ||||
| static char *fgets_strip(char *buf, int size, FILE *f_in) | static char *fgets_strip(char *buf, int size, FILE *f_in) | ||||
| { | { | ||||
| // strip trailing spaces, and truncate lines at // comment | // strip trailing spaces, and truncate lines at // comment | ||||
| return buf; | return buf; | ||||
| } | } | ||||
| static int LookupTune(const char *name) | static int LookupTune(const char *name) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| return -1; | return -1; | ||||
| } | } | ||||
| static void SetToneAdjust(voice_t *voice, int *tone_pts) | static void SetToneAdjust(voice_t *voice, int *tone_pts) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| } | } | ||||
| } | } | ||||
| void ReadTonePoints(char *string, int *tone_pts) | void ReadTonePoints(char *string, int *tone_pts) | ||||
| { | { | ||||
| // tone_pts[] is int[12] | // tone_pts[] is int[12] | ||||
| &tone_pts[8], &tone_pts[9]); | &tone_pts[8], &tone_pts[9]); | ||||
| } | } | ||||
| static espeak_VOICE *ReadVoiceFile(FILE *f_in, const char *fname, const char *leafname) | 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 | // Read a Voice file, allocate a VOICE_DATA and set data from the | ||||
| char vlanguage[80]; | char vlanguage[80]; | ||||
| char languages[300]; // allow space for several alternate language names and priorities | char languages[300]; // allow space for several alternate language names and priorities | ||||
| unsigned int len; | unsigned int len; | ||||
| int langix = 0; | int langix = 0; | ||||
| int n_languages = 0; | int n_languages = 0; | ||||
| return voice_data; | return voice_data; | ||||
| } | } | ||||
| void VoiceReset(int tone_only) | void VoiceReset(int tone_only) | ||||
| { | { | ||||
| // Set voice to the default values | // Set voice to the default values | ||||
| } | } | ||||
| } | } | ||||
| static void VoiceFormant(char *p) | static void VoiceFormant(char *p) | ||||
| { | { | ||||
| // Set parameters for a formant | // Set parameters for a formant | ||||
| voice->freqadd[formant] = freqadd; | voice->freqadd[formant] = freqadd; | ||||
| } | } | ||||
| static void PhonemeReplacement(int type, char *p) | static void PhonemeReplacement(int type, char *p) | ||||
| { | { | ||||
| int n; | int n; | ||||
| replace_phonemes[n_replace_phonemes++].type = flags; | replace_phonemes[n_replace_phonemes++].type = flags; | ||||
| } | } | ||||
| static int Read8Numbers(char *data_in, int *data) | static int Read8Numbers(char *data_in, int *data) | ||||
| { | { | ||||
| // Read 8 integer numbers | // Read 8 integer numbers | ||||
| &data[0], &data[1], &data[2], &data[3], &data[4], &data[5], &data[6], &data[7]); | &data[0], &data[1], &data[2], &data[3], &data[4], &data[5], &data[6], &data[7]); | ||||
| } | } | ||||
| static unsigned int StringToWord2(const char *string) | static unsigned int StringToWord2(const char *string) | ||||
| { | { | ||||
| // Convert a language name string to a word such as L('e','n') | // Convert a language name string to a word such as L('e','n') | ||||
| return value; | return value; | ||||
| } | } | ||||
| voice_t *LoadVoice(const char *vname, int control) | voice_t *LoadVoice(const char *vname, int control) | ||||
| { | { | ||||
| // control, bit 0 1= no_default | // control, bit 0 1= no_default | ||||
| static const char *voices_europe = | 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 "; | "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)); | strncpy0(voicename, vname, sizeof(voicename)); | ||||
| if (control & 0x10) { | if (control & 0x10) { | ||||
| strcpy(buf, vname); | strcpy(buf, vname); | ||||
| strcpy(new_dictionary, language_type); | strcpy(new_dictionary, language_type); | ||||
| strcpy(phonemes_name, language_type); | strcpy(phonemes_name, language_type); | ||||
| if (!tone_only) { | if (!tone_only) { | ||||
| voice = &voicedata; | voice = &voicedata; | ||||
| strncpy0(voice_identifier, vname, sizeof(voice_identifier)); | strncpy0(voice_identifier, vname, sizeof(voice_identifier)); | ||||
| if (!tone_only) | if (!tone_only) | ||||
| SelectPhonemeTableName(phonemes_name); // set up phoneme_tab | SelectPhonemeTableName(phonemes_name); // set up phoneme_tab | ||||
| while ((f_voice != NULL) && (fgets_strip(buf, sizeof(buf), f_voice) != NULL)) { | while ((f_voice != NULL) && (fgets_strip(buf, sizeof(buf), f_voice) != NULL)) { | ||||
| // isolate the attribute name | // isolate the attribute name | ||||
| for (p = buf; (*p != 0) && !isspace(*p); p++) ; | for (p = buf; (*p != 0) && !isspace(*p); p++) ; | ||||
| strncpy0(voice->language_name, language_name, sizeof(voice->language_name)); | strncpy0(voice->language_name, language_name, sizeof(voice->language_name)); | ||||
| } | } | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case V_NAME: | case V_NAME: | ||||
| if (tone_only == 0) { | if (tone_only == 0) { | ||||
| while (isspace(*p)) p++; | while (isspace(*p)) p++; | ||||
| strncpy0(voice_name, p, sizeof(voice_name)); | strncpy0(voice_name, p, sizeof(voice_name)); | ||||
| } | } | ||||
| break; | break; | ||||
| case V_GENDER: | case V_GENDER: | ||||
| { | { | ||||
| int age = 0; | int age = 0; | ||||
| current_voice_selected.gender = LookupMnem(genders, vgender); | current_voice_selected.gender = LookupMnem(genders, vgender); | ||||
| current_voice_selected.age = age; | current_voice_selected.age = age; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case V_TRANSLATOR: | case V_TRANSLATOR: | ||||
| if (tone_only) break; | if (tone_only) break; | ||||
| new_translator = SelectTranslator(translator_name); | new_translator = SelectTranslator(translator_name); | ||||
| langopts = &new_translator->langopts; | langopts = &new_translator->langopts; | ||||
| break; | break; | ||||
| case V_DICTIONARY: // dictionary | case V_DICTIONARY: // dictionary | ||||
| sscanf(p, "%s", new_dictionary); | sscanf(p, "%s", new_dictionary); | ||||
| break; | break; | ||||
| case V_PHONEMES: // phoneme table | case V_PHONEMES: // phoneme table | ||||
| sscanf(p, "%s", phonemes_name); | sscanf(p, "%s", phonemes_name); | ||||
| break; | break; | ||||
| case V_FORMANT: | case V_FORMANT: | ||||
| VoiceFormant(p); | VoiceFormant(p); | ||||
| break; | break; | ||||
| case V_PITCH: | case V_PITCH: | ||||
| { | { | ||||
| double factor; | double factor; | ||||
| factor = (double)(pitch1 - 82)/82; | factor = (double)(pitch1 - 82)/82; | ||||
| voice->formant_factor = (int)((1+factor/4) * 256); // nominal formant shift for a different voice pitch | voice->formant_factor = (int)((1+factor/4) * 256); // nominal formant shift for a different voice pitch | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case V_STRESSLENGTH: // stressLength | case V_STRESSLENGTH: // stressLength | ||||
| stress_lengths_set = Read8Numbers(p, stress_lengths); | stress_lengths_set = Read8Numbers(p, stress_lengths); | ||||
| break; | break; | ||||
| case V_STRESSAMP: // stressAmp | case V_STRESSAMP: // stressAmp | ||||
| stress_amps_set = Read8Numbers(p, stress_amps); | stress_amps_set = Read8Numbers(p, stress_amps); | ||||
| break; | break; | ||||
| case V_STRESSADD: // stressAdd | case V_STRESSADD: // stressAdd | ||||
| stress_add_set = Read8Numbers(p, stress_add); | stress_add_set = Read8Numbers(p, stress_add); | ||||
| break; | break; | ||||
| case V_INTONATION: // intonation | case V_INTONATION: // intonation | ||||
| sscanf(p, "%d %d", &option_tone_flags, &option_tone2); | sscanf(p, "%d %d", &option_tone_flags, &option_tone2); | ||||
| if ((option_tone_flags & 0xff) != 0) | if ((option_tone_flags & 0xff) != 0) | ||||
| langopts->intonation_group = option_tone_flags & 0xff; | langopts->intonation_group = option_tone_flags & 0xff; | ||||
| break; | break; | ||||
| case V_TUNES: | case V_TUNES: | ||||
| n = sscanf(p, "%s %s %s %s %s %s", names[0], names[1], names[2], names[3], names[4], names[5]); | 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; | langopts->intonation_group = 0; | ||||
| langopts->tunes[ix] = value; | langopts->tunes[ix] = value; | ||||
| } | } | ||||
| break; | break; | ||||
| case V_DICTRULES: // conditional dictionary rules and list entries | case V_DICTRULES: // conditional dictionary rules and list entries | ||||
| case V_NUMBERS: | case V_NUMBERS: | ||||
| case V_STRESSOPT: | case V_STRESSOPT: | ||||
| } | } | ||||
| ProcessLanguageOptions(langopts); | ProcessLanguageOptions(langopts); | ||||
| break; | break; | ||||
| case V_REPLACE: | case V_REPLACE: | ||||
| if (phonemes_set == 0) { | if (phonemes_set == 0) { | ||||
| // must set up a phoneme table before we can lookup phoneme mnemonics | // must set up a phoneme table before we can lookup phoneme mnemonics | ||||
| } | } | ||||
| PhonemeReplacement(key, p); | PhonemeReplacement(key, p); | ||||
| break; | break; | ||||
| case V_WORDGAP: // words | case V_WORDGAP: // words | ||||
| sscanf(p, "%d %d", &langopts->word_gap, &langopts->vowel_pause); | sscanf(p, "%d %d", &langopts->word_gap, &langopts->vowel_pause); | ||||
| break; | break; | ||||
| case V_STRESSRULE: | case V_STRESSRULE: | ||||
| sscanf(p, "%d %d %d %d", &langopts->stress_rule, | sscanf(p, "%d %d %d %d", &langopts->stress_rule, | ||||
| &langopts->stress_flags, | &langopts->stress_flags, | ||||
| &langopts->unstressed_wd1, | &langopts->unstressed_wd1, | ||||
| &langopts->unstressed_wd2); | &langopts->unstressed_wd2); | ||||
| break; | break; | ||||
| case V_CHARSET: | case V_CHARSET: | ||||
| if ((sscanf(p, "%d", &value) == 1) && (value < N_CHARSETS)) | if ((sscanf(p, "%d", &value) == 1) && (value < N_CHARSETS)) | ||||
| new_translator->charset_a0 = charsets[value]; | new_translator->charset_a0 = charsets[value]; | ||||
| break; | break; | ||||
| case V_OPTION: | case V_OPTION: | ||||
| value2 = 0; | value2 = 0; | ||||
| if (((sscanf(p, "%s %d %d", option_name, &value, &value2) >= 2) && ((ix = LookupMnem(options_tab, option_name)) >= 0)) || | if (((sscanf(p, "%s %d %d", option_name, &value, &value2) >= 2) && ((ix = LookupMnem(options_tab, option_name)) >= 0)) || | ||||
| } else | } else | ||||
| fprintf(stderr, "Bad voice option: %s %s\n", buf, p); | fprintf(stderr, "Bad voice option: %s %s\n", buf, p); | ||||
| break; | break; | ||||
| case V_ECHO: | case V_ECHO: | ||||
| // echo. suggest: 135mS 11% | // echo. suggest: 135mS 11% | ||||
| value = 0; | value = 0; | ||||
| voice->echo_amp = 0; | voice->echo_amp = 0; | ||||
| sscanf(p, "%d %d", &voice->echo_delay, &voice->echo_amp); | sscanf(p, "%d %d", &voice->echo_delay, &voice->echo_amp); | ||||
| break; | break; | ||||
| case V_FLUTTER: // flutter | case V_FLUTTER: // flutter | ||||
| if (sscanf(p, "%d", &value) == 1) | if (sscanf(p, "%d", &value) == 1) | ||||
| voice->flutter = value * 32; | voice->flutter = value * 32; | ||||
| break; | break; | ||||
| case V_ROUGHNESS: // roughness | case V_ROUGHNESS: // roughness | ||||
| if (sscanf(p, "%d", &value) == 1) | if (sscanf(p, "%d", &value) == 1) | ||||
| voice->roughness = value; | voice->roughness = value; | ||||
| break; | break; | ||||
| case V_CLARITY: // formantshape | case V_CLARITY: // formantshape | ||||
| if (sscanf(p, "%d", &value) == 1) { | if (sscanf(p, "%d", &value) == 1) { | ||||
| if (value > 4) { | if (value > 4) { | ||||
| voice->n_harmonic_peaks = 1+value; | voice->n_harmonic_peaks = 1+value; | ||||
| } | } | ||||
| break; | break; | ||||
| case V_TONE: | case V_TONE: | ||||
| { | { | ||||
| int tone_data[12]; | int tone_data[12]; | ||||
| ReadTonePoints(p, tone_data); | ReadTonePoints(p, tone_data); | ||||
| SetToneAdjust(voice, tone_data); | SetToneAdjust(voice, tone_data); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case V_VOICING: | case V_VOICING: | ||||
| if (sscanf(p, "%d", &value) == 1) | if (sscanf(p, "%d", &value) == 1) | ||||
| voice->voicing = (value * 64)/100; | voice->voicing = (value * 64)/100; | ||||
| break; | break; | ||||
| case V_BREATH: | case V_BREATH: | ||||
| voice->breath[0] = Read8Numbers(p, &voice->breath[1]); | voice->breath[0] = Read8Numbers(p, &voice->breath[1]); | ||||
| for (ix = 1; ix < 8; ix++) { | for (ix = 1; ix < 8; ix++) { | ||||
| voice->breath[ix] = -voice->breath[ix]; | voice->breath[ix] = -voice->breath[ix]; | ||||
| } | } | ||||
| break; | break; | ||||
| case V_BREATHW: | case V_BREATHW: | ||||
| voice->breathw[0] = Read8Numbers(p, &voice->breathw[1]); | voice->breathw[0] = Read8Numbers(p, &voice->breathw[1]); | ||||
| break; | break; | ||||
| case V_CONSONANTS: | case V_CONSONANTS: | ||||
| value = sscanf(p, "%d %d", &voice->consonant_amp, &voice->consonant_ampv); | value = sscanf(p, "%d %d", &voice->consonant_amp, &voice->consonant_ampv); | ||||
| break; | break; | ||||
| case V_SPEED: | case V_SPEED: | ||||
| sscanf(p, "%d", &voice->speed_percent); | sscanf(p, "%d", &voice->speed_percent); | ||||
| break; | break; | ||||
| case V_MBROLA: | case V_MBROLA: | ||||
| { | { | ||||
| int srate = 16000; | int srate = 16000; | ||||
| } | } | ||||
| voice->samplerate = srate; | voice->samplerate = srate; | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case V_KLATT: | case V_KLATT: | ||||
| voice->klattv[0] = 1; // default source: IMPULSIVE | voice->klattv[0] = 1; // default source: IMPULSIVE | ||||
| Read8Numbers(p, voice->klattv); | Read8Numbers(p, voice->klattv); | ||||
| voice->klattv[KLATT_Kopen] -= 40; | voice->klattv[KLATT_Kopen] -= 40; | ||||
| break; | break; | ||||
| case V_FAST: | case V_FAST: | ||||
| Read8Numbers(p, speed.fast_settings); | Read8Numbers(p, speed.fast_settings); | ||||
| SetSpeed(3); | SetSpeed(3); | ||||
| break; | break; | ||||
| case V_DICTMIN: | case V_DICTMIN: | ||||
| sscanf(p, "%d", &dict_min); | sscanf(p, "%d", &dict_min); | ||||
| break; | break; | ||||
| case V_ALPHABET2: | case V_ALPHABET2: | ||||
| { | { | ||||
| ALPHABET *alphabet; | ALPHABET *alphabet; | ||||
| } else | } else | ||||
| fprintf(stderr, "alphabet name '%s' not found\n", name1); | fprintf(stderr, "alphabet name '%s' not found\n", name1); | ||||
| } | } | ||||
| break; | |||||
| break; | |||||
| case V_DICTDIALECT: | case V_DICTDIALECT: | ||||
| // specify a dialect to use for foreign words, eg, en-us for _^_EN | // specify a dialect to use for foreign words, eg, en-us for _^_EN | ||||
| if (sscanf(p, "%s", name1) == 1) { | if (sscanf(p, "%s", name1) == 1) { | ||||
| fprintf(stderr, "dictdialect name '%s' not recognized\n", name1); | fprintf(stderr, "dictdialect name '%s' not recognized\n", name1); | ||||
| } | } | ||||
| break; | break; | ||||
| default: | default: | ||||
| if ((key & 0xff00) == 0x100) | if ((key & 0xff00) == 0x100) | ||||
| sscanf(p, "%d", &langopts->param[key &0xff]); | sscanf(p, "%d", &langopts->param[key &0xff]); | ||||
| return voice; | return voice; | ||||
| } | } | ||||
| static char *ExtractVoiceVariantName(char *vname, int variant_num, int add_dir) | static char *ExtractVoiceVariantName(char *vname, int variant_num, int add_dir) | ||||
| { | { | ||||
| // Remove any voice variant suffix (name or number) from a voice name | // Remove any voice variant suffix (name or number) from a voice name | ||||
| return variant_name; | return variant_name; | ||||
| } | } | ||||
| voice_t *LoadVoiceVariant(const char *vname, int variant_num) | voice_t *LoadVoiceVariant(const char *vname, int variant_num) | ||||
| { | { | ||||
| // Load a voice file. | // Load a voice file. | ||||
| return v; | return v; | ||||
| } | } | ||||
| static int __cdecl VoiceNameSorter(const void *p1, const void *p2) | static int __cdecl VoiceNameSorter(const void *p1, const void *p2) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| espeak_VOICE *v1 = *(espeak_VOICE **)p1; | espeak_VOICE *v1 = *(espeak_VOICE **)p1; | ||||
| espeak_VOICE *v2 = *(espeak_VOICE **)p2; | espeak_VOICE *v2 = *(espeak_VOICE **)p2; | ||||
| if ((ix = strcmp(&v1->languages[1], &v2->languages[1])) != 0) // primary language name | if ((ix = strcmp(&v1->languages[1], &v2->languages[1])) != 0) // primary language name | ||||
| return ix; | return ix; | ||||
| if ((ix = v1->languages[0] - v2->languages[0]) != 0) // priority number | if ((ix = v1->languages[0] - v2->languages[0]) != 0) // priority number | ||||
| return strcmp(v1->name, v2->name); | return strcmp(v1->name, v2->name); | ||||
| } | } | ||||
| static int __cdecl VoiceScoreSorter(const void *p1, const void *p2) | static int __cdecl VoiceScoreSorter(const void *p1, const void *p2) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| return strcmp(v1->name, v2->name); | return strcmp(v1->name, v2->name); | ||||
| } | } | ||||
| static int ScoreVoice(espeak_VOICE *voice_spec, const char *spec_language, int spec_n_parts, int spec_lang_len, espeak_VOICE *voice) | static int ScoreVoice(espeak_VOICE *voice_spec, const char *spec_language, int spec_n_parts, int spec_lang_len, espeak_VOICE *voice) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| return score; | return score; | ||||
| } | } | ||||
| static int SetVoiceScores(espeak_VOICE *voice_select, espeak_VOICE **voices, int control) | static int SetVoiceScores(espeak_VOICE *voice_select, espeak_VOICE **voices, int control) | ||||
| { | { | ||||
| // control: bit0=1 include mbrola voices | // control: bit0=1 include mbrola voices | ||||
| language[lang_len] = 0; | language[lang_len] = 0; | ||||
| n_parts = -1; | n_parts = -1; | ||||
| } | } | ||||
| } | } | ||||
| // select those voices which match the specified language | // select those voices which match the specified language | ||||
| return nv; | return nv; | ||||
| } | } | ||||
| espeak_VOICE *SelectVoiceByName(espeak_VOICE **voices, const char *name2) | espeak_VOICE *SelectVoiceByName(espeak_VOICE **voices, const char *name2) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| return voices[match_name]; | return voices[match_name]; | ||||
| } | } | ||||
| char const *SelectVoice(espeak_VOICE *voice_select, int *found) | char const *SelectVoice(espeak_VOICE *voice_select, int *found) | ||||
| { | { | ||||
| // Returns a path within espeak-voices, with a possible +variant suffix | // Returns a path within espeak-voices, with a possible +variant suffix | ||||
| return vp->identifier; | return vp->identifier; | ||||
| } | } | ||||
| static void GetVoices(const char *path) | static void GetVoices(const char *path) | ||||
| { | { | ||||
| FILE *f_voice; | FILE *f_voice; | ||||
| } | } | ||||
| } while (FindNextFileA(hFind, &FindFileData) != 0); | } while (FindNextFileA(hFind, &FindFileData) != 0); | ||||
| FindClose(hFind); | FindClose(hFind); | ||||
| #else | #else | ||||
| DIR *dir; | DIR *dir; | ||||
| struct dirent *ent; | struct dirent *ent; | ||||
| #endif | #endif | ||||
| } | } | ||||
| espeak_ERROR SetVoiceByName(const char *name) | espeak_ERROR SetVoiceByName(const char *name) | ||||
| { | { | ||||
| espeak_VOICE *v; | espeak_VOICE *v; | ||||
| return EE_INTERNAL_ERROR; // voice name not found | return EE_INTERNAL_ERROR; // voice name not found | ||||
| } | } | ||||
| espeak_ERROR SetVoiceByProperties(espeak_VOICE *voice_selector) | espeak_ERROR SetVoiceByProperties(espeak_VOICE *voice_selector) | ||||
| { | { | ||||
| const char *voice_id; | const char *voice_id; | ||||
| return EE_OK; | return EE_OK; | ||||
| } | } | ||||
| void FreeVoiceList() | void FreeVoiceList() | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| n_voices_list = 0; | n_voices_list = 0; | ||||
| } | } | ||||
| #pragma GCC visibility push(default) | #pragma GCC visibility push(default) | ||||
| ESPEAK_API const espeak_VOICE **espeak_ListVoices(espeak_VOICE *voice_spec) | ESPEAK_API const espeak_VOICE **espeak_ListVoices(espeak_VOICE *voice_spec) | ||||
| { | { | ||||
| char path_voices[sizeof(path_home)+12]; | char path_voices[sizeof(path_home)+12]; | ||||
| #ifdef PLATFORM_RISCOS | #ifdef PLATFORM_RISCOS | ||||
| if (n_voices_list == 0) { | if (n_voices_list == 0) { | ||||
| sprintf(path_voices, "%s%cvoices", path_home, PATHSEP); | sprintf(path_voices, "%s%cvoices", path_home, PATHSEP); | ||||
| voices_list[n_voices_list] = NULL; // voices list terminator | voices_list[n_voices_list] = NULL; // voices list terminator | ||||
| } | } | ||||
| return (const espeak_VOICE **)voices_list; | return (const espeak_VOICE **)voices_list; | ||||
| #else | #else | ||||
| int ix; | int ix; | ||||
| int j; | int j; | ||||
| qsort(voices_list, n_voices_list, sizeof(espeak_VOICE *), | qsort(voices_list, n_voices_list, sizeof(espeak_VOICE *), | ||||
| (int(__cdecl *)(const void *, const void *))VoiceNameSorter); | (int(__cdecl *)(const void *, const void *))VoiceNameSorter); | ||||
| if (voice_spec) { | if (voice_spec) { | ||||
| // select the voices which match the voice_spec, and sort them by preference | // select the voices which match the voice_spec, and sort them by preference | ||||
| SetVoiceScores(voice_spec, voices, 1); | SetVoiceScores(voice_spec, voices, 1); | ||||
| #endif | #endif | ||||
| } | } | ||||
| ESPEAK_API espeak_VOICE *espeak_GetCurrentVoice(void) | ESPEAK_API espeak_VOICE *espeak_GetCurrentVoice(void) | ||||
| { | { | ||||
| return ¤t_voice_selected; | return ¤t_voice_selected; |
| }; | }; | ||||
| #endif /* HAVE_STRUCT_TIMESPEC */ | #endif /* HAVE_STRUCT_TIMESPEC */ | ||||
| enum { ONE_BILLION = 1000000000 }; | enum { ONE_BILLION = 1000000000 }; | ||||
| #ifdef USE_PORTAUDIO | #ifdef USE_PORTAUDIO | ||||
| #define USE_PORTAUDIO 18 | #define USE_PORTAUDIO 18 | ||||
| #endif | #endif | ||||
| #ifdef USE_PULSEAUDIO | #ifdef USE_PULSEAUDIO | ||||
| // create some wrappers for runtime detection | // create some wrappers for runtime detection | ||||
| #endif // USE_PULSEAUDIO | #endif // USE_PULSEAUDIO | ||||
| static t_wave_callback *my_callback_is_output_enabled = NULL; | static t_wave_callback *my_callback_is_output_enabled = NULL; | ||||
| #define N_WAV_BUF 10 | #define N_WAV_BUF 10 | ||||
| return aResult; | return aResult; | ||||
| } | } | ||||
| void wave_flush(void *theHandler) | void wave_flush(void *theHandler) | ||||
| { | { | ||||
| ENTER("wave_flush"); | ENTER("wave_flush"); | ||||
| out_channels = 1; | out_channels = 1; | ||||
| #if USE_PORTAUDIO == 18 | #if USE_PORTAUDIO == 18 | ||||
| PaDeviceID playbackDevice = Pa_GetDefaultOutputDeviceID(); | PaDeviceID playbackDevice = Pa_GetDefaultOutputDeviceID(); | ||||
| PaError err = Pa_OpenStream(&pa_stream, | PaError err = Pa_OpenStream(&pa_stream, | ||||
| SHOW("wave_is_busy: %d\n", active); | SHOW("wave_is_busy: %d\n", active); | ||||
| return active == 1; | return active == 1; | ||||
| } | } | ||||
| ENTER("wave_terminate"); | ENTER("wave_terminate"); | ||||
| Pa_Terminate(); | Pa_Terminate(); | ||||
| } | } | ||||
| uint32_t wave_get_read_position(void *theHandler) | uint32_t wave_get_read_position(void *theHandler) | ||||
| return myWrite; | return myWrite; | ||||
| } | } | ||||
| #else | #else | ||||
| int wave_init(int srate) { | int wave_init(int srate) { | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| ts->tv_nsec = (long int)t_ns; | ts->tv_nsec = (long int)t_ns; | ||||
| } | } | ||||
| #endif // USE_ASYNC | #endif // USE_ASYNC |
| case PA_CONTEXT_FAILED: | case PA_CONTEXT_FAILED: | ||||
| pa_threaded_mainloop_signal(mainloop, 0); | pa_threaded_mainloop_signal(mainloop, 0); | ||||
| break; | break; | ||||
| case PA_CONTEXT_UNCONNECTED: | case PA_CONTEXT_UNCONNECTED: | ||||
| case PA_CONTEXT_CONNECTING: | case PA_CONTEXT_CONNECTING: | ||||
| case PA_CONTEXT_AUTHORIZING: | case PA_CONTEXT_AUTHORIZING: | ||||
| switch (pa_stream_get_state(s)) | switch (pa_stream_get_state(s)) | ||||
| { | { | ||||
| case PA_STREAM_READY: | case PA_STREAM_READY: | ||||
| case PA_STREAM_FAILED: | case PA_STREAM_FAILED: | ||||
| case PA_STREAM_TERMINATED: | case PA_STREAM_TERMINATED: | ||||
| pa_threaded_mainloop_signal(mainloop, 0); | pa_threaded_mainloop_signal(mainloop, 0); | ||||
| break; | break; | ||||
| case PA_STREAM_UNCONNECTED: | case PA_STREAM_UNCONNECTED: | ||||
| case PA_STREAM_CREATING: | case PA_STREAM_CREATING: | ||||
| break; | break; | ||||
| static void pulse_write(void *ptr, int length) { | static void pulse_write(void *ptr, int length) { | ||||
| ENTER(__FUNCTION__); | ENTER(__FUNCTION__); | ||||
| SHOW("pulse_write > length=%d\n", length); | SHOW("pulse_write > length=%d\n", length); | ||||
| CHECK_CONNECTED_NO_RETVAL(); | CHECK_CONNECTED_NO_RETVAL(); | ||||
| do_trigger = 0; | do_trigger = 0; | ||||
| written += length; | written += length; | ||||
| fail: | fail: | ||||
| pa_threaded_mainloop_unlock(mainloop); | pa_threaded_mainloop_unlock(mainloop); | ||||
| } | } | ||||
| SHOW("pa_stream_drain() failed: %s\n", pa_strerror(pa_context_errno(context))); | SHOW("pa_stream_drain() failed: %s\n", pa_strerror(pa_context_errno(context))); | ||||
| else | else | ||||
| ret = PULSE_OK; | ret = PULSE_OK; | ||||
| fail: | fail: | ||||
| SHOW_TIME("pa_operation_unref (call)"); | SHOW_TIME("pa_operation_unref (call)"); | ||||
| if (o) | if (o) | ||||
| return ret; | return ret; | ||||
| } | } | ||||
| static void pulse_close(void) { | static void pulse_close(void) { | ||||
| ENTER(__FUNCTION__); | ENTER(__FUNCTION__); | ||||
| } | } | ||||
| static int pulse_open() | static int pulse_open() | ||||
| { | { | ||||
| ENTER(__FUNCTION__); | ENTER(__FUNCTION__); | ||||
| SHOW_TIME("pulse_open (ret true)"); | SHOW_TIME("pulse_open (ret true)"); | ||||
| return PULSE_OK; | return PULSE_OK; | ||||
| unlock_and_fail: | unlock_and_fail: | ||||
| if (o) | if (o) | ||||
| pa_operation_unref(o); | pa_operation_unref(o); | ||||
| pa_threaded_mainloop_unlock(mainloop); | pa_threaded_mainloop_unlock(mainloop); | ||||
| fail: | fail: | ||||
| if (ret == PULSE_NO_CONNECTION) { | if (ret == PULSE_NO_CONNECTION) { | ||||
| if (context) { | if (context) { | ||||
| SHOW_TIME("pa_context_disconnect (call)"); | SHOW_TIME("pa_context_disconnect (call)"); | ||||
| SHOW_TIME("pulse_open (ret false)"); | SHOW_TIME("pulse_open (ret false)"); | ||||
| return ret; | return ret; | ||||
| } | } | ||||
| void wave_flush(void *theHandler) | void wave_flush(void *theHandler) | ||||
| } | } | ||||
| pulse_write(aBuffer, bytes_to_write); | pulse_write(aBuffer, bytes_to_write); | ||||
| terminate: | terminate: | ||||
| pthread_mutex_unlock(&pulse_mutex); | pthread_mutex_unlock(&pulse_mutex); | ||||
| SHOW("wave_write: theSize=%d", theSize); | SHOW("wave_write: theSize=%d", theSize); | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| #else | #else | ||||
| int wave_init(return 1; ) { | int wave_init(return 1; ) { | ||||
| } | } | ||||
| void *wave_open(const char *the_api) { | void *wave_open(const char *the_api) { | ||||
| } | } | ||||
| #endif // ifndef USE_PORTAUDIO | #endif // ifndef USE_PORTAUDIO | ||||
| #endif // USE_ASYNC | #endif // USE_ASYNC |
| #include <stdint.h> | #include <stdint.h> | ||||
| #endif | #endif | ||||
| #include "speak_lib.h" | #include "speak_lib.h" | ||||
| #include "speech.h" | #include "speech.h" | ||||
| #include "phoneme.h" | #include "phoneme.h" | ||||
| #define N_SINTAB 2048 | #define N_SINTAB 2048 | ||||
| #include "sintab.h" | #include "sintab.h" | ||||
| #define PI 3.1415927 | #define PI 3.1415927 | ||||
| #define PI2 6.283185307 | #define PI2 6.283185307 | ||||
| #define N_WAV_BUF 10 | #define N_WAV_BUF 10 | ||||
| static int harm_sqrt_n = 0; | static int harm_sqrt_n = 0; | ||||
| #define N_LOWHARM 30 | #define N_LOWHARM 30 | ||||
| static int harm_inc[N_LOWHARM]; // only for these harmonics do we interpolate amplitude between steps | static int harm_inc[N_LOWHARM]; // only for these harmonics do we interpolate amplitude between steps | ||||
| static int *harmspect; | static int *harmspect; | ||||
| static int glottal_flag = 0; | static int glottal_flag = 0; | ||||
| static int glottal_reduce = 0; | static int glottal_reduce = 0; | ||||
| WGEN_DATA wdata; | WGEN_DATA wdata; | ||||
| static int amp_ix; | static int amp_ix; | ||||
| static double minus_pi_t; | static double minus_pi_t; | ||||
| static double two_pi_t; | static double two_pi_t; | ||||
| unsigned char *out_ptr; | unsigned char *out_ptr; | ||||
| unsigned char *out_start; | unsigned char *out_start; | ||||
| unsigned char *out_end; | unsigned char *out_end; | ||||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 | ||||
| }; | }; | ||||
| // set from y = pow(2,x) * 128, x=-1 to 1 | // set from y = pow(2,x) * 128, x=-1 to 1 | ||||
| unsigned char pitch_adjust_tab[MAX_PITCH_VALUE+1] = { | unsigned char pitch_adjust_tab[MAX_PITCH_VALUE+1] = { | ||||
| 64, 65, 66, 67, 68, 69, 70, 71, | 64, 65, 66, 67, 68, 69, 70, 71, | ||||
| 242, 246, 249, 252, 254, 255 | 242, 246, 249, 252, 254, 255 | ||||
| }; | }; | ||||
| void WcmdqStop() | void WcmdqStop() | ||||
| { | { | ||||
| wcmdq_head = 0; | wcmdq_head = 0; | ||||
| MbrolaReset(); | MbrolaReset(); | ||||
| } | } | ||||
| int WcmdqFree() | int WcmdqFree() | ||||
| { | { | ||||
| int i; | int i; | ||||
| return N_WCMDQ - WcmdqFree(); | return N_WCMDQ - WcmdqFree(); | ||||
| } | } | ||||
| void WcmdqInc() | void WcmdqInc() | ||||
| { | { | ||||
| wcmdq_tail++; | wcmdq_tail++; | ||||
| if (wcmdq_head >= N_WCMDQ) wcmdq_head = 0; | if (wcmdq_head >= N_WCMDQ) wcmdq_head = 0; | ||||
| } | } | ||||
| #define PEAKSHAPEW 256 | #define PEAKSHAPEW 256 | ||||
| unsigned char pk_shape1[PEAKSHAPEW+1] = { | unsigned char pk_shape1[PEAKSHAPEW+1] = { | ||||
| static unsigned char *pk_shape; | static unsigned char *pk_shape; | ||||
| #ifdef USE_PORTAUDIO | #ifdef USE_PORTAUDIO | ||||
| // PortAudio interface | // PortAudio interface | ||||
| unsigned char *outbuffer = NULL; | unsigned char *outbuffer = NULL; | ||||
| int outbuffer_size = 0; | int outbuffer_size = 0; | ||||
| #if USE_PORTAUDIO == 18 | #if USE_PORTAUDIO == 18 | ||||
| static int WaveCallback(void *inputBuffer, void *outputBuffer, | static int WaveCallback(void *inputBuffer, void *outputBuffer, | ||||
| unsigned long framesPerBuffer, PaTimestamp outTime, void *userData) | unsigned long framesPerBuffer, PaTimestamp outTime, void *userData) | ||||
| #else | #else | ||||
| return result; | return result; | ||||
| #endif | #endif | ||||
| } | } | ||||
| #if USE_PORTAUDIO == 19 | #if USE_PORTAUDIO == 19 | ||||
| /* This is a fixed version of Pa_OpenDefaultStream() for use if the version in portaudio V19 | /* This is a fixed version of Pa_OpenDefaultStream() for use if the version in portaudio V19 | ||||
| is broken */ | is broken */ | ||||
| } | } | ||||
| #endif | #endif | ||||
| int WavegenOpenSound() | int WavegenOpenSound() | ||||
| { | { | ||||
| PaError err, err2; | PaError err, err2; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| int WavegenCloseSound() | int WavegenCloseSound() | ||||
| { | { | ||||
| PaError active; | PaError active; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| int WavegenInitSound() | int WavegenInitSound() | ||||
| { | { | ||||
| PaError err; | PaError err; | ||||
| } | } | ||||
| #endif | #endif | ||||
| void WavegenInit(int rate, int wavemult_fact) | void WavegenInit(int rate, int wavemult_fact) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| for (ix = 0; ix < N_EMBEDDED_VALUES; ix++) | for (ix = 0; ix < N_EMBEDDED_VALUES; ix++) | ||||
| embedded_value[ix] = embedded_default[ix]; | embedded_value[ix] = embedded_default[ix]; | ||||
| // set up window to generate a spread of harmonics from a | // set up window to generate a spread of harmonics from a | ||||
| // single peak for HF peaks | // single peak for HF peaks | ||||
| wavemult_max = (samplerate * wavemult_fact)/(256 * 50); | wavemult_max = (samplerate * wavemult_fact)/(256 * 50); | ||||
| #endif | #endif | ||||
| } | } | ||||
| int GetAmplitude(void) | int GetAmplitude(void) | ||||
| { | { | ||||
| int amp; | int amp; | ||||
| return general_amplitude; | return general_amplitude; | ||||
| } | } | ||||
| static void WavegenSetEcho(void) | static void WavegenSetEcho(void) | ||||
| { | { | ||||
| int delay; | int delay; | ||||
| general_amplitude = ((general_amplitude * (500-amp))/500); | general_amplitude = ((general_amplitude * (500-amp))/500); | ||||
| } | } | ||||
| int PeaksToHarmspect(wavegen_peaks_t *peaks, int pitch, int *htab, int control) | int PeaksToHarmspect(wavegen_peaks_t *peaks, int pitch, int *htab, int control) | ||||
| { | { | ||||
| // Calculate the amplitude of each harmonics from the formants | // Calculate the amplitude of each harmonics from the formants | ||||
| return hmax; // highest harmonic number | return hmax; // highest harmonic number | ||||
| } | } | ||||
| static void AdvanceParameters() | static void AdvanceParameters() | ||||
| { | { | ||||
| // Called every 64 samples to increment the formant freq, height, and widths | // Called every 64 samples to increment the formant freq, height, and widths | ||||
| #endif | #endif | ||||
| } | } | ||||
| #ifndef PLATFORM_RISCOS | #ifndef PLATFORM_RISCOS | ||||
| static double resonator(RESONATOR *r, double input) | static double resonator(RESONATOR *r, double input) | ||||
| { | { | ||||
| return x; | return x; | ||||
| } | } | ||||
| static void setresonator(RESONATOR *rp, int freq, int bwidth, int init) | static void setresonator(RESONATOR *rp, int freq, int bwidth, int init) | ||||
| { | { | ||||
| // freq Frequency of resonator in Hz | // freq Frequency of resonator in Hz | ||||
| } | } | ||||
| #endif | #endif | ||||
| void InitBreath(void) | void InitBreath(void) | ||||
| { | { | ||||
| #ifndef PLATFORM_RISCOS | #ifndef PLATFORM_RISCOS | ||||
| #endif | #endif | ||||
| } | } | ||||
| static void SetBreath() | static void SetBreath() | ||||
| { | { | ||||
| #ifndef PLATFORM_RISCOS | #ifndef PLATFORM_RISCOS | ||||
| #endif | #endif | ||||
| } | } | ||||
| static int ApplyBreath(void) | static int ApplyBreath(void) | ||||
| { | { | ||||
| int value = 0; | int value = 0; | ||||
| return value; | return value; | ||||
| } | } | ||||
| int Wavegen() | int Wavegen() | ||||
| { | { | ||||
| unsigned short waveph; | unsigned short waveph; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static int PlaySilence(int length, int resume) | static int PlaySilence(int length, int resume) | ||||
| { | { | ||||
| static int n_samples; | static int n_samples; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static int PlayWave(int length, int resume, unsigned char *data, int scale, int amp) | static int PlayWave(int length, int resume, unsigned char *data, int scale, int amp) | ||||
| { | { | ||||
| static int n_samples; | static int n_samples; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static int SetWithRange0(int value, int max) | static int SetWithRange0(int value, int max) | ||||
| { | { | ||||
| if (value < 0) | if (value < 0) | ||||
| return value; | return value; | ||||
| } | } | ||||
| static void SetPitchFormants() | static void SetPitchFormants() | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| wvoice->height[1] = (wvoice->height2[1] * (256 - factor))/256; | wvoice->height[1] = (wvoice->height2[1] * (256 - factor))/256; | ||||
| } | } | ||||
| void SetEmbedded(int control, int value) | void SetEmbedded(int control, int value) | ||||
| { | { | ||||
| // there was an embedded command in the text at this point | // there was an embedded command in the text at this point | ||||
| case EMBED_P: | case EMBED_P: | ||||
| SetPitchFormants(); | SetPitchFormants(); | ||||
| break; | break; | ||||
| case EMBED_A: // amplitude | case EMBED_A: // amplitude | ||||
| general_amplitude = GetAmplitude(); | general_amplitude = GetAmplitude(); | ||||
| break; | break; | ||||
| case EMBED_F: // emphasis | case EMBED_F: // emphasis | ||||
| general_amplitude = GetAmplitude(); | general_amplitude = GetAmplitude(); | ||||
| break; | break; | ||||
| case EMBED_H: | case EMBED_H: | ||||
| WavegenSetEcho(); | WavegenSetEcho(); | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| void WavegenSetVoice(voice_t *v) | void WavegenSetVoice(voice_t *v) | ||||
| { | { | ||||
| static voice_t v2; | static voice_t v2; | ||||
| MarkerEvent(espeakEVENT_SAMPLERATE, 0, wvoice->samplerate, 0, out_ptr); | MarkerEvent(espeakEVENT_SAMPLERATE, 0, wvoice->samplerate, 0, out_ptr); | ||||
| } | } | ||||
| static void SetAmplitude(int length, unsigned char *amp_env, int value) | static void SetAmplitude(int length, unsigned char *amp_env, int value) | ||||
| { | { | ||||
| amp_ix = 0; | amp_ix = 0; | ||||
| amplitude_env = amp_env; | amplitude_env = amp_env; | ||||
| } | } | ||||
| void SetPitch2(voice_t *voice, int pitch1, int pitch2, int *pitch_base, int *pitch_range) | void SetPitch2(voice_t *voice, int pitch1, int pitch2, int *pitch_base, int *pitch_range) | ||||
| { | { | ||||
| int x; | int x; | ||||
| *pitch_range = base + (pitch2 * range)/2 - *pitch_base; | *pitch_range = base + (pitch2 * range)/2 - *pitch_base; | ||||
| } | } | ||||
| void SetPitch(int length, unsigned char *env, int pitch1, int pitch2) | void SetPitch(int length, unsigned char *env, int pitch1, int pitch2) | ||||
| { | { | ||||
| // length in samples | // length in samples | ||||
| wdata.pitch = ((wdata.pitch_env[0] * wdata.pitch_range) >>8) + wdata.pitch_base; // Hz << 12 | wdata.pitch = ((wdata.pitch_env[0] * wdata.pitch_range) >>8) + wdata.pitch_base; // Hz << 12 | ||||
| flutter_amp = wvoice->flutter; | flutter_amp = wvoice->flutter; | ||||
| } | } | ||||
| void SetSynth(int length, int modn, frame_t *fr1, frame_t *fr2, voice_t *v) | void SetSynth(int length, int modn, frame_t *fr1, frame_t *fr2, voice_t *v) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| } | } | ||||
| } | } | ||||
| static int Wavegen2(int length, int modulation, int resume, frame_t *fr1, frame_t *fr2) | static int Wavegen2(int length, int modulation, int resume, frame_t *fr1, frame_t *fr2) | ||||
| { | { | ||||
| if (resume == 0) | if (resume == 0) | ||||
| } | } | ||||
| } | } | ||||
| int WavegenFill2(int fill_zeros) | int WavegenFill2(int fill_zeros) | ||||
| { | { | ||||
| // Pick up next wavegen commands from the queue | // Pick up next wavegen commands from the queue | ||||
| case WCMD_PITCH: | case WCMD_PITCH: | ||||
| SetPitch(length, (unsigned char *)q[2], q[3] >> 16, q[3] & 0xffff); | SetPitch(length, (unsigned char *)q[2], q[3] >> 16, q[3] & 0xffff); | ||||
| break; | break; | ||||
| case WCMD_PAUSE: | case WCMD_PAUSE: | ||||
| if (resume == 0) | if (resume == 0) | ||||
| echo_complete -= length; | echo_complete -= length; | ||||
| #endif | #endif | ||||
| result = PlaySilence(length, resume); | result = PlaySilence(length, resume); | ||||
| break; | break; | ||||
| case WCMD_WAVE: | case WCMD_WAVE: | ||||
| echo_complete = echo_length; | echo_complete = echo_length; | ||||
| wdata.n_mix_wavefile = 0; | wdata.n_mix_wavefile = 0; | ||||
| #endif | #endif | ||||
| result = PlayWave(length, resume, (unsigned char *)q[2], q[3] & 0xff, q[3] >> 8); | result = PlayWave(length, resume, (unsigned char *)q[2], q[3] & 0xff, q[3] >> 8); | ||||
| break; | break; | ||||
| case WCMD_WAVE2: | case WCMD_WAVE2: | ||||
| // wave file to be played at the same time as synthesis | // wave file to be played at the same time as synthesis | ||||
| wdata.mix_wave_amp = q[3] >> 8; | wdata.mix_wave_amp = q[3] >> 8; | ||||
| wdata.mix_wavefile_offset = 0; | wdata.mix_wavefile_offset = 0; | ||||
| wdata.mix_wavefile = (unsigned char *)q[2]; | wdata.mix_wavefile = (unsigned char *)q[2]; | ||||
| break; | break; | ||||
| case WCMD_SPECT2: // as WCMD_SPECT but stop any concurrent wave file | case WCMD_SPECT2: // as WCMD_SPECT but stop any concurrent wave file | ||||
| wdata.n_mix_wavefile = 0; // ... and drop through to WCMD_SPECT case | wdata.n_mix_wavefile = 0; // ... and drop through to WCMD_SPECT case | ||||
| case WCMD_SPECT: | case WCMD_SPECT: | ||||
| echo_complete = echo_length; | echo_complete = echo_length; | ||||
| result = Wavegen2(length & 0xffff, q[1] >> 16, resume, (frame_t *)q[2], (frame_t *)q[3]); | result = Wavegen2(length & 0xffff, q[1] >> 16, resume, (frame_t *)q[2], (frame_t *)q[3]); | ||||
| break; | break; | ||||
| #ifdef INCLUDE_KLATT | #ifdef INCLUDE_KLATT | ||||
| case WCMD_KLATT2: // as WCMD_SPECT but stop any concurrent wave file | case WCMD_KLATT2: // as WCMD_SPECT but stop any concurrent wave file | ||||
| wdata.n_mix_wavefile = 0; // ... and drop through to WCMD_SPECT case | wdata.n_mix_wavefile = 0; // ... and drop through to WCMD_SPECT case | ||||
| result = Wavegen_Klatt2(length & 0xffff, q[1] >> 16, resume, (frame_t *)q[2], (frame_t *)q[3]); | result = Wavegen_Klatt2(length & 0xffff, q[1] >> 16, resume, (frame_t *)q[2], (frame_t *)q[3]); | ||||
| break; | break; | ||||
| #endif | #endif | ||||
| case WCMD_MARKER: | case WCMD_MARKER: | ||||
| marker_type = q[0] >> 8; | marker_type = q[0] >> 8; | ||||
| MarkerEvent(marker_type, q[1], q[2], q[3], out_ptr); | MarkerEvent(marker_type, q[1], q[2], q[3], out_ptr); | ||||
| if (marker_type == 1) // word marker | if (marker_type == 1) // word marker | ||||
| current_source_index = q[1] & 0xffffff; | current_source_index = q[1] & 0xffffff; | ||||
| break; | break; | ||||
| case WCMD_AMPLITUDE: | case WCMD_AMPLITUDE: | ||||
| SetAmplitude(length, (unsigned char *)q[2], q[3]); | SetAmplitude(length, (unsigned char *)q[2], q[3]); | ||||
| break; | break; | ||||
| case WCMD_VOICE: | case WCMD_VOICE: | ||||
| WavegenSetVoice((voice_t *)q[2]); | WavegenSetVoice((voice_t *)q[2]); | ||||
| free((voice_t *)q[2]); | free((voice_t *)q[2]); | ||||
| break; | break; | ||||
| case WCMD_EMBEDDED: | case WCMD_EMBEDDED: | ||||
| SetEmbedded(q[1], q[2]); | SetEmbedded(q[1], q[2]); | ||||
| break; | break; | ||||
| case WCMD_MBROLA_DATA: | case WCMD_MBROLA_DATA: | ||||
| result = MbrolaFill(length, resume, (general_amplitude * wvoice->voicing)/64); | result = MbrolaFill(length, resume, (general_amplitude * wvoice->voicing)/64); | ||||
| break; | break; | ||||
| case WCMD_FMT_AMPLITUDE: | case WCMD_FMT_AMPLITUDE: | ||||
| if ((wdata.amplitude_fmt = q[1]) == 0) | if ((wdata.amplitude_fmt = q[1]) == 0) | ||||
| wdata.amplitude_fmt = 100; // percentage, but value=0 means 100% | wdata.amplitude_fmt = 100; // percentage, but value=0 means 100% | ||||
| break; | break; | ||||
| #if HAVE_SONIC_H | #if HAVE_SONIC_H | ||||
| case WCMD_SONIC_SPEED: | case WCMD_SONIC_SPEED: | ||||
| sonicSpeed = (double)q[1] / 1024; | sonicSpeed = (double)q[1] / 1024; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| #if HAVE_SONIC_H | #if HAVE_SONIC_H | ||||
| /* Speed up the audio samples with libsonic. */ | /* Speed up the audio samples with libsonic. */ | ||||
| static int SpeedUp(short *outbuf, int length_in, int length_out, int end_of_text) | static int SpeedUp(short *outbuf, int length_in, int length_out, int end_of_text) | ||||
| } | } | ||||
| #endif | #endif | ||||
| /* Call WavegenFill2, and then speed up the output samples. */ | /* Call WavegenFill2, and then speed up the output samples. */ | ||||
| int WavegenFill(int fill_zeros) | int WavegenFill(int fill_zeros) | ||||
| { | { |
| #include "voice.h" | #include "voice.h" | ||||
| #include "translate.h" | #include "translate.h" | ||||
| extern void Write4Bytes(FILE *f, int value); | extern void Write4Bytes(FILE *f, int value); | ||||
| char path_home[N_PATH_HOME]; // this is the espeak-data directory | char path_home[N_PATH_HOME]; // this is the espeak-data directory | ||||
| "\t List the available voices for the specified language.\n" | "\t List the available voices for the specified language.\n" | ||||
| "\t If <language> is omitted, then list all voices.\n"; | "\t If <language> is omitted, then list all voices.\n"; | ||||
| void DisplayVoices(FILE *f_out, char *language); | void DisplayVoices(FILE *f_out, char *language); | ||||
| USHORT voice_pcnt[N_PEAKS+1][3]; | USHORT voice_pcnt[N_PEAKS+1][3]; | ||||
| void DisplayVoices(FILE *f_out, char *language) | void DisplayVoices(FILE *f_out, char *language) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| } | } | ||||
| } | } | ||||
| static int OpenWaveFile(const char *path, int rate) | static int OpenWaveFile(const char *path, int rate) | ||||
| { | { | ||||
| // Set the length of 0x7ffff000 for --stdout | // Set the length of 0x7ffff000 for --stdout | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| static void CloseWaveFile() | static void CloseWaveFile() | ||||
| { | { | ||||
| unsigned int pos; | unsigned int pos; | ||||
| fclose(f_wave); | fclose(f_wave); | ||||
| f_wave = NULL; | f_wave = NULL; | ||||
| } | } | ||||
| static int WavegenFile(void) | static int WavegenFile(void) | ||||
| { | { | ||||
| int finished; | int finished; | ||||
| return finished; | return finished; | ||||
| } | } | ||||
| static void init_path(char *argv0, char *path_specified) | static void init_path(char *argv0, char *path_specified) | ||||
| { | { | ||||
| if (path_specified) { | if (path_specified) { | ||||
| sprintf(path_home, "%s/espeak-data", path_specified); | sprintf(path_home, "%s/espeak-data", path_specified); | ||||
| return; | return; | ||||
| #endif | #endif | ||||
| } | } | ||||
| static int initialise(void) | static int initialise(void) | ||||
| { | { | ||||
| int param; | int param; | ||||
| } | } | ||||
| #endif | #endif | ||||
| if ((result = LoadPhData(&srate)) != 1) { | if ((result = LoadPhData(&srate)) != 1) { | ||||
| if (result == -1) { | if (result == -1) { | ||||
| fprintf(stderr, "Failed to load espeak-data\n"); | fprintf(stderr, "Failed to load espeak-data\n"); | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| #ifdef NEED_GETOPT | #ifdef NEED_GETOPT | ||||
| struct option { | struct option { | ||||
| char *name; | char *name; | ||||
| if ((sscanf(optarg2, "%d", &value) == 1) && (value <= 4)) | if ((sscanf(optarg2, "%d", &value) == 1) && (value <= 4)) | ||||
| option_multibyte = value; | option_multibyte = value; | ||||
| break; | break; | ||||
| case 'h': | case 'h': | ||||
| init_path(argv[0], data_path); | init_path(argv[0], data_path); | ||||
| printf("\nspeak text-to-speech: %s Data at: %s\n%s", version_string, path_home, help_text); | printf("\nspeak text-to-speech: %s Data at: %s\n%s", version_string, path_home, help_text); | ||||
| exit(0); | exit(0); | ||||
| case 'k': | case 'k': | ||||
| option_capitals = atoi(optarg2); | option_capitals = atoi(optarg2); | ||||
| break; | break; | ||||
| case 'x': | case 'x': | ||||
| phoneme_options |= espeakPHONEMES_SHOW; | phoneme_options |= espeakPHONEMES_SHOW; | ||||
| break; | break; | ||||
| case 'X': | case 'X': | ||||
| phoneme_options |= espeakPHONEMES_TRACE; | phoneme_options |= espeakPHONEMES_TRACE; | ||||
| break; | break; | ||||
| case 'm': | case 'm': | ||||
| option_ssml = 1; | option_ssml = 1; | ||||
| break; | break; | ||||
| case 'p': | case 'p': | ||||
| pitch_adjustment = atoi(optarg2); | pitch_adjustment = atoi(optarg2); | ||||
| if (pitch_adjustment > 99) pitch_adjustment = 99; | if (pitch_adjustment > 99) pitch_adjustment = 99; | ||||
| break; | break; | ||||
| case 'q': | case 'q': | ||||
| quiet = 1; | quiet = 1; | ||||
| break; | break; | ||||
| case 'f': | case 'f': | ||||
| strncpy0(filename, optarg2, sizeof(filename)); | strncpy0(filename, optarg2, sizeof(filename)); | ||||
| break; | break; | ||||
| case 'l': | case 'l': | ||||
| value = 0; | value = 0; | ||||
| value = atoi(optarg2); | value = atoi(optarg2); | ||||
| option_linelength = value; | option_linelength = value; | ||||
| break; | break; | ||||
| case 'a': | case 'a': | ||||
| amp = atoi(optarg2); | amp = atoi(optarg2); | ||||
| break; | break; | ||||
| case 's': | case 's': | ||||
| speed = atoi(optarg2); | speed = atoi(optarg2); | ||||
| break; | break; | ||||
| case 'g': | case 'g': | ||||
| wordgap = atoi(optarg2); | wordgap = atoi(optarg2); | ||||
| break; | break; | ||||
| case 'v': | case 'v': | ||||
| strncpy0(voicename, optarg2, sizeof(voicename)); | strncpy0(voicename, optarg2, sizeof(voicename)); | ||||
| break; | break; | ||||
| case 'w': | case 'w': | ||||
| option_waveout = 1; | option_waveout = 1; | ||||
| strncpy0(wavefile, optarg2, sizeof(wavefile)); | strncpy0(wavefile, optarg2, sizeof(wavefile)); | ||||
| break; | break; | ||||
| case 'z': | case 'z': | ||||
| option_endpause = 0; | option_endpause = 0; | ||||
| break; | break; | ||||
| case 0x100: // --stdin | case 0x100: // --stdin | ||||
| flag_stdin = 1; | flag_stdin = 1; | ||||
| break; | break; | ||||
| case 0x105: // --stdout | case 0x105: // --stdout | ||||
| option_waveout = 1; | option_waveout = 1; | ||||
| strcpy(wavefile, "stdout"); | strcpy(wavefile, "stdout"); | ||||
| break; | break; | ||||
| case 0x101: // --compile-debug | case 0x101: // --compile-debug | ||||
| case 0x102: // --compile | case 0x102: // --compile | ||||
| if (optarg2 != NULL) | if (optarg2 != NULL) | ||||
| strncpy0(voicename, optarg2, sizeof(voicename)); | strncpy0(voicename, optarg2, sizeof(voicename)); | ||||
| flag_compile = c; | flag_compile = c; | ||||
| break; | break; | ||||
| case 0x103: // --punct | case 0x103: // --punct | ||||
| option_punctuation = 1; | option_punctuation = 1; | ||||
| if (optarg2 != NULL) { | if (optarg2 != NULL) { | ||||
| option_punctuation = 2; | option_punctuation = 2; | ||||
| } | } | ||||
| break; | break; | ||||
| case 0x104: // --voices | case 0x104: // --voices | ||||
| init_path(argv[0], data_path); | init_path(argv[0], data_path); | ||||
| DisplayVoices(stdout, optarg2); | DisplayVoices(stdout, optarg2); | ||||
| exit(0); | exit(0); | ||||
| case 0x106: // -- split | case 0x106: // -- split | ||||
| if (optarg2 == NULL) | if (optarg2 == NULL) | ||||
| samples_split = 30; // default 30 minutes | samples_split = 30; // default 30 minutes | ||||
| else | else | ||||
| samples_split = atoi(optarg2); | samples_split = atoi(optarg2); | ||||
| break; | break; | ||||
| case 0x107: // --path | case 0x107: // --path | ||||
| data_path = optarg2; | data_path = optarg2; | ||||
| break; | break; | ||||
| case 0x108: // --phonout | case 0x108: // --phonout | ||||
| if ((f_trans = fopen(optarg2, "w")) == NULL) { | if ((f_trans = fopen(optarg2, "w")) == NULL) { | ||||
| fprintf(stderr, "Can't write to: %s\n", optarg2); | fprintf(stderr, "Can't write to: %s\n", optarg2); | ||||
| f_trans = stderr; | f_trans = stderr; | ||||
| } | } | ||||
| break; | break; | ||||
| case 0x109: // --pho | case 0x109: // --pho | ||||
| phoneme_options |= espeakPHONEMES_MBROLA; | phoneme_options |= espeakPHONEMES_MBROLA; | ||||
| break; | break; | ||||
| case 0x10a: // --ipa | case 0x10a: // --ipa | ||||
| phoneme_options |= espeakPHONEMES_IPA; | phoneme_options |= espeakPHONEMES_IPA; | ||||
| if (optarg2 != NULL) { | if (optarg2 != NULL) { | ||||
| phoneme_options |= espeakPHONEMES_TIE; | phoneme_options |= espeakPHONEMES_TIE; | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| case 0x10b: // --version | case 0x10b: // --version | ||||
| init_path(argv[0], data_path); | init_path(argv[0], data_path); | ||||
| printf("speak text-to-speech: %s Data at: %s\n", version_string, path_home); | printf("speak text-to-speech: %s Data at: %s\n", version_string, path_home); | ||||
| exit(0); | exit(0); | ||||
| case 0x10c: // --sep | case 0x10c: // --sep | ||||
| phoneme_options |= espeakPHONEMES_SHOW; | phoneme_options |= espeakPHONEMES_SHOW; | ||||
| if (optarg2 == 0) | if (optarg2 == 0) | ||||
| if (phonemes_separator == 'z') | if (phonemes_separator == 'z') | ||||
| phonemes_separator = 0x200c; // ZWNJ | phonemes_separator = 0x200c; // ZWNJ | ||||
| break; | break; | ||||
| case 0x10d: // --tie | case 0x10d: // --tie | ||||
| phoneme_options |= (espeakPHONEMES_SHOW | espeakPHONEMES_TIE); | phoneme_options |= (espeakPHONEMES_SHOW | espeakPHONEMES_TIE); | ||||
| if (optarg2 == 0) | if (optarg2 == 0) | ||||
| if (phonemes_separator == 'z') | if (phonemes_separator == 'z') | ||||
| phonemes_separator = 0x200d; // ZWJ | phonemes_separator = 0x200d; // ZWJ | ||||
| break; | break; | ||||
| default: | default: | ||||
| exit(0); | exit(0); | ||||
| } | } | ||||
| exit(0); | exit(0); | ||||
| } | } | ||||
| SetParameter(espeakRATE, speed, 0); | SetParameter(espeakRATE, speed, 0); | ||||
| SetParameter(espeakVOLUME, amp, 0); | SetParameter(espeakVOLUME, amp, 0); | ||||
| SetParameter(espeakCAPITALS, option_capitals, 0); | SetParameter(espeakCAPITALS, option_capitals, 0); |