int main(int argc, char **argv) { static struct option long_options[] = { { "help", no_argument, 0, 'h' }, { "stdin", no_argument, 0, 0x100 }, { "compile-debug", optional_argument, 0, 0x101 }, { "compile", optional_argument, 0, 0x102 }, { "punct", optional_argument, 0, 0x103 }, { "voices", optional_argument, 0, 0x104 }, { "stdout", no_argument, 0, 0x105 }, { "split", optional_argument, 0, 0x106 }, { "path", required_argument, 0, 0x107 }, { "phonout", required_argument, 0, 0x108 }, { "pho", no_argument, 0, 0x109 }, { "ipa", optional_argument, 0, 0x10a }, { "version", no_argument, 0, 0x10b }, { "sep", optional_argument, 0, 0x10c }, { "tie", optional_argument, 0, 0x10d }, { "compile-mbrola", optional_argument, 0, 0x10e }, { "compile-intonations", no_argument, 0, 0x10f }, { "compile-phonemes", optional_argument, 0, 0x110 }, { 0, 0, 0, 0 } }; FILE *f_text = NULL; char *p_text = NULL; FILE *f_phonemes_out = stdout; char *data_path = NULL; // use default path for espeak-ng-data int option_index = 0; int c; int ix; char *optarg2; int value; int flag_stdin = 0; int flag_compile = 0; int filesize = 0; int synth_flags = espeakCHARS_AUTO | espeakPHONEMES | espeakENDPAUSE; int volume = -1; int speed = -1; int pitch = -1; int wordgap = -1; int option_capitals = -1; int option_punctuation = -1; int phonemes_separator = 0; int phoneme_options = 0; int option_linelength = 0; int option_waveout = 0; espeak_VOICE voice_select; char filename[200]; char voicename[40]; char devicename[200]; #define N_PUNCTLIST 100 wchar_t option_punctlist[N_PUNCTLIST]; voicename[0] = 0; wavefile[0] = 0; filename[0] = 0; devicename[0] = 0; option_punctlist[0] = 0; while (true) { c = getopt_long(argc, argv, "a:b:d:f:g:hk:l:mp:qs:v:w:xXz", long_options, &option_index); // Detect the end of the options. if (c == -1) break; optarg2 = optarg; switch (c) { case 'b': // input character encoding, 8bit, 16bit, UTF8 if ((sscanf(optarg2, "%d", &value) == 1) && (value <= 4)) synth_flags |= value; else synth_flags |= espeakCHARS_8BIT; break; case 'd': strncpy0(devicename, optarg2, sizeof(devicename)); break; case 'h': printf("\n"); PrintVersion(); printf("%s", help_text); return 0; case 'k': option_capitals = atoi(optarg2); break; case 'x': phoneme_options |= espeakPHONEMES_SHOW; break; case 'X': phoneme_options |= espeakPHONEMES_TRACE; break; case 'm': synth_flags |= espeakSSML; break; case 'p': pitch = atoi(optarg2); break; case 'q': quiet = true; break; case 'f': strncpy0(filename, optarg2, sizeof(filename)); break; case 'l': option_linelength = atoi(optarg2); break; case 'a': volume = atoi(optarg2); break; case 's': speed = atoi(optarg2); break; case 'g': wordgap = atoi(optarg2); break; case 'v': strncpy0(voicename, optarg2, sizeof(voicename)); break; case 'w': option_waveout = 1; strncpy0(wavefile, optarg2, sizeof(filename)); break; case 'z': // remove pause from the end of a sentence synth_flags &= ~espeakENDPAUSE; break; case 0x100: // --stdin flag_stdin = 1; break; case 0x105: // --stdout option_waveout = 1; strcpy(wavefile, "stdout"); break; case 0x101: // --compile-debug case 0x102: // --compile if (optarg2 != NULL && *optarg2) { strncpy0(voicename, optarg2, sizeof(voicename)); flag_compile = c; quiet = true; break; } else { fprintf(stderr, "Voice name to '%s' not specified.\n", c == 0x101 ? "--compile-debug" : "--compile"); exit(EXIT_FAILURE); } case 0x103: // --punct option_punctuation = 1; if (optarg2 != NULL) { ix = 0; while ((ix < N_PUNCTLIST) && ((option_punctlist[ix] = optarg2[ix]) != 0)) ix++; option_punctlist[N_PUNCTLIST-1] = 0; option_punctuation = 2; } break; case 0x104: // --voices espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS, 0, data_path, 0); DisplayVoices(stdout, optarg2); exit(0); case 0x106: // -- split if (optarg2 == NULL) samples_split_seconds = 30 * 60; // default 30 minutes else samples_split_seconds = atoi(optarg2) * 60; break; case 0x107: // --path data_path = optarg2; break; case 0x108: // --phonout if ((f_phonemes_out = fopen(optarg2, "w")) == NULL) fprintf(stderr, "Can't write to: %s\n", optarg2); break; case 0x109: // --pho phoneme_options |= espeakPHONEMES_MBROLA; break; case 0x10a: // --ipa phoneme_options |= espeakPHONEMES_IPA; if (optarg2 != NULL) { // deprecated and obsolete switch (atoi(optarg2)) { case 1: phonemes_separator = '_'; break; case 2: phonemes_separator = 0x0361; phoneme_options |= espeakPHONEMES_TIE; break; case 3: phonemes_separator = 0x200d; // ZWJ phoneme_options |= espeakPHONEMES_TIE; break; } } break; case 0x10b: // --version PrintVersion(); exit(0); case 0x10c: // --sep phoneme_options |= espeakPHONEMES_SHOW; if (optarg2 == 0) phonemes_separator = ' '; else utf8_in(&phonemes_separator, optarg2); if (phonemes_separator == 'z') phonemes_separator = 0x200c; // ZWNJ break; case 0x10d: // --tie phoneme_options |= (espeakPHONEMES_SHOW | espeakPHONEMES_TIE); if (optarg2 == 0) phonemes_separator = 0x0361; // default: combining-double-inverted-breve else utf8_in(&phonemes_separator, optarg2); if (phonemes_separator == 'z') phonemes_separator = 0x200d; // ZWJ break; case 0x10e: // --compile-mbrola { espeak_ng_InitializePath(data_path); espeak_ng_ERROR_CONTEXT context = NULL; espeak_ng_STATUS result = espeak_ng_CompileMbrolaVoice(optarg2, stdout, &context); if (result != ENS_OK) { espeak_ng_PrintStatusCodeMessage(result, stderr, context); espeak_ng_ClearErrorContext(&context); return EXIT_FAILURE; } return EXIT_SUCCESS; } case 0x10f: // --compile-intonations { espeak_ng_InitializePath(data_path); espeak_ng_ERROR_CONTEXT context = NULL; espeak_ng_STATUS result = espeak_ng_CompileIntonation(stdout, &context); if (result != ENS_OK) { espeak_ng_PrintStatusCodeMessage(result, stderr, context); espeak_ng_ClearErrorContext(&context); return EXIT_FAILURE; } return EXIT_SUCCESS; } case 0x110: // --compile-phonemes { espeak_ng_InitializePath(data_path); espeak_ng_ERROR_CONTEXT context = NULL; espeak_ng_STATUS result; if (optarg2) { result = espeak_ng_CompilePhonemeDataPath(22050, optarg2, NULL, stdout, &context); } else { result = espeak_ng_CompilePhonemeData(22050, stdout, &context); } if (result != ENS_OK) { espeak_ng_PrintStatusCodeMessage(result, stderr, context); espeak_ng_ClearErrorContext(&context); return EXIT_FAILURE; } return EXIT_SUCCESS; } default: exit(0); } } espeak_ng_InitializePath(data_path); espeak_ng_ERROR_CONTEXT context = NULL; espeak_ng_STATUS result = espeak_ng_Initialize(&context); if (result != ENS_OK) { espeak_ng_PrintStatusCodeMessage(result, stderr, context); espeak_ng_ClearErrorContext(&context); exit(1); } if (option_waveout || quiet) { // writing to a file (or no output), we can use synchronous mode result = espeak_ng_InitializeOutput(ENOUTPUT_MODE_SYNCHRONOUS, 0, devicename[0] ? devicename : NULL); samplerate = espeak_ng_GetSampleRate(); samples_split = samplerate * samples_split_seconds; espeak_SetSynthCallback(SynthCallback); if (samples_split) { char *extn; extn = strrchr(wavefile, '.'); if ((extn != NULL) && ((wavefile + strlen(wavefile) - extn) <= 4)) { strcpy(filetype, extn); *extn = 0; } } } else { // play the sound output result = espeak_ng_InitializeOutput(PLAYBACK_MODE, 0, devicename[0] ? devicename : NULL); samplerate = espeak_ng_GetSampleRate(); } if (result != ENS_OK) { espeak_ng_PrintStatusCodeMessage(result, stderr, NULL); exit(EXIT_FAILURE); } if (voicename[0] == 0) strcpy(voicename, ESPEAKNG_DEFAULT_VOICE); result = espeak_ng_SetVoiceByName(voicename); if (result != ENS_OK) { memset(&voice_select, 0, sizeof(voice_select)); voice_select.languages = voicename; result = espeak_ng_SetVoiceByProperties(&voice_select); if (result != ENS_OK) { espeak_ng_PrintStatusCodeMessage(result, stderr, NULL); exit(EXIT_FAILURE); } } if (flag_compile) { // This must be done after the voice is set espeak_ng_ERROR_CONTEXT context = NULL; espeak_ng_STATUS result = espeak_ng_CompileDictionary("", NULL, stderr, flag_compile & 0x1, &context); if (result != ENS_OK) { espeak_ng_PrintStatusCodeMessage(result, stderr, context); espeak_ng_ClearErrorContext(&context); return EXIT_FAILURE; } return EXIT_SUCCESS; } // set any non-default values of parameters. This must be done after espeak_Initialize() if (speed > 0) espeak_SetParameter(espeakRATE, speed, 0); if (volume >= 0) espeak_SetParameter(espeakVOLUME, volume, 0); if (pitch >= 0) espeak_SetParameter(espeakPITCH, pitch, 0); if (option_capitals >= 0) espeak_SetParameter(espeakCAPITALS, option_capitals, 0); if (option_punctuation >= 0) espeak_SetParameter(espeakPUNCTUATION, option_punctuation, 0); if (wordgap >= 0) espeak_SetParameter(espeakWORDGAP, wordgap, 0); if (option_linelength > 0) espeak_SetParameter(espeakLINELENGTH, option_linelength, 0); if (option_punctuation == 2) espeak_SetPunctuationList(option_punctlist); espeak_SetPhonemeTrace(phoneme_options | (phonemes_separator << 8), f_phonemes_out); if (filename[0] == 0) { if ((optind < argc) && (flag_stdin == 0)) { // there's a non-option parameter, and no -f or --stdin // use it as text p_text = argv[optind]; } else { f_text = stdin; if (flag_stdin == 0) flag_stdin = 2; } } else { struct stat st; if (stat(filename, &st) != 0) { fprintf(stderr, "Failed to stat() file '%s'\n", filename); exit(EXIT_FAILURE); } filesize = GetFileLength(filename); f_text = fopen(filename, "r"); if (f_text == NULL) { fprintf(stderr, "Failed to read file '%s'\n", filename); exit(EXIT_FAILURE); } if (S_ISFIFO(st.st_mode)) { flag_stdin = 2; } } if (p_text != NULL) { int size; size = strlen(p_text); espeak_Synth(p_text, size+1, 0, POS_CHARACTER, 0, synth_flags, NULL, NULL); } else if (flag_stdin) { size_t max = 1000; if ((p_text = (char *)malloc(max)) == NULL) { espeak_ng_PrintStatusCodeMessage(ENOMEM, stderr, NULL); exit(EXIT_FAILURE); } if (flag_stdin == 2) { // line by line input on stdin or from FIFO while (fgets(p_text, max, f_text) != NULL) { p_text[max-1] = 0; espeak_Synth(p_text, max, 0, POS_CHARACTER, 0, synth_flags, NULL, NULL); // Allow subprocesses to use the audio data through pipes. fflush(stdout); } if (f_text != stdin) { fclose(f_text); } } else { // bulk input on stdin ix = 0; while (true) { if ((c = fgetc(stdin)) == EOF) break; p_text[ix++] = (char)c; if (ix >= (max-1)) { char *new_text = NULL; if (max <= SIZE_MAX - 1000) { max += 1000; new_text = (char *)realloc(p_text, max); } if (new_text == NULL) { free(p_text); espeak_ng_PrintStatusCodeMessage(ENOMEM, stderr, NULL); exit(EXIT_FAILURE); } p_text = new_text; } } if (ix > 0) { p_text[ix-1] = 0; espeak_Synth(p_text, ix+1, 0, POS_CHARACTER, 0, synth_flags, NULL, NULL); } } free(p_text); } else if (f_text != NULL) { if ((p_text = (char *)malloc(filesize+1)) == NULL) { espeak_ng_PrintStatusCodeMessage(ENOMEM, stderr, NULL); exit(EXIT_FAILURE); } fread(p_text, 1, filesize, f_text); p_text[filesize] = 0; espeak_Synth(p_text, filesize+1, 0, POS_CHARACTER, 0, synth_flags, NULL, NULL); fclose(f_text); free(p_text); } result = espeak_ng_Synchronize(); if (result != ENS_OK) { espeak_ng_PrintStatusCodeMessage(result, stderr, NULL); exit(EXIT_FAILURE); } if (f_phonemes_out != stdout) fclose(f_phonemes_out); CloseWavFile(); espeak_ng_Terminate(); return 0; }
ESPEAK_API espeak_ERROR espeak_SetVoiceByProperties(espeak_VOICE *voice_selector) { return status_to_espeak_error(espeak_ng_SetVoiceByProperties(voice_selector)); }