Voicefile generation: implement string corrections.

Voicefile generation now can correct strings for the TTS system similar
to what voice.pl does. The current implementation has some limitations:
- only implemented for voicefile creation.
- the corrections file is built in and can't get changed.
- string corrections can be disabled in the configuration dialog.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30628 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Dominik Riebeling 2011-10-01 19:48:58 +00:00
parent 7f2defc453
commit 4f56b50df4
10 changed files with 119 additions and 8 deletions

View file

@ -48,6 +48,7 @@ const static struct {
#else
{ RbSettings::Tts, "tts", "espeak" },
#endif
{ RbSettings::UseTtsCorrections, "use_tts_corrections", "true" },
{ RbSettings::LastTalkedFolder, "last_talked_folder", "" },
{ RbSettings::VoiceLanguage, "voicelanguage", "" },
{ RbSettings::TtsLanguage, ":tts:/language", "" },

View file

@ -42,6 +42,7 @@ class RbSettings : public QObject
Platform,
Language,
Tts,
UseTtsCorrections,
LastTalkedFolder,
VoiceLanguage,
TtsLanguage,

View file

@ -50,6 +50,7 @@ bool TalkFileCreator::createTalkFiles()
// generate entries
{
TalkGenerator generator(this);
// no string corrections yet: do not set language for TalkGenerator.
connect(&generator,SIGNAL(done(bool)),this,SIGNAL(done(bool)));
connect(&generator,SIGNAL(logItem(QString,int)),this,SIGNAL(logItem(QString,int)));
connect(&generator,SIGNAL(logProgress(int,int)),this,SIGNAL(logProgress(int,int)));

View file

@ -25,6 +25,7 @@
TalkGenerator::TalkGenerator(QObject* parent): QObject(parent), encFutureWatcher(this), ttsFutureWatcher(this)
{
m_userAborted = false;
m_lang = "";
}
//! \brief Creates Talkfiles.
@ -113,6 +114,11 @@ TalkGenerator::Status TalkGenerator::voiceList(QList<TalkEntry>* list,int wavtri
(*list)[i].refs.tts = m_tts;
(*list)[i].refs.wavtrim = wavtrimth;
(*list)[i].refs.generator = this;
// enable voice corrections only if a language is set.
if(!m_lang.isEmpty()) {
QString s = (*list)[i].toSpeak;
(*list)[i].toSpeak = correctString(s);
}
// skip duplicated wav entries
if(!duplicates.contains(list->at(i).wavfilename))
@ -247,7 +253,7 @@ TalkGenerator::Status TalkGenerator::encodeList(QList<TalkEntry>* list)
TalkGenerators.*/
}
connect(&encFutureWatcher, SIGNAL(progressValueChanged(int)),
connect(&encFutureWatcher, SIGNAL(progressValueChanged(int)),
this, SLOT(encProgress(int)));
encFutureWatcher.setFuture(QtConcurrent::map(*list, &TalkGenerator::encEntryPoint));
@ -302,3 +308,76 @@ void TalkGenerator::abort()
m_userAborted = true;
}
QString TalkGenerator::correctString(QString s)
{
QString corrected = s;
int i = 0;
int max = m_corrections.size();
while(i < max) {
corrected = corrected.replace(QRegExp(m_corrections.at(i).search,
m_corrections.at(i).modifier.contains("i")
? Qt::CaseInsensitive : Qt::CaseSensitive),
m_corrections.at(i).replace);
i++;
}
if(corrected != s)
qDebug() << "[VoiceFileCreator] corrected string" << s << "to" << corrected;
return corrected;
}
void TalkGenerator::setLang(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());
QString vendor = tts->voiceVendor();
delete tts;
if(m_lang.isEmpty())
m_lang = "english";
qDebug() << "[TalkGenerator] 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;
QRegExp re_lang(items.at(0));
QRegExp re_engine(items.at(1));
QRegExp re_vendor(items.at(2));
if(!re_lang.exactMatch(m_lang)) {
continue;
}
if(!re_vendor.exactMatch(vendor)) {
continue;
}
if(!re_engine.exactMatch(engine)) {
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(QRegExp("\\$(\\d+)"), "\\\\1");
co.modifier = items.at(5);
m_corrections.append(co);
}
correctionsFile.close();
}

View file

@ -67,11 +67,13 @@ public:
TalkGenerator(QObject* parent);
Status process(QList<TalkEntry>* list,int wavtrimth = -1);
QString correctString(QString s);
public slots:
void abort();
void encProgress(int value);
void ttsProgress(int value);
void setLang(QString name);
signals:
void done(bool);
@ -95,6 +97,15 @@ private:
bool m_ttsWarnings;
bool m_userAborted;
QString m_lang;
struct CorrectionItems
{
QString search;
QString replace;
QString modifier;
};
QList<struct CorrectionItems> m_corrections;
};

View file

@ -132,6 +132,7 @@ void VoiceFileCreator::downloadDone(bool error)
QString id, voice;
bool idfound = false;
bool voicefound=false;
bool useCorrection = RbSettings::value(RbSettings::UseTtsCorrections).toBool();
while (!in.atEnd())
{
QString line = in.readLine();
@ -151,7 +152,8 @@ void VoiceFileCreator::downloadDone(bool error)
TalkGenerator::TalkEntry entry;
entry.toSpeak = voice;
entry.wavfilename = m_path + "/" + id + ".wav";
entry.talkfilename = m_path + "/" + id + ".mp3"; //voicefont wants them with .mp3 extension
//voicefont wants them with .mp3 extension
entry.talkfilename = m_path + "/" + id + ".mp3";
entry.voiced = false;
entry.encoded = false;
if(id == "VOICE_PAUSE")
@ -178,6 +180,9 @@ void VoiceFileCreator::downloadDone(bool error)
// generate files
{
TalkGenerator generator(this);
// set language for string correction. If not set no correction will be made.
if(useCorrection)
generator.setLang(m_lang);
connect(&generator,SIGNAL(done(bool)),this,SIGNAL(done(bool)));
connect(&generator,SIGNAL(logItem(QString,int)),this,SIGNAL(logItem(QString,int)));
connect(&generator,SIGNAL(logProgress(int,int)),this,SIGNAL(logProgress(int,int)));

View file

@ -40,7 +40,7 @@ public:
bool createVoiceFile();
void setMountPoint(QString mountpoint) {m_mountpoint =mountpoint; }
void setLang(QString name){m_lang =name;}
void setLang(QString name) { m_lang = name; }
void setWavtrimThreshold(int th){m_wavtrimThreshold = th;}
public slots:
@ -56,8 +56,9 @@ private slots:
void downloadDone(bool error);
private:
void cleanup();
HttpGet *getter;
QString filename; //the temporary file
QString m_mountpoint; //mountpoint of the device

View file

@ -205,6 +205,7 @@ void Config::accept()
RbSettings::setValue(RbSettings::CacheOffline, ui.cacheOfflineMode->isChecked());
// tts settings
RbSettings::setValue(RbSettings::UseTtsCorrections, ui.ttsCorrections->isChecked());
int i = ui.comboTts->currentIndex();
RbSettings::setValue(RbSettings::Tts, ui.comboTts->itemData(i).toString());
@ -288,6 +289,9 @@ void Config::setUserSettings()
ui.cacheDisable->setChecked(RbSettings::value(RbSettings::CacheDisabled).toBool());
ui.cacheOfflineMode->setChecked(RbSettings::value(RbSettings::CacheOffline).toBool());
updateCacheInfo(RbSettings::value(RbSettings::CachePath).toString());
// TTS tab
ui.ttsCorrections->setChecked(RbSettings::value(RbSettings::UseTtsCorrections).toBool());
}

View file

@ -449,6 +449,13 @@
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<widget class="QCheckBox" name="ttsCorrections">
<property name="text">
<string>&amp;Use string corrections for TTS</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View file

@ -1,12 +1,13 @@
<RCC>
<qresource prefix="/" >
<qresource prefix="/">
<file>../../docs/CREDITS</file>
<file>../../docs/gpl-2.0.html</file>
</qresource>
<qresource>
<qresource>
<file alias="builtin/VOICE_PAUSE.wav">../../tools/VOICE_PAUSE.wav</file>
<file alias="builtin/voice-corrections.txt">../../tools/voice-corrections.txt</file>
</qresource>
<qresource prefix="/" >
<qresource prefix="/">
<file>icons/audio-input-microphone.png</file>
<file>icons/bootloader_btn.png</file>
<file>icons/dialog-error.png</file>
@ -36,7 +37,7 @@
<file>icons/wizard.jpg</file>
<file alias="icons/rockbox-clef.svg">../../docs/logo/rockbox-clef.svg</file>
</qresource>
<qresource prefix="/ini" >
<qresource prefix="/ini">
<file>rbutil.ini</file>
</qresource>
</RCC>