eSpeak NG is an open source speech synthesizer that supports more than hundred languages and accents.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

espeakengine.cpp 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. /*
  2. * Copyright (C) 2008 Google Inc.
  3. * Copyright (C) 2012 Reece H. Dunn
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /*
  18. * This file contains the TtsEngine implementation for the eSpeak
  19. * Text-to-Speech engine.
  20. *
  21. * Android Version: 2.2 (Froyo)
  22. * API Version: 8
  23. */
  24. #include <stdio.h>
  25. #include <unistd.h>
  26. #include <stdlib.h>
  27. #define LOG_TAG "eSpeak Engine"
  28. #define DEBUG true
  29. #include <speak_lib.h>
  30. #include <TtsEngine.h>
  31. #include <Log.h>
  32. /*
  33. * This is the Manager layer. It sits on top of the native eSpeak engine
  34. * and provides the interface to the defined Google TTS engine API.
  35. * The Google engine API is the boundary to allow a TTS engine to be swapped.
  36. * The Manager layer also provide the SSML tag interpretation.
  37. * The supported SSML tags are mapped to corresponding tags natively supported by eSpeak.
  38. * Native eSpeak functions always begin with espeak_XXX.
  39. *
  40. * Only a subset of SSML 1.0 tags are supported.
  41. * Some SSML tags involve significant complexity.
  42. * If the language is changed through an SSML tag, there is a latency for the load.
  43. */
  44. using namespace android;
  45. const char *ESPEAK_DIRECTORY = "espeak-data";
  46. const char *eSpeakBaseResources[] = {"intonations", "phondata", "phonindex", "phontab",
  47. "en_dict", "voices/en/en-us" };
  48. const int NUM_BASE_RESOURCES = 6;
  49. // Format is {espeak voice, iso3 code, name}
  50. const char *eSpeakSupportedVoices[][3] = {
  51. {"en-us", "eng", "English"},
  52. {"en-us", "eng-USA", "English (US)"},
  53. {"en", "eng-GBR", "English (UK)"},
  54. {"en-sc", "eng-GBR-sc", "English (Scottish)"},
  55. {"en-n", "eng-GBR-n", "English (Northern UK)"},
  56. {"en-rp", "eng-GBR-rp", "English (Received Pronunciation)"},
  57. {"en-wm", "eng-GBR-wm", "English (West Midlands)"},
  58. {"af", "afr", "Afrikaans"},
  59. {"bs", "bos", "Bosnian"},
  60. {"ca", "cat", "Catalan"},
  61. {"cs", "ces", "Czech"},
  62. {"da", "dan", "Danish"},
  63. {"de", "deu", "German"},
  64. {"el", "ell", "Greek"},
  65. {"eo", "epo", "Esperanto"},
  66. {"es", "spa", "Spanish"},
  67. {"es-la", "spa-MEX", "Spanish (Latin America)"},
  68. {"fi", "fin", "Finnish"},
  69. {"fr", "fra", "French"},
  70. {"hr", "hrv", "Croatian"},
  71. {"hu", "hun", "Hungarian"},
  72. {"it", "ita", "Italian"},
  73. {"kn", "kan", "Kannada"},
  74. {"ku", "kur", "Kurdish"},
  75. {"lv", "lav", "Latvian"},
  76. {"nl", "nld", "Dutch"},
  77. {"pl", "pol", "Polish"},
  78. {"pt", "por", "Portuguese (Brazil)"},
  79. {"pt", "por-BRA", "Portuguese (Brazil)"},
  80. {"pt-pt", "por-PRT", "Portuguese"},
  81. {"ro", "ron", "Romanian"},
  82. {"sk", "slk", "Slovak"},
  83. {"sr", "srp", "Serbian"},
  84. {"sv", "swe", "Swedish"},
  85. {"sw", "swa", "Swahili"},
  86. {"ta", "tam", "Tamil"},
  87. {"tr", "tur", "Turkish"},
  88. {"zh", "zho", "Chinese (Mandarin)"},
  89. {"cy", "cym", "Welsh"},
  90. {"hi", "hin", "Hindi"},
  91. {"hy", "hye", "Armenian"},
  92. {"id", "ind", "Indonesian"},
  93. {"is", "isl", "Icelandic"},
  94. {"ka", "kat", "Georgian"},
  95. {"la", "lat", "Latin"},
  96. {"mk", "mkd", "Macedonian"},
  97. {"no", "nor", "Norwegian"},
  98. {"ru", "rus", "Russian"},
  99. {"sq", "sqi", "Albanian"},
  100. {"vi", "vie", "Vietnamese"},
  101. {"zh-yue", "zho-HKG", "Chinese (Cantonese)"},
  102. {"grc", "grc", "Ancient Greek"},
  103. {"jbo", "jbo", "Lojban"},
  104. {"nci", "nci", "Nahuatl (Classical)"},
  105. {"pap", "pap", "Papiamento" }
  106. };
  107. const int NUM_SUPPORTED_VOICES = 55;
  108. // Callback to the TTS API
  109. synthDoneCB_t *ttsSynthDoneCBPointer;
  110. char *eSpeakDataPath = NULL;
  111. char currentLanguage[33];
  112. char currentLang[10];
  113. char currentCountry[10];
  114. char currentVariant[10];
  115. int sampleRate = 0;
  116. bool hasInitialized = false;
  117. /* Functions internal to the eSpeak engine wrapper */
  118. static void setSpeechRate(int speechRate) {
  119. espeak_ERROR err = espeak_SetParameter(espeakRATE, speechRate, 0);
  120. }
  121. /* Functions exposed to the TTS API */
  122. /* Callback from espeak. Should call back to the TTS API */
  123. static int eSpeakCallback(short *wav, int numsamples, espeak_EVENT *events) {
  124. LOGI("Callback with %d samples", numsamples);
  125. int8_t * castedWav = (int8_t *) wav;
  126. size_t bufferSize = 0;
  127. if (numsamples < 1) {
  128. int8_t silenceData[] = { 0, 0 };
  129. size_t silenceBufferSize = sizeof(silenceData)/sizeof(silenceData[0]);
  130. int8_t *silence = silenceData; // Passing in an empty buffer can cause a crash.
  131. ttsSynthDoneCBPointer(events->user_data, 22050, TTS_AUDIO_FORMAT_PCM_16_BIT, 1, silence,
  132. silenceBufferSize, TTS_SYNTH_DONE);
  133. return 1;
  134. }
  135. bufferSize = numsamples * sizeof(short);
  136. ttsSynthDoneCBPointer(events->user_data, 22050, TTS_AUDIO_FORMAT_PCM_16_BIT, 1, castedWav,
  137. bufferSize, TTS_SYNTH_PENDING);
  138. return 0; // continue synthesis (1 is to abort)
  139. }
  140. static bool fileExists(char *fileName) {
  141. if (DEBUG) LOGV("%s", __FUNCTION__);
  142. FILE *file = fopen(fileName, "r");
  143. if (file == NULL) {
  144. return false;
  145. } else {
  146. fclose(file);
  147. return true;
  148. }
  149. }
  150. static bool hasBaseResources() {
  151. if (DEBUG) LOGV("%s", __FUNCTION__);
  152. char filename[255];
  153. for (int i = 0; i < NUM_BASE_RESOURCES; i++) {
  154. sprintf(filename, "%s/%s/%s", eSpeakDataPath, ESPEAK_DIRECTORY, eSpeakBaseResources[i]);
  155. if (!fileExists(filename)) {
  156. LOGE("Missing resource: %s", filename);
  157. return false;
  158. }
  159. }
  160. return true;
  161. }
  162. /* Google Engine API function implementations */
  163. tts_result attemptInit() {
  164. if (DEBUG) LOGV("%s", __FUNCTION__);
  165. if (hasInitialized) {
  166. return TTS_SUCCESS;
  167. }
  168. if (!hasBaseResources()) {
  169. return TTS_FAILURE;
  170. }
  171. // TODO Make sure that the speech data is loaded in
  172. // the directory /sdcard/espeak-data before calling this.
  173. sampleRate = espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS, 4096, eSpeakDataPath, 0);
  174. if (sampleRate <= 0) {
  175. LOGE("eSpeak initialization failed!");
  176. return TTS_FAILURE;
  177. }
  178. espeak_SetSynthCallback(eSpeakCallback);
  179. espeak_VOICE voice;
  180. memset(&voice, 0, sizeof(espeak_VOICE)); // Zero out the voice first
  181. const char *langNativeString = "en-us"; //Default to US English
  182. voice.languages = langNativeString;
  183. voice.variant = 0;
  184. espeak_SetVoiceByProperties(&voice);
  185. strcpy(currentLanguage, "en-us");
  186. hasInitialized = true;
  187. return TTS_SUCCESS;
  188. }
  189. /** init
  190. * Allocates eSpeak memory block and initializes the eSpeak system.
  191. * synthDoneCBPtr - Pointer to callback function which will receive generated samples
  192. * config - the engine configuration parameters, not used here
  193. * return tts_result
  194. */
  195. tts_result TtsEngine::init(synthDoneCB_t synthDoneCBPtr, const char *engineConfig) {
  196. if (DEBUG) LOGV("%s", __FUNCTION__);
  197. ttsSynthDoneCBPointer = synthDoneCBPtr;
  198. hasInitialized = false;
  199. if ((engineConfig != NULL) && (strlen(engineConfig) > 0)) {
  200. eSpeakDataPath = (char *) malloc(strlen(engineConfig));
  201. strcpy(eSpeakDataPath, engineConfig);
  202. } else {
  203. eSpeakDataPath = NULL;
  204. LOGE("Data path not specified!");
  205. return TTS_FAILURE;
  206. }
  207. return attemptInit();
  208. }
  209. /** shutdown
  210. * Unloads all eSpeak resources; terminates eSpeak system and frees eSpeak memory block.
  211. * return tts_result
  212. */
  213. tts_result TtsEngine::shutdown(void) {
  214. if (DEBUG) LOGV("%s", __FUNCTION__);
  215. if (eSpeakDataPath != NULL) {
  216. free(eSpeakDataPath);
  217. }
  218. espeak_Terminate();
  219. return TTS_SUCCESS;
  220. }
  221. tts_result TtsEngine::loadLanguage(const char *lang, const char *country, const char *variant) {
  222. if (DEBUG) LOGV("loadLanguage(\"%s\", \"%s\", \"%s\")", lang, country, variant);
  223. return TTS_FAILURE;
  224. }
  225. tts_support_result isLanguageSupported(const char *lang, const char *country, const char *variant,
  226. int *pindex) {
  227. if (DEBUG) LOGV("isLanguageSupported(\"%s\", \"%s\", \"%s\")", lang, country, variant);
  228. if ((lang == NULL) || (strlen(lang) == 0)) {
  229. LOGE("TtsEngine::isLanguageAvailable called with no language");
  230. return TTS_LANG_NOT_SUPPORTED;
  231. }
  232. if (pindex != NULL) {
  233. *pindex = -1;
  234. }
  235. int langIndex = -1;
  236. int countryIndex = -1;
  237. int variantIndex = -1;
  238. if (strlen(lang) == 3) {
  239. for (int i = 0; i < NUM_SUPPORTED_VOICES; i++) {
  240. if (strncmp(lang, eSpeakSupportedVoices[i][1], 3) == 0) {
  241. LOGI("Found ISO3 language at index %d", i);
  242. langIndex = i;
  243. break;
  244. }
  245. }
  246. } else if (strlen(lang) == 2) {
  247. for (int i = 0; i < NUM_SUPPORTED_VOICES; i++) {
  248. if (strncmp(lang, eSpeakSupportedVoices[i][0], 2) == 0) {
  249. LOGI("Found ISO2 language at index %d", i);
  250. langIndex = i;
  251. break;
  252. }
  253. }
  254. }
  255. if (langIndex < 0) {
  256. LOGV("TtsEngine::isLanguageAvailable called with unsupported language");
  257. return TTS_LANG_NOT_SUPPORTED;
  258. }
  259. if ((country == NULL) || (strlen(country) == 0)) {
  260. // TODO: Check whether resources are available for this language.
  261. if (pindex != NULL) {
  262. *pindex = langIndex;
  263. }
  264. LOGI("No country specified, language is available");
  265. return TTS_LANG_AVAILABLE;
  266. }
  267. char lang_country[10];
  268. sprintf(lang_country, "%s-%s", lang, country);
  269. // Find country
  270. if (strlen(country) == 3) {
  271. for (int i = langIndex; i < NUM_SUPPORTED_VOICES; i++) {
  272. if ((strcmp(lang_country, eSpeakSupportedVoices[i][1]) == 0)) {
  273. LOGI("Found ISO3 country at index %d", i);
  274. countryIndex = i;
  275. break;
  276. }
  277. }
  278. } else if (strlen(country) == 2) {
  279. for (int i = langIndex; i < NUM_SUPPORTED_VOICES; i++) {
  280. if ((strcmp(lang_country, eSpeakSupportedVoices[i][0]) == 0)) {
  281. LOGI("Found ISO2 country at index %d", i);
  282. countryIndex = i;
  283. break;
  284. }
  285. }
  286. }
  287. if (countryIndex < 0) {
  288. if (pindex != NULL) {
  289. *pindex = langIndex;
  290. }
  291. LOGI("No country found, language is available");
  292. return TTS_LANG_AVAILABLE;
  293. }
  294. if ((variant == NULL) || (strlen(variant) == 0)) {
  295. if (pindex != NULL) {
  296. *pindex = countryIndex;
  297. }
  298. LOGI("No variant specified, language and country are available");
  299. return TTS_LANG_COUNTRY_AVAILABLE;
  300. }
  301. char lang_country_variant[15];
  302. sprintf(lang_country_variant, "%s-%s-%s", lang, country, variant);
  303. // Find variant
  304. for (int i = countryIndex; i < NUM_SUPPORTED_VOICES; i++) {
  305. if ((strcmp(lang_country_variant, eSpeakSupportedVoices[i][1]) == 0)) {
  306. LOGI("Found variant at index %d", i);
  307. variantIndex = i;
  308. break;
  309. }
  310. }
  311. if (variantIndex < 0) {
  312. if (pindex != NULL) {
  313. *pindex = countryIndex;
  314. }
  315. LOGI("No variant found, language and country are available");
  316. return TTS_LANG_COUNTRY_AVAILABLE;
  317. }
  318. if (pindex != NULL) {
  319. *pindex = variantIndex;
  320. }
  321. LOGI("Language, country, and variant are available");
  322. return TTS_LANG_COUNTRY_VAR_AVAILABLE;
  323. }
  324. tts_result TtsEngine::setLanguage(const char *lang, const char *country, const char *variant) {
  325. if (DEBUG) LOGV("setLanguage(\"%s\", \"%s\", \"%s\")", lang, country, variant);
  326. // Make sure the engine is initialized!
  327. attemptInit();
  328. int index = -1;
  329. isLanguageSupported(lang, country, variant, &index);
  330. if (index < 0) {
  331. LOGE("setLanguage called with unsupported language");
  332. return TTS_FAILURE;
  333. }
  334. strcpy(currentLanguage, lang);
  335. strcpy(currentLang, lang);
  336. strcpy(currentCountry, country);
  337. strcpy(currentVariant, variant);
  338. char espeakLangStr[7];
  339. strcpy(espeakLangStr, eSpeakSupportedVoices[index][0]);
  340. espeak_VOICE voice;
  341. memset(&voice, 0, sizeof(espeak_VOICE)); // Zero out the voice first
  342. voice.variant = 0;
  343. voice.languages = espeakLangStr;
  344. espeak_ERROR err = espeak_SetVoiceByProperties(&voice);
  345. if (err != EE_OK) {
  346. LOGE("Error code %d when setting voice properties!", err);
  347. return TTS_FAILURE;
  348. }
  349. return TTS_SUCCESS;
  350. }
  351. tts_support_result TtsEngine::isLanguageAvailable(const char *lang, const char *country,
  352. const char *variant) {
  353. if (DEBUG) LOGV("%s", __FUNCTION__);
  354. return isLanguageSupported(lang, country, variant, NULL);
  355. }
  356. tts_result TtsEngine::getLanguage(char *language, char *country, char *variant) {
  357. if (DEBUG) LOGV("%s", __FUNCTION__);
  358. strcpy(language, currentLang);
  359. strcpy(country, currentCountry);
  360. strcpy(variant, currentVariant);
  361. return TTS_SUCCESS;
  362. }
  363. /** setAudioFormat
  364. * sets the audio format to use for synthesis, returns what is actually used.
  365. * @encoding - reference to encoding format
  366. * @rate - reference to sample rate
  367. * @channels - reference to number of channels
  368. * return tts_result
  369. * */
  370. tts_result TtsEngine::setAudioFormat(tts_audio_format& encoding, uint32_t& rate, int& channels) {
  371. LOGE("setAudioFormat(%d, %d, %d) is unsupported", encoding, rate, channels);
  372. encoding = TTS_AUDIO_FORMAT_PCM_16_BIT;
  373. rate = sampleRate;
  374. channels = 1;
  375. return TTS_SUCCESS;
  376. }
  377. // Sets the property with the specified value
  378. tts_result TtsEngine::setProperty(const char *property, const char *value, const size_t size) {
  379. if (DEBUG) LOGV("setProperty(\"%s\", \"%s\", %d)", property, value, size);
  380. /* Set a specific property for the engine.
  381. Supported properties include: language (locale), rate, pitch, volume. */
  382. /* Sanity check */
  383. if (property == NULL) {
  384. LOGE("setProperty called with property NULL");
  385. return TTS_PROPERTY_UNSUPPORTED;
  386. }
  387. if (value == NULL) {
  388. LOGE("setProperty called with value NULL");
  389. return TTS_VALUE_INVALID;
  390. }
  391. espeak_ERROR result;
  392. if (strncmp(property, "language", 8) == 0) {
  393. // TODO: Set this property
  394. result = EE_OK;
  395. } else if (strncmp(property, "rate", 4) == 0) {
  396. int rate = atoi(value) * espeak_GetParameter(espeakRATE, 0) / 100;
  397. if (DEBUG) LOGV("setProperty rate : rate=%s, wpm=%d", value, rate);
  398. result = espeak_SetParameter(espeakRATE, rate, 0);
  399. } else if (strncmp(property, "pitch", 5) == 0) {
  400. int pitch = atoi(value);
  401. // The values of pitch from android range from 50 - 200, with 100 being normal.
  402. // The values espeak supports are from 0 - 100, with 50 being normal.
  403. // Therefore, halve the value to get the value that espeak supports:
  404. pitch = pitch / 2;
  405. if (DEBUG) LOGV("setProperty pitch : pitch=%d", pitch);
  406. result = espeak_SetParameter(espeakPITCH, pitch, 0);
  407. } else if (strncmp(property, "volume", 6) == 0) {
  408. int volume = atoi(value);
  409. result = espeak_SetParameter(espeakVOLUME, volume, 0);
  410. } else {
  411. return TTS_PROPERTY_UNSUPPORTED;
  412. }
  413. if (result == EE_OK) {
  414. return TTS_SUCCESS;
  415. } else {
  416. return TTS_FAILURE;
  417. }
  418. }
  419. // Sets the property with the specified value
  420. tts_result TtsEngine::getProperty(const char *property, char *value, size_t *iosize) {
  421. if (DEBUG) LOGV("getProperty(\"%s\", ...)", property);
  422. /* Get the property for the engine.
  423. This property was previously set by setProperty or by default. */
  424. /* sanity check */
  425. if (property == NULL) {
  426. LOGE("getProperty called with property NULL");
  427. return TTS_PROPERTY_UNSUPPORTED;
  428. }
  429. if (value == NULL) {
  430. LOGE("getProperty called with value NULL");
  431. return TTS_VALUE_INVALID;
  432. }
  433. if (strncmp(property, "language", 8) == 0) {
  434. if (currentLanguage == NULL) {
  435. strcpy(value, "");
  436. } else {
  437. if (*iosize < strlen(currentLanguage)+1) {
  438. *iosize = strlen(currentLanguage) + 1;
  439. return TTS_PROPERTY_SIZE_TOO_SMALL;
  440. }
  441. strcpy(value, currentLanguage);
  442. }
  443. return TTS_SUCCESS;
  444. } else if (strncmp(property, "rate", 4) == 0) {
  445. int rate = espeak_GetParameter(espeakRATE, 1) * 100 / espeak_GetParameter(espeakRATE, 0);
  446. char tmprate[4];
  447. sprintf(tmprate, "%d", rate);
  448. if (*iosize < strlen(tmprate)+1) {
  449. *iosize = strlen(tmprate) + 1;
  450. return TTS_PROPERTY_SIZE_TOO_SMALL;
  451. }
  452. strcpy(value, tmprate);
  453. return TTS_SUCCESS;
  454. } else if (strncmp(property, "pitch", 5) == 0) {
  455. char tmppitch[4];
  456. sprintf(tmppitch, "%d", (espeak_GetParameter(espeakPITCH, 1) * 2));
  457. if (*iosize < strlen(tmppitch)+1) {
  458. *iosize = strlen(tmppitch) + 1;
  459. return TTS_PROPERTY_SIZE_TOO_SMALL;
  460. }
  461. strcpy(value, tmppitch);
  462. return TTS_SUCCESS;
  463. } else if (strncmp(property, "volume", 6) == 0) {
  464. char tmpvolume[4];
  465. sprintf(tmpvolume, "%d", espeak_GetParameter(espeakVOLUME, 1));
  466. if (*iosize < strlen(tmpvolume)+1) {
  467. *iosize = strlen(tmpvolume) + 1;
  468. return TTS_PROPERTY_SIZE_TOO_SMALL;
  469. }
  470. strcpy(value, tmpvolume);
  471. return TTS_SUCCESS;
  472. }
  473. LOGE("Unsupported property");
  474. return TTS_PROPERTY_UNSUPPORTED;
  475. }
  476. /** synthesizeText
  477. * Synthesizes a text string.
  478. * The text string could be annotated with SSML tags.
  479. * @text - text to synthesize
  480. * @buffer - buffer which will receive generated samples
  481. * @bufferSize - size of buffer
  482. * @userdata - pointer to user data which will be passed back to callback function
  483. * return tts_result
  484. */
  485. tts_result TtsEngine::synthesizeText(const char *text, int8_t *buffer, size_t bufferSize,
  486. void *userdata) {
  487. if (DEBUG) LOGV("%s", __FUNCTION__);
  488. espeak_SetSynthCallback(eSpeakCallback);
  489. unsigned int unique_identifier;
  490. espeak_Synth(text, strlen(text), 0, // position
  491. POS_CHARACTER, 0, // end position (0 means no end position)
  492. espeakCHARS_UTF8, // text is UTF-8 encoded
  493. &unique_identifier, userdata);
  494. espeak_Synchronize();
  495. LOGI("Synthesis done");
  496. return TTS_SUCCESS;
  497. }
  498. /** stop
  499. * Aborts the running synthesis.
  500. * return tts_result
  501. */
  502. tts_result TtsEngine::stop() {
  503. if (DEBUG) LOGV("%s", __FUNCTION__);
  504. espeak_Cancel();
  505. return TTS_SUCCESS;
  506. }
  507. #ifdef __cplusplus
  508. extern "C" {
  509. #endif
  510. TtsEngine* getTtsEngine() {
  511. if (DEBUG) LOGV("%s", __FUNCTION__);
  512. return new TtsEngine();
  513. }
  514. #ifdef __cplusplus
  515. }
  516. #endif