cmu_indic_lex.c 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914
  1. /*************************************************************************/
  2. /* */
  3. /* Language Technologies Institute */
  4. /* Carnegie Mellon University */
  5. /* Copyright (c) 2013 */
  6. /* All Rights Reserved. */
  7. /* */
  8. /* Permission is hereby granted, free of charge, to use and distribute */
  9. /* this software and its documentation without restriction, including */
  10. /* without limitation the rights to use, copy, modify, merge, publish, */
  11. /* distribute, sublicense, and/or sell copies of this work, and to */
  12. /* permit persons to whom this work is furnished to do so, subject to */
  13. /* the following conditions: */
  14. /* 1. The code must retain the above copyright notice, this list of */
  15. /* conditions and the following disclaimer. */
  16. /* 2. Any modifications must be clearly marked as such. */
  17. /* 3. Original authors' names are not deleted. */
  18. /* 4. The authors' names are not used to endorse or promote products */
  19. /* derived from this software without specific prior written */
  20. /* permission. */
  21. /* */
  22. /* CARNEGIE MELLON UNIVERSITY AND THE CONTRIBUTORS TO THIS WORK */
  23. /* DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING */
  24. /* ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT */
  25. /* SHALL CARNEGIE MELLON UNIVERSITY NOR THE CONTRIBUTORS BE LIABLE */
  26. /* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES */
  27. /* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN */
  28. /* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, */
  29. /* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF */
  30. /* THIS SOFTWARE. */
  31. /* */
  32. /*************************************************************************/
  33. /* */
  34. /* indic Lexical function */
  35. /* */
  36. /*************************************************************************/
  37. #include "flite.h"
  38. #include "cst_val.h"
  39. #include "cst_voice.h"
  40. #include "cst_lexicon.h"
  41. #include "cst_ffeatures.h"
  42. #include "cmu_indic_lex.h"
  43. #include "../cmu_indic_lang/cmu_indic_lang.h"
  44. extern cst_lexicon cmu_lex;
  45. static const struct cmu_indic_char cmu_indic_offset_char[128] = {
  46. /*000*/ {IND_IGNORE, ""},
  47. /*001*/ {IND_ANUSWAAR, "nX"},
  48. /*002*/ {IND_ANUSWAAR, "nX"},
  49. /*003*/ {IND_VISARGA, "h"},
  50. /*004*/ {IND_INDEPENDENT_VOWEL, "A"},
  51. /*005*/ {IND_INDEPENDENT_VOWEL, "A"},
  52. /*006*/ {IND_INDEPENDENT_VOWEL, "A:"},
  53. /*007*/ {IND_INDEPENDENT_VOWEL, "i"},
  54. /*008*/ {IND_INDEPENDENT_VOWEL, "i:"},
  55. /*009*/ {IND_INDEPENDENT_VOWEL, "u"},
  56. /*00A*/ {IND_INDEPENDENT_VOWEL, "u:"},
  57. /*00B*/ {IND_INDEPENDENT_VOWEL, "9r="},
  58. /*00C*/ {IND_INDEPENDENT_VOWEL, "l="},
  59. /*00D*/ {IND_INDEPENDENT_VOWEL, "ay"},
  60. /*00E*/ {IND_INDEPENDENT_VOWEL, "e"},
  61. /*00F*/ {IND_INDEPENDENT_VOWEL, "e"},
  62. /*010*/ {IND_INDEPENDENT_VOWEL, "aI"},
  63. /*011*/ {IND_INDEPENDENT_VOWEL, "ow"},
  64. /*012*/ {IND_INDEPENDENT_VOWEL, "ow"},
  65. /*013*/ {IND_INDEPENDENT_VOWEL, "o"},
  66. /*014*/ {IND_INDEPENDENT_VOWEL, "aU"},
  67. /*015*/ {IND_CONSONANT, "k"},
  68. /*016*/ {IND_CONSONANT, "kh"},
  69. /*017*/ {IND_CONSONANT, "g"},
  70. /*018*/ {IND_CONSONANT, "gh"},
  71. /*019*/ {IND_CONSONANT, "N"},
  72. /*01A*/ {IND_CONSONANT, "c"},
  73. /*01B*/ {IND_CONSONANT, "ch"},
  74. /*01C*/ {IND_CONSONANT, "J"},
  75. /*01D*/ {IND_CONSONANT, "Jh"},
  76. /*01E*/ {IND_CONSONANT, "n~"},
  77. /*01F*/ {IND_CONSONANT, "tr"},
  78. /*020*/ {IND_CONSONANT, "tR"},
  79. /*021*/ {IND_CONSONANT, "dr"},
  80. /*022*/ {IND_CONSONANT, "dR"},
  81. /*023*/ {IND_CONSONANT, "nr"},
  82. /*024*/ {IND_CONSONANT, "tB"},
  83. /*025*/ {IND_CONSONANT, "tBh"},
  84. /*026*/ {IND_CONSONANT, "dB"},
  85. /*027*/ {IND_CONSONANT, "dBh"},
  86. /*028*/ {IND_CONSONANT, "nB"},
  87. /*029*/ {IND_CONSONANT, "nB"},
  88. /*02A*/ {IND_CONSONANT, "p"},
  89. /*02B*/ {IND_CONSONANT, "ph"},
  90. /*02C*/ {IND_CONSONANT, "b"},
  91. /*02D*/ {IND_CONSONANT, "bh"},
  92. /*02E*/ {IND_CONSONANT, "m"},
  93. /*02F*/ {IND_CONSONANT, "j"},
  94. /*030*/ {IND_CONSONANT, "9r"},
  95. /*031*/ {IND_CONSONANT, "9r"},
  96. /*032*/ {IND_CONSONANT, "l"},
  97. /*033*/ {IND_CONSONANT, "lr"},
  98. /*034*/ {IND_CONSONANT, "lr"},
  99. /*035*/ {IND_CONSONANT, "v"},
  100. /*036*/ {IND_CONSONANT, "c}"},
  101. /*037*/ {IND_CONSONANT, "sr"},
  102. /*038*/ {IND_CONSONANT, "s"},
  103. /*039*/ {IND_CONSONANT, "hv"},
  104. /*03A*/ {IND_VOWEL, "e"},
  105. /*03B*/ {IND_VOWEL, "e"},
  106. /*03C*/ {IND_NUKTA, ""},
  107. /*03D*/ {IND_AVAGRAHA, ""},
  108. /*03E*/ {IND_VOWEL, "A:"},
  109. /*03F*/ {IND_VOWEL, "i"},
  110. /*040*/ {IND_VOWEL, "i:"},
  111. /*041*/ {IND_VOWEL, "u"},
  112. /*042*/ {IND_VOWEL, "u:"},
  113. /*043*/ {IND_VOWEL, "9r="},
  114. /*044*/ {IND_VOWEL, "9r="},
  115. /*045*/ {IND_VOWEL, "ay"},
  116. /*046*/ {IND_VOWEL, "e"},
  117. /*047*/ {IND_VOWEL, "e"},
  118. /*048*/ {IND_VOWEL, "aI"},
  119. /*049*/ {IND_VOWEL, "ow"},
  120. /*04A*/ {IND_VOWEL, "o"},
  121. /*04B*/ {IND_VOWEL, "o"},
  122. /*04C*/ {IND_VOWEL, "aU"},
  123. /*04D*/ {IND_HALANT, ""},
  124. /*04E*/ {IND_IGNORE, ""},
  125. /*04F*/ {IND_VOWEL, "ow"},
  126. /*050*/ {IND_INDEPENDENT_VOWEL, "A u m"},
  127. /*051*/ {IND_IGNORE, ""},
  128. /*052*/ {IND_IGNORE, ""},
  129. /*053*/ {IND_IGNORE, ""},
  130. /*054*/ {IND_IGNORE, ""},
  131. /*055*/ {IND_VOWEL, "e"},
  132. /*056*/ {IND_VOWEL, "u e"},
  133. /*057*/ {IND_VOWEL, "u: e"},
  134. /*058*/ {IND_CONSONANT, "q"},
  135. /*059*/ {IND_CONSONANT, "x"},
  136. /*05A*/ {IND_CONSONANT, "G"},
  137. /*05B*/ {IND_CONSONANT, "z"},
  138. /*05C*/ {IND_CONSONANT, "rr"},
  139. /*05D*/ {IND_CONSONANT, "rrh"},
  140. /*05E*/ {IND_CONSONANT, "f"},
  141. /*05F*/ {IND_CONSONANT, "j"},
  142. /*060*/ {IND_INDEPENDENT_VOWEL, "9r="},
  143. /*061*/ {IND_INDEPENDENT_VOWEL, "lr="},
  144. /*062*/ {IND_VOWEL, "lr="},
  145. /*063*/ {IND_VOWEL, "lr="},
  146. /*064*/ {IND_PUNC, ""},
  147. /*065*/ {IND_PUNC, ""},
  148. /*066*/ {IND_DIGIT, ""},
  149. /*067*/ {IND_DIGIT, ""},
  150. /*068*/ {IND_DIGIT, ""},
  151. /*069*/ {IND_DIGIT, ""},
  152. /*06A*/ {IND_DIGIT, ""},
  153. /*06B*/ {IND_DIGIT, ""},
  154. /*06C*/ {IND_DIGIT, ""},
  155. /*06D*/ {IND_DIGIT, ""},
  156. /*06E*/ {IND_DIGIT, ""},
  157. /*06F*/ {IND_DIGIT, ""},
  158. /*070*/ {IND_PUNC, ""},
  159. /*071*/ {IND_PUNC, ""},
  160. /*072*/ {IND_INDEPENDENT_VOWEL, "ay"},
  161. /*073*/ {IND_INDEPENDENT_VOWEL, "u e"},
  162. /*074*/ {IND_INDEPENDENT_VOWEL, "u: e"},
  163. /*075*/ {IND_INDEPENDENT_VOWEL, "ow"},
  164. /*076*/ {IND_INDEPENDENT_VOWEL, "u e"},
  165. /*077*/ {IND_INDEPENDENT_VOWEL, "u: e"},
  166. /*078*/ {IND_IGNORE, ""},
  167. /*079*/ {IND_CONSONANT, "z"},
  168. /*07A*/ {IND_CONSONANT, "j"},
  169. /*07B*/ {IND_CONSONANT, "G"},
  170. /*07C*/ {IND_CONSONANT, "z"},
  171. /*07D*/ {IND_CONSONANT, ""},
  172. /*07E*/ {IND_CONSONANT, "dr"},
  173. /*07F*/ {IND_CONSONANT, "b"},
  174. };
  175. static int cmu_indic_is_vowel(const char *p)
  176. {
  177. /* this happens works for the vowels in the indic set */
  178. if (strchr("aeiouAEIOU",p[0]) == NULL)
  179. return FALSE;
  180. else
  181. return TRUE;
  182. }
  183. static int cmu_indic_lex_ord_to_offset(const int indic_ord) {
  184. int output=0;
  185. int i;
  186. int offset;
  187. i = indic_ord;
  188. if ((i >= 0x0900) && (i <= 0x097F))
  189. offset = 0x0900;
  190. if ((i >= 0x0980) && (i <= 0x09FF))
  191. offset = 0x0980;
  192. if ((i >= 0x0A00) && (i <= 0x0A7F))
  193. offset = 0x0A00;
  194. if ((i >= 0x0A80) && (i <= 0x0AFF))
  195. offset = 0x0A80;
  196. if ((i >= 0x0B00) && (i <= 0x0B7F))
  197. offset = 0x0B00;
  198. if ((i >= 0x0B80) && (i <= 0x0BFF))
  199. offset = 0x0B80;
  200. if ((i >= 0x0C00) && (i <= 0x0C7F))
  201. offset = 0x0C00;
  202. if ((i >= 0x0C80) && (i <= 0x0CFF))
  203. offset = 0x0C80;
  204. if ((i >= 0x0D00) && (i <= 0x0D7F))
  205. offset = 0x0D00;
  206. if (!offset) {
  207. cst_errmsg("Indic language can not process character 0x%x\n", i);
  208. } else {
  209. output = i - offset;
  210. }
  211. return output;
  212. }
  213. static int cmu_indic_get_char_type(const cst_val *indic_char) {
  214. int c;
  215. if (!indic_char) return IND_IGNORE;
  216. c = val_int(indic_char);
  217. if ((c < 0x0900) || (c > 0x0D7F))
  218. return IND_IGNORE;
  219. c = cmu_indic_lex_ord_to_offset(c);
  220. return cmu_indic_offset_char[c].type;
  221. }
  222. static const cst_val* cmu_indic_get_char_phoneme(const cst_val *indic_char) {
  223. int c;
  224. if (!indic_char) return string_val("");
  225. c = val_int(indic_char);
  226. c = cmu_indic_lex_ord_to_offset(c);
  227. return string_val(cmu_indic_offset_char[c].phoneme);
  228. }
  229. static cst_val *cmu_indic_lex_remove_ignore_chars(const cst_val *indic_ords) {
  230. cst_val *output = 0;
  231. const cst_val *v;
  232. for(v=indic_ords; v; v=val_cdr(v)) {
  233. if (cmu_indic_get_char_type(val_car(v)) == IND_IGNORE)
  234. continue;
  235. output = cons_val(val_car(v), output);
  236. }
  237. return val_reverse(output);
  238. }
  239. static cst_val *cmu_indic_lex_map_nukta_chars(const cst_val *indic_ords) {
  240. cst_val *output = 0;
  241. const cst_val *v;
  242. int i;
  243. int mapped_val;
  244. for(v=indic_ords; v; v=val_cdr(v)) {
  245. i = val_int(val_car(v));
  246. if (val_cdr(v) && cmu_indic_get_char_type(val_car(val_cdr(v))) == IND_NUKTA) {
  247. switch (i) {
  248. // Devanagari
  249. case 2325: mapped_val=2392; break; // क़
  250. case 2326: mapped_val=2393; break; // ख़
  251. case 2327: mapped_val=2394; break; // ग़
  252. case 2332: mapped_val=2395; break; // ज़
  253. case 2337: mapped_val=2396; break; // ड़
  254. case 2338: mapped_val=2397; break; // ढ़
  255. case 2347: mapped_val=2398; break; // फ़
  256. case 2351: mapped_val=2399; break; // य़
  257. // Bengali
  258. case 2465: mapped_val=2524; break;
  259. case 2566: mapped_val=2525; break;
  260. case 2479: mapped_val=2527; break;
  261. // Tamil
  262. case 2962: mapped_val=2964; break;
  263. default:
  264. mapped_val=i;
  265. };
  266. output = cons_val(int_val(mapped_val), output);
  267. v = val_cdr(v);
  268. } else {
  269. // Copy current item into output
  270. output = cons_val(val_car(v), output);
  271. }
  272. }
  273. return val_reverse(output);
  274. }
  275. cst_val *cmu_indic_lex_ord_to_phones(const cst_val *ords,
  276. int cmu_indic_variant_deletes_word_final_schwa)
  277. {
  278. cst_val *in_ords = 0;
  279. cst_val *out_phone_strings = 0;
  280. cst_val *out_phones = 0;
  281. const cst_val *prev_char = 0;
  282. const cst_val *cur_char = 0;
  283. const cst_val *next_char = 0;
  284. const cst_val *remainder = 0;
  285. int cur_char_type = 0;
  286. int next_char_type = 0;
  287. // Ignore chars (filter)
  288. in_ords = cmu_indic_lex_remove_ignore_chars(ords);
  289. if (!in_ords) {
  290. return out_phones;
  291. }
  292. // Map Nukta Chars (filter)
  293. in_ords = cmu_indic_lex_map_nukta_chars(in_ords);
  294. prev_char = 0;
  295. cur_char = val_car(in_ords);
  296. if (val_cdr(in_ords)) {
  297. next_char = val_car(val_cdr(in_ords));
  298. remainder = val_cdr(val_cdr(in_ords));
  299. }
  300. else {
  301. next_char = 0;
  302. remainder = 0;
  303. }
  304. while (cur_char) {
  305. cur_char_type = cmu_indic_get_char_type(cur_char);
  306. if (next_char)
  307. next_char_type = cmu_indic_get_char_type(next_char);
  308. else
  309. next_char_type = IND_IGNORE;
  310. if (cur_char_type == IND_CONSONANT) {
  311. // Add consonant to the output list
  312. out_phone_strings = cons_val(cmu_indic_get_char_phoneme(cur_char),
  313. out_phone_strings);
  314. // If a consonant is followed by a combination vowel, a
  315. // halant, a punctuation then don't add a schwa after
  316. // it. Otherwise, insert a schwa. For end-of-word, check
  317. // whether we should insert schwa in this language.
  318. if (!next_char) { // We are in last char. Add schwa?
  319. if ((!prev_char) || // Always add schwa for one-char words
  320. (!cmu_indic_variant_deletes_word_final_schwa)) {
  321. out_phone_strings = cons_val(string_val("A"), out_phone_strings);
  322. } else {
  323. // Schwa deletion should probably happen depending
  324. // on whether there is a consonant cluster or not,
  325. // at the end. But Adding that rule here seems to
  326. // not have worked properly. Hence, we always
  327. // delete the final schwa.
  328. }
  329. } else { // Not a final char
  330. if ( (next_char_type != IND_VOWEL) &&
  331. (next_char_type != IND_PUNC) &&
  332. (next_char_type != IND_HALANT) &&
  333. (next_char_type != IND_IGNORE)) {
  334. out_phone_strings = cons_val(string_val("A"), out_phone_strings);
  335. }
  336. }
  337. } else if ( (cur_char_type == IND_VOWEL) ||
  338. (cur_char_type == IND_INDEPENDENT_VOWEL) ||
  339. (cur_char_type == IND_DIGIT)) {
  340. // Add whatever the pronunciation is
  341. out_phone_strings = cons_val(cmu_indic_get_char_phoneme(cur_char),
  342. out_phone_strings);
  343. } else if ( cur_char_type == IND_HALANT) {
  344. // Ignore
  345. } else if (cur_char_type == IND_AVAGRAHA) {
  346. // Lengthen previous vowel
  347. if (cmu_indic_get_char_type(prev_char) == IND_VOWEL) {
  348. out_phone_strings = cons_val(cmu_indic_get_char_phoneme(prev_char),
  349. out_phone_strings);
  350. }
  351. } else if (cur_char_type == IND_ANUSWAAR) {
  352. // The realization of anuswaar is context dependent: We
  353. // only generate a placeholder symbol and let postlexical
  354. // rules take care of this.
  355. out_phone_strings = cons_val(string_val("nX"),
  356. out_phone_strings);
  357. } else if (cur_char_type == IND_VISARGA) {
  358. // Add the glottal fricative
  359. out_phone_strings = cons_val(string_val("h"),
  360. out_phone_strings);
  361. } else {
  362. // IDEALLY we should warn for unhandled characters!
  363. // TODO?
  364. }
  365. prev_char = cur_char;
  366. cur_char = next_char;
  367. if (remainder) {
  368. next_char = val_car(remainder);
  369. remainder = val_cdr(remainder);
  370. } else {
  371. next_char = 0;
  372. }
  373. }
  374. const cst_val *v, *vx;
  375. cst_val *ph;
  376. for(v=out_phone_strings; v; v=val_cdr(v)) {
  377. ph = val_readlist_string(val_string(val_car(v)));
  378. for(vx=ph; vx; vx=val_cdr(vx)) {
  379. out_phones = cons_val(val_car(vx), out_phones);
  380. }
  381. delete_val(ph);
  382. }
  383. return val_reverse(out_phones);
  384. }
  385. cst_val *cmu_indic_lex_nasal_postfixes(const cst_val *in_phones) {
  386. // Given a phone sequence containing a special character nX
  387. // (contextual nasal), replace it with the appropriate nasal phone
  388. // based on its context
  389. if ((!in_phones) || !val_car(in_phones)) return 0;
  390. // Nazalise vowels at ends of words
  391. if ((val_cdr(in_phones)) &&
  392. (cmu_indic_is_vowel(val_string(val_car(in_phones)))) &&
  393. (!strcmp("nX", val_string(val_car(val_cdr(in_phones))))) &&
  394. ((!val_cdr(val_cdr(in_phones))) || (!val_car(val_cdr(val_cdr(in_phones)))))) {
  395. if (!strcmp("A", val_string(val_car(in_phones)))) { // If it's a schwa, it's not nasalized. nX becomes m
  396. return cons_val(val_car(in_phones),
  397. cons_val(string_val("m"),
  398. cmu_indic_lex_nasal_postfixes(val_cdr(val_cdr(in_phones)))));
  399. } else {
  400. return cons_val(string_val(cst_strcat(val_string(val_car(in_phones)),
  401. "nas")),
  402. cmu_indic_lex_nasal_postfixes(val_cdr(val_cdr(in_phones))));
  403. }
  404. } else if ( (val_cdr(in_phones)) &&
  405. (!strcmp("nX", val_string(val_car(in_phones))))) {
  406. // Choose nasal variant based on next consonant
  407. const char *next_c = val_string(phone_feature(&cmu_indic_phoneset,
  408. val_string(val_car(val_cdr(in_phones))),
  409. "cplace"));
  410. char *repl_ph;
  411. if (next_c) {
  412. switch (next_c[0]) {
  413. case 'v': repl_ph = "N"; break;
  414. case 'p': repl_ph = "n~"; break;
  415. case 'a': repl_ph = "nr"; break;
  416. case 'd': repl_ph = "nB"; break;
  417. case 'l': repl_ph = "m"; break;
  418. default: repl_ph = "nB";
  419. };
  420. return cons_val(string_val(repl_ph),
  421. cons_val(val_car(val_cdr(in_phones)),
  422. cmu_indic_lex_nasal_postfixes(val_cdr(val_cdr(in_phones)))));
  423. } else {
  424. return cons_val(val_car(in_phones),
  425. cmu_indic_lex_nasal_postfixes(val_cdr(in_phones)));
  426. }
  427. } else {
  428. return cons_val(val_car(in_phones),
  429. cmu_indic_lex_nasal_postfixes(val_cdr(in_phones)));
  430. }
  431. }
  432. /* For English derived pronunciation (latin scripted tokens) we map them */
  433. /* to (hindi) phones -- this has to modified for other indic languages */
  434. static const char * const eng_to_indic[99][3] =
  435. {
  436. {"aa", "A:", NULL },
  437. {"ae", "A", NULL }, /* changed this to A rather than e */
  438. {"ah", "A", NULL },
  439. {"ao", "o", NULL },
  440. {"aw", "aU", NULL },
  441. {"ax", "A", NULL },
  442. {"axr", "A", NULL },
  443. {"ay", "aI", NULL },
  444. {"b", "b", NULL },
  445. {"ch", "c", NULL },
  446. {"d", "dr", NULL },
  447. {"dh", "dB", NULL },
  448. {"eh", "e", NULL },
  449. {"er", "E", "9r" },
  450. {"ey", "ay", NULL },
  451. {"f", "ph", NULL },
  452. {"g", "g", NULL },
  453. {"hh", "hv", NULL },
  454. {"ih", "i", NULL },
  455. {"iy", "i:", NULL },
  456. {"jh", "J", NULL },
  457. {"k", "k", NULL },
  458. {"l", "l", NULL },
  459. {"m", "m", NULL },
  460. {"n", "nB", NULL },
  461. {"nx", "nB", NULL },
  462. {"ng", "nB", NULL },
  463. {"ow", "o", NULL },
  464. {"oy", "o", "j" },
  465. {"p", "p", NULL },
  466. {"r", "9r", NULL },
  467. {"s", "s", NULL },
  468. {"sh", "c}", NULL },
  469. {"t", "tr", NULL },
  470. {"th", "tBh", NULL },
  471. {"uh", "u", NULL },
  472. {"uw", "u:", NULL },
  473. {"v", "v", NULL },
  474. {"w", "v", NULL },
  475. {"y", "j", NULL },
  476. {"z", "s", NULL },
  477. {"zh", "c}", NULL },
  478. {NULL, NULL, NULL }
  479. };
  480. cst_val *map_english_to_indic_phones(const char *indic_variant,
  481. const cst_val *english_phones)
  482. {
  483. /* Map English (radio) phones to their Indic equivalent */
  484. cst_val *ip = NULL;
  485. const cst_val *v;
  486. char *english_phone;
  487. int i;
  488. for (v=english_phones; v; v=val_cdr(v))
  489. {
  490. english_phone = cst_strdup(val_string(val_car(v)));
  491. if ((english_phone[cst_strlen(english_phone)-1] == '0') ||
  492. (english_phone[cst_strlen(english_phone)-1] == '1'))
  493. /* It has a stress calue on it */
  494. english_phone[cst_strlen(english_phone)-1] = '\0';
  495. for (i=0; eng_to_indic[i][0]; i++)
  496. {
  497. if (cst_streq(english_phone,eng_to_indic[i][0]))
  498. {
  499. ip = cons_val(string_val(eng_to_indic[i][1]),ip);
  500. if (eng_to_indic[i][2])
  501. ip = cons_val(string_val(eng_to_indic[i][2]),ip);
  502. }
  503. /* if there is no mapping, we drop the phone */
  504. }
  505. cst_free(english_phone);
  506. }
  507. ip = val_reverse(ip);
  508. return ip;
  509. }
  510. static cst_val *delete_medial_schwa(const cst_val *rphones)
  511. {
  512. /* This schwa deletion follows the technique by Narsimhan et al (2001). */
  513. /* 1. Process input from right to left */
  514. /* 2. If a schwa is found in a VC_CV context, then delete it. */
  515. /* There are exceptions to this: (i) Phonotactic constraints of */
  516. /* Hindi not violated, and no (ii) morpheme boundary present on the */
  517. /* left. But I don't know how to handle these yet. So this will be */
  518. /* wrong more often than the 11% reported in that paper. -- AUP */
  519. if (rphones == NULL)
  520. return NULL;
  521. else if ((val_length(rphones) > 4) &&
  522. (cst_streq(val_string(val_car(val_cdr(val_cdr(rphones)))),"A")))
  523. { /* we have a schwa comming up */
  524. if ((cmu_indic_is_vowel(val_string(val_car(rphones)))) &&
  525. (!cmu_indic_is_vowel(val_string(val_car(val_cdr(rphones))))) &&
  526. (!cmu_indic_is_vowel(val_string(val_car(val_cdr(val_cdr(val_cdr(rphones))))))) &&
  527. (cmu_indic_is_vowel(val_string(val_car(val_cdr(val_cdr(val_cdr(val_cdr(rphones)))))))))
  528. /* delete the schwa */
  529. return cons_val(val_car(rphones),
  530. cons_val(val_car(val_cdr(rphones)),
  531. delete_medial_schwa(
  532. val_cdr(val_cdr(val_cdr(rphones))))));
  533. else
  534. return cons_val(val_car(rphones),
  535. delete_medial_schwa(val_cdr(rphones)));
  536. }
  537. else
  538. return cons_val(val_car(rphones),
  539. delete_medial_schwa(val_cdr(rphones)));
  540. }
  541. static cst_val *cmu_indic_hindi_schwa_fixes(cst_val *phones)
  542. {
  543. cst_val *dd;
  544. if ((val_length(phones) > 3) &&
  545. (cst_streq("A",val_string(val_car(val_cdr(phones))))) &&
  546. (cst_streq("hv",val_string(val_car(val_cdr(val_cdr(phones)))))) &&
  547. ((cst_streq("A",val_string(val_car(val_cdr(val_cdr(val_cdr(phones))))))) ||
  548. (!cmu_indic_is_vowel(val_string(val_car(val_cdr(val_cdr(val_cdr(phones)))))))))
  549. {
  550. dd = (cst_val *)(void *)val_cdr(phones);
  551. set_cdr(phones,val_cdr(val_cdr(phones)));
  552. set_cdr(dd,NULL);
  553. delete_val(dd);
  554. return phones;
  555. }
  556. else
  557. return phones;
  558. }
  559. cst_val *cmu_indic_lex_lts_function(const struct lexicon_struct *l,
  560. const char *word, const char *pos,
  561. const cst_features *feats)
  562. {
  563. cst_val *utflets = 0;
  564. cst_val *ords = 0;
  565. cst_val *phones = 0;
  566. cst_val *english_phones;
  567. cst_val *base_phones = NULL, *rp;
  568. const char *indic_variant = 0;
  569. int cmu_indic_variant_deletes_word_final_schwa=0;
  570. const cst_val *v;
  571. cst_val *tmpv;
  572. indic_variant = get_param_string(feats, "variant", "none");
  573. if (!strcmp(indic_variant, "hin")) {
  574. cmu_indic_variant_deletes_word_final_schwa = 1;
  575. } else if (!strcmp(indic_variant, "mar")) {
  576. cmu_indic_variant_deletes_word_final_schwa = 1;
  577. } else if (!strcmp(indic_variant, "tel")) {
  578. cmu_indic_variant_deletes_word_final_schwa = 0;
  579. } else if (!strcmp(indic_variant, "tam")) {
  580. cmu_indic_variant_deletes_word_final_schwa = 0;
  581. } else {
  582. cmu_indic_variant_deletes_word_final_schwa = 0;
  583. printf("Unknown indic variant: %s!\n", indic_variant);
  584. }
  585. if (cst_regex_match(cst_rx_not_indic,word))
  586. { /* Its English like so us English Lexicon to find Phones and map
  587. to indic phones */
  588. /* printf("awb_debug cmu_indic_lex: English >%s<\n",word); */
  589. english_phones = lex_lookup(&cmu_lex,word,pos,feats);
  590. base_phones = map_english_to_indic_phones(indic_variant,english_phones);
  591. delete_val(english_phones);
  592. }
  593. else
  594. {
  595. /* string to utf8 chars */
  596. utflets = cst_utf8_explode(word);
  597. /* chars to ord */
  598. for (v=utflets; v; v=val_cdr(v)) {
  599. tmpv = cst_utf8_ord(val_car(v));
  600. ords = cons_val(tmpv, ords);
  601. }
  602. ords = val_reverse(ords);
  603. // Ords to Phone List (Map)
  604. base_phones = val_reverse(cmu_indic_lex_ord_to_phones(ords,
  605. cmu_indic_variant_deletes_word_final_schwa));
  606. delete_val(utflets);
  607. cst_free(ords);
  608. }
  609. // Postfix Indic Nasals
  610. phones = cmu_indic_lex_nasal_postfixes(base_phones);
  611. delete_val(base_phones); base_phones = phones;
  612. if (cst_streq(indic_variant,"hin"))
  613. { /* Do schwa deletion */
  614. rp = val_reverse(base_phones);
  615. phones = val_reverse(delete_medial_schwa(rp));
  616. delete_val(rp); base_phones = phones;
  617. phones = cmu_indic_hindi_schwa_fixes(base_phones);
  618. }
  619. /* phone list to syls (done as post lexical rules) */
  620. #if 0
  621. printf("cmu_indic_lex.c: indic word \"%s\" \n",word);
  622. val_print(stdout, phones);
  623. printf("\n");
  624. #endif
  625. return phones;
  626. }
  627. static int cmu_indic_contains_vowel(const cst_val *r)
  628. {
  629. const cst_val *x;
  630. for (x=r; x; x=val_cdr(x))
  631. {
  632. if (cmu_indic_is_vowel(val_string(val_car(x))))
  633. return TRUE;
  634. }
  635. return FALSE;
  636. }
  637. static int cmu_indic_has_vowel_in_syl(const cst_item *i)
  638. {
  639. const cst_item *n;
  640. for (n=i; n; n=item_prev(n))
  641. if (cmu_indic_is_vowel(ffeature_string(n,"name")))
  642. return TRUE;
  643. return FALSE;
  644. }
  645. int cmu_indic_syl_boundary(const cst_item *i,const cst_val *rest)
  646. {
  647. if (!rest)
  648. return TRUE;
  649. else if (!cmu_indic_contains_vowel(rest))
  650. return FALSE;
  651. else if (!cmu_indic_has_vowel_in_syl(i))
  652. return FALSE;
  653. else if (rest && val_cdr(rest) &&
  654. cst_streq("n",val_string(val_car(rest))) &&
  655. !cmu_indic_is_vowel(val_string(val_car(rest))))
  656. return FALSE;
  657. else if (rest && val_cdr(rest) &&
  658. cmu_indic_is_vowel(ffeature_string(i,"name")) &&
  659. !cmu_indic_is_vowel(val_string(val_car(rest))) &&
  660. !cmu_indic_is_vowel(val_string(val_car(val_cdr(rest)))))
  661. return FALSE;
  662. else if (rest && val_cdr(rest) && val_cdr(val_cdr(rest)) &&
  663. !cmu_indic_is_vowel(val_string(val_car(rest))) &&
  664. !cmu_indic_is_vowel(val_string(val_car(val_cdr(rest)))) &&
  665. !cmu_indic_is_vowel(val_string(val_car(val_cdr(val_cdr(rest))))))
  666. return FALSE;
  667. else if (rest && val_cdr(rest) &&
  668. (cst_streq(val_string(val_car(rest)),
  669. val_string(val_car(val_cdr(rest))))))
  670. return FALSE;
  671. else
  672. return TRUE;
  673. }
  674. #if 0
  675. int cmu_indic_syl_boundary(const cst_item *i,const cst_val *rest)
  676. {
  677. /* For debugging the syl boundary code */
  678. int x;
  679. printf("syl boundary %s | ",ffeature_string(i,"name"));
  680. if (rest)
  681. printf("%s ",val_string(val_car(rest)));
  682. if (rest && val_cdr(rest))
  683. printf("%s ",val_string(val_car(val_cdr(rest))));
  684. if (rest && val_cdr(rest) && val_cdr(val_cdr(rest)))
  685. printf("%s ",val_string(val_car(val_cdr(val_cdr(rest)))));
  686. x = cmu_indic_syl_boundary_x(i,rest);
  687. printf("is %d",x);
  688. printf("\n");
  689. return x;
  690. }
  691. #endif
  692. cst_utterance *cmu_indic_assign_lex_stress(cst_utterance *u)
  693. {
  694. /* Assign stress to a lexical entry: from indic_lexicon.scm (aup) */
  695. /* The stress is placed on the syllable with the highest weight. */
  696. /* If there is a tie, the last-most syllable with highest weight */
  697. /* is chosen. However, the last syllable of the word does not */
  698. /* participate in tie-breaking. That is, it is stressed only when */
  699. /* there are no ties. (Hussein 1997) */
  700. /* not sure this code actually follows the above rules, but its */
  701. /* the same as the festival code -- awb 20140606 */
  702. const cst_item *syl, *w;
  703. const char *x1, *x2, *x3;
  704. int syl_weight, best_weight, sw;
  705. const cst_item *stress_position;
  706. for (w=relation_head(utt_relation(u,"SylStructure")); w; w=item_next(w))
  707. {
  708. for (syl=item_daughter(w); syl; syl=item_next(syl))
  709. { /* Assign weight to each syllable */
  710. syl_weight = 0;
  711. x1 = ffeature_string(syl,"R:SylStructure.daughtern.name");
  712. x2 = ffeature_string(syl,"R:SylStructure.daughtern.p.name");
  713. x3 = ffeature_string(syl,"R:SylStructure.daughtern.p.p.name");
  714. if (cmu_indic_is_vowel(x1))
  715. { /* If syllable is open vowel */
  716. if (cst_streq(x1,"A") || cst_streq(x1,"i") ||
  717. cst_streq(x1,"u"))
  718. syl_weight = 1;
  719. else
  720. syl_weight = 2;
  721. }
  722. else
  723. {
  724. if (cmu_indic_is_vowel(x2))
  725. {
  726. if (cst_streq(x2,"A") || cst_streq(x2,"i") ||
  727. cst_streq(x2,"u"))
  728. syl_weight = 2;
  729. else
  730. syl_weight = 3;
  731. }
  732. else if (cmu_indic_is_vowel(x3))
  733. syl_weight = 3;
  734. }
  735. item_set_int(syl,"syl_weight",syl_weight);
  736. }
  737. }
  738. for (w=relation_head(utt_relation(u,"SylStructure")); w; w=item_next(w))
  739. {
  740. best_weight = 0;
  741. stress_position = NULL;
  742. for (syl=item_daughter(w); syl; syl=item_next(syl))
  743. {
  744. sw = ffeature_int(syl,"syl_weight");
  745. if (sw > best_weight)
  746. {
  747. best_weight = sw;
  748. stress_position = syl;
  749. }
  750. else if ((sw == best_weight) && item_next(syl))
  751. stress_position = syl;
  752. }
  753. if (stress_position)
  754. item_set_string(stress_position,"stress","1");
  755. }
  756. return u;
  757. }
  758. cst_utterance *cmu_indic_postlex(cst_utterance *u)
  759. {
  760. /* Post lexical rules */
  761. cmu_indic_assign_lex_stress(u);
  762. #if 0
  763. /* Print out words as 'festival' lexical entries */
  764. const cst_item *word, *syl, *seg;
  765. for (word=relation_head(utt_relation(u,"Word"));
  766. word; word=item_next(word))
  767. {
  768. printf("( \"%s\" nil (",ffeature_string(word,"name"));
  769. for (syl=item_daughter(item_as(word,"SylStructure"));
  770. syl;syl=item_next(syl))
  771. {
  772. printf("(( ");
  773. for (seg=item_daughter(item_as(syl,"SylStructure")); seg;
  774. seg=item_next(seg))
  775. {
  776. printf("%s ",ffeature_string(seg,"name"));
  777. }
  778. printf(") %s %d) ",
  779. ffeature_string(syl,"stress"),
  780. ffeature_int(syl,"syl_weight"));
  781. }
  782. printf("))\n");
  783. }
  784. #endif
  785. return u;
  786. }
  787. cst_lexicon cmu_indic_lex;
  788. cst_lexicon *cmu_indic_lex_init(void)
  789. {
  790. /* Should it be global const or dynamic */
  791. /* Can make lts_rules just a cart tree like others */
  792. cst_lexicon *l;
  793. if (cmu_indic_lex.lts_function)
  794. return &cmu_indic_lex;
  795. l = &cmu_indic_lex;
  796. l->name = "cmu_indic_lex";
  797. l->lts_function = cmu_indic_lex_lts_function;
  798. l->syl_boundary = cmu_indic_syl_boundary;
  799. l->postlex = cmu_indic_postlex;
  800. return l;
  801. }