rockbox/utils/rbutilqt/base/talkgenerator.cpp
Dominik Riebeling 948637c42c rbutil: Fix TalkGenerator string correction.
- Fix regexp case insensitive option being inverted.
- Pass more objects by reference.
- Some const-ness.

Change-Id: I1c77ad22ab1e505c8dce8c083d9c52e76a6860be
2022-03-23 19:34:30 +01:00

337 lines
9.8 KiB
C++

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2007 by Dominik Wenger
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "talkgenerator.h"
#include "rbsettings.h"
#include "playerbuildinfo.h"
#include "wavtrim.h"
#include "Logger.h"
TalkGenerator::TalkGenerator(QObject* parent): QObject(parent)
{
}
//! \brief Creates Talkfiles.
//!
TalkGenerator::Status TalkGenerator::process(QList<TalkEntry>* list,int wavtrimth)
{
m_abort = false;
QString errStr;
bool warnings = false;
//tts
emit logItem(tr("Starting TTS Engine"), LOGINFO);
m_tts = TTSBase::getTTS(this, RbSettings::value(RbSettings::Tts).toString());
if(!m_tts)
{
LOG_ERROR() << "getting the TTS object failed!";
emit logItem(tr("Init of TTS engine failed"), LOGERROR);
emit done(true);
return eERROR;
}
if(!m_tts->start(&errStr))
{
emit logItem(errStr.trimmed(),LOGERROR);
emit logItem(tr("Init of TTS engine failed"), LOGERROR);
emit done(true);
return eERROR;
}
QCoreApplication::processEvents();
// Encoder
emit logItem(tr("Starting Encoder Engine"),LOGINFO);
m_enc = EncoderBase::getEncoder(this, PlayerBuildInfo::instance()->value(
PlayerBuildInfo::Encoder).toString());
if(!m_enc->start())
{
emit logItem(tr("Init of Encoder engine failed"),LOGERROR);
emit done(true);
m_tts->stop();
return eERROR;
}
QCoreApplication::processEvents();
emit logProgress(0,0);
// Voice entries
emit logItem(tr("Voicing entries..."),LOGINFO);
Status voiceStatus= voiceList(list,wavtrimth);
if(voiceStatus == eERROR)
{
m_tts->stop();
m_enc->stop();
emit done(true);
return eERROR;
}
else if( voiceStatus == eWARNING)
warnings = true;
QCoreApplication::processEvents();
// Encoding Entries
emit logItem(tr("Encoding files..."),LOGINFO);
Status encoderStatus = encodeList(list);
if( encoderStatus == eERROR)
{
m_tts->stop();
m_enc->stop();
emit done(true);
return eERROR;
}
else if( voiceStatus == eWARNING)
warnings = true;
QCoreApplication::processEvents();
m_tts->stop();
m_enc->stop();
emit logProgress(1,1);
if(warnings)
return eWARNING;
return eOK;
}
//! \brief Voices a List of string
//!
TalkGenerator::Status TalkGenerator::voiceList(QList<TalkEntry>* list,int wavtrimth)
{
int progressMax = list->size();
int m_progress = 0;
emit logProgress(m_progress,progressMax);
QStringList errors;
QStringList duplicates;
bool warnings = false;
for(int i=0; i < list->size(); i++)
{
if(m_abort)
{
emit logItem(tr("Voicing aborted"), LOGERROR);
return eERROR;
}
// skip duplicated wav entrys
if(!duplicates.contains(list->at(i).wavfilename))
duplicates.append(list->at(i).wavfilename);
else
{
LOG_INFO() << "duplicate skipped";
(*list)[i].voiced = true;
emit logProgress(++m_progress,progressMax);
continue;
}
// skip already voiced entrys
if(list->at(i).voiced == true)
{
emit logProgress(++m_progress,progressMax);
continue;
}
// skip entry whith empty text
if(list->at(i).toSpeak == "")
{
emit logProgress(++m_progress,progressMax);
continue;
}
// voice entry
QString error;
LOG_INFO() << "voicing: " << list->at(i).toSpeak
<< "to" << list->at(i).wavfilename;
TTSStatus status = m_tts->voice(list->at(i).toSpeak,
list->at(i).wavfilename, &error);
if(status == Warning)
{
warnings = true;
emit logItem(tr("Voicing of %1 failed: %2")
.arg(list->at(i).toSpeak, error), LOGWARNING);
}
else if (status == FatalError)
{
emit logItem(tr("Voicing of %1 failed: %2")
.arg(list->at(i).toSpeak, error), LOGERROR);
return eERROR;
}
else
(*list)[i].voiced = true;
// wavtrim if needed
if(wavtrimth != -1)
{
char buffer[255];
if(wavtrim(list->at(i).wavfilename.toLocal8Bit().data(),
wavtrimth, buffer, 255))
{
LOG_ERROR() << "wavtrim returned error on"
<< list->at(i).wavfilename;
return eERROR;
}
}
emit logProgress(++m_progress,progressMax);
QCoreApplication::processEvents();
}
if(warnings)
return eWARNING;
else
return eOK;
}
//! \brief Encodes a List of strings
//!
TalkGenerator::Status TalkGenerator::encodeList(QList<TalkEntry>* list)
{
QStringList duplicates;
int progressMax = list->size();
int m_progress = 0;
emit logProgress(m_progress,progressMax);
for(int i=0; i < list->size(); i++)
{
if(m_abort)
{
emit logItem(tr("Encoding aborted"), LOGERROR);
return eERROR;
}
//skip non-voiced entrys
if(list->at(i).voiced == false)
{
LOG_WARNING() << "non voiced entry detected:"
<< list->at(i).toSpeak;
emit logProgress(++m_progress,progressMax);
continue;
}
//skip duplicates
if(!duplicates.contains(list->at(i).talkfilename))
duplicates.append(list->at(i).talkfilename);
else
{
LOG_INFO() << "duplicate skipped";
(*list)[i].encoded = true;
emit logProgress(++m_progress,progressMax);
continue;
}
//encode entry
LOG_INFO() << "encoding " << list->at(i).wavfilename
<< "to" << list->at(i).talkfilename;
if(!m_enc->encode(list->at(i).wavfilename,list->at(i).talkfilename))
{
emit logItem(tr("Encoding of %1 failed").arg(
QFileInfo(list->at(i).wavfilename).baseName()), LOGERROR);
return eERROR;
}
(*list)[i].encoded = true;
emit logProgress(++m_progress,progressMax);
QCoreApplication::processEvents();
}
return eOK;
}
//! \brief slot, which is connected to the abort of the Logger.
//Sets a flag, so Creating Talkfiles ends at the next possible position
//!
void TalkGenerator::abort()
{
m_abort = true;
}
QString TalkGenerator::correctString(const QString& s)
{
QString corrected = s;
int i = 0;
int max = m_corrections.size();
while(i < max) {
corrected = corrected.replace(QRegularExpression(m_corrections.at(i).search,
(m_corrections.at(i).modifier.contains("i")
? QRegularExpression::CaseInsensitiveOption
: QRegularExpression::NoPatternOption)
| QRegularExpression::UseUnicodePropertiesOption),
m_corrections.at(i).replace);
i++;
}
if(corrected != s)
LOG_INFO() << "corrected string" << s << "to" << corrected;
return corrected;
}
void TalkGenerator::setLang(const QString& name)
{
m_lang = name;
// re-initialize corrections list
m_corrections.clear();
QFile correctionsFile(":/builtin/voice-corrections.txt");
correctionsFile.open(QIODevice::ReadOnly);
QString engine = RbSettings::value(RbSettings::Tts).toString();
TTSBase* tts = TTSBase::getTTS(this, RbSettings::value(RbSettings::Tts).toString());
if(!tts)
{
LOG_ERROR() << "getting the TTS object failed!";
return;
}
QString vendor = tts->voiceVendor();
delete tts;
if(m_lang.isEmpty())
m_lang = "english";
LOG_INFO() << "building string corrections list for"
<< m_lang << engine << vendor;
QTextStream stream(&correctionsFile);
while(!stream.atEnd()) {
QString line = stream.readLine();
if(line.startsWith(" ") || line.length() < 10)
continue;
// separator is first character
QString separator = line.at(0);
line.remove(0, 1);
QStringList items = line.split(separator);
// we need to have at least 6 separate entries.
if(items.size() < 6)
continue;
QRegularExpression re_lang(items.at(0));
QRegularExpression re_engine(items.at(1));
QRegularExpression re_vendor(items.at(2));
if(!re_lang.match(m_lang).hasMatch()) {
continue;
}
if(!re_vendor.match(vendor).hasMatch()) {
continue;
}
if(!re_engine.match(engine).hasMatch()) {
continue;
}
struct CorrectionItems co;
co.search = items.at(3);
co.replace = items.at(4);
// Qt uses backslash for back references, Perl uses dollar sign.
co.replace.replace(QRegularExpression("\\$(\\d+)"), "\\\\1");
co.modifier = items.at(5);
m_corrections.append(co);
}
correctionsFile.close();
}