/* Phoneme Model.
*
* Copyright (C) 2013-2015 Reece H. Dunn
*
* This file is part of cainteoir-engine.
*
* cainteoir-engine 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.
*
* cainteoir-engine 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 cainteoir-engine. If not, see .
*/
#include "config.h"
#include "i18n.h"
#include "compatibility.hpp"
#include
#include
#include
namespace tts = cainteoir::tts;
namespace ipa = cainteoir::ipa;
struct feature_data
{
const char *abbreviation;
ipa::phoneme::value_type value;
ipa::phoneme::value_type mask;
};
static const std::initializer_list kirshenbaum = {
{ "adv", ipa::advanced, ipa::articulation }, // extension
{ "alp", ipa::alveolo_palatal, ipa::place_of_articulation }, // extension
{ "alv", ipa::alveolar, ipa::place_of_articulation },
{ "apc", ipa::apical, ipa::articulation }, // extension
{ "apr", ipa::approximant, ipa::manner_of_articulation },
{ "atr", ipa::advanced_tongue_root, ipa::tongue_root }, // extension
{ "bck", ipa::back, ipa::vowel_backness },
{ "blb", ipa::bilabial, ipa::place_of_articulation },
{ "brv", ipa::breathy_voice, ipa::phonation },
{ "clk", ipa::click, ipa::manner_of_articulation },
{ "cnt", ipa::center, ipa::vowel_backness },
{ "con", 0, ipa::phoneme_type }, // extension
{ "crv", ipa::creaky_voice, ipa::phonation }, // extension
{ "ctl", 0, ipa::lateral },
{ "czd", ipa::centralized, ipa::articulation }, // extension
{ "dcz", ipa::diacritized, ipa::diacritized }, // extension
{ "dnt", ipa::dental, ipa::place_of_articulation },
{ "dst", ipa::downstep, ipa::phoneme_type }, // extension
{ "dzd", ipa::dentalized, ipa::articulation }, // extension
{ "ejc", ipa::ejective, ipa::ejective },
{ "epg", ipa::epiglottal, ipa::place_of_articulation }, // extension
{ "est", ipa::extra_short, ipa::length }, // extension
{ "fbr", ipa::foot_break, ipa::phoneme_type }, // extension
{ "flp", ipa::flap, ipa::manner_of_articulation },
{ "fnt", ipa::front, ipa::vowel_backness },
{ "frc", ipa::fricative, ipa::manner_of_articulation },
{ "glf", ipa::global_fall, ipa::phoneme_type }, // extension
{ "glr", ipa::global_rise, ipa::phoneme_type }, // extension
{ "glt", ipa::glottal, ipa::place_of_articulation },
{ "hgh", ipa::high, ipa::vowel_height },
{ "hlg", ipa::half_long, ipa::length }, // extension
{ "ibr", ipa::intonation_break, ipa::phoneme_type }, // extension
{ "imp", ipa::implosive, ipa::manner_of_articulation },
{ "lat", ipa::lateral, ipa::lateral },
{ "lbd", ipa::labio_dental, ipa::place_of_articulation },
{ "lbp", ipa::labio_palatal, ipa::place_of_articulation }, // extension
{ "lbv", ipa::labio_velar, ipa::place_of_articulation },
{ "lgl", ipa::linguolabial, ipa::articulation }, // extension
{ "lmd", ipa::lower_mid, ipa::vowel_height },
{ "lmn", ipa::laminal, ipa::articulation }, // extension
{ "lng", ipa::long_, ipa::length },
{ "lnk", ipa::linking, ipa::phoneme_type }, // extension
{ "low", ipa::low, ipa::vowel_height },
{ "lrd", ipa::less_rounded, ipa::rounding }, // extension
{ "lwr", ipa::lowered, ipa::articulation }, // extension
{ "mcz", ipa::mid_centralized, ipa::articulation }, // extension
{ "mid", ipa::mid, ipa::vowel_height },
{ "mrd", ipa::more_rounded, ipa::rounding }, // extension
{ "mrm", ipa::murmured, ipa::voicing },
{ "nas", ipa::nasal, ipa::manner_of_articulation },
{ "nsy", ipa::non_syllabic, ipa::syllabicity },
{ "nzd", ipa::nasalized, ipa::coarticulation },
{ "orl", 0, 0 }, // ignored -- Kirshenbaum uses {stp}/{nas} instead of {orl,stp}/{nas,stp}.
{ "pal", ipa::palatal, ipa::place_of_articulation },
{ "pau", ipa::separator, ipa::phoneme_type }, // extension
{ "phr", ipa::pharyngeal, ipa::place_of_articulation },
{ "pla", ipa::palato_alveolar, ipa::place_of_articulation },
{ "ret", ipa::retracted, ipa::articulation }, // extension
{ "rfx", ipa::retroflex, ipa::place_of_articulation },
{ "rnd", ipa::rounded, ipa::rounded },
{ "rsd", ipa::raised, ipa::articulation }, // extension
{ "rtr", ipa::retracted_tongue_root, ipa::tongue_root }, // extension
{ "rzd", ipa::rhoticized, ipa::rhoticized },
{ "sbr", ipa::syllable_break, ipa::phoneme_type }, // extension
{ "sib", ipa::sibilant, ipa::sibilant }, // extension
{ "slv", ipa::slack_voice, ipa::phonation }, // extension
{ "smh", ipa::semi_high, ipa::vowel_height },
{ "sml", ipa::semi_low, ipa::vowel_height }, // extension
{ "st1", ipa::primary_stress, ipa::stress }, // extension
{ "st2", ipa::secondary_stress, ipa::stress }, // extension
{ "st3", ipa::extra_stress, ipa::stress }, // extension
{ "stp", ipa::plosive, ipa::manner_of_articulation },
{ "stv", ipa::stiff_voice, ipa::phonation }, // extension
{ "syl", ipa::syllabic, ipa::syllabicity },
{ "te1", ipa::tone_end_bottom, ipa::tone_end }, // extension
{ "te2", ipa::tone_end_low, ipa::tone_end }, // extension
{ "te3", ipa::tone_end_mid, ipa::tone_end }, // extension
{ "te4", ipa::tone_end_high, ipa::tone_end }, // extension
{ "te5", ipa::tone_end_top, ipa::tone_end }, // extension
{ "tie", ipa::joined_to_next_phoneme, ipa::joined_to_next_phoneme }, // extension
{ "tm1", ipa::tone_middle_bottom, ipa::tone_middle }, // extension
{ "tm2", ipa::tone_middle_low, ipa::tone_middle }, // extension
{ "tm3", ipa::tone_middle_mid, ipa::tone_middle }, // extension
{ "tm4", ipa::tone_middle_high, ipa::tone_middle }, // extension
{ "tm5", ipa::tone_middle_top, ipa::tone_middle }, // extension
{ "trl", ipa::trill, ipa::manner_of_articulation },
{ "ts1", ipa::tone_start_bottom, ipa::tone_start }, // extension
{ "ts2", ipa::tone_start_low, ipa::tone_start }, // extension
{ "ts3", ipa::tone_start_mid, ipa::tone_start }, // extension
{ "ts4", ipa::tone_start_high, ipa::tone_start }, // extension
{ "ts5", ipa::tone_start_top, ipa::tone_start }, // extension
{ "umd", ipa::upper_mid, ipa::vowel_height },
{ "unr", 0, ipa::rounded },
{ "unx", ipa::unexploded, ipa::unexploded },
{ "ust", ipa::upstep, ipa::phoneme_type }, // extension
{ "uvl", ipa::uvular, ipa::place_of_articulation },
{ "vcd", ipa::voiced, ipa::voicing },
{ "vel", ipa::velar, ipa::place_of_articulation },
{ "vfz", ipa::velarized_or_pharyngealized, ipa::coarticulation }, // extension
{ "vls", ipa::voiceless, ipa::voicing },
{ "vwl", ipa::vowel, ipa::phoneme_type },
};
bool ipa::phoneme::get(const char *feature) const
{
if (!feature) throw tts::phoneme_error("unknown phoneme feature '(null)'");
int begin = 0;
int end = kirshenbaum.size() - 1;
while (begin <= end)
{
int pos = (begin + end) / 2;
auto &item = *(kirshenbaum.begin() + pos);
int comp = strcmp(item.abbreviation, feature);
if (comp == 0)
return (item.mask == 0) ? false : (get(item.mask) == item.value);
else if (comp < 0)
begin = pos + 1;
else
end = pos - 1;
}
char msg[64];
sprintf(msg, i18n("unknown phoneme feature '%s'"), feature);
throw tts::phoneme_error(msg);
}
ipa::phoneme &ipa::phoneme::set(const char *feature)
{
if (!feature) throw tts::phoneme_error("unknown phoneme feature '(null)'");
int begin = 0;
int end = kirshenbaum.size() - 1;
while (begin <= end)
{
int pos = (begin + end) / 2;
auto &item = *(kirshenbaum.begin() + pos);
int comp = strcmp(item.abbreviation, feature);
if (comp == 0)
return set(item.value, item.mask);
else if (comp < 0)
begin = pos + 1;
else
end = pos - 1;
}
char msg[64];
sprintf(msg, i18n("unknown phoneme feature '%s'"), feature);
throw tts::phoneme_error(msg);
}