From 8bec80dccc9f4fe147a500486813f4e89a0d56d8 Mon Sep 17 00:00:00 2001 From: Mathieu Parent Date: Sun, 25 Oct 2009 15:19:01 +0100 Subject: [PATCH 3/7] pico2wave: Convert text to .wav using svox text-to-speech system. --- pico/.gitignore | 1 + pico/Makefile.am | 7 + pico/bin/pico2wave.c | 341 ++++++++++++++++++++++++++++++++++++++++++++++++++ pico/configure.in | 3 + 4 files changed, 352 insertions(+), 0 deletions(-) create mode 100644 pico/bin/pico2wave.c diff --git a/pico/.gitignore b/pico/.gitignore index 4235569..a110298 100644 --- a/pico/.gitignore +++ b/pico/.gitignore @@ -29,4 +29,5 @@ libtool *.lo .libs libttspico.la +pico2wave diff --git a/pico/Makefile.am b/pico/Makefile.am index 6d8a10c..0d9472d 100644 --- a/pico/Makefile.am +++ b/pico/Makefile.am @@ -34,3 +34,10 @@ libttspico_la_SOURCES = \ lib/picotrns.c \ lib/picowa.c +bin_PROGRAMS = pico2wave +pico2wave_SOURCES = \ + bin/pico2wave.c +pico2wave_LDADD = \ + libttspico.la -lm -lpopt +pico2wave_CFLAGS = -Wall -I lib + diff --git a/pico/bin/pico2wave.c b/pico/bin/pico2wave.c new file mode 100644 index 0000000..0c035a7 --- /dev/null +++ b/pico/bin/pico2wave.c @@ -0,0 +1,341 @@ +/* pico2wave.c + + * Copyright (C) 2009 Mathieu Parent + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Convert text to .wav using svox text-to-speech system. + * + */ + + +#include +#include +#include +#include + +#include +#include +#include + + +/* adaptation layer defines */ +#define PICO_MEM_SIZE 2500000 +#define DummyLen 100000000 + +/* string constants */ +#define MAX_OUTBUF_SIZE 128 +const char * PICO_LINGWARE_PATH = "./lang/"; +const char * PICO_VOICE_NAME = "PicoVoice"; + +/* supported voices + Pico does not seperately specify the voice and locale. */ +const char * picoSupportedLangIso3[] = { "eng", "eng", "deu", "spa", "fra", "ita" }; +const char * picoSupportedCountryIso3[] = { "USA", "GBR", "DEU", "ESP", "FRA", "ITA" }; +const char * picoSupportedLang[] = { "en-US", "en-GB", "de-DE", "es-ES", "fr-FR", "it-IT" }; +const char * picoInternalLang[] = { "en-US", "en-GB", "de-DE", "es-ES", "fr-FR", "it-IT" }; +const char * picoInternalTaLingware[] = { "en-US_ta.bin", "en-GB_ta.bin", "de-DE_ta.bin", "es-ES_ta.bin", "fr-FR_ta.bin", "it-IT_ta.bin" }; +const char * picoInternalSgLingware[] = { "en-US_lh0_sg.bin", "en-GB_kh0_sg.bin", "de-DE_gl0_sg.bin", "es-ES_zl0_sg.bin", "fr-FR_nk0_sg.bin", "it-IT_cm0_sg.bin" }; +const char * picoInternalUtppLingware[] = { "en-US_utpp.bin", "en-GB_utpp.bin", "de-DE_utpp.bin", "es-ES_utpp.bin", "fr-FR_utpp.bin", "it-IT_utpp.bin" }; +const int picoNumSupportedVocs = 6; + +/* adapation layer global variables */ +void * picoMemArea = NULL; +pico_System picoSystem = NULL; +pico_Resource picoTaResource = NULL; +pico_Resource picoSgResource = NULL; +pico_Resource picoUtppResource = NULL; +pico_Engine picoEngine = NULL; +pico_Char * picoTaFileName = NULL; +pico_Char * picoSgFileName = NULL; +pico_Char * picoUtppFileName = NULL; +pico_Char * picoTaResourceName = NULL; +pico_Char * picoSgResourceName = NULL; +pico_Char * picoUtppResourceName = NULL; +int picoSynthAbort = 0; + + +int main(int argc, const char *argv[]) { + char * wavefile = NULL; + char * lang = "en-US"; + int langIndex = -1, langIndexTmp = -1; + char * text; + int8_t * buffer; + size_t bufferSize = 256; + + /* Parsing options */ + poptContext optCon; /* context for parsing command-line options */ + int opt; /* used for argument parsing */ + + struct poptOption optionsTable[] = { + { "wave", 'w', POPT_ARG_STRING, &wavefile, 0, + "Write output to this WAV file (extension SHOULD be .wav)", "filename.wav" }, + { "lang", 'l', POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &lang, 0, + "Language", "lang" }, + POPT_AUTOHELP + POPT_TABLEEND + }; + optCon = poptGetContext(NULL, argc, argv, optionsTable, POPT_CONTEXT_POSIXMEHARDER); + poptSetOtherOptionHelp(optCon, ""); + + /* Reporting about invalid extra options */ + while ((opt = poptGetNextOpt(optCon)) != -1) { + switch (opt) { + default: + fprintf(stderr, "Invalid option %s: %s\n", + poptBadOption(optCon, 0), poptStrerror(opt)); + poptPrintHelp(optCon, stderr, 0); + exit(1); + } + } + + /* Mandatory option: --wave */ + if(!wavefile) { + fprintf(stderr, "Mandatory option: %s\n\n", + "--wave=filename.wav"); + poptPrintHelp(optCon, stderr, 0); + exit(1); + } + /* option: --lang */ + for(langIndexTmp =0; langIndexTmp */ + const char **extra_argv; + extra_argv = poptGetArgs(optCon); + if(extra_argv) { + text = (char *) &(*extra_argv)[0]; + } else { + //TODO: stdin not supported yet. + fprintf(stderr, "Missing argument: %s\n\n", + ""); + poptPrintHelp(optCon, stderr, 0); + exit(1); + } + + poptFreeContext(optCon); + + buffer = malloc( bufferSize ); + + int ret, getstatus; + pico_Char * inp = NULL; + pico_Char * local_text = NULL; + short outbuf[MAX_OUTBUF_SIZE/2]; + pico_Int16 bytes_sent, bytes_recv, text_remaining, out_data_type; + pico_Retstring outMessage; + + picoSynthAbort = 0; + + picoMemArea = malloc( PICO_MEM_SIZE ); + if((ret = pico_initialize( picoMemArea, PICO_MEM_SIZE, &picoSystem ))) { + pico_getSystemStatusMessage(picoSystem, ret, outMessage); + fprintf(stderr, "Cannot initialize pico (%i): %s\n", ret, outMessage); + goto terminate; + } + + /* Load the text analysis Lingware resource file. */ + picoTaFileName = (pico_Char *) malloc( PICO_MAX_DATAPATH_NAME_SIZE + PICO_MAX_FILE_NAME_SIZE ); + strcpy((char *) picoTaFileName, PICO_LINGWARE_PATH); + strcat((char *) picoTaFileName, (const char *) picoInternalTaLingware[langIndex]); + if((ret = pico_loadResource( picoSystem, picoTaFileName, &picoTaResource ))) { + pico_getSystemStatusMessage(picoSystem, ret, outMessage); + fprintf(stderr, "Cannot load text analysis resource file (%i): %s\n", ret, outMessage); + goto unloadTaResource; + } + + /* Load the signal generation Lingware resource file. */ + picoSgFileName = (pico_Char *) malloc( PICO_MAX_DATAPATH_NAME_SIZE + PICO_MAX_FILE_NAME_SIZE ); + strcpy((char *) picoSgFileName, PICO_LINGWARE_PATH); + strcat((char *) picoSgFileName, (const char *) picoInternalSgLingware[langIndex]); + if((ret = pico_loadResource( picoSystem, picoSgFileName, &picoSgResource ))) { + pico_getSystemStatusMessage(picoSystem, ret, outMessage); + fprintf(stderr, "Cannot load signal generation Lingware resource file (%i): %s\n", ret, outMessage); + goto unloadSgResource; + } + + /* Load the utpp Lingware resource file if exists - NOTE: this file is optional + and is currently not used. Loading is only attempted for future compatibility. + If this file is not present the loading will still succeed. // + picoUtppFileName = (pico_Char *) malloc( PICO_MAX_DATAPATH_NAME_SIZE + PICO_MAX_FILE_NAME_SIZE ); + strcpy((char *) picoUtppFileName, PICO_LINGWARE_PATH); + strcat((char *) picoUtppFileName, (const char *) picoInternalUtppLingware[langIndex]); + ret = pico_loadResource( picoSystem, picoUtppFileName, &picoUtppResource ); + pico_getSystemStatusMessage(picoSystem, ret, outMessage); + printf("pico_loadResource: %i: %s\n", ret, outMessage); + */ + + /* Get the text analysis resource name. */ + picoTaResourceName = (pico_Char *) malloc( PICO_MAX_RESOURCE_NAME_SIZE ); + if((ret = pico_getResourceName( picoSystem, picoTaResource, (char *) picoTaResourceName ))) { + pico_getSystemStatusMessage(picoSystem, ret, outMessage); + fprintf(stderr, "Cannot get the text analysis resource name (%i): %s\n", ret, outMessage); + goto unloadUtppResource; + } + + /* Get the signal generation resource name. */ + picoSgResourceName = (pico_Char *) malloc( PICO_MAX_RESOURCE_NAME_SIZE ); + if((ret = pico_getResourceName( picoSystem, picoSgResource, (char *) picoSgResourceName ))) { + pico_getSystemStatusMessage(picoSystem, ret, outMessage); + fprintf(stderr, "Cannot get the signal generation resource name (%i): %s\n", ret, outMessage); + goto unloadUtppResource; + } + + + /* Create a voice definition. */ + if((ret = pico_createVoiceDefinition( picoSystem, (const pico_Char *) PICO_VOICE_NAME ))) { + pico_getSystemStatusMessage(picoSystem, ret, outMessage); + fprintf(stderr, "Cannot create voice definition (%i): %s\n", ret, outMessage); + goto unloadUtppResource; + } + + /* Add the text analysis resource to the voice. */ + if((ret = pico_addResourceToVoiceDefinition( picoSystem, (const pico_Char *) PICO_VOICE_NAME, picoTaResourceName ))) { + pico_getSystemStatusMessage(picoSystem, ret, outMessage); + fprintf(stderr, "Cannot add the text analysis resource to the voice (%i): %s\n", ret, outMessage); + goto unloadUtppResource; + } + + /* Add the signal generation resource to the voice. */ + if((ret = pico_addResourceToVoiceDefinition( picoSystem, (const pico_Char *) PICO_VOICE_NAME, picoSgResourceName ))) { + pico_getSystemStatusMessage(picoSystem, ret, outMessage); + fprintf(stderr, "Cannot add the signal generation resource to the voice (%i): %s\n", ret, outMessage); + goto unloadUtppResource; + } + + /* Create a new Pico engine. */ + if((ret = pico_newEngine( picoSystem, (const pico_Char *) PICO_VOICE_NAME, &picoEngine ))) { + pico_getSystemStatusMessage(picoSystem, ret, outMessage); + fprintf(stderr, "Cannot create a new pico engine (%i): %s\n", ret, outMessage); + goto disposeEngine; + } + + local_text = (pico_Char *) text ; + text_remaining = strlen((const char *) local_text) + 1; + + inp = (pico_Char *) local_text; + + size_t bufused = 0; + + picoos_Common common = (picoos_Common) pico_sysGetCommon(picoSystem); + + picoos_SDFile sdOutFile = NULL; + + picoos_bool done = TRUE; + if(TRUE != (done = picoos_sdfOpenOut(common, &sdOutFile, + (picoos_char *) wavefile, SAMPLE_FREQ_16KHZ, PICOOS_ENC_LIN))) + { + fprintf(stderr, "Cannot open output wave file\n"); + ret = 1; + goto disposeEngine; + } + + /* synthesis loop */ + while (text_remaining) { + /* Feed the text into the engine. */ + if((ret = pico_putTextUtf8( picoEngine, inp, text_remaining, &bytes_sent ))) { + pico_getSystemStatusMessage(picoSystem, ret, outMessage); + fprintf(stderr, "Cannot put Text (%i): %s\n", ret, outMessage); + goto disposeEngine; + } + + text_remaining -= bytes_sent; + inp += bytes_sent; + + do { + if (picoSynthAbort) { + goto disposeEngine; + } + /* Retrieve the samples and add them to the buffer. */ + getstatus = pico_getData( picoEngine, (void *) outbuf, + MAX_OUTBUF_SIZE, &bytes_recv, &out_data_type ); + if((getstatus !=PICO_STEP_BUSY) && (getstatus !=PICO_STEP_IDLE)){ + pico_getSystemStatusMessage(picoSystem, getstatus, outMessage); + fprintf(stderr, "Cannot get Data (%i): %s\n", getstatus, outMessage); + goto disposeEngine; + } + if (bytes_recv) { + if ((bufused + bytes_recv) <= bufferSize) { + memcpy(buffer+bufused, (int8_t *) outbuf, bytes_recv); + bufused += bytes_recv; + } else { + done = picoos_sdfPutSamples( + sdOutFile, + bufused / 2, + (picoos_int16*) (buffer)); + bufused = 0; + memcpy(buffer, (int8_t *) outbuf, bytes_recv); + bufused += bytes_recv; + } + } + } while (PICO_STEP_BUSY == getstatus); + /* This chunk of synthesis is finished; pass the remaining samples. */ + if (!picoSynthAbort) { + done = picoos_sdfPutSamples( + sdOutFile, + bufused / 2, + (picoos_int16*) (buffer)); + } + picoSynthAbort = 0; + } + + if(TRUE != (done = picoos_sdfCloseOut(common, &sdOutFile))) + { + fprintf(stderr, "Cannot close output wave file\n"); + ret = 1; + goto disposeEngine; + } + +disposeEngine: + if (picoEngine) { + pico_disposeEngine( picoSystem, &picoEngine ); + pico_releaseVoiceDefinition( picoSystem, (pico_Char *) PICO_VOICE_NAME ); + picoEngine = NULL; + } +unloadUtppResource: + if (picoUtppResource) { + pico_unloadResource( picoSystem, &picoUtppResource ); + picoUtppResource = NULL; + } +unloadSgResource: + if (picoSgResource) { + pico_unloadResource( picoSystem, &picoSgResource ); + picoSgResource = NULL; + } +unloadTaResource: + if (picoTaResource) { + pico_unloadResource( picoSystem, &picoTaResource ); + picoTaResource = NULL; + } +terminate: + if (picoSystem) { + pico_terminate(&picoSystem); + picoSystem = NULL; + } + exit(ret); +} + diff --git a/pico/configure.in b/pico/configure.in index 0afb56d..349eb1d 100644 --- a/pico/configure.in +++ b/pico/configure.in @@ -14,3 +14,6 @@ AC_CONFIG_FILES([Makefile]) AC_OUTPUT AC_CONFIG_MACRO_DIR([m4]) + +AC_CHECK_LIB(popt, poptGetContext) + -- 1.7.1