| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914 |
- /*************************************************************************/
- /* */
- /* Language Technologies Institute */
- /* Carnegie Mellon University */
- /* Copyright (c) 2013 */
- /* All Rights Reserved. */
- /* */
- /* Permission is hereby granted, free of charge, to use and distribute */
- /* this software and its documentation without restriction, including */
- /* without limitation the rights to use, copy, modify, merge, publish, */
- /* distribute, sublicense, and/or sell copies of this work, and to */
- /* permit persons to whom this work is furnished to do so, subject to */
- /* the following conditions: */
- /* 1. The code must retain the above copyright notice, this list of */
- /* conditions and the following disclaimer. */
- /* 2. Any modifications must be clearly marked as such. */
- /* 3. Original authors' names are not deleted. */
- /* 4. The authors' names are not used to endorse or promote products */
- /* derived from this software without specific prior written */
- /* permission. */
- /* */
- /* CARNEGIE MELLON UNIVERSITY AND THE CONTRIBUTORS TO THIS WORK */
- /* DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING */
- /* ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT */
- /* SHALL CARNEGIE MELLON UNIVERSITY NOR THE CONTRIBUTORS BE LIABLE */
- /* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES */
- /* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN */
- /* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, */
- /* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF */
- /* THIS SOFTWARE. */
- /* */
- /*************************************************************************/
- /* */
- /* indic Lexical function */
- /* */
- /*************************************************************************/
- #include "flite.h"
- #include "cst_val.h"
- #include "cst_voice.h"
- #include "cst_lexicon.h"
- #include "cst_ffeatures.h"
- #include "cmu_indic_lex.h"
- #include "../cmu_indic_lang/cmu_indic_lang.h"
- extern cst_lexicon cmu_lex;
- static const struct cmu_indic_char cmu_indic_offset_char[128] = {
- /*000*/ {IND_IGNORE, ""},
- /*001*/ {IND_ANUSWAAR, "nX"},
- /*002*/ {IND_ANUSWAAR, "nX"},
- /*003*/ {IND_VISARGA, "h"},
- /*004*/ {IND_INDEPENDENT_VOWEL, "A"},
- /*005*/ {IND_INDEPENDENT_VOWEL, "A"},
- /*006*/ {IND_INDEPENDENT_VOWEL, "A:"},
- /*007*/ {IND_INDEPENDENT_VOWEL, "i"},
- /*008*/ {IND_INDEPENDENT_VOWEL, "i:"},
- /*009*/ {IND_INDEPENDENT_VOWEL, "u"},
- /*00A*/ {IND_INDEPENDENT_VOWEL, "u:"},
- /*00B*/ {IND_INDEPENDENT_VOWEL, "9r="},
- /*00C*/ {IND_INDEPENDENT_VOWEL, "l="},
- /*00D*/ {IND_INDEPENDENT_VOWEL, "ay"},
- /*00E*/ {IND_INDEPENDENT_VOWEL, "e"},
- /*00F*/ {IND_INDEPENDENT_VOWEL, "e"},
- /*010*/ {IND_INDEPENDENT_VOWEL, "aI"},
- /*011*/ {IND_INDEPENDENT_VOWEL, "ow"},
- /*012*/ {IND_INDEPENDENT_VOWEL, "ow"},
- /*013*/ {IND_INDEPENDENT_VOWEL, "o"},
- /*014*/ {IND_INDEPENDENT_VOWEL, "aU"},
- /*015*/ {IND_CONSONANT, "k"},
- /*016*/ {IND_CONSONANT, "kh"},
- /*017*/ {IND_CONSONANT, "g"},
- /*018*/ {IND_CONSONANT, "gh"},
- /*019*/ {IND_CONSONANT, "N"},
- /*01A*/ {IND_CONSONANT, "c"},
- /*01B*/ {IND_CONSONANT, "ch"},
- /*01C*/ {IND_CONSONANT, "J"},
- /*01D*/ {IND_CONSONANT, "Jh"},
- /*01E*/ {IND_CONSONANT, "n~"},
- /*01F*/ {IND_CONSONANT, "tr"},
- /*020*/ {IND_CONSONANT, "tR"},
- /*021*/ {IND_CONSONANT, "dr"},
- /*022*/ {IND_CONSONANT, "dR"},
- /*023*/ {IND_CONSONANT, "nr"},
- /*024*/ {IND_CONSONANT, "tB"},
- /*025*/ {IND_CONSONANT, "tBh"},
- /*026*/ {IND_CONSONANT, "dB"},
- /*027*/ {IND_CONSONANT, "dBh"},
- /*028*/ {IND_CONSONANT, "nB"},
- /*029*/ {IND_CONSONANT, "nB"},
- /*02A*/ {IND_CONSONANT, "p"},
- /*02B*/ {IND_CONSONANT, "ph"},
- /*02C*/ {IND_CONSONANT, "b"},
- /*02D*/ {IND_CONSONANT, "bh"},
- /*02E*/ {IND_CONSONANT, "m"},
- /*02F*/ {IND_CONSONANT, "j"},
- /*030*/ {IND_CONSONANT, "9r"},
- /*031*/ {IND_CONSONANT, "9r"},
- /*032*/ {IND_CONSONANT, "l"},
- /*033*/ {IND_CONSONANT, "lr"},
- /*034*/ {IND_CONSONANT, "lr"},
- /*035*/ {IND_CONSONANT, "v"},
- /*036*/ {IND_CONSONANT, "c}"},
- /*037*/ {IND_CONSONANT, "sr"},
- /*038*/ {IND_CONSONANT, "s"},
- /*039*/ {IND_CONSONANT, "hv"},
- /*03A*/ {IND_VOWEL, "e"},
- /*03B*/ {IND_VOWEL, "e"},
- /*03C*/ {IND_NUKTA, ""},
- /*03D*/ {IND_AVAGRAHA, ""},
- /*03E*/ {IND_VOWEL, "A:"},
- /*03F*/ {IND_VOWEL, "i"},
- /*040*/ {IND_VOWEL, "i:"},
- /*041*/ {IND_VOWEL, "u"},
- /*042*/ {IND_VOWEL, "u:"},
- /*043*/ {IND_VOWEL, "9r="},
- /*044*/ {IND_VOWEL, "9r="},
- /*045*/ {IND_VOWEL, "ay"},
- /*046*/ {IND_VOWEL, "e"},
- /*047*/ {IND_VOWEL, "e"},
- /*048*/ {IND_VOWEL, "aI"},
- /*049*/ {IND_VOWEL, "ow"},
- /*04A*/ {IND_VOWEL, "o"},
- /*04B*/ {IND_VOWEL, "o"},
- /*04C*/ {IND_VOWEL, "aU"},
- /*04D*/ {IND_HALANT, ""},
- /*04E*/ {IND_IGNORE, ""},
- /*04F*/ {IND_VOWEL, "ow"},
- /*050*/ {IND_INDEPENDENT_VOWEL, "A u m"},
- /*051*/ {IND_IGNORE, ""},
- /*052*/ {IND_IGNORE, ""},
- /*053*/ {IND_IGNORE, ""},
- /*054*/ {IND_IGNORE, ""},
- /*055*/ {IND_VOWEL, "e"},
- /*056*/ {IND_VOWEL, "u e"},
- /*057*/ {IND_VOWEL, "u: e"},
- /*058*/ {IND_CONSONANT, "q"},
- /*059*/ {IND_CONSONANT, "x"},
- /*05A*/ {IND_CONSONANT, "G"},
- /*05B*/ {IND_CONSONANT, "z"},
- /*05C*/ {IND_CONSONANT, "rr"},
- /*05D*/ {IND_CONSONANT, "rrh"},
- /*05E*/ {IND_CONSONANT, "f"},
- /*05F*/ {IND_CONSONANT, "j"},
- /*060*/ {IND_INDEPENDENT_VOWEL, "9r="},
- /*061*/ {IND_INDEPENDENT_VOWEL, "lr="},
- /*062*/ {IND_VOWEL, "lr="},
- /*063*/ {IND_VOWEL, "lr="},
- /*064*/ {IND_PUNC, ""},
- /*065*/ {IND_PUNC, ""},
- /*066*/ {IND_DIGIT, ""},
- /*067*/ {IND_DIGIT, ""},
- /*068*/ {IND_DIGIT, ""},
- /*069*/ {IND_DIGIT, ""},
- /*06A*/ {IND_DIGIT, ""},
- /*06B*/ {IND_DIGIT, ""},
- /*06C*/ {IND_DIGIT, ""},
- /*06D*/ {IND_DIGIT, ""},
- /*06E*/ {IND_DIGIT, ""},
- /*06F*/ {IND_DIGIT, ""},
- /*070*/ {IND_PUNC, ""},
- /*071*/ {IND_PUNC, ""},
- /*072*/ {IND_INDEPENDENT_VOWEL, "ay"},
- /*073*/ {IND_INDEPENDENT_VOWEL, "u e"},
- /*074*/ {IND_INDEPENDENT_VOWEL, "u: e"},
- /*075*/ {IND_INDEPENDENT_VOWEL, "ow"},
- /*076*/ {IND_INDEPENDENT_VOWEL, "u e"},
- /*077*/ {IND_INDEPENDENT_VOWEL, "u: e"},
- /*078*/ {IND_IGNORE, ""},
- /*079*/ {IND_CONSONANT, "z"},
- /*07A*/ {IND_CONSONANT, "j"},
- /*07B*/ {IND_CONSONANT, "G"},
- /*07C*/ {IND_CONSONANT, "z"},
- /*07D*/ {IND_CONSONANT, ""},
- /*07E*/ {IND_CONSONANT, "dr"},
- /*07F*/ {IND_CONSONANT, "b"},
- };
- static int cmu_indic_is_vowel(const char *p)
- {
- /* this happens works for the vowels in the indic set */
- if (strchr("aeiouAEIOU",p[0]) == NULL)
- return FALSE;
- else
- return TRUE;
- }
- static int cmu_indic_lex_ord_to_offset(const int indic_ord) {
- int output=0;
- int i;
- int offset;
- i = indic_ord;
- if ((i >= 0x0900) && (i <= 0x097F))
- offset = 0x0900;
- if ((i >= 0x0980) && (i <= 0x09FF))
- offset = 0x0980;
- if ((i >= 0x0A00) && (i <= 0x0A7F))
- offset = 0x0A00;
- if ((i >= 0x0A80) && (i <= 0x0AFF))
- offset = 0x0A80;
- if ((i >= 0x0B00) && (i <= 0x0B7F))
- offset = 0x0B00;
- if ((i >= 0x0B80) && (i <= 0x0BFF))
- offset = 0x0B80;
- if ((i >= 0x0C00) && (i <= 0x0C7F))
- offset = 0x0C00;
- if ((i >= 0x0C80) && (i <= 0x0CFF))
- offset = 0x0C80;
- if ((i >= 0x0D00) && (i <= 0x0D7F))
- offset = 0x0D00;
- if (!offset) {
- cst_errmsg("Indic language can not process character 0x%x\n", i);
- } else {
- output = i - offset;
- }
- return output;
- }
- static int cmu_indic_get_char_type(const cst_val *indic_char) {
- int c;
- if (!indic_char) return IND_IGNORE;
- c = val_int(indic_char);
- if ((c < 0x0900) || (c > 0x0D7F))
- return IND_IGNORE;
- c = cmu_indic_lex_ord_to_offset(c);
- return cmu_indic_offset_char[c].type;
- }
- static const cst_val* cmu_indic_get_char_phoneme(const cst_val *indic_char) {
- int c;
- if (!indic_char) return string_val("");
- c = val_int(indic_char);
- c = cmu_indic_lex_ord_to_offset(c);
- return string_val(cmu_indic_offset_char[c].phoneme);
- }
- static cst_val *cmu_indic_lex_remove_ignore_chars(const cst_val *indic_ords) {
- cst_val *output = 0;
- const cst_val *v;
- for(v=indic_ords; v; v=val_cdr(v)) {
- if (cmu_indic_get_char_type(val_car(v)) == IND_IGNORE)
- continue;
- output = cons_val(val_car(v), output);
- }
- return val_reverse(output);
- }
- static cst_val *cmu_indic_lex_map_nukta_chars(const cst_val *indic_ords) {
- cst_val *output = 0;
- const cst_val *v;
- int i;
- int mapped_val;
- for(v=indic_ords; v; v=val_cdr(v)) {
- i = val_int(val_car(v));
- if (val_cdr(v) && cmu_indic_get_char_type(val_car(val_cdr(v))) == IND_NUKTA) {
- switch (i) {
- // Devanagari
- case 2325: mapped_val=2392; break; // क़
- case 2326: mapped_val=2393; break; // ख़
- case 2327: mapped_val=2394; break; // ग़
- case 2332: mapped_val=2395; break; // ज़
- case 2337: mapped_val=2396; break; // ड़
- case 2338: mapped_val=2397; break; // ढ़
- case 2347: mapped_val=2398; break; // फ़
- case 2351: mapped_val=2399; break; // य़
- // Bengali
- case 2465: mapped_val=2524; break;
- case 2566: mapped_val=2525; break;
- case 2479: mapped_val=2527; break;
- // Tamil
- case 2962: mapped_val=2964; break;
- default:
- mapped_val=i;
- };
- output = cons_val(int_val(mapped_val), output);
- v = val_cdr(v);
- } else {
- // Copy current item into output
- output = cons_val(val_car(v), output);
- }
- }
- return val_reverse(output);
- }
- cst_val *cmu_indic_lex_ord_to_phones(const cst_val *ords,
- int cmu_indic_variant_deletes_word_final_schwa)
- {
- cst_val *in_ords = 0;
- cst_val *out_phone_strings = 0;
- cst_val *out_phones = 0;
- const cst_val *prev_char = 0;
- const cst_val *cur_char = 0;
- const cst_val *next_char = 0;
- const cst_val *remainder = 0;
- int cur_char_type = 0;
- int next_char_type = 0;
- // Ignore chars (filter)
- in_ords = cmu_indic_lex_remove_ignore_chars(ords);
- if (!in_ords) {
- return out_phones;
- }
- // Map Nukta Chars (filter)
- in_ords = cmu_indic_lex_map_nukta_chars(in_ords);
- prev_char = 0;
- cur_char = val_car(in_ords);
- if (val_cdr(in_ords)) {
- next_char = val_car(val_cdr(in_ords));
- remainder = val_cdr(val_cdr(in_ords));
- }
- else {
- next_char = 0;
- remainder = 0;
- }
- while (cur_char) {
- cur_char_type = cmu_indic_get_char_type(cur_char);
- if (next_char)
- next_char_type = cmu_indic_get_char_type(next_char);
- else
- next_char_type = IND_IGNORE;
- if (cur_char_type == IND_CONSONANT) {
- // Add consonant to the output list
- out_phone_strings = cons_val(cmu_indic_get_char_phoneme(cur_char),
- out_phone_strings);
- // If a consonant is followed by a combination vowel, a
- // halant, a punctuation then don't add a schwa after
- // it. Otherwise, insert a schwa. For end-of-word, check
- // whether we should insert schwa in this language.
- if (!next_char) { // We are in last char. Add schwa?
- if ((!prev_char) || // Always add schwa for one-char words
- (!cmu_indic_variant_deletes_word_final_schwa)) {
- out_phone_strings = cons_val(string_val("A"), out_phone_strings);
- } else {
- // Schwa deletion should probably happen depending
- // on whether there is a consonant cluster or not,
- // at the end. But Adding that rule here seems to
- // not have worked properly. Hence, we always
- // delete the final schwa.
- }
- } else { // Not a final char
- if ( (next_char_type != IND_VOWEL) &&
- (next_char_type != IND_PUNC) &&
- (next_char_type != IND_HALANT) &&
- (next_char_type != IND_IGNORE)) {
- out_phone_strings = cons_val(string_val("A"), out_phone_strings);
- }
- }
- } else if ( (cur_char_type == IND_VOWEL) ||
- (cur_char_type == IND_INDEPENDENT_VOWEL) ||
- (cur_char_type == IND_DIGIT)) {
- // Add whatever the pronunciation is
- out_phone_strings = cons_val(cmu_indic_get_char_phoneme(cur_char),
- out_phone_strings);
- } else if ( cur_char_type == IND_HALANT) {
- // Ignore
- } else if (cur_char_type == IND_AVAGRAHA) {
- // Lengthen previous vowel
- if (cmu_indic_get_char_type(prev_char) == IND_VOWEL) {
- out_phone_strings = cons_val(cmu_indic_get_char_phoneme(prev_char),
- out_phone_strings);
- }
- } else if (cur_char_type == IND_ANUSWAAR) {
- // The realization of anuswaar is context dependent: We
- // only generate a placeholder symbol and let postlexical
- // rules take care of this.
- out_phone_strings = cons_val(string_val("nX"),
- out_phone_strings);
- } else if (cur_char_type == IND_VISARGA) {
- // Add the glottal fricative
- out_phone_strings = cons_val(string_val("h"),
- out_phone_strings);
- } else {
- // IDEALLY we should warn for unhandled characters!
- // TODO?
- }
- prev_char = cur_char;
- cur_char = next_char;
- if (remainder) {
- next_char = val_car(remainder);
- remainder = val_cdr(remainder);
- } else {
- next_char = 0;
- }
- }
- const cst_val *v, *vx;
- cst_val *ph;
- for(v=out_phone_strings; v; v=val_cdr(v)) {
- ph = val_readlist_string(val_string(val_car(v)));
- for(vx=ph; vx; vx=val_cdr(vx)) {
- out_phones = cons_val(val_car(vx), out_phones);
- }
- delete_val(ph);
- }
- return val_reverse(out_phones);
- }
- cst_val *cmu_indic_lex_nasal_postfixes(const cst_val *in_phones) {
- // Given a phone sequence containing a special character nX
- // (contextual nasal), replace it with the appropriate nasal phone
- // based on its context
- if ((!in_phones) || !val_car(in_phones)) return 0;
- // Nazalise vowels at ends of words
- if ((val_cdr(in_phones)) &&
- (cmu_indic_is_vowel(val_string(val_car(in_phones)))) &&
- (!strcmp("nX", val_string(val_car(val_cdr(in_phones))))) &&
- ((!val_cdr(val_cdr(in_phones))) || (!val_car(val_cdr(val_cdr(in_phones)))))) {
- if (!strcmp("A", val_string(val_car(in_phones)))) { // If it's a schwa, it's not nasalized. nX becomes m
- return cons_val(val_car(in_phones),
- cons_val(string_val("m"),
- cmu_indic_lex_nasal_postfixes(val_cdr(val_cdr(in_phones)))));
- } else {
- return cons_val(string_val(cst_strcat(val_string(val_car(in_phones)),
- "nas")),
- cmu_indic_lex_nasal_postfixes(val_cdr(val_cdr(in_phones))));
- }
- } else if ( (val_cdr(in_phones)) &&
- (!strcmp("nX", val_string(val_car(in_phones))))) {
- // Choose nasal variant based on next consonant
- const char *next_c = val_string(phone_feature(&cmu_indic_phoneset,
- val_string(val_car(val_cdr(in_phones))),
- "cplace"));
- char *repl_ph;
- if (next_c) {
- switch (next_c[0]) {
- case 'v': repl_ph = "N"; break;
- case 'p': repl_ph = "n~"; break;
- case 'a': repl_ph = "nr"; break;
- case 'd': repl_ph = "nB"; break;
- case 'l': repl_ph = "m"; break;
- default: repl_ph = "nB";
- };
- return cons_val(string_val(repl_ph),
- cons_val(val_car(val_cdr(in_phones)),
- cmu_indic_lex_nasal_postfixes(val_cdr(val_cdr(in_phones)))));
- } else {
- return cons_val(val_car(in_phones),
- cmu_indic_lex_nasal_postfixes(val_cdr(in_phones)));
- }
- } else {
- return cons_val(val_car(in_phones),
- cmu_indic_lex_nasal_postfixes(val_cdr(in_phones)));
- }
- }
- /* For English derived pronunciation (latin scripted tokens) we map them */
- /* to (hindi) phones -- this has to modified for other indic languages */
- static const char * const eng_to_indic[99][3] =
- {
- {"aa", "A:", NULL },
- {"ae", "A", NULL }, /* changed this to A rather than e */
- {"ah", "A", NULL },
- {"ao", "o", NULL },
- {"aw", "aU", NULL },
- {"ax", "A", NULL },
- {"axr", "A", NULL },
- {"ay", "aI", NULL },
- {"b", "b", NULL },
- {"ch", "c", NULL },
- {"d", "dr", NULL },
- {"dh", "dB", NULL },
- {"eh", "e", NULL },
- {"er", "E", "9r" },
- {"ey", "ay", NULL },
- {"f", "ph", NULL },
- {"g", "g", NULL },
- {"hh", "hv", NULL },
- {"ih", "i", NULL },
- {"iy", "i:", NULL },
- {"jh", "J", NULL },
- {"k", "k", NULL },
- {"l", "l", NULL },
- {"m", "m", NULL },
- {"n", "nB", NULL },
- {"nx", "nB", NULL },
- {"ng", "nB", NULL },
- {"ow", "o", NULL },
- {"oy", "o", "j" },
- {"p", "p", NULL },
- {"r", "9r", NULL },
- {"s", "s", NULL },
- {"sh", "c}", NULL },
- {"t", "tr", NULL },
- {"th", "tBh", NULL },
- {"uh", "u", NULL },
- {"uw", "u:", NULL },
- {"v", "v", NULL },
- {"w", "v", NULL },
- {"y", "j", NULL },
- {"z", "s", NULL },
- {"zh", "c}", NULL },
- {NULL, NULL, NULL }
- };
- cst_val *map_english_to_indic_phones(const char *indic_variant,
- const cst_val *english_phones)
- {
- /* Map English (radio) phones to their Indic equivalent */
- cst_val *ip = NULL;
- const cst_val *v;
- char *english_phone;
- int i;
- for (v=english_phones; v; v=val_cdr(v))
- {
- english_phone = cst_strdup(val_string(val_car(v)));
- if ((english_phone[cst_strlen(english_phone)-1] == '0') ||
- (english_phone[cst_strlen(english_phone)-1] == '1'))
- /* It has a stress calue on it */
- english_phone[cst_strlen(english_phone)-1] = '\0';
- for (i=0; eng_to_indic[i][0]; i++)
- {
- if (cst_streq(english_phone,eng_to_indic[i][0]))
- {
- ip = cons_val(string_val(eng_to_indic[i][1]),ip);
- if (eng_to_indic[i][2])
- ip = cons_val(string_val(eng_to_indic[i][2]),ip);
- }
- /* if there is no mapping, we drop the phone */
- }
- cst_free(english_phone);
- }
- ip = val_reverse(ip);
- return ip;
- }
- static cst_val *delete_medial_schwa(const cst_val *rphones)
- {
- /* This schwa deletion follows the technique by Narsimhan et al (2001). */
- /* 1. Process input from right to left */
- /* 2. If a schwa is found in a VC_CV context, then delete it. */
- /* There are exceptions to this: (i) Phonotactic constraints of */
- /* Hindi not violated, and no (ii) morpheme boundary present on the */
- /* left. But I don't know how to handle these yet. So this will be */
- /* wrong more often than the 11% reported in that paper. -- AUP */
- if (rphones == NULL)
- return NULL;
- else if ((val_length(rphones) > 4) &&
- (cst_streq(val_string(val_car(val_cdr(val_cdr(rphones)))),"A")))
- { /* we have a schwa comming up */
- if ((cmu_indic_is_vowel(val_string(val_car(rphones)))) &&
- (!cmu_indic_is_vowel(val_string(val_car(val_cdr(rphones))))) &&
- (!cmu_indic_is_vowel(val_string(val_car(val_cdr(val_cdr(val_cdr(rphones))))))) &&
- (cmu_indic_is_vowel(val_string(val_car(val_cdr(val_cdr(val_cdr(val_cdr(rphones)))))))))
- /* delete the schwa */
- return cons_val(val_car(rphones),
- cons_val(val_car(val_cdr(rphones)),
- delete_medial_schwa(
- val_cdr(val_cdr(val_cdr(rphones))))));
- else
- return cons_val(val_car(rphones),
- delete_medial_schwa(val_cdr(rphones)));
- }
- else
- return cons_val(val_car(rphones),
- delete_medial_schwa(val_cdr(rphones)));
- }
- static cst_val *cmu_indic_hindi_schwa_fixes(cst_val *phones)
- {
- cst_val *dd;
- if ((val_length(phones) > 3) &&
- (cst_streq("A",val_string(val_car(val_cdr(phones))))) &&
- (cst_streq("hv",val_string(val_car(val_cdr(val_cdr(phones)))))) &&
- ((cst_streq("A",val_string(val_car(val_cdr(val_cdr(val_cdr(phones))))))) ||
- (!cmu_indic_is_vowel(val_string(val_car(val_cdr(val_cdr(val_cdr(phones)))))))))
- {
- dd = (cst_val *)(void *)val_cdr(phones);
- set_cdr(phones,val_cdr(val_cdr(phones)));
- set_cdr(dd,NULL);
- delete_val(dd);
- return phones;
- }
- else
- return phones;
- }
- cst_val *cmu_indic_lex_lts_function(const struct lexicon_struct *l,
- const char *word, const char *pos,
- const cst_features *feats)
- {
- cst_val *utflets = 0;
- cst_val *ords = 0;
- cst_val *phones = 0;
- cst_val *english_phones;
- cst_val *base_phones = NULL, *rp;
- const char *indic_variant = 0;
- int cmu_indic_variant_deletes_word_final_schwa=0;
- const cst_val *v;
- cst_val *tmpv;
- indic_variant = get_param_string(feats, "variant", "none");
- if (!strcmp(indic_variant, "hin")) {
- cmu_indic_variant_deletes_word_final_schwa = 1;
- } else if (!strcmp(indic_variant, "mar")) {
- cmu_indic_variant_deletes_word_final_schwa = 1;
- } else if (!strcmp(indic_variant, "tel")) {
- cmu_indic_variant_deletes_word_final_schwa = 0;
- } else if (!strcmp(indic_variant, "tam")) {
- cmu_indic_variant_deletes_word_final_schwa = 0;
- } else {
- cmu_indic_variant_deletes_word_final_schwa = 0;
- printf("Unknown indic variant: %s!\n", indic_variant);
- }
- if (cst_regex_match(cst_rx_not_indic,word))
- { /* Its English like so us English Lexicon to find Phones and map
- to indic phones */
- /* printf("awb_debug cmu_indic_lex: English >%s<\n",word); */
- english_phones = lex_lookup(&cmu_lex,word,pos,feats);
- base_phones = map_english_to_indic_phones(indic_variant,english_phones);
- delete_val(english_phones);
- }
- else
- {
- /* string to utf8 chars */
- utflets = cst_utf8_explode(word);
- /* chars to ord */
- for (v=utflets; v; v=val_cdr(v)) {
- tmpv = cst_utf8_ord(val_car(v));
- ords = cons_val(tmpv, ords);
- }
- ords = val_reverse(ords);
- // Ords to Phone List (Map)
- base_phones = val_reverse(cmu_indic_lex_ord_to_phones(ords,
- cmu_indic_variant_deletes_word_final_schwa));
- delete_val(utflets);
- cst_free(ords);
- }
- // Postfix Indic Nasals
- phones = cmu_indic_lex_nasal_postfixes(base_phones);
- delete_val(base_phones); base_phones = phones;
- if (cst_streq(indic_variant,"hin"))
- { /* Do schwa deletion */
- rp = val_reverse(base_phones);
- phones = val_reverse(delete_medial_schwa(rp));
- delete_val(rp); base_phones = phones;
- phones = cmu_indic_hindi_schwa_fixes(base_phones);
- }
- /* phone list to syls (done as post lexical rules) */
- #if 0
- printf("cmu_indic_lex.c: indic word \"%s\" \n",word);
- val_print(stdout, phones);
- printf("\n");
- #endif
- return phones;
- }
- static int cmu_indic_contains_vowel(const cst_val *r)
- {
- const cst_val *x;
- for (x=r; x; x=val_cdr(x))
- {
- if (cmu_indic_is_vowel(val_string(val_car(x))))
- return TRUE;
- }
- return FALSE;
- }
- static int cmu_indic_has_vowel_in_syl(const cst_item *i)
- {
- const cst_item *n;
- for (n=i; n; n=item_prev(n))
- if (cmu_indic_is_vowel(ffeature_string(n,"name")))
- return TRUE;
- return FALSE;
- }
- int cmu_indic_syl_boundary(const cst_item *i,const cst_val *rest)
- {
- if (!rest)
- return TRUE;
- else if (!cmu_indic_contains_vowel(rest))
- return FALSE;
- else if (!cmu_indic_has_vowel_in_syl(i))
- return FALSE;
- else if (rest && val_cdr(rest) &&
- cst_streq("n",val_string(val_car(rest))) &&
- !cmu_indic_is_vowel(val_string(val_car(rest))))
- return FALSE;
- else if (rest && val_cdr(rest) &&
- cmu_indic_is_vowel(ffeature_string(i,"name")) &&
- !cmu_indic_is_vowel(val_string(val_car(rest))) &&
- !cmu_indic_is_vowel(val_string(val_car(val_cdr(rest)))))
- return FALSE;
- else if (rest && val_cdr(rest) && val_cdr(val_cdr(rest)) &&
- !cmu_indic_is_vowel(val_string(val_car(rest))) &&
- !cmu_indic_is_vowel(val_string(val_car(val_cdr(rest)))) &&
- !cmu_indic_is_vowel(val_string(val_car(val_cdr(val_cdr(rest))))))
- return FALSE;
- else if (rest && val_cdr(rest) &&
- (cst_streq(val_string(val_car(rest)),
- val_string(val_car(val_cdr(rest))))))
- return FALSE;
- else
- return TRUE;
- }
- #if 0
- int cmu_indic_syl_boundary(const cst_item *i,const cst_val *rest)
- {
- /* For debugging the syl boundary code */
- int x;
- printf("syl boundary %s | ",ffeature_string(i,"name"));
- if (rest)
- printf("%s ",val_string(val_car(rest)));
- if (rest && val_cdr(rest))
- printf("%s ",val_string(val_car(val_cdr(rest))));
- if (rest && val_cdr(rest) && val_cdr(val_cdr(rest)))
- printf("%s ",val_string(val_car(val_cdr(val_cdr(rest)))));
- x = cmu_indic_syl_boundary_x(i,rest);
- printf("is %d",x);
- printf("\n");
- return x;
- }
- #endif
- cst_utterance *cmu_indic_assign_lex_stress(cst_utterance *u)
- {
- /* Assign stress to a lexical entry: from indic_lexicon.scm (aup) */
- /* The stress is placed on the syllable with the highest weight. */
- /* If there is a tie, the last-most syllable with highest weight */
- /* is chosen. However, the last syllable of the word does not */
- /* participate in tie-breaking. That is, it is stressed only when */
- /* there are no ties. (Hussein 1997) */
- /* not sure this code actually follows the above rules, but its */
- /* the same as the festival code -- awb 20140606 */
- const cst_item *syl, *w;
- const char *x1, *x2, *x3;
- int syl_weight, best_weight, sw;
- const cst_item *stress_position;
- for (w=relation_head(utt_relation(u,"SylStructure")); w; w=item_next(w))
- {
- for (syl=item_daughter(w); syl; syl=item_next(syl))
- { /* Assign weight to each syllable */
- syl_weight = 0;
- x1 = ffeature_string(syl,"R:SylStructure.daughtern.name");
- x2 = ffeature_string(syl,"R:SylStructure.daughtern.p.name");
- x3 = ffeature_string(syl,"R:SylStructure.daughtern.p.p.name");
- if (cmu_indic_is_vowel(x1))
- { /* If syllable is open vowel */
- if (cst_streq(x1,"A") || cst_streq(x1,"i") ||
- cst_streq(x1,"u"))
- syl_weight = 1;
- else
- syl_weight = 2;
- }
- else
- {
- if (cmu_indic_is_vowel(x2))
- {
- if (cst_streq(x2,"A") || cst_streq(x2,"i") ||
- cst_streq(x2,"u"))
- syl_weight = 2;
- else
- syl_weight = 3;
- }
- else if (cmu_indic_is_vowel(x3))
- syl_weight = 3;
- }
- item_set_int(syl,"syl_weight",syl_weight);
- }
- }
- for (w=relation_head(utt_relation(u,"SylStructure")); w; w=item_next(w))
- {
- best_weight = 0;
- stress_position = NULL;
- for (syl=item_daughter(w); syl; syl=item_next(syl))
- {
- sw = ffeature_int(syl,"syl_weight");
- if (sw > best_weight)
- {
- best_weight = sw;
- stress_position = syl;
- }
- else if ((sw == best_weight) && item_next(syl))
- stress_position = syl;
- }
- if (stress_position)
- item_set_string(stress_position,"stress","1");
- }
- return u;
- }
- cst_utterance *cmu_indic_postlex(cst_utterance *u)
- {
- /* Post lexical rules */
- cmu_indic_assign_lex_stress(u);
- #if 0
- /* Print out words as 'festival' lexical entries */
- const cst_item *word, *syl, *seg;
- for (word=relation_head(utt_relation(u,"Word"));
- word; word=item_next(word))
- {
- printf("( \"%s\" nil (",ffeature_string(word,"name"));
- for (syl=item_daughter(item_as(word,"SylStructure"));
- syl;syl=item_next(syl))
- {
- printf("(( ");
- for (seg=item_daughter(item_as(syl,"SylStructure")); seg;
- seg=item_next(seg))
- {
- printf("%s ",ffeature_string(seg,"name"));
- }
- printf(") %s %d) ",
- ffeature_string(syl,"stress"),
- ffeature_int(syl,"syl_weight"));
- }
- printf("))\n");
- }
- #endif
- return u;
- }
- cst_lexicon cmu_indic_lex;
- cst_lexicon *cmu_indic_lex_init(void)
- {
- /* Should it be global const or dynamic */
- /* Can make lts_rules just a cart tree like others */
- cst_lexicon *l;
- if (cmu_indic_lex.lts_function)
- return &cmu_indic_lex;
- l = &cmu_indic_lex;
- l->name = "cmu_indic_lex";
- l->lts_function = cmu_indic_lex_lts_function;
- l->syl_boundary = cmu_indic_syl_boundary;
- l->postlex = cmu_indic_postlex;
- return l;
- }
|