com_svox_picottsengine.cpp revision 5da148ef02f1ed01db73163c2befde420b875a9f
139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi/* com_svox_picottsengine.cpp
239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
3b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen * Copyright (C) 2008-2009 SVOX AG, Baslerstr. 30, 8048 Zuerich, Switzerland
4b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *
5b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen * Licensed under the Apache License, Version 2.0 (the "License");
6b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen * you may not use this file except in compliance with the License.
7b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen * You may obtain a copy of the License at
8b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *
9b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *     http://www.apache.org/licenses/LICENSE-2.0
10b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *
11b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen * Unless required by applicable law or agreed to in writing, software
12b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen * distributed under the License is distributed on an "AS IS" BASIS,
13b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen * See the License for the specific language governing permissions and
15b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen * limitations under the License.
164bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi *
174bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi *   This is the Manager layer.  It sits on top of the native Pico engine
184bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi *   and provides the interface to the defined Google TTS engine API.
194bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi *   The Google engine API is the boundary to allow a TTS engine to be swapped.
204bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi *   The Manager layer also provide the SSML tag interpretation.
214bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi *   The supported SSML tags are mapped to corresponding tags natively supported by Pico.
224bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi *   Native Pico functions always begin with picoXXX.
234bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi *
244bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi *   In the Pico engine, the language cannot be changed indpendently of the voice.
254bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi *   If either the voice or locale/language are changed, a new resource is loaded.
264bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi *
274bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi *   Only a subset of SSML 1.0 tags are supported.
284bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi *   Some SSML tags involve significant complexity.
294bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi *   If the language is changed through an SSML tag, there is a latency for the load.
304bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi *
31b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen */
32b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
33b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen#include <stdio.h>
34b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen#include <unistd.h>
354bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi#include <stdlib.h>
36b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
37b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen#define LOG_TAG "SVOX Pico Engine"
38b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
39b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen#include <utils/Log.h>
4039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi#include <utils/String16.h>                     /* for strlen16 */
41b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen#include <android_runtime/AndroidRuntime.h>
42b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen#include <tts/TtsEngine.h>
4339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi#include <cutils/jstring.h>
44b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen#include <picoapi.h>
45b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen#include <picodefs.h>
4639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi#include "svox_ssml_parser.h"
47b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
48b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chenusing namespace android;
49b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
50b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen/* adaptation layer defines */
514eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi#define PICO_MEM_SIZE       2500000
5254823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi/* speaking rate    */
5339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi#define PICO_MIN_RATE        20
54b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen#define PICO_MAX_RATE       500
5539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi#define PICO_DEF_RATE       100
5654823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi/* speaking pitch   */
5739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi#define PICO_MIN_PITCH       50
58b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen#define PICO_MAX_PITCH      200
5939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi#define PICO_DEF_PITCH      100
6054823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi/* speaking volume  */
6139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi#define PICO_MIN_VOLUME       0
62b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen#define PICO_MAX_VOLUME     500
6339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi#define PICO_DEF_VOLUME     250
6439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
6554823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi/* string constants */
66b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen#define MAX_OUTBUF_SIZE     128
6739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Triviconst char * PICO_LINGWARE_PATH             = "/sdcard/svox/";
6839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Triviconst char * PICO_VOICE_NAME                = "PicoVoice";
6939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Triviconst char * PICO_SPEED_OPEN_TAG            = "<speed level='%d'>";
7039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Triviconst char * PICO_SPEED_CLOSE_TAG           = "</speed>";
7139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Triviconst char * PICO_PITCH_OPEN_TAG            = "<pitch level='%d'>";
7239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Triviconst char * PICO_PITCH_CLOSE_TAG           = "</pitch>";
7339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Triviconst char * PICO_VOLUME_OPEN_TAG           = "<volume level='%d'>";
7439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Triviconst char * PICO_VOLUME_CLOSE_TAG          = "</volume>";
7539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Triviconst char * PICO_PHONEME_OPEN_TAG          = "<phoneme ph='";
764bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Triviconst char * PICO_PHONEME_CLOSE_TAG         = "'/>";
7739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
7839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi/* supported voices
7939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi   Pico does not seperately specify the voice and locale.   */
8039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Triviconst char * picoSupportedLangIso3[]        = { "eng",              "eng",              "deu",              "spa",              "fra",              "ita" };
8139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Triviconst char * picoSupportedCountryIso3[]     = { "USA",              "GBR",              "DEU",              "ESP",              "FRA",              "ITA" };
8239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Triviconst char * picoSupportedLang[]            = { "en-rUS",           "en-rGB",           "de-rDE",           "es-rES",           "fr-rFR",           "it-rIT" };
8339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Triviconst char * picoInternalLang[]             = { "en-US",            "en-GB",            "de-DE",            "es-ES",            "fr-FR",            "it-IT" };
8439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Triviconst 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" };
8539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Triviconst 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" };
8639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Triviconst 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" };
8739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Triviconst int picoNumSupportedVocs              = 6;
88b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
8954823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi/* supported properties */
9039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Triviconst char * picoSupportedProperties[]      = { "language", "rate", "pitch", "volume" };
9139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Triviconst int    picoNumSupportedProperties     = 4;
92b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
9354823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi
9454823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi/* adapation layer global variables */
9539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel TrivisynthDoneCB_t * picoSynthDoneCBPtr;
9639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivivoid *          picoMemArea         = NULL;
9739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivipico_System     picoSystem          = NULL;
9839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivipico_Resource   picoTaResource      = NULL;
9939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivipico_Resource   picoSgResource      = NULL;
10039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivipico_Resource   picoUtppResource    = NULL;
10139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivipico_Engine     picoEngine          = NULL;
10239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivipico_Char *     picoTaFileName      = NULL;
10339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivipico_Char *     picoSgFileName      = NULL;
10439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivipico_Char *     picoUtppFileName    = NULL;
10539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivipico_Char *     picoTaResourceName  = NULL;
10639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivipico_Char *     picoSgResourceName  = NULL;
10739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivipico_Char *     picoUtppResourceName = NULL;
10854823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Triviint     picoSynthAbort = 0;
10939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivichar *  picoProp_currLang   = NULL;                 /* current language */
11054823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Triviint     picoProp_currRate   = PICO_DEF_RATE;        /* current rate     */
11154823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Triviint     picoProp_currPitch  = PICO_DEF_PITCH;       /* current pitch    */
11254823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Triviint     picoProp_currVolume = PICO_DEF_VOLUME;      /* current volume   */
11354823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi
1149b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Triviint picoCurrentLangIndex = -1;
115b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
116b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
117b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen/* internal helper functions */
118b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
11939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi/** checkForLocale
12039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  Check whether the requested locale is among the supported locales.
12139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  @locale -  the locale to check, either in xx or xx-rYY format
12239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  return index of the locale, or -1 if not supported.
123b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen*/
12439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivistatic int checkForLocale( const char * locale )
125b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen{
12654823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi     int found = -1;                                         /* language not found   */
12739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi     int i;
12839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi     if (locale == NULL) {
1295da148ef02f1ed01db73163c2befde420b875a9fJean-Michel Trivi        LOGE("checkForLocale called with NULL language");
1304bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        return found;
13139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi     }
13254823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi
13354823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi    /* Verify that the requested locale is a locale that we support.    */
1344bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    for (i = 0; i < picoNumSupportedVocs; i ++) {
1354bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        if (strcmp(locale, picoSupportedLang[i]) == 0) { /* in array */
136b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            found = i;
137b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            break;
138b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        }
13954823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi    };
14039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
14139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* The locale was not found.    */
1424bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (found < 0) {
14354823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi        /* We didn't find an exact match; it may have been specified with only the first 2 characters.
14439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi           This could overmatch ISO 639-3 language codes.%%                                   */
1454bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        for (i = 0; i < picoNumSupportedVocs; i ++) {
1464bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            if (strncmp(locale, picoSupportedLang[i], 2) == 0) {
147b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen                found = i;
148b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen                break;
149b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            }
150b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        }
1514bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        if (found < 0) {
15239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            LOGE("TtsEngine::set language called with unsupported locale");
153b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        }
15454823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi    };
155b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    return found;
156b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen}
157b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
15854823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi
159b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen/** cleanResources
16054823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi *  Unloads any loaded Pico resources.
161b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen*/
16254823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivistatic void cleanResources( void )
163b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen{
1644bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (picoEngine) {
16554823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi        pico_disposeEngine( picoSystem, &picoEngine );
16639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        pico_releaseVoiceDefinition( picoSystem, (pico_Char *) PICO_VOICE_NAME );
167b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        picoEngine = NULL;
168b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
1694bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (picoUtppResource) {
17054823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi        pico_unloadResource( picoSystem, &picoUtppResource );
171b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        picoUtppResource = NULL;
172b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
1734bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (picoTaResource) {
17454823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi        pico_unloadResource( picoSystem, &picoTaResource );
175b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        picoTaResource = NULL;
176b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
1774bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (picoSgResource) {
17854823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi        pico_unloadResource( picoSystem, &picoSgResource );
179b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        picoSgResource = NULL;
180b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
1819b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    picoCurrentLangIndex = -1;
182b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen}
183b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
18454823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi
185b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen/** cleanFiles
18654823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi *  Frees any memory allocated for file and resource strings.
187b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen*/
18854823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivistatic void cleanFiles( void )
189b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen{
1904bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (picoProp_currLang) {
19139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        free( picoProp_currLang );
192b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        picoProp_currLang = NULL;
193b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
1944eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
1954bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (picoTaFileName) {
19639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        free( picoTaFileName );
197b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        picoTaFileName = NULL;
198b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
1994eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
2004bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (picoSgFileName) {
20139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        free( picoSgFileName );
202b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        picoSgFileName = NULL;
203b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
2044eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
2054bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (picoUtppFileName) {
20639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        free( picoUtppFileName );
207b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        picoUtppFileName = NULL;
208b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
2094eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
2104bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (picoTaResourceName) {
21139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        free( picoTaResourceName );
212b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        picoTaResourceName = NULL;
213b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
2144eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
2154bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (picoSgResourceName) {
21639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        free( picoSgResourceName );
217b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        picoSgResourceName = NULL;
218b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
2194eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
2204bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (picoUtppResourceName) {
22139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        free( picoUtppResourceName );
222b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        picoUtppResourceName = NULL;
223b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
224b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen}
225b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
2269b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi/** hasResourcesForLanguage
2279b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi *  Check to see if the resources required to load the language at the specified index
2289b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi *  are properly installed
2299b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi *  @langIndex - the index of the language to check the resources for. The index is valid.
2309b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi *  return true if the required resources are installed, false otherwise
2319b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi */
2329b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivistatic bool hasResourcesForLanguage(int langIndex) {
2339b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    FILE * pFile;
2349b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    char* fileName = (char*)malloc(PICO_MAX_DATAPATH_NAME_SIZE + PICO_MAX_FILE_NAME_SIZE);
2354bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi
2369b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    strcpy((char*)fileName, PICO_LINGWARE_PATH);
2379b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    strcat((char*)fileName, (const char*)picoInternalTaLingware[langIndex]);
2384bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    pFile = fopen(fileName, "r");
2394bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (pFile == NULL) {
2409b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        free(fileName);
2419b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        return false;
2429b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    } else {
2439b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        fclose (pFile);
2449b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    }
2459b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi
2469b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    strcpy((char*)fileName, PICO_LINGWARE_PATH);
2479b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    strcat((char*)fileName, (const char*)picoInternalSgLingware[langIndex]);
2489b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    pFile = fopen(fileName, "r");
2499b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    if (pFile == NULL) {
2509b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        free(fileName);
2519b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        return false;
2529b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    } else {
2539b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        fclose(pFile);
2549b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        free(fileName);
2559b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        return true;
2569b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    }
2579b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi}
2589b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi
2594eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi/** doLanguageSwitchFromLangIndex
26039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  Switch to the requested locale.
26139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  If the locale is already loaded, it returns immediately.
26239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  If another locale is already is loaded, it will first be unloaded and the new one then loaded.
26339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  If no locale is loaded, the requested locale will be loaded.
26439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  @langIndex -  the index of the locale/voice to load, which is guaranteed to be supported.
265b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *  return TTS_SUCCESS or TTS_FAILURE
2664eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi */
26739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivistatic tts_result doLanguageSwitchFromLangIndex( int langIndex )
268b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen{
26939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    int ret;                                        /* function result code */
27039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
27139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* If we already have a loaded locale, check whether it is the same one as requested.   */
2724bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (picoProp_currLang && (strcmp(picoProp_currLang, picoSupportedLang[langIndex]) == 0)) {
2734eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi        LOGI("Language already loaded (%s == %s)", picoProp_currLang, picoSupportedLang[langIndex]);
274b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_SUCCESS;
275b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
276b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
27739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* It is not the same locale; unload the current one first. */
278b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    cleanResources();
2794eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
28039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* Allocate memory for file and resource names.     */
281b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    cleanFiles();
28239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    picoProp_currLang   = (char *)      malloc( 10 );
28339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    picoTaFileName      = (pico_Char *) malloc( PICO_MAX_DATAPATH_NAME_SIZE + PICO_MAX_FILE_NAME_SIZE );
28439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    picoSgFileName      = (pico_Char *) malloc( PICO_MAX_DATAPATH_NAME_SIZE + PICO_MAX_FILE_NAME_SIZE );
28539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    picoUtppFileName    = (pico_Char *) malloc( PICO_MAX_DATAPATH_NAME_SIZE + PICO_MAX_FILE_NAME_SIZE );
28639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    picoTaResourceName  = (pico_Char *) malloc( PICO_MAX_RESOURCE_NAME_SIZE );
28739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    picoSgResourceName  = (pico_Char *) malloc( PICO_MAX_RESOURCE_NAME_SIZE );
28839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    picoUtppResourceName =(pico_Char *) malloc( PICO_MAX_RESOURCE_NAME_SIZE );
28939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
29039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* Set the path and file names for resource files.  */
29139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    strcpy((char *) picoTaFileName,   PICO_LINGWARE_PATH);
29239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    strcat((char *) picoTaFileName,   (const char *) picoInternalTaLingware[langIndex]);
29339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    strcpy((char *) picoSgFileName,   PICO_LINGWARE_PATH);
29439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    strcat((char *) picoSgFileName,   (const char *) picoInternalSgLingware[langIndex]);
29539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    strcpy((char *) picoUtppFileName, PICO_LINGWARE_PATH);
29639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    strcat((char *) picoUtppFileName, (const char *) picoInternalUtppLingware[langIndex]);
29739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
29839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* Load the text analysis Lingware resource file.   */
29939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    ret = pico_loadResource( picoSystem, picoTaFileName, &picoTaResource );
3004bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (PICO_OK != ret) {
3014eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi        LOGE("Failed to load textana resource for %s [%d]", picoSupportedLang[langIndex], ret);
302b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        cleanResources();
303b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        cleanFiles();
304b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_FAILURE;
305b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
3064eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
30739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* Load the signal generation Lingware resource file.   */
30839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    ret = pico_loadResource( picoSystem, picoSgFileName, &picoSgResource );
3094bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (PICO_OK != ret) {
3104eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi        LOGE("Failed to load siggen resource for %s [%d]", picoSupportedLang[langIndex], ret);
311b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        cleanResources();
312b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        cleanFiles();
313b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_FAILURE;
314b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
3154eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
31639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* Load the utpp Lingware resource file if exists - NOTE: this file is optional
31739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi       and is currently not used. Loading is only attempted for future compatibility.
31839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi       If this file is not present the loading will still succeed.                      */
31939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    ret = pico_loadResource( picoSystem, picoUtppFileName, &picoUtppResource );
3204bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if ((PICO_OK != ret) && (ret != PICO_EXC_CANT_OPEN_FILE)) {
3214eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi        LOGE("Failed to load utpp resource for %s [%d]", picoSupportedLang[langIndex], ret);
322b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        cleanResources();
323b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        cleanFiles();
324b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_FAILURE;
3254bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    }
3264eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
32739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* Get the text analysis resource name.     */
32839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    ret = pico_getResourceName( picoSystem, picoTaResource, (char *) picoTaResourceName );
3294bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (PICO_OK != ret) {
3304eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi        LOGE("Failed to get textana resource name for %s [%d]", picoSupportedLang[langIndex], ret);
331b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        cleanResources();
332b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        cleanFiles();
333b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_FAILURE;
334b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
3354eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
33639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* Get the signal generation resource name. */
33739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    ret = pico_getResourceName( picoSystem, picoSgResource, (char *) picoSgResourceName );
338744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi    if ((PICO_OK == ret) && (picoUtppResource != NULL)) {
33939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        /* Get utpp resource name - optional: see note above.   */
34039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        ret = pico_getResourceName( picoSystem, picoUtppResource, (char *) picoUtppResourceName );
3414bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        if (PICO_OK != ret)  {
3424eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi            LOGE("Failed to get utpp resource name for %s [%d]", picoSupportedLang[langIndex], ret);
343b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            cleanResources();
344b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            cleanFiles();
345b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            return TTS_FAILURE;
346b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        }
347b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
3484bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (PICO_OK != ret) {
3494eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi        LOGE("Failed to get siggen resource name for %s [%d]", picoSupportedLang[langIndex], ret);
350b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        cleanResources();
351b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        cleanFiles();
352b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_FAILURE;
353b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
3544eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
35539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* Create a voice definition.   */
35639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    ret = pico_createVoiceDefinition( picoSystem, (const pico_Char *) PICO_VOICE_NAME );
3574bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (PICO_OK != ret) {
3584eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi        LOGE("Failed to create voice for %s [%d]", picoSupportedLang[langIndex], ret);
359b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        cleanResources();
360b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        cleanFiles();
361b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_FAILURE;
362b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
3634eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
36439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* Add the text analysis resource to the voice. */
36539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    ret = pico_addResourceToVoiceDefinition( picoSystem, (const pico_Char *) PICO_VOICE_NAME, picoTaResourceName );
3664bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (PICO_OK != ret) {
3674eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi        LOGE("Failed to add textana resource to voice for %s [%d]", picoSupportedLang[langIndex], ret);
368b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        cleanResources();
369b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        cleanFiles();
370b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_FAILURE;
371b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
3724eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
37339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* Add the signal generation resource to the voice. */
37439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    ret = pico_addResourceToVoiceDefinition( picoSystem, (const pico_Char *) PICO_VOICE_NAME, picoSgResourceName );
375744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi    if ((PICO_OK == ret) && (picoUtppResource != NULL)) {
37639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        /* Add utpp resource to voice - optional: see note above.   */
37739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        ret = pico_addResourceToVoiceDefinition( picoSystem, (const pico_Char *) PICO_VOICE_NAME, picoUtppResourceName );
3784bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        if (PICO_OK != ret) {
3794eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi            LOGE("Failed to add utpp resource to voice for %s [%d]", picoSupportedLang[langIndex], ret);
380b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            cleanResources();
381b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            cleanFiles();
382b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            return TTS_FAILURE;
383b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        }
384b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
3854eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
3864bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (PICO_OK != ret) {
3874eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi        LOGE("Failed to add siggen resource to voice for %s [%d]", picoSupportedLang[langIndex], ret);
388b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        cleanResources();
389b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        cleanFiles();
390b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_FAILURE;
391b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
392b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
39339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    ret = pico_newEngine( picoSystem, (const pico_Char *) PICO_VOICE_NAME, &picoEngine );
3944bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (PICO_OK != ret) {
3954eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi        LOGE("Failed to create engine for %s [%d]", picoSupportedLang[langIndex], ret);
396b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        cleanResources();
397b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        cleanFiles();
398b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_FAILURE;
399b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
4004eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
40139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* Set the current locale/voice.    */
40239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    strcpy( picoProp_currLang, picoSupportedLang[langIndex] );
4035da148ef02f1ed01db73163c2befde420b875a9fJean-Michel Trivi    picoCurrentLangIndex = langIndex;
404b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    LOGI("loaded %s successfully", picoProp_currLang);
405b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    return TTS_SUCCESS;
406b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen}
407b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
40839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
4094eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi/** doLanguageSwitch
41039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  Switch to the requested locale.
41139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  If this locale is already loaded, it returns immediately.
41239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  If another locale is already loaded, this will first be unloaded
41339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  and the new one then loaded.
41439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  If no locale is loaded, the requested will be loaded.
41539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  @locale -  the locale to check, either in xx or xx-rYY format (i.e "en" or "en-rUS")
4164eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi *  return TTS_SUCCESS or TTS_FAILURE
4174eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi*/
41839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivistatic tts_result doLanguageSwitch( const char * locale )
4194eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi{
42039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    int loclIndex;                              /* locale index */
4214eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
42239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* Load the new locale. */
42339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    loclIndex = checkForLocale( locale );
4244bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (loclIndex < 0)  {
42539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        LOGE("Tried to swith to non-supported locale %s", locale);
42639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        return TTS_FAILURE;
4274bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    }
42839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    LOGI("Found supported locale %s", picoSupportedLang[loclIndex]);
42939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    return doLanguageSwitchFromLangIndex( loclIndex );
4304eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi}
4314eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
43239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
433b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen/** doAddProperties
43439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  Add <speed>, <pitch> and <volume> tags to the text,
43539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  if the properties have been set to non-default values, and return the new string.
43639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  The calling function is responsible for freeing the returned string.
437b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *  @str - text to apply tags to
438b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *  return new string with tags applied
439b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen*/
44039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivistatic char * doAddProperties( const char * str )
441b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen{
44239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    char *  data = NULL;
44339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    int     haspitch, hasspeed, hasvol;                 /* parameters           */
44439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    int     textlen;                                    /* property string length   */
4454eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
44639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    haspitch = 0; hasspeed = 0; hasvol = 0;
44739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    textlen = strlen(str) + 1;
4484bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (picoProp_currPitch != PICO_DEF_PITCH) {          /* non-default pitch    */
449b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        textlen += strlen(PICO_PITCH_OPEN_TAG) + 5;
450b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        textlen += strlen(PICO_PITCH_CLOSE_TAG);
451b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        haspitch = 1;
452b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
4534bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (picoProp_currRate != PICO_DEF_RATE) {            /* non-default rate     */
454b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        textlen += strlen(PICO_SPEED_OPEN_TAG) + 5;
455b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        textlen += strlen(PICO_SPEED_CLOSE_TAG);
456b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        hasspeed = 1;
457b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
4584bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (picoProp_currVolume != PICO_DEF_VOLUME) {        /* non-default volume   */
459b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        textlen += strlen(PICO_VOLUME_OPEN_TAG) + 5;
460b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        textlen += strlen(PICO_VOLUME_CLOSE_TAG);
461b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        hasvol = 1;
462b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
4634eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
46439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* Compose the property strings.    */
46539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    data = (char *) malloc( textlen );                  /* allocate string      */
4664bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (!data) {
467b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return NULL;
468b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
46954823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi    memset(data, 0, textlen);                           /* clear it             */
4704bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (haspitch) {
471b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        char* tmp = (char*)malloc(strlen(PICO_PITCH_OPEN_TAG) + strlen(PICO_PITCH_CLOSE_TAG) + 5);
472b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        sprintf(tmp, PICO_PITCH_OPEN_TAG, picoProp_currPitch);
473b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        strcat(data, tmp);
474b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        free(tmp);
475b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
4764eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
4774bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (hasspeed) {
478b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        char* tmp = (char*)malloc(strlen(PICO_SPEED_OPEN_TAG) + strlen(PICO_SPEED_CLOSE_TAG) + 5);
479b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        sprintf(tmp, PICO_SPEED_OPEN_TAG, picoProp_currRate);
480b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        strcat(data, tmp);
481b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        free(tmp);
482b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
4834eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
4844bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (hasvol) {
485b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        char* tmp = (char*)malloc(strlen(PICO_VOLUME_OPEN_TAG) + strlen(PICO_VOLUME_CLOSE_TAG) + 5);
486b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        sprintf(tmp, PICO_VOLUME_OPEN_TAG, picoProp_currVolume);
487b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        strcat(data, tmp);
488b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        free(tmp);
489b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
4904eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
491b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    strcat(data, str);
4924eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
4934bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (hasvol) {
494b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        strcat(data, PICO_VOLUME_CLOSE_TAG);
495b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
4964eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
4974bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (hasspeed) {
498b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        strcat(data, PICO_SPEED_CLOSE_TAG);
499b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
5004eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
5014bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (haspitch) {
502b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        strcat(data, PICO_PITCH_CLOSE_TAG);
503b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
5044eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
505b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    return data;
506b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen}
507b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
50854823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi
50939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi/** createPhonemeString
51039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  Wrap all individual words in <phoneme> tags.
51139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  The Pico <phoneme> tag only supports one word in each tag,
51239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  therefore they must be individually wrapped!
51339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  @xsampa - text to convert to Pico phomene string
51439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  @length - length of the input string
51539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  return new string with tags applied
51639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi*/
5174bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Triviextern char * createPhonemeString( const char * xsampa, int length )
51839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi{
51939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    char *  convstring = NULL;
52039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    int     origStrLen = strlen(xsampa);
52139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    int     numWords   = 1;
52239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    int     start, totalLength, i, j;
52339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
524744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi    for (i = 0; i < origStrLen; i ++) {
525744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        if ((xsampa[i] == ' ') || (xsampa[i] == '#')) {
526744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi            numWords ++;
527744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        }
528744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi    }
529744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi
530744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi    if (numWords == 1) {
531744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        convstring = new char[origStrLen + 17];
532744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        convstring[0] = '\0';
533744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        strcat(convstring, PICO_PHONEME_OPEN_TAG);
534744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        strcat(convstring, xsampa);
535744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        strcat(convstring, PICO_PHONEME_CLOSE_TAG);
536744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi    } else {
537744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        char * words[numWords];
538744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        start = 0; totalLength = 0; i = 0; j = 0;
539744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        for (i=0, j=0; i < origStrLen; i++) {
540744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi            if ((xsampa[i] == ' ') || (xsampa[i] == '#')) {
541744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi                words[j]    = new char[i+1-start+17];
542744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi                words[j][0] = '\0';
543744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi                strcat( words[j], PICO_PHONEME_OPEN_TAG);
544744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi                strncat(words[j], xsampa+start, i-start);
545744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi                strcat( words[j], PICO_PHONEME_CLOSE_TAG);
546744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi                start = i + 1;
547744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi                j++;
548744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi                totalLength += strlen(words[j-1]);
549744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi            }
550744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        }
551744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        words[j]    = new char[i+1-start+17];
552744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        words[j][0] = '\0';
553744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        strcat(words[j], PICO_PHONEME_OPEN_TAG);
554744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        strcat(words[j], xsampa+start);
555744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        strcat(words[j], PICO_PHONEME_CLOSE_TAG);
556744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        totalLength += strlen(words[j]);
557744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        convstring = new char[totalLength + 1];
558744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        convstring[0] = '\0';
559744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        for (i=0 ; i < numWords ; i++) {
560744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi            strcat(convstring, words[i]);
561744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi            delete [] words[i];
562744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        }
563744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi    }
564744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi
565744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi    return convstring;
56639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi}
56739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
5684bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi/* The XSAMPA uses as many as 5 characters to represent a single IPA code.  */
56939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivitypedef struct tagPhnArr
5704bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi{
57139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    char16_t    strIPA;             /* IPA Unicode symbol       */
5724bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    char        strXSAMPA[6];       /* SAMPA sequence           */
5734bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi} PArr;
57439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
5754bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi#define phn_cnt (134)
57639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
57739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel TriviPArr    PhnAry[phn_cnt] = {
57839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
57939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* XSAMPA conversion table      */
58039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
581744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi    /* Vowels (23) incomplete     */
58239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x025B,        "E"},
58339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0251,        "A"},
58439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0254,        "O"},
58539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x00F8,        "2"},
58639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0153,        "9"},
58739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0276,        "&"},
58839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0252,        "Q"},
58939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x028C,        "V"},
59039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0264,        "7"},
59139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x026F,        "M"},
59239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0268,        "1"},
59339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0289,        "}"},
59439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x026A,        "I"},
59539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x028F,        "Y"},
59639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x028A,        "U"},
59739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0259,        "@"},
59839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0275,        "8"},
59939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0250,        "6"},
60039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x00E6,        "{"},
60139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x025C,        "3"},
6024bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x025A,        "@`"},
6034bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x025E,        "3\\\\"},
6044bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x0258,        "@\\\\"},
60539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
606744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi    /* Consonants (60) incomplete */
6074bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x0288,        "t`"},
6084bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x0256,        "d`"},
6094bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x025F,        "J\\\\"},
61039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0261,        "g"},
6114bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x0262,        "G\\\\"},
61239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0294,        "?"},
61339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0271,        "F"},
6144bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x0273,        "n`"},
61539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0272,        "J"},
61639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x014B,        "N"},
6174bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x0274,        "N\\\\"},
6184bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x0299,        "B\\\\"},
6194bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x0280,        "R\\\\"},
62039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x027E,        "4"},
6214bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x027D,        "r`"},
6224bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x0278,        "p\\\\"},
62339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x03B2,        "B"},
62439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x03B8,        "T"},
62539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x00F0,        "D"},
62639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0283,        "S"},
62739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0292,        "Z"},
6284bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x0282,        "s`"},
6294bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x0290,        "z`"},
63039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x00E7,        "C"},
6314bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x029D,        "j\\\\"},
63239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0263,        "G"},
63339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x03C7,        "X"},
63439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0281,        "R"},
6354bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x0127,        "X\\\\"},
6364bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x0295,        "?\\\\"},
6374bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x0266,        "h\\\\"},
63839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x026C,        "K"},
6394bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x026E,        "K\\\\"},
64039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x028B,        "P"},
6414bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x0279,        "r\\\\"},
6424bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x027B,        "r\\\\'"},
6434bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x0270,        "M\\\\"},
6444bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x026D,        "l`"},
64539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x028E,        "L"},
6464bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x029F,        "L\\\\"},
64739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0253,        "b_<"},
64839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0257,        "d_<"},
64939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0284,        "J\\_<"},
65039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0260,        "g_<"},
65139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x029B,        "G\\_<"},
65239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x028D,        "W"},
65339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0265,        "H"},
6544bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x029C,        "H\\\\"},
6554bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x02A1,        ">\\\\"},
6564bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x02A2,        "<\\\\"},
6574bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x0298,        "O\\\\"},
6584bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x01C0,        "|\\\\"},
6594bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x01C3,        "!\\\\"},
66039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x01C2,        "=\\"},
66139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x01C1,        "|\\|\\"},
6624bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x027A,        "l\\\\"},
6634bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x0255,        "s\\\\"},
6644bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x0291,        "z\\\\"},
6654bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x0267,        "x\\\\"},
66639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x026B,        "l_G"},
66739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
668744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi    /* Diacritics (34) incomplete */
66939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x02BC,        "_>"},
67039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0325,        "_0"},
67139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x030A,        "_0"},
67239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x032C,        "_v"},
67339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x02B0,        "_h"},
67439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0324,        "_t"},
67539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0330,        "_k"},
67639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x033C,        "_N"},
67739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x032A,        "_d"},
67839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x033A,        "_a"},
67939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x033B,        "_m"},
68039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0339,        "_O"},
68139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x031C,        "_c"},
68239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x031F,        "_+"},
68339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0320,        "_-"},
6844bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x0308,        "_\""},     /* centralized  */
68539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x033D,        "_x"},
68639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0318,        "_A"},
68739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0319,        "_q"},
6884bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x02DE,        "`"},
68939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x02B7,        "_w"},
69039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x02B2,        "_j"},
69139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x02E0,        "_G"},
6924bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x02E4,        "_?\\\\"},
69339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0303,        "~"},
69439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x207F,        "_n"},
69539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x02E1,        "_l"},
69639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x031A,        "_}"},
69739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0334,        "_e"},
69839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x031D,        "_r"},
69939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x031E,        "_o"},
70039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0329,        "="},
70139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x032F,        "_^"},
70239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x02D0,        ":"},
70339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
7044bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    /* Others (11) complete */
70539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0361,        "_"},
70639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x035C,        "_"},
7074bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x02C8,        "\""},
70839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x02CC,        "%"},
7094bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x02D1,        ":\\\\"},
71039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x0306,        "_X"},
71139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x2016,        "||"},
7124bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x203F,        "-\\\\"},
71339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x2197,        "<R>"},
71439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x2198,        "<F>"},
7154bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x025D,                "3`"},
71639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
71739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* Affricates (6) complete  */
71839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x02A3,        "d_z"},
71939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x02A4,        "d_Z"},
7204bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x02A5,        "d_z\\\\"},
72139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x02A6,        "t_s"},
72239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    {0x02A7,        "t_S"},
7234bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    {0x02A8,        "t_s\\\\"}
72439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    };
72539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
72639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
72739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivivoid CnvIPAPnt( const char16_t IPnt, char * XPnt )
72839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi{
72939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    char16_t        ThisPnt = IPnt;                     /* local copy of single IPA codepoint   */
73039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    int             idx;                                /* index into table         */
731744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi
73239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* Convert an individual IPA codepoint.
73339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi       A single IPA code could map to a string.
73439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi       Search the table.  If it is not found, use the same character.
73539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi       Since most codepoints can be contained within 16 bits,
73639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi       they are represented as wide chars.              */
73739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    XPnt[0] = 0;                                        /* clear the result string  */
73839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
73939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* Search the table for the conversion. */
7404bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    for (idx = 0; idx < phn_cnt; idx ++) {               /* for each item in table   */
741744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        if (IPnt == PhnAry[idx].strIPA) {                /* matches IPA code         */
7424bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            strcat( XPnt, (const char *)&(PhnAry[idx].strXSAMPA) ); /* copy the XSAMPA string   */
7434bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            return;
74439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        }
7454bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    }
74639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    strcat(XPnt, (const char *)&ThisPnt);               /* just copy it             */
74739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi}
74839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
74939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
75039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi/** cnvIpaToXsampa
75139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  Convert an IPA character string to an XSAMPA character string.
75239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  @ipaString - input IPA string to convert
75339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  @outXsampaString - converted XSAMPA string is passed back in this parameter
75439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  return size of the new string
75539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi*/
7564bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi
7574bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Triviint cnvIpaToXsampa( const char16_t * ipaString, size_t ipaStringSize, char ** outXsampaString )
75839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi{
7594bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    size_t xsize;                                  /* size of result               */
7604bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    int    ipidx;                                  /* index into IPA string        */
7614bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    char * XPnt;                                   /* short XSAMPA char sequence   */
76239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
76339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* Convert an IPA string to an XSAMPA string and store the xsampa string in *outXsampaString.
76439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi       It is the responsibility of the caller to free the allocated string.
76539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi       Increment through the string.  For each base & combination convert it to the XSAMP equivalent.
76639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi       Because of the XSAMPA limitations, not all IPA characters will be covered.       */
76739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    XPnt = (char *) malloc(6);
7684bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    xsize   = (4 * ipaStringSize) + 8;          /* assume more than double size */
769744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi    *outXsampaString = (char *) malloc( xsize );/* allocate return string   */
77039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    *outXsampaString[0] = 0;
771744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi    xsize = 0;                                  /* clear final              */
7724bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi
773744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi    for (ipidx = 0; ipidx < ipaStringSize; ipidx ++) { /* for each IPA code        */
774744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        CnvIPAPnt( ipaString[ipidx], XPnt );           /* get converted character  */
775744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi        strcat((char *)*outXsampaString, XPnt );       /* concatenate XSAMPA       */
7764bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    }
77739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    free(XPnt);
778744a0e6d86f037a9f815adbcbb2dc5f410aa5007Jean-Michel Trivi    xsize = strlen(*outXsampaString);                  /* get the final length     */
77939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    return xsize;
78039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi}
78139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
78239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
78339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi/* Google Engine API function implementations */
784b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
785b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen/** init
78639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  Allocates Pico memory block and initializes the Pico system.
787b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *  synthDoneCBPtr - Pointer to callback function which will receive generated samples
788b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *  return tts_result
789b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen*/
79054823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivitts_result TtsEngine::init( synthDoneCB_t synthDoneCBPtr )
791b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen{
7924bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (synthDoneCBPtr == NULL) {
793b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        LOGE("Callback pointer is NULL");
794b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_FAILURE;
795b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
7964eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
79739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    picoMemArea = malloc( PICO_MEM_SIZE );
7984bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (!picoMemArea) {
799b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        LOGE("Failed to allocate memory for Pico system");
800b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_FAILURE;
801b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
8024eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
80339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    pico_Status ret = pico_initialize( picoMemArea, PICO_MEM_SIZE, &picoSystem );
8044bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (PICO_OK != ret) {
805b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        LOGE("Failed to initialize Pico system");
80639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        free( picoMemArea );
807b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        picoMemArea = NULL;
808b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_FAILURE;
809b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
8104eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
811b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    picoSynthDoneCBPtr = synthDoneCBPtr;
8124bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi
8139b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    picoCurrentLangIndex = -1;
8144bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi
815b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    return TTS_SUCCESS;
816b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen}
817b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
81854823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi
819b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen/** shutdown
82054823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi *  Unloads all Pico resources; terminates Pico system and frees Pico memory block.
821b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *  return tts_result
822b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen*/
82354823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivitts_result TtsEngine::shutdown( void )
824b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen{
825b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    cleanResources();
8264eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
8274bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (picoSystem) {
828b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        pico_terminate(&picoSystem);
829b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        picoSystem = NULL;
830b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
8314bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (picoMemArea) {
832b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        free(picoMemArea);
833b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        picoMemArea = NULL;
834b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
8354eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
836b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    cleanFiles();
837b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    return TTS_SUCCESS;
838b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen}
839b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
84039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
84139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi/** loadLanguage
84239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  Load a new language.
84339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  @lang - string with ISO 3 letter language code.
84439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  @country - string with ISO 3 letter country code .
84539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  @variant - string with language variant for that language and country pair.
84639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  return tts_result
84739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi*/
84839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivitts_result TtsEngine::loadLanguage(const char *lang, const char *country, const char *variant)
84939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi{
85039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    return TTS_FAILURE;
85139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    //return setProperty("language", value, size);
85239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi}
85339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
85439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
85539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi/** setLanguage
85639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  Load a new language (locale).  Use the ISO 639-3 language codes.
85739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  @lang - string with ISO 639-3 language code.
85839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  @country - string with ISO 3 letter country code.
85939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  @variant - string with language variant for that language and country pair.
86039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  return tts_result
86139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi */
86239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivitts_result TtsEngine::setLanguage( const char * lang, const char * country, const char * variant )
86339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi{
86439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    int langIndex;
86539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    int countryIndex;
86639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    int i;
86739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
86839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    if (lang == NULL)
86939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        {
87039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        LOGE("TtsEngine::setLanguage called with NULL language");
87139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        return TTS_FAILURE;
87239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        }
87339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
87439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* We look for a match on the language first
87539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi       then we look for a match on the country.
87639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi       If no match on the language:
87739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi             return an error.
87839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi       If match on the language, but no match on the country:
87939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi             load the language found for the language match.
88039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi       If match on the language, and match on the country:
88139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi             load the language found for the country match.     */
88239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
88339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* Find a match on the language.    */
88439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    langIndex = -1;                                     /* no match */
88539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    for (i = 0; i < picoNumSupportedVocs; i ++)
88639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        {
88739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        if (strcmp(lang, picoSupportedLangIso3[i]) == 0)
88839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            {
88939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            langIndex = i;
89039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            break;
89139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            }
89239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        }
89339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    if (langIndex < 0)
89439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        {
89539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        /* The language isn't supported.    */
89639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        LOGE("TtsEngine::setLanguage called with unsupported language");
89739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        return TTS_FAILURE;
89839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        }
89939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
90039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* Find a match on the country, if there is one.    */
90139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    if (country != NULL)
90239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        {
90339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        countryIndex = -1;
90439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        for (i = langIndex; i < picoNumSupportedVocs; i ++)
90539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            {
90639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            if (   (strcmp(lang,    picoSupportedLangIso3[i])    == 0)
90739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi                && (strcmp(country, picoSupportedCountryIso3[i]) == 0))
90839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi                {
90939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi                countryIndex = i;
91039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi                break;
91139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi                }
91239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            }
91339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
91439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        if (countryIndex < 0)
91539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            {
91639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            /* We didn't find a match on the country, but we had a match on the language.
91739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi               Use that language.                                                       */
91839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            LOGI("TtsEngine::setLanguage found matching language(%s) but not matching country(%s).",
91939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi                    lang, country);
92039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            }
92139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        else
92239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            {
92339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            /* We have a match on both the language and the country.    */
92439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            langIndex = countryIndex;
92539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            }
92639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        }
92739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
92839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    return doLanguageSwitchFromLangIndex( langIndex );      /* switch the language  */
92939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi}
93039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
93139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
932b8f46add889c597c8c305691b1aa656d68bfa5c8Jean-Michel Trivi/** isLanguageAvailable
933b8f46add889c597c8c305691b1aa656d68bfa5c8Jean-Michel Trivi *  Returns the level of support for a language.
934b8f46add889c597c8c305691b1aa656d68bfa5c8Jean-Michel Trivi *  @lang - string with ISO 3 letter language code.
935b8f46add889c597c8c305691b1aa656d68bfa5c8Jean-Michel Trivi *  @country - string with ISO 3 letter country code .
936b8f46add889c597c8c305691b1aa656d68bfa5c8Jean-Michel Trivi *  @variant - string with language variant for that language and country pair.
9379b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi *  return tts_support_result
938b8f46add889c597c8c305691b1aa656d68bfa5c8Jean-Michel Trivi*/
939b8f46add889c597c8c305691b1aa656d68bfa5c8Jean-Michel Trivitts_support_result TtsEngine::isLanguageAvailable(const char *lang, const char *country,
940b8f46add889c597c8c305691b1aa656d68bfa5c8Jean-Michel Trivi            const char *variant) {
9419b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    int langIndex = -1;
9429b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    int countryIndex = -1;
9439b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    //-------------------------
9449b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    // language matching
9459b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    // if no language specified
9469b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    if (lang == NULL)  {
9479b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        LOGE("TtsEngine::isLanguageAvailable called with no language");
9489b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        return TTS_LANG_NOT_SUPPORTED;
9499b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    }
9509b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi
9519b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    // find a match on the language
95239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    for (int i = 0; i < picoNumSupportedVocs; i++)
9539b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    {
9549b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        if (strcmp(lang, picoSupportedLangIso3[i]) == 0) {
9559b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi            langIndex = i;
9569b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi            break;
9579b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        }
9589b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    }
9599b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    if (langIndex < 0) {
9609b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        // language isn't supported
9619b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        LOGV("TtsEngine::isLanguageAvailable called with unsupported language");
9629b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        return TTS_LANG_NOT_SUPPORTED;
9639b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    }
9649b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi
9659b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    //-------------------------
9669b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    // country matching
9679b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    // if no country specified
9689b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    if ((country == NULL) || (strlen(country) == 0)) {
9699b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        // check installation of matched language
9709b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        return (hasResourcesForLanguage(langIndex) ? TTS_LANG_AVAILABLE : TTS_LANG_MISSING_DATA);
9719b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    }
9724bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi
9739b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    // find a match on the country
97439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    for (int i = langIndex; i < picoNumSupportedVocs; i++) {
9759b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        if ((strcmp(lang, picoSupportedLangIso3[i]) == 0)
9769b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi                && (strcmp(country, picoSupportedCountryIso3[i]) == 0)) {
9779b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi            countryIndex = i;
9789b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi            break;
9799b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        }
9809b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    }
9819b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    if (countryIndex < 0)  {
9829b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        // we didn't find a match on the country, but we had a match on the language
9839b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        // check installation of matched language
9849b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        return (hasResourcesForLanguage(langIndex) ? TTS_LANG_AVAILABLE : TTS_LANG_MISSING_DATA);
9859b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    } else {
9869b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        // we have a match on the language and the country
9879b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        langIndex = countryIndex;
9889b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        // check installation of matched language + country
9899b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        return (hasResourcesForLanguage(langIndex) ? TTS_LANG_COUNTRY_AVAILABLE : TTS_LANG_MISSING_DATA);
9909b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    }
9914bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi
9929b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    // no variants supported in this library, TTS_LANG_COUNTRY_VAR_AVAILABLE cannot be returned.
993b8f46add889c597c8c305691b1aa656d68bfa5c8Jean-Michel Trivi}
994b8f46add889c597c8c305691b1aa656d68bfa5c8Jean-Michel Trivi
9954eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
996b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen/** getLanguage
99754823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi *  Get the currently loaded language - if any.
9989b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi *  @lang - string with current ISO 3 letter language code, empty string if no loaded language.
9999b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi *  @country - string with current ISO 3 letter country code, empty string if no loaded language.
10009b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi *  @variant - string with current language variant, empty string if no loaded language.
1001b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *  return tts_result
1002b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen*/
10039b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivitts_result TtsEngine::getLanguage(char *language, char *country, char *variant)
10049b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi{
10059b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    if (picoCurrentLangIndex == -1) {
10069b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        strcpy(language, "\0");
10079b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        strcpy(country, "\0");
10089b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        strcpy(variant, "\0");
10099b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    } else {
10105f6105e73f0c74acd10126175d4952ade10a0b05Jean-Michel Trivi        strcpy(language, picoSupportedLangIso3[picoCurrentLangIndex]);
10115f6105e73f0c74acd10126175d4952ade10a0b05Jean-Michel Trivi        strcpy(country, picoSupportedCountryIso3[picoCurrentLangIndex]);
10129b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        // no variant in this implementation
10139b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi        strcpy(variant, "\0");
10149b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    }
10159b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    return TTS_SUCCESS;
10169b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi}
10179b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi
10189b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi
10199b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi/** setAudioFormat
10209b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi * sets the audio format to use for synthesis, returns what is actually used.
10219b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi * @encoding - reference to encoding format
10229b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi * @rate - reference to sample rate
10239b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi * @channels - reference to number of channels
10249b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi * return tts_result
10259b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi * */
10269b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivitts_result TtsEngine::setAudioFormat(AudioSystem::audio_format& encoding, uint32_t& rate,
10279b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi            int& channels)
1028b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen{
10299b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    // ignore the input parameters, the enforced audio parameters are fixed here
10309b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    encoding = AudioSystem::PCM_16_BIT;
10319b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    rate = 16000;
10329b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    channels = 1;
10339b08cc440f25c4722ca112642be87053fc47f918Jean-Michel Trivi    return TTS_SUCCESS;
1034b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen}
1035b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
103654823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi
1037b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen/** setProperty
103854823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi *  Set property. The supported properties are:  language, rate, pitch and volume.
1039b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *  @property - name of property to set
1040b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *  @value - value to set
1041b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *  @size - size of value
1042b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *  return tts_result
1043b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen*/
104454823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivitts_result TtsEngine::setProperty( const char * property, const char * value, const size_t size )
1045b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen{
104639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    int rate;
104739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    int pitch;
104839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    int volume;
104939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
105039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* Set a specific property for the engine.
105139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi       Supported properties include: language (locale), rate, pitch, volume.    */
105254823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi    /* Sanity check */
10534bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (property == NULL) {
1054b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        LOGE("setProperty called with property NULL");
1055b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_PROPERTY_UNSUPPORTED;
10564bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    }
10574eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
10584bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (value == NULL) {
1059b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        LOGE("setProperty called with value NULL");
1060b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_VALUE_INVALID;
10614bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    }
10624eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
10634bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (strncmp(property, "language", 8) == 0) {
106439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        /* Verify it's in correct format.   */
10654bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        if (strlen(value) != 2 && strlen(value) != 6) {
1066b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            LOGE("change language called with incorrect format");
1067b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            return TTS_VALUE_INVALID;
10684bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        }
10694eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
107039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        /* Try to switch to specified language. */
10714bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        if (doLanguageSwitch(value) == TTS_FAILURE) {
1072b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            LOGE("failed to load language");
1073b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            return TTS_FAILURE;
10744bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        } else {
1075b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            return TTS_SUCCESS;
1076b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        }
10774bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    } else if (strncmp(property, "rate", 4) == 0) {
107839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        rate = atoi(value);
10794bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        if (rate < PICO_MIN_RATE) {
108039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            rate = PICO_MIN_RATE;
10814bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        }
10824bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        if (rate > PICO_MAX_RATE) {
108339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            rate = PICO_MAX_RATE;
10844bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        }
1085b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        picoProp_currRate = rate;
1086b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_SUCCESS;
10874bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    } else if (strncmp(property, "pitch", 5) == 0) {
108839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        pitch = atoi(value);
10894bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        if (pitch < PICO_MIN_PITCH) {
109039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            pitch = PICO_MIN_PITCH;
10914bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        }
10924bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        if (pitch > PICO_MAX_PITCH) {
109339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            pitch = PICO_MAX_PITCH;
10944bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        }
1095b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        picoProp_currPitch = pitch;
1096b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_SUCCESS;
10974bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    } else if (strncmp(property, "volume", 6) == 0) {
109839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        volume = atoi(value);
10994bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        if (volume < PICO_MIN_VOLUME) {
110039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            volume = PICO_MIN_VOLUME;
11014bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        }
11024bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        if (volume > PICO_MAX_VOLUME) {
110339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            volume = PICO_MAX_VOLUME;
11044bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        }
1105b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        picoProp_currVolume = volume;
1106b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_SUCCESS;
11074bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    }
1108b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
1109b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    return TTS_PROPERTY_UNSUPPORTED;
1110b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen}
1111b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
111254823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi
1113b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen/** getProperty
111454823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi *  Get the property.  Supported properties are:  language, rate, pitch and volume.
1115b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *  @property - name of property to get
111639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  @value    - buffer which will receive value of property
111739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  @iosize   - size of value - if size is too small on return this will contain actual size needed
1118b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *  return tts_result
1119b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen*/
112039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivitts_result TtsEngine::getProperty( const char * property, char * value, size_t * iosize )
1121b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen{
112239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* Get the property for the engine.
112339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi       This property was previously set by setProperty or by default.       */
112454823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi    /* sanity check */
11254bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (property == NULL) {
1126b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        LOGE("getProperty called with property NULL");
1127b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_PROPERTY_UNSUPPORTED;
11284bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    }
11294eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
11304bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (value == NULL) {
1131b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        LOGE("getProperty called with value NULL");
1132b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_VALUE_INVALID;
11334bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    }
11344eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
11354bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (strncmp(property, "language", 8) == 0) {
11364bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        if (picoProp_currLang == NULL) {
1137b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            strcpy(value, "");
11384bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        } else {
11394bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            if (*iosize < strlen(picoProp_currLang)+1)  {
1140b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen                *iosize = strlen(picoProp_currLang) + 1;
1141b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen                return TTS_PROPERTY_SIZE_TOO_SMALL;
114239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            }
11434bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            strcpy(value, picoProp_currLang);
114439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        }
11454bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        return TTS_SUCCESS;
11464bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    } else if (strncmp(property, "rate", 4) == 0) {
1147b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        char tmprate[4];
1148b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        sprintf(tmprate, "%d", picoProp_currRate);
11494bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        if (*iosize < strlen(tmprate)+1) {
1150b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            *iosize = strlen(tmprate) + 1;
1151b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            return TTS_PROPERTY_SIZE_TOO_SMALL;
1152b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        }
1153b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        strcpy(value, tmprate);
1154b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_SUCCESS;
11554bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    } else if (strncmp(property, "pitch", 5) == 0) {
1156b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        char tmppitch[4];
1157b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        sprintf(tmppitch, "%d", picoProp_currPitch);
11584bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        if (*iosize < strlen(tmppitch)+1) {
1159b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            *iosize = strlen(tmppitch) + 1;
1160b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            return TTS_PROPERTY_SIZE_TOO_SMALL;
1161b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        }
1162b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        strcpy(value, tmppitch);
1163b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_SUCCESS;
11644bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    } else if (strncmp(property, "volume", 6) == 0) {
1165b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        char tmpvol[4];
1166b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        sprintf(tmpvol, "%d", picoProp_currVolume);
11674bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        if (*iosize < strlen(tmpvol)+1) {
1168b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            *iosize = strlen(tmpvol) + 1;
1169b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            return TTS_PROPERTY_SIZE_TOO_SMALL;
11704bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        }
1171b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        strcpy(value, tmpvol);
1172b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_SUCCESS;
11734bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    }
117439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi
117539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* Unknown property */
117639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    LOGE("Unsupported property");
117739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    return TTS_PROPERTY_UNSUPPORTED;
1178b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen}
1179b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
118054823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi
1181b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen/** synthesizeText
118254823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi *  Synthesizes a text string.
118339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  The text string could be annotated with SSML tags.
118439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  @text     - text to synthesize
118539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi *  @buffer   - buffer which will receive generated samples
1186b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *  @bufferSize - size of buffer
1187b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *  @userdata - pointer to user data which will be passed back to callback function
1188b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *  return tts_result
1189b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen*/
119039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivitts_result TtsEngine::synthesizeText( const char * text, int8_t * buffer, size_t bufferSize, void * userdata )
1191b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen{
119239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    int         err;
119339358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    int         cbret;
119439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    pico_Char * inp = NULL;
119539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    pico_Char * local_text = NULL;
119639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    short       outbuf[MAX_OUTBUF_SIZE/2];
119739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    pico_Int16  bytes_sent, bytes_recv, text_remaining, out_data_type;
1198b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    pico_Status ret;
119939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    SvoxSsmlParser * parser = NULL;
12004eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
120139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    picoSynthAbort = 0;
12024bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (text == NULL) {
1203b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        LOGE("synthesizeText called with NULL string");
1204b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_FAILURE;
12054bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    }
12064eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
12074bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if (buffer == NULL) {
1208b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        LOGE("synthesizeText called with NULL buffer");
1209b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        return TTS_FAILURE;
121039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    }
12114bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi
12124bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    if ( (strncmp(text, "<speak", 6) == 0) || (strncmp(text, "<?xml", 5) == 0) ) {
12134bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        /* SSML input */
12144bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        parser = new SvoxSsmlParser();
12154bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        if (parser && parser->initSuccessful()) {
12164bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            err = parser->parseDocument(text, 1);
12174bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            if (err == XML_STATUS_ERROR) {
12184bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                /* Note: for some reason expat always thinks the input document has an error
12194bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                   at the end, even when the XML document is perfectly formed */
12204bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                LOGI("Warning: SSML document parsed with errors");
12214bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            }
12224bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            char * parsed_text = parser->getParsedDocument();
12234bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            if (parsed_text) {
12244bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                /* Add property tags to the string - if any.    */
12254bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                local_text = (pico_Char *) doAddProperties( parsed_text );
12264bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                if (!local_text) {
12274bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                    LOGE("Failed to allocate memory for text string");
12284bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                    delete parser;
12294bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                    return TTS_FAILURE;
12304bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                }
12314bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                char * lang = parser->getParsedDocumentLanguage();
12325da148ef02f1ed01db73163c2befde420b875a9fJean-Michel Trivi                if (lang != NULL) {
12335da148ef02f1ed01db73163c2befde420b875a9fJean-Michel Trivi                    if (doLanguageSwitch(lang) == TTS_FAILURE) {
12345da148ef02f1ed01db73163c2befde420b875a9fJean-Michel Trivi                        LOGE("Failed to switch to language (%s) specified in SSML document.", lang);
12355da148ef02f1ed01db73163c2befde420b875a9fJean-Michel Trivi                        delete parser;
12365da148ef02f1ed01db73163c2befde420b875a9fJean-Michel Trivi                        return TTS_FAILURE;
12375da148ef02f1ed01db73163c2befde420b875a9fJean-Michel Trivi                    }
12385da148ef02f1ed01db73163c2befde420b875a9fJean-Michel Trivi                } else {
12395da148ef02f1ed01db73163c2befde420b875a9fJean-Michel Trivi                    // lang is NULL, pick a language so the synthesis can be performed
12405da148ef02f1ed01db73163c2befde420b875a9fJean-Michel Trivi                    if (picoCurrentLangIndex == -1) {
12415da148ef02f1ed01db73163c2befde420b875a9fJean-Michel Trivi                        // no current language loaded, pick the first one and load it
12425da148ef02f1ed01db73163c2befde420b875a9fJean-Michel Trivi                        if (doLanguageSwitchFromLangIndex(0) == TTS_FAILURE) {
12435da148ef02f1ed01db73163c2befde420b875a9fJean-Michel Trivi                            LOGE("Failed to switch to default language.");
12445da148ef02f1ed01db73163c2befde420b875a9fJean-Michel Trivi                            delete parser;
12455da148ef02f1ed01db73163c2befde420b875a9fJean-Michel Trivi                            return TTS_FAILURE;
12465da148ef02f1ed01db73163c2befde420b875a9fJean-Michel Trivi                        }
12475da148ef02f1ed01db73163c2befde420b875a9fJean-Michel Trivi                    }
12485da148ef02f1ed01db73163c2befde420b875a9fJean-Michel Trivi                    LOGE("No language in SSML, using current language (%s).", picoProp_currLang);
12494bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                }
12504bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                delete parser;
12514bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            } else {
12524bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                LOGE("Failed to parse SSML document");
12534bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                delete parser;
12544bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                return TTS_FAILURE;
12554bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            }
12564bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        } else {
12574bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            LOGE("Failed to create SSML parser");
12584bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            if (parser) {
12594bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                delete parser;
12604bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            }
12614bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            return TTS_FAILURE;
12624bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        }
12634bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    } else {
12644bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        /* Add property tags to the string - if any.    */
12654bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        local_text = (pico_Char *) doAddProperties( text );
12664bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        if (!local_text) {
1267b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        LOGE("Failed to allocate memory for text string");
12684bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            return TTS_FAILURE;
12694bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        }
1270b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
12714eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
127239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    text_remaining = strlen((const char *) local_text) + 1;
12734eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
127439358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    inp = (pico_Char *) local_text;
12754eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
1276b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    size_t bufused = 0;
12774eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
127854823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi    /* synthesis loop   */
12794bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    while (text_remaining) {
12804bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi
12814bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        if (picoSynthAbort) {
128239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            ret = pico_resetEngine( picoEngine );
1283b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            break;
12844bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        }
12854eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
128654823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi        /* Feed the text into the engine.   */
128739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        ret = pico_putTextUtf8( picoEngine, inp, text_remaining, &bytes_sent );
12884bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        if (ret != PICO_OK) {
1289b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            LOGE("Error synthesizing string '%s': [%d]", text, ret);
12904bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            if (local_text) {
129139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi                free( local_text );
129239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            }
12934bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            return TTS_FAILURE;
12944bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        }
12954eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
1296b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        text_remaining -= bytes_sent;
1297b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        inp += bytes_sent;
12984bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        do {
12994bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            if (picoSynthAbort) {
1300b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen                break;
1301b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            }
130254823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi            /* Retrieve the samples and add them to the buffer. */
13034bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            ret = pico_getData( picoEngine, (void *) outbuf, MAX_OUTBUF_SIZE, &bytes_recv,
13044bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                    &out_data_type );
13054bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            if (bytes_recv) {
13064bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                if ((bufused + bytes_recv) <= bufferSize) {
130739358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi                    memcpy(buffer+bufused, (int8_t *) outbuf, bytes_recv);
1308b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen                    bufused += bytes_recv;
13094bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                } else {
131054823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi                    /* The buffer filled; pass this on to the callback function.    */
13114bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                    cbret = picoSynthDoneCBPtr(userdata, 16000, AudioSystem::PCM_16_BIT, 1, buffer,
13124bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                            bufused, TTS_SYNTH_PENDING);
13134bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                    if (cbret == TTS_CALLBACK_HALT) {
1314b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen                        LOGI("Halt requested by caller. Halting.");
1315b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen                        picoSynthAbort = 1;
1316b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen                        break;
13174bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                    }
1318b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen                    bufused = 0;
131939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi                    memcpy(buffer, (int8_t *) outbuf, bytes_recv);
13204eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi                    bufused += bytes_recv;
1321b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen                }
13224bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            }
1323b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        } while (PICO_STEP_BUSY == ret);
13244eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
132539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        /* This chunk of synthesis is finished; pass the remaining samples.
132654823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi           Use 16 KHz, 16-bit samples.                                              */
13274bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        if (!picoSynthAbort) {
13284bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            picoSynthDoneCBPtr( userdata, 16000, AudioSystem::PCM_16_BIT, 1, buffer, bufused,
13294bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                    TTS_SYNTH_PENDING);
1330b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        }
1331b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        picoSynthAbort = 0;
13324eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
13334bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi        if (ret != PICO_STEP_IDLE) {
1334b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            LOGE("Error occurred during synthesis [%d]", ret);
13354bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            if (local_text) {
13364bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                free(local_text);
13374bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            }
133839358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi            LOGV("Synth loop: sending TTS_SYNTH_DONE after error");
13394bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            picoSynthDoneCBPtr( userdata, 16000, AudioSystem::PCM_16_BIT, 1, buffer, bufused,
13404bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi                    TTS_SYNTH_DONE);
1341b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen            return TTS_FAILURE;
1342b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen        }
1343b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    }
13444bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi
134539358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    /* Synthesis is done; notify the caller */
134639358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    LOGV("Synth loop: sending TTS_SYNTH_DONE after all done, or was asked to stop");
13474bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi    picoSynthDoneCBPtr( userdata, 16000, AudioSystem::PCM_16_BIT, 1, buffer, bufused,
13484bbdb2d4013fa7deb73da0189e9a0e4819e69da9Jean-Michel Trivi            TTS_SYNTH_DONE);
13494eea6515d68b88ebd2215309283d467b3212aa2bJean-Michel Trivi
135039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    if (local_text) {
135139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi        free( local_text );
135239358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    }
1353b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    return TTS_SUCCESS;
1354b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen}
1355b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
135654823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi
1357b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen/** synthesizeIpa
135854823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi *  Synthesizes a phonetic string in IPA format.
135939358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    The Pico engine understands only XSAMPA, so the IPA must be converted.
136039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    The string must also be parceled into words since it can only hanlde a word at a time.
136139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    Each phonemic word must be wrapped with a tag.
1362b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *  @ipa - phonetic string to synthesize
1363b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *  @buffer - buffer which will receive generated samples
1364b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *  @bufferSize - size of buffer
1365b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *  @userdata - pointer to user data which will be passed back to callback function
1366b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *  return tts_result
1367b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen*/
136854823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivitts_result TtsEngine::synthesizeIpa( const char * ipa, int8_t * buffer, size_t bufferSize, void * userdata )
1369b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen{
137039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    // deprecated call
137139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivi    return TTS_FAILURE;
137254823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi
1373b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen}
1374b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
137554823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi
1376b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen/** stop
137754823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi *  Aborts the running synthesis.
1378b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen *  return tts_result
1379b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen*/
138039358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel Trivitts_result TtsEngine::stop( void )
1381b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen{
1382b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    picoSynthAbort = 1;
1383b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    return TTS_SUCCESS;
1384b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen}
1385b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
138654823983fdcb4ee88d9a6c4e680ff2064dd95929Jean-Michel Trivi
1387b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen#ifdef __cplusplus
1388b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chenextern "C" {
1389b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen#endif
1390b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
139139358f0dacad8cece6c2d3ef1055030f57090c79Jean-Michel TriviTtsEngine * getTtsEngine( void )
1392b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen{
1393b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen    return new TtsEngine();
1394b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen}
1395b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
1396b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen#ifdef __cplusplus
1397b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen}
1398b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen#endif
1399b190149a69b110e6719ce0a41877a683f8db7ae7Charles Chen
1400