/
gozaltech
/
RHVoice2
Обзор
Документация
Войти
/
gozaltech
/
RHVoice2
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/core/document.cpp
473 строки
17 KB
Beqa Gozalishvili
SSML: Add time attribute to break element (#994)
18 ноя 2025, 19:55
Не верифицирован
18 ноя 2025, 19:55
47c50a5
Код
Авторство
О чём код?
/* Copyright (C) 2012, 2014, 2019, 2020, 2021 Olga Yakovleva <olga@rhvoice.org> */ /* Copyright (C) 2022 Non-Routine LLC. <lp@louderpages.org> */ /* This program is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU Lesser General Public License as published by */ /* the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. */ /* You should have received a copy of the GNU Lesser General Public License */ /* along with this program. If not, see <https://www.gnu.org/licenses/>. */ #include "core/voice.hpp" #include "core/document.hpp" namespace RHVoice { void sentence::append_token::execute(utterance& u) const { item& token=u.get_language().append_token(u,name, eos); token.set("whitespace",whitespace); token.set("position",position); token.set("length",length); u.get_relation("Event",true).append(token); } void sentence::append_chars::execute(utterance& u) const { u.get_language().on_token_break(u); const language_info& lang_info=u.get_language().get_info(); item& parent_token=u.get_relation("TokStructure",true).append(); parent_token.set("name",name); parent_token.set("whitespace",whitespace); parent_token.set("position",position); parent_token.set("length",length); u.get_relation("Token",true).append(parent_token); u.get_relation("Event",true).append(parent_token); std::string::const_iterator pos=name.begin(); std::string::const_iterator prev_pos; utf8::uint32_t c; while(pos!=name.end()) { prev_pos=pos; c=utf8::next(pos,name.end()); item& token=parent_token.append_child(); token.set("name",std::string(prev_pos,pos)); token.set<std::string>("pos","sym"); token.set("verbosity",verbosity_level); } } void sentence::append_emoji::execute(utterance& u) const { item& token=u.get_language().supports_emoji()?u.get_language().append_emoji(u,name):u.get_language().append_token(u,name, eos); token.set("whitespace",whitespace); token.set("position",position); token.set("length",length); u.get_relation("Event",true).append(token); } void sentence::append_key::execute(utterance& u) const { u.get_language().on_token_break(u); const language_info& lang_info=u.get_language().get_info(); item& parent_token=u.get_relation("TokStructure",true).append(); parent_token.set("name",name); parent_token.set("whitespace",whitespace); parent_token.set("position",position); parent_token.set("length",length); u.get_relation("Token",true).append(parent_token); u.get_relation("Event",true).append(parent_token); str::tokenizer<str::is_equal_to> tokenizer(parent_token.get("name").as<std::string>(),str::is_equal_to('_')); for(str::tokenizer<str::is_equal_to>::iterator it=tokenizer.begin();it!=tokenizer.end();++it) { const std::string& name=*it; item& token=parent_token.append_child(); token.set("name",name); token.set<std::string>("pos","key"); token.set<verbosity_t>("verbosity",verbosity_name); std::string::const_iterator pos=name.begin(); utf8::uint32_t cp=utf8::next(pos,name.end()); if(pos==name.end()) { token.set<std::string>("pos",lang_info.is_letter(cp)?"lseq":"char"); token.set<verbosity_t>("verbosity",verbosity_name|verbosity_spell); } } } void sentence::append_phones::execute(utterance& u) const { u.get_language().on_token_break(u); item& parent_token=u.get_relation("TokStructure",true).append(); parent_token.set("name",name); u.get_relation("Token",true).append(parent_token); parent_token.set("whitespace",whitespace); parent_token.set("position",position); parent_token.set("length",length); u.get_relation("Event",true).append(parent_token); item& token=parent_token.append_child(); token.set("name",name); token.set<std::string>("pos","ph"); token.set<verbosity_t>("verbosity",verbosity_name); } sentence::sentence(document* parent_): parent(parent_), rate(1), pitch(1), volume(1), language_and_voice(parent->get_engine().get_languages().end(),parent->get_engine().get_voices().end()), length(0), num_tokens(0), en_id(parent->get_voice_profile()), initial_break_time_ms(0) { } bool sentence::next_token_starts_new_sentence(const tts_markup& markup_info) const { if(empty()) return false; if(!markup_info.autosplit_sentences) return false; std::size_t newline_count=str::count_newlines(prev_token.whitespace.begin(),prev_token.whitespace.end()); if(newline_count>1) return true; if(prev_token.type==content_key) return false; std::vector<utf8::uint32_t>::const_iterator final_punctuation_start=std::find_if(prev_token.text.rbegin(),prev_token.text.rend(),std::not1(str::is_punct())).base(); if(final_punctuation_start==prev_token.text.end()) return false; if(prev_token.text.back()=='.') { if((next_token.type!=content_key)&&str::isalpha(next_token.text[0])) { if(final_punctuation_start==prev_token.text.begin()) return true; else { std::vector<utf8::uint32_t>::const_iterator last_char_pos=final_punctuation_start-1; bool is_alpha=str::isalpha(*last_char_pos); bool is_digit=str::isdigit(*last_char_pos); if(is_alpha || is_digit) { if(is_alpha && last_char_pos==prev_token.text.begin()) return false; else { bool unicase=false; if(language_and_voice.first!=parent->get_engine().get_languages().end()) unicase=language_and_voice.first->has_unicase_alphabet(); return (unicase||str::isupper(next_token.text[0])); } } else return true; } } else return true; } else { utf8::uint32_t cp; for(std::vector<utf8::uint32_t>::const_iterator it(final_punctuation_start);it!=prev_token.text.end();++it) { cp=*it; if(language_and_voice.first!=parent->get_engine().get_languages().end()) { if(language_and_voice.first->get_instance().is_eos_punct(cp)) return true; } if((cp=='.')||(cp=='\?')||(cp=='!')||(cp==';')||(cp==':')) return true; } return false; } } voice_list::const_iterator sentence::determine_next_token_voice() { voice_list::const_iterator no_result=parent->get_engine().get_voices().end(); if(en_id.is_enabled()) { en_id.add_word(next_token.text.begin(), next_token.text.end()); return no_result; } const voice_profile& profile=parent->get_voice_profile(); if(profile.empty()) return no_result; if(profile.voice_count()==1) return profile.primary(); voice_profile::iterator voice_in_profile=profile.end(); if(next_token.type==content_key) { if((next_token.text.size()==1)||(*(next_token.text.end()-2)=='_')) voice_in_profile=profile.voice_for_text(next_token.text.end()-1,next_token.text.end()); } else voice_in_profile=profile.voice_for_text(next_token.text.begin(),next_token.text.end()); if(voice_in_profile!=profile.end()) return *voice_in_profile; else if(parent->get_engine().prefer_primary_language) return profile.primary(); else return no_result; } language_voice_pair sentence::get_language_and_voice_from_markup(const tts_markup& markup_info) const { language_voice_pair result; const language_list& languages=parent->get_engine().get_languages(); const voice_list& voices=parent->get_engine().get_voices(); result.first=languages.end(); result.second=voices.end(); if(!markup_info.language_criteria.empty()) { language_list::const_iterator requested_language=std::find_if(languages.begin(),languages.end(),markup_info.language_criteria); if(requested_language!=languages.end()) result.first=requested_language; } if(!markup_info.voice_criteria.empty()) { voice_search_criteria voice_criteria=markup_info.voice_criteria; if(result.first!=languages.end()) voice_criteria.set_language(*(result.first)); voice_list::const_iterator requested_voice=std::find_if(voices.begin(),voices.end(),voice_criteria); if(requested_voice!=voices.end()) { result.first=requested_voice->get_language(); result.second=requested_voice; } } return result; } std::unique_ptr<utterance> sentence::new_utterance() const { const voice_profile& profile=parent->get_voice_profile(); int lang_index=0; if(en_id.is_enabled()) { auto r=en_id.get_result(); if(r>=0) lang_index=r; } std::unique_ptr<utterance> u; const language_list& languages=parent->get_engine().get_languages(); language_list::const_iterator current_language=language_and_voice.first; const voice_list& voices=parent->get_engine().get_voices(); voice_list::const_iterator current_voice=language_and_voice.second; if(current_language==languages.end()) { if(!profile.empty()) current_language=profile.get_voice(lang_index)->get_language(); else current_language=languages.begin(); } if(current_voice==voices.end()) { if(!profile.empty()) { voice_profile::iterator voice_in_profile=profile.voice_for_language(current_language); if(voice_in_profile!=profile.end()) current_voice=*voice_in_profile; } if(current_voice==voices.end()) { voice_search_criteria c; c.set_language(*current_language); c.set_preferred(); current_voice=std::find_if(voices.begin(),voices.end(),c); if(current_voice==voices.end()) { c.clear_preferred(); current_voice=std::find_if(voices.begin(),voices.end(),c); } } } const language& language_ref=current_language->get_instance(); u.reset(new utterance(language_ref)); if(current_voice!=voices.end()) u->set_voice(current_voice->get_instance()); return u; } void sentence::apply_speech_settings(utterance& u) const { u.set_absolute_rate(parent->speech_settings.absolute.rate); u.set_relative_rate(parent->speech_settings.relative.rate*rate); u.set_absolute_pitch(parent->speech_settings.absolute.pitch); u.set_relative_pitch(parent->speech_settings.relative.pitch*pitch); u.set_absolute_volume(parent->speech_settings.absolute.volume); u.set_relative_volume(parent->speech_settings.relative.volume*volume); } void sentence::execute_commands(utterance& u) const { for(auto it=commands.rbegin(); it!=commands.rend(); ++it) { if((*it)->has_text()) { (*it)->set_eos(); break; } } if(initial_break_time_ms>0) { relation& meta_rel=u.get_relation("Meta",true); if(meta_rel.empty()) meta_rel.append(); meta_rel.first().set("initial_break_time",initial_break_time_ms); } for(std::list<command_ptr>::const_iterator it(commands.begin());it!=commands.end();++it) { (*it)->execute(u); } } void sentence::apply_language_processing(utterance& u) const { const language& language_ref=u.get_language(); language_ref.do_text_analysis(u); language_ref.do_pos_tagging(u); language_ref.phrasify(u); language_ref.detect_utt_type(u); language_ref.do_g2p(u); language_ref.syllabify(u); language_ref.insert_pauses(u); language_ref.do_post_lexical_processing(u); language_ref.do_syl_accents(u); language_ref.set_pitch_modifications(u); language_ref.set_duration_modifications(u); } void sentence::apply_verbosity_settings(utterance& u) const { if((parent->verbosity_settings.punctuation_mode!=RHVoice_punctuation_none)|| (parent->verbosity_settings.capitals_mode!=RHVoice_capitals_off)) { relation& tokstruct_rel=u.get_relation("TokStructure"); for(relation::iterator parent_token_iter=tokstruct_rel.begin();parent_token_iter!=tokstruct_rel.end();++parent_token_iter) { if(parent_token_iter->has_feature("emoji")) continue; for(item::iterator token_iter=parent_token_iter->begin();token_iter!=parent_token_iter->end();++token_iter) { const std::string& pos=token_iter->get("pos").as<std::string>(); const std::string& name=token_iter->get("name").as<std::string>(); utf8::uint32_t c=utf8::peek_next(name.begin(),name.end()); if((pos=="sym")&& (((parent->verbosity_settings.punctuation_mode==RHVoice_punctuation_all)&& (str::ispunct(c)||str::issym(c)))|| ((parent->verbosity_settings.punctuation_mode==RHVoice_punctuation_some)&& parent->verbosity_settings.punctuation_list.includes(c)))) token_iter->set<verbosity_t>("verbosity",verbosity_name); if(parent->verbosity_settings.capitals_mode!=RHVoice_capitals_off) { verbosity_t verbosity_level=token_iter->get("verbosity").as<verbosity_t>(); if((verbosity_level&verbosity_spell)&& (!(verbosity_level&verbosity_full_name))&& str::isupper(c)) { if(parent->verbosity_settings.capitals_mode==RHVoice_capitals_pitch) verbosity_level|=verbosity_pitch; else if(parent->verbosity_settings.capitals_mode==RHVoice_capitals_sound) verbosity_level|=verbosity_sound; else verbosity_level|=verbosity_full_name; token_iter->set("verbosity",verbosity_level); } } } } } } void sentence::set_spell_single_symbol(utterance& u) const { relation& tokstruct=u.get_relation("TokStructure"); if(tokstruct.empty()) return; item& token=tokstruct.first(); if(token.has_next()) return; item& subtoken=token.first_child(); if(subtoken.has_next()) return; const std::string& pos=subtoken.get("pos").as<std::string>(); if(pos!="sym") return; verbosity_t level=subtoken.get("verbosity").as<verbosity_t>(); if(level!=verbosity_silent) return; level=verbosity_name; subtoken.set("verbosity",level); } std::unique_ptr<utterance> sentence::create_utterance(sentence_position pos) const { std::unique_ptr<utterance> u=new_utterance(); u->set_bilingual_enabled(parent->enable_bilingual); apply_speech_settings(*u); execute_commands(*u); u->get_language().tokenize(*u); if(pos==sentence_position_single) set_spell_single_symbol(*u); apply_verbosity_settings(*u); apply_language_processing(*u); u->set_quality(parent->quality); u->set_flags(parent->get_flags()); return u; } bool sentence::has_text() const { for(std::list<command_ptr>::const_iterator it(commands.begin());it!=commands.end();++it) { if((*it)->has_text()) return true; } return false; } bool sentence::notify_client() { for(std::list<command_ptr>::const_iterator it(commands.begin());it!=commands.end();++it) { if(!((*it)->notify_client(parent->get_owner()))) return false; } return true; } void document::synthesize() { if(!has_owner()) return; std::unique_ptr<utterance> u; sentence_position pos=sentence_position_initial; for(iterator it(begin());it!=end();++it) { if(!(it->has_text())) { if(it->notify_client()) continue; else return; } const_iterator tmp_it=it; ++tmp_it; if(tmp_it==end()) { if(pos==sentence_position_initial) pos=sentence_position_single; else pos=sentence_position_final; } u=it->create_utterance(pos); if((u.get()!=0)&&(u->has_voice())) if(!(u->get_voice().synthesize(*u,get_owner()))) return; pos=sentence_position_middle; } if(owner->get_supported_events()&event_done) owner->done(); } }