8ef4757e6302b1e71ad49c852248ca63cf8e5910
[librecmc/librecmc-fossil.git] /
1 From 8bec80dccc9f4fe147a500486813f4e89a0d56d8 Mon Sep 17 00:00:00 2001
2 From: Mathieu Parent <math.parent@gmail.com>
3 Date: Sun, 25 Oct 2009 15:19:01 +0100
4 Subject: [PATCH 3/7] pico2wave: Convert text to .wav using svox text-to-speech system.
5
6 ---
7  pico/.gitignore      |    1 +
8  pico/Makefile.am     |    7 +
9  pico/bin/pico2wave.c |  341 ++++++++++++++++++++++++++++++++++++++++++++++++++
10  pico/configure.in    |    3 +
11  4 files changed, 352 insertions(+), 0 deletions(-)
12  create mode 100644 pico/bin/pico2wave.c
13
14 diff --git a/pico/.gitignore b/pico/.gitignore
15 index 4235569..a110298 100644
16 --- a/pico/.gitignore
17 +++ b/pico/.gitignore
18 @@ -29,4 +29,5 @@ libtool
19  *.lo
20  .libs
21  libttspico.la
22 +pico2wave
23  
24 diff --git a/pico/Makefile.am b/pico/Makefile.am
25 index 6d8a10c..0d9472d 100644
26 --- a/pico/Makefile.am
27 +++ b/pico/Makefile.am
28 @@ -34,3 +34,10 @@ libttspico_la_SOURCES = \
29         lib/picotrns.c \
30         lib/picowa.c
31  
32 +bin_PROGRAMS = pico2wave
33 +pico2wave_SOURCES = \
34 +       bin/pico2wave.c
35 +pico2wave_LDADD = \
36 +       libttspico.la -lm -lpopt
37 +pico2wave_CFLAGS = -Wall -I lib
38 +
39 diff --git a/pico/bin/pico2wave.c b/pico/bin/pico2wave.c
40 new file mode 100644
41 index 0000000..0c035a7
42 --- /dev/null
43 +++ b/pico/bin/pico2wave.c
44 @@ -0,0 +1,341 @@
45 +/* pico2wave.c
46 +
47 + * Copyright (C) 2009 Mathieu Parent <math.parent@gmail.com>
48 + *
49 + * Licensed under the Apache License, Version 2.0 (the "License");
50 + * you may not use this file except in compliance with the License.
51 + * You may obtain a copy of the License at
52 + *
53 + *     http://www.apache.org/licenses/LICENSE-2.0
54 + *
55 + * Unless required by applicable law or agreed to in writing, software
56 + * distributed under the License is distributed on an "AS IS" BASIS,
57 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
58 + * See the License for the specific language governing permissions and
59 + * limitations under the License.
60 + *
61 + *   Convert text to .wav using svox text-to-speech system.
62 + *
63 + */
64 +
65 +
66 +#include <popt.h>
67 +#include <stdio.h>
68 +#include <stdlib.h>
69 +#include <string.h>
70 +
71 +#include <picoapi.h>
72 +#include <picoapid.h>
73 +#include <picoos.h>
74 +
75 +
76 +/* adaptation layer defines */
77 +#define PICO_MEM_SIZE       2500000
78 +#define DummyLen 100000000
79 +
80 +/* string constants */
81 +#define MAX_OUTBUF_SIZE     128
82 +const char * PICO_LINGWARE_PATH             = "./lang/";
83 +const char * PICO_VOICE_NAME                = "PicoVoice";
84 +
85 +/* supported voices
86 +   Pico does not seperately specify the voice and locale.   */
87 +const char * picoSupportedLangIso3[]        = { "eng",              "eng",              "deu",              "spa",              "fra",              "ita" };
88 +const char * picoSupportedCountryIso3[]     = { "USA",              "GBR",              "DEU",              "ESP",              "FRA",              "ITA" };
89 +const char * picoSupportedLang[]            = { "en-US",            "en-GB",            "de-DE",            "es-ES",            "fr-FR",            "it-IT" };
90 +const char * picoInternalLang[]             = { "en-US",            "en-GB",            "de-DE",            "es-ES",            "fr-FR",            "it-IT" };
91 +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" };
92 +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" };
93 +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" };
94 +const int picoNumSupportedVocs              = 6;
95 +
96 +/* adapation layer global variables */
97 +void *          picoMemArea         = NULL;
98 +pico_System     picoSystem          = NULL;
99 +pico_Resource   picoTaResource      = NULL;
100 +pico_Resource   picoSgResource      = NULL;
101 +pico_Resource   picoUtppResource    = NULL;
102 +pico_Engine     picoEngine          = NULL;
103 +pico_Char *     picoTaFileName      = NULL;
104 +pico_Char *     picoSgFileName      = NULL;
105 +pico_Char *     picoUtppFileName    = NULL;
106 +pico_Char *     picoTaResourceName  = NULL;
107 +pico_Char *     picoSgResourceName  = NULL;
108 +pico_Char *     picoUtppResourceName = NULL;
109 +int     picoSynthAbort = 0;
110 +
111 +
112 +int main(int argc, const char *argv[]) {
113 +    char * wavefile = NULL;
114 +    char * lang = "en-US";
115 +    int langIndex = -1, langIndexTmp = -1;
116 +    char * text;
117 +    int8_t * buffer;
118 +    size_t bufferSize = 256;
119 +    
120 +    /* Parsing options */
121 +       poptContext optCon; /* context for parsing command-line options */
122 +       int opt; /* used for argument parsing */
123 +
124 +       struct poptOption optionsTable[] = {
125 +               { "wave", 'w', POPT_ARG_STRING, &wavefile, 0,
126 +                 "Write output to this WAV file (extension SHOULD be .wav)", "filename.wav" },
127 +               { "lang", 'l', POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &lang, 0,
128 +                 "Language", "lang" },
129 +               POPT_AUTOHELP
130 +               POPT_TABLEEND
131 +       };
132 +       optCon = poptGetContext(NULL, argc, argv, optionsTable, POPT_CONTEXT_POSIXMEHARDER);
133 +    poptSetOtherOptionHelp(optCon, "<words>");
134 +
135 +    /* Reporting about invalid extra options */
136 +       while ((opt = poptGetNextOpt(optCon)) != -1) {
137 +               switch (opt) {
138 +               default:
139 +                       fprintf(stderr, "Invalid option %s: %s\n", 
140 +                               poptBadOption(optCon, 0), poptStrerror(opt));
141 +                       poptPrintHelp(optCon, stderr, 0);
142 +                       exit(1);
143 +               }
144 +       }
145 +
146 +    /* Mandatory option: --wave */
147 +       if(!wavefile) {
148 +               fprintf(stderr, "Mandatory option: %s\n\n", 
149 +                       "--wave=filename.wav");
150 +               poptPrintHelp(optCon, stderr, 0);
151 +               exit(1);
152 +       }
153 +       /* option: --lang */
154 +       for(langIndexTmp =0; langIndexTmp<picoNumSupportedVocs; langIndexTmp++) {
155 +           if(!strcmp(picoSupportedLang[langIndexTmp], lang)) {
156 +               langIndex = langIndexTmp;
157 +               break;
158 +           }
159 +       }
160 +       if(langIndex == -1) {
161 +               fprintf(stderr, "Unknown language: %s\nValid languages:\n", 
162 +                       lang);
163 +           for(langIndexTmp =0; langIndexTmp<picoNumSupportedVocs; langIndexTmp++) {
164 +               fprintf(stderr, "%s\n", picoSupportedLang[langIndexTmp]);
165 +           }
166 +           lang = "en-US";
167 +               fprintf(stderr, "\n");
168 +               poptPrintHelp(optCon, stderr, 0);
169 +               exit(1);
170 +       }
171 +
172 +       /* Remaining argument is <words> */
173 +       const char **extra_argv;
174 +       extra_argv = poptGetArgs(optCon);
175 +    if(extra_argv) {
176 +               text = (char *) &(*extra_argv)[0];
177 +    } else {
178 +        //TODO: stdin not supported yet.
179 +               fprintf(stderr, "Missing argument: %s\n\n", 
180 +                       "<words>");
181 +               poptPrintHelp(optCon, stderr, 0);
182 +               exit(1);
183 +    }
184 +
185 +    poptFreeContext(optCon);
186 +    
187 +    buffer = malloc( bufferSize );
188 +    
189 +    int ret, getstatus;
190 +    pico_Char * inp = NULL;
191 +    pico_Char * local_text = NULL;
192 +    short       outbuf[MAX_OUTBUF_SIZE/2];
193 +    pico_Int16  bytes_sent, bytes_recv, text_remaining, out_data_type;
194 +    pico_Retstring outMessage;
195 +    
196 +    picoSynthAbort = 0;
197 +
198 +    picoMemArea = malloc( PICO_MEM_SIZE );
199 +    if((ret = pico_initialize( picoMemArea, PICO_MEM_SIZE, &picoSystem ))) {
200 +        pico_getSystemStatusMessage(picoSystem, ret, outMessage);
201 +        fprintf(stderr, "Cannot initialize pico (%i): %s\n", ret, outMessage);
202 +        goto terminate;
203 +    }
204 +    
205 +    /* Load the text analysis Lingware resource file.   */
206 +    picoTaFileName      = (pico_Char *) malloc( PICO_MAX_DATAPATH_NAME_SIZE + PICO_MAX_FILE_NAME_SIZE );
207 +    strcpy((char *) picoTaFileName,   PICO_LINGWARE_PATH);
208 +    strcat((char *) picoTaFileName,   (const char *) picoInternalTaLingware[langIndex]);
209 +    if((ret = pico_loadResource( picoSystem, picoTaFileName, &picoTaResource ))) {
210 +        pico_getSystemStatusMessage(picoSystem, ret, outMessage);
211 +        fprintf(stderr, "Cannot load text analysis resource file (%i): %s\n", ret, outMessage);
212 +        goto unloadTaResource;
213 +    }
214 +    
215 +    /* Load the signal generation Lingware resource file.   */
216 +    picoSgFileName      = (pico_Char *) malloc( PICO_MAX_DATAPATH_NAME_SIZE + PICO_MAX_FILE_NAME_SIZE );
217 +    strcpy((char *) picoSgFileName,   PICO_LINGWARE_PATH);
218 +    strcat((char *) picoSgFileName,   (const char *) picoInternalSgLingware[langIndex]);
219 +    if((ret = pico_loadResource( picoSystem, picoSgFileName, &picoSgResource ))) {
220 +        pico_getSystemStatusMessage(picoSystem, ret, outMessage);
221 +        fprintf(stderr, "Cannot load signal generation Lingware resource file (%i): %s\n", ret, outMessage);
222 +        goto unloadSgResource;
223 +    }
224
225 +    /* Load the utpp Lingware resource file if exists - NOTE: this file is optional
226 +       and is currently not used. Loading is only attempted for future compatibility.
227 +       If this file is not present the loading will still succeed.                      //
228 +    picoUtppFileName      = (pico_Char *) malloc( PICO_MAX_DATAPATH_NAME_SIZE + PICO_MAX_FILE_NAME_SIZE );
229 +    strcpy((char *) picoUtppFileName,   PICO_LINGWARE_PATH);
230 +    strcat((char *) picoUtppFileName,   (const char *) picoInternalUtppLingware[langIndex]);
231 +    ret = pico_loadResource( picoSystem, picoUtppFileName, &picoUtppResource );
232 +    pico_getSystemStatusMessage(picoSystem, ret, outMessage);
233 +    printf("pico_loadResource: %i: %s\n", ret, outMessage);
234 +    */
235 +    
236 +    /* Get the text analysis resource name.     */
237 +    picoTaResourceName  = (pico_Char *) malloc( PICO_MAX_RESOURCE_NAME_SIZE );
238 +    if((ret = pico_getResourceName( picoSystem, picoTaResource, (char *) picoTaResourceName ))) {
239 +        pico_getSystemStatusMessage(picoSystem, ret, outMessage);
240 +        fprintf(stderr, "Cannot get the text analysis resource name (%i): %s\n", ret, outMessage);
241 +        goto unloadUtppResource;
242 +    }
243 +
244 +    /* Get the signal generation resource name. */
245 +    picoSgResourceName  = (pico_Char *) malloc( PICO_MAX_RESOURCE_NAME_SIZE );
246 +    if((ret = pico_getResourceName( picoSystem, picoSgResource, (char *) picoSgResourceName ))) {
247 +        pico_getSystemStatusMessage(picoSystem, ret, outMessage);
248 +        fprintf(stderr, "Cannot get the signal generation resource name (%i): %s\n", ret, outMessage);
249 +        goto unloadUtppResource;
250 +    }
251 +
252 +
253 +    /* Create a voice definition.   */
254 +    if((ret = pico_createVoiceDefinition( picoSystem, (const pico_Char *) PICO_VOICE_NAME ))) {
255 +        pico_getSystemStatusMessage(picoSystem, ret, outMessage);
256 +        fprintf(stderr, "Cannot create voice definition (%i): %s\n", ret, outMessage);
257 +        goto unloadUtppResource;
258 +    }
259 +
260 +    /* Add the text analysis resource to the voice. */
261 +    if((ret = pico_addResourceToVoiceDefinition( picoSystem, (const pico_Char *) PICO_VOICE_NAME, picoTaResourceName ))) {
262 +        pico_getSystemStatusMessage(picoSystem, ret, outMessage);
263 +        fprintf(stderr, "Cannot add the text analysis resource to the voice (%i): %s\n", ret, outMessage);
264 +        goto unloadUtppResource;
265 +    }
266 +    
267 +    /* Add the signal generation resource to the voice. */
268 +    if((ret = pico_addResourceToVoiceDefinition( picoSystem, (const pico_Char *) PICO_VOICE_NAME, picoSgResourceName ))) {
269 +        pico_getSystemStatusMessage(picoSystem, ret, outMessage);
270 +        fprintf(stderr, "Cannot add the signal generation resource to the voice (%i): %s\n", ret, outMessage);
271 +        goto unloadUtppResource;
272 +    }
273 +
274 +    /* Create a new Pico engine. */
275 +    if((ret = pico_newEngine( picoSystem, (const pico_Char *) PICO_VOICE_NAME, &picoEngine ))) {
276 +        pico_getSystemStatusMessage(picoSystem, ret, outMessage);
277 +        fprintf(stderr, "Cannot create a new pico engine (%i): %s\n", ret, outMessage);
278 +        goto disposeEngine;
279 +    }
280 +    
281 +    local_text = (pico_Char *) text ;
282 +    text_remaining = strlen((const char *) local_text) + 1;
283 +
284 +    inp = (pico_Char *) local_text;
285 +    
286 +    size_t bufused = 0;
287 +    
288 +    picoos_Common common = (picoos_Common) pico_sysGetCommon(picoSystem);
289 +
290 +    picoos_SDFile sdOutFile = NULL;
291 +
292 +    picoos_bool done = TRUE;
293 +    if(TRUE != (done = picoos_sdfOpenOut(common, &sdOutFile,
294 +        (picoos_char *) wavefile, SAMPLE_FREQ_16KHZ, PICOOS_ENC_LIN)))
295 +    {
296 +        fprintf(stderr, "Cannot open output wave file\n");
297 +        ret = 1;
298 +        goto disposeEngine;
299 +    }
300 +    
301 +    /* synthesis loop   */
302 +    while (text_remaining) {
303 +        /* Feed the text into the engine.   */
304 +        if((ret = pico_putTextUtf8( picoEngine, inp, text_remaining, &bytes_sent ))) {
305 +            pico_getSystemStatusMessage(picoSystem, ret, outMessage);
306 +            fprintf(stderr, "Cannot put Text (%i): %s\n", ret, outMessage);
307 +            goto disposeEngine;
308 +        }
309 +    
310 +        text_remaining -= bytes_sent;
311 +        inp += bytes_sent;
312 +
313 +        do {
314 +            if (picoSynthAbort) {
315 +                goto disposeEngine;
316 +            }
317 +            /* Retrieve the samples and add them to the buffer. */
318 +            getstatus = pico_getData( picoEngine, (void *) outbuf,
319 +                      MAX_OUTBUF_SIZE, &bytes_recv, &out_data_type );
320 +            if((getstatus !=PICO_STEP_BUSY) && (getstatus !=PICO_STEP_IDLE)){
321 +                pico_getSystemStatusMessage(picoSystem, getstatus, outMessage);
322 +                fprintf(stderr, "Cannot get Data (%i): %s\n", getstatus, outMessage);
323 +                goto disposeEngine;
324 +            }
325 +            if (bytes_recv) {
326 +                if ((bufused + bytes_recv) <= bufferSize) {
327 +                    memcpy(buffer+bufused, (int8_t *) outbuf, bytes_recv);
328 +                    bufused += bytes_recv;
329 +                } else {
330 +                    done = picoos_sdfPutSamples(
331 +                                        sdOutFile,
332 +                                        bufused / 2,
333 +                                        (picoos_int16*) (buffer));
334 +                    bufused = 0;
335 +                    memcpy(buffer, (int8_t *) outbuf, bytes_recv);
336 +                    bufused += bytes_recv;
337 +                }
338 +            }
339 +        } while (PICO_STEP_BUSY == getstatus);
340 +        /* This chunk of synthesis is finished; pass the remaining samples. */
341 +        if (!picoSynthAbort) {
342 +                    done = picoos_sdfPutSamples(
343 +                                        sdOutFile,
344 +                                        bufused / 2,
345 +                                        (picoos_int16*) (buffer));
346 +        }
347 +        picoSynthAbort = 0;
348 +    }
349 +    
350 +    if(TRUE != (done = picoos_sdfCloseOut(common, &sdOutFile)))
351 +    {
352 +        fprintf(stderr, "Cannot close output wave file\n");
353 +        ret = 1;
354 +        goto disposeEngine;
355 +    }
356 +
357 +disposeEngine:
358 +    if (picoEngine) {
359 +        pico_disposeEngine( picoSystem, &picoEngine );
360 +        pico_releaseVoiceDefinition( picoSystem, (pico_Char *) PICO_VOICE_NAME );
361 +        picoEngine = NULL;
362 +    }
363 +unloadUtppResource:
364 +    if (picoUtppResource) {
365 +        pico_unloadResource( picoSystem, &picoUtppResource );
366 +        picoUtppResource = NULL;
367 +    }
368 +unloadSgResource:
369 +    if (picoSgResource) {
370 +        pico_unloadResource( picoSystem, &picoSgResource );
371 +        picoSgResource = NULL;
372 +    }
373 +unloadTaResource:
374 +    if (picoTaResource) {
375 +        pico_unloadResource( picoSystem, &picoTaResource );
376 +        picoTaResource = NULL;
377 +    }
378 +terminate:
379 +    if (picoSystem) {
380 +        pico_terminate(&picoSystem);
381 +        picoSystem = NULL;
382 +    }
383 +    exit(ret);
384 +}
385 +
386 diff --git a/pico/configure.in b/pico/configure.in
387 index 0afb56d..349eb1d 100644
388 --- a/pico/configure.in
389 +++ b/pico/configure.in
390 @@ -14,3 +14,6 @@ AC_CONFIG_FILES([Makefile])
391  AC_OUTPUT
392  
393  AC_CONFIG_MACRO_DIR([m4])
394 +
395 +AC_CHECK_LIB(popt, poptGetContext)
396 +
397 -- 
398 1.7.1
399