/* * Copyright (C) 2017 Reece H. Dunn * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see: . */ #include "config.h" #include #include #include #include #include #include "phoneme.h" #include "error.h" #define FEATURE(a, b, c) ((a << 16) | (b << 8) | (c)) // See docs/phonemes.md for the list of supported features. enum feature_t { // invalid phoneme feature name inv, // Not in docs/phonemes.md. This is used to signal an unknown feature name. // manner of articulation nas = FEATURE('n', 'a', 's'), stp = FEATURE('s', 't', 'p'), afr = FEATURE('a', 'f', 'r'), frc = FEATURE('f', 'r', 'c'), flp = FEATURE('f', 'l', 'p'), trl = FEATURE('t', 'r', 'l'), apr = FEATURE('a', 'p', 'r'), clk = FEATURE('c', 'l', 'k'), ejc = FEATURE('e', 'j', 'c'), imp = FEATURE('i', 'm', 'p'), vwl = FEATURE('v', 'w', 'l'), lat = FEATURE('l', 'a', 't'), sib = FEATURE('s', 'i', 'b'), // place of articulation blb = FEATURE('b', 'l', 'b'), lbd = FEATURE('l', 'b', 'd'), bld = FEATURE('b', 'l', 'd'), dnt = FEATURE('d', 'n', 't'), alv = FEATURE('a', 'l', 'v'), pla = FEATURE('p', 'l', 'a'), rfx = FEATURE('r', 'f', 'x'), alp = FEATURE('a', 'l', 'p'), pal = FEATURE('p', 'a', 'l'), vel = FEATURE('v', 'e', 'l'), lbv = FEATURE('l', 'b', 'v'), uvl = FEATURE('u', 'v', 'l'), phr = FEATURE('p', 'h', 'r'), glt = FEATURE('g', 'l', 't'), // voice vcd = FEATURE('v', 'c', 'd'), vls = FEATURE('v', 'l', 's'), // vowel height hgh = FEATURE('h', 'g', 'h'), smh = FEATURE('s', 'm', 'h'), umd = FEATURE('u', 'm', 'd'), mid = FEATURE('m', 'i', 'd'), lmd = FEATURE('l', 'm', 'd'), sml = FEATURE('s', 'm', 'l'), low = FEATURE('l', 'o', 'w'), // vowel backness fnt = FEATURE('f', 'n', 't'), cnt = FEATURE('c', 'n', 't'), bck = FEATURE('b', 'c', 'k'), // rounding unr = FEATURE('u', 'n', 'r'), rnd = FEATURE('r', 'n', 'd'), // articulation lgl = FEATURE('l', 'g', 'l'), idt = FEATURE('i', 'd', 't'), apc = FEATURE('a', 'p', 'c'), lmn = FEATURE('l', 'm', 'n'), // air flow egs = FEATURE('e', 'g', 's'), igs = FEATURE('i', 'g', 's'), // phonation brv = FEATURE('b', 'r', 'v'), slv = FEATURE('s', 'l', 'v'), stv = FEATURE('s', 't', 'v'), crv = FEATURE('c', 'r', 'v'), glc = FEATURE('g', 'l', 'c'), // rounding and labialization ptr = FEATURE('p', 't', 'r'), cmp = FEATURE('c', 'm', 'p'), mrd = FEATURE('m', 'r', 'd'), lrd = FEATURE('l', 'r', 'd'), // syllabicity syl = FEATURE('s', 'y', 'l'), nsy = FEATURE('n', 's', 'y'), // consonant release asp = FEATURE('a', 's', 'p'), nrs = FEATURE('n', 'r', 's'), lrs = FEATURE('l', 'r', 's'), unx = FEATURE('u', 'n', 'x'), // coarticulation pzd = FEATURE('p', 'z', 'd'), vzd = FEATURE('v', 'z', 'd'), fzd = FEATURE('f', 'z', 'd'), nzd = FEATURE('n', 'z', 'd'), rzd = FEATURE('r', 'z', 'd'), // tongue root atr = FEATURE('a', 't', 'r'), rtr = FEATURE('r', 't', 'r'), // fortis and lenis fts = FEATURE('f', 't', 's'), lns = FEATURE('l', 'n', 's'), }; uint32_t lookup_feature(const char *feature) { if (strlen(feature) != 3) return inv; return FEATURE(feature[0], feature[1], feature[2]); } espeak_ng_STATUS phoneme_add_feature(PHONEME_TAB *phoneme, const char *feature, espeak_ng_ERROR_CONTEXT *context) { if (!phoneme || !feature) return EINVAL; switch (lookup_feature(feature)) { // manner of articulation case nas: phoneme->type = phNASAL; break; case stp: case afr: // FIXME: eSpeak treats 'afr' as 'stp'. phoneme->type = phSTOP; break; case frc: case apr: // FIXME: eSpeak is using this for [h], with 'liquid' used for [l] and [r]. phoneme->type = phFRICATIVE; break; case flp: // FIXME: Why is eSpeak using a vstop (vcd + stp) for this? phoneme->type = phVSTOP; break; case trl: // FIXME: 'trill' should be the type; 'liquid' should be a flag (phoneme files specify both). phoneme->phflags |= phTRILL; break; case clk: case ejc: case imp: case lat: // Not supported by eSpeak. break; case vwl: phoneme->type = phVOWEL; break; case sib: phoneme->phflags |= phSIBILANT; break; // place of articulation case blb: phoneme->phflags &= ~phARTICULATION; phoneme->phflags |= 1 << 16; break; case lbd: phoneme->phflags &= ~phARTICULATION; phoneme->phflags |= 2 << 16; break; case dnt: phoneme->phflags &= ~phARTICULATION; phoneme->phflags |= 3 << 16; break; case alv: phoneme->phflags &= ~phARTICULATION; phoneme->phflags |= 4 << 16; break; case rfx: phoneme->phflags &= ~phARTICULATION; phoneme->phflags |= 5 << 16; break; case pla: phoneme->phflags &= ~phARTICULATION; phoneme->phflags |= 6 << 16; break; case pal: phoneme->phflags &= ~phARTICULATION; phoneme->phflags |= 7 << 16; break; case vel: phoneme->phflags &= ~phARTICULATION; phoneme->phflags |= 8 << 16; break; case lbv: phoneme->phflags &= ~phARTICULATION; phoneme->phflags |= 9 << 16; break; case uvl: phoneme->phflags &= ~phARTICULATION; phoneme->phflags |= 10 << 16; break; case phr: phoneme->phflags &= ~phARTICULATION; phoneme->phflags |= 11 << 16; break; case glt: phoneme->phflags &= ~phARTICULATION; phoneme->phflags |= 12 << 16; break; case bld: // FIXME: Not supported by eSpeak. Used in German p͡f. break; case alp: // FIXME: Not supported by eSpeak. Used in Chinese/Japanese ɕ and ʑ. break; // voice case vcd: phoneme->phflags |= phVOICED; break; case vls: phoneme->phflags |= phVOICELESS; break; // vowel height case hgh: case smh: case umd: case mid: case lmd: case sml: case low: // Not supported by eSpeak. break; // vowel backness case fnt: case cnt: case bck: // Not supported by eSpeak. break; // rounding case unr: case rnd: // Not supported by eSpeak. break; // articulation case lgl: case idt: case apc: case lmn: // Not supported by eSpeak. break; // air flow case egs: case igs: // Not supported by eSpeak. break; // phonation case brv: case slv: case stv: case crv: case glc: // Not supported by eSpeak. break; // rounding and labialization case ptr: case cmp: case mrd: case lrd: // Not supported by eSpeak. break; // syllabicity case syl: // Not supported by eSpeak. break; case nsy: phoneme->phflags |= phNONSYLLABIC; break; // consonant release case asp: case nrs: case lrs: case unx: // Not supported by eSpeak. break; // coarticulation case pzd: phoneme->phflags |= phPALATAL; break; case vzd: case fzd: case nzd: // Not supported by eSpeak. break; case rzd: phoneme->phflags |= phRHOTIC; break; // tongue root case atr: case rtr: // Not supported by eSpeak. break; // fortis and lenis case fts: case lns: // Not supported by eSpeak. break; // invalid phoneme feature default: return create_name_error_context(context, ENS_UNKNOWN_PHONEME_FEATURE, feature); } return ENS_OK; }