forked from len0rd/rockbox
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:
parent
7f2defc453
commit
4f56b50df4
10 changed files with 119 additions and 8 deletions
|
|
@ -48,6 +48,7 @@ const static struct {
|
||||||
#else
|
#else
|
||||||
{ RbSettings::Tts, "tts", "espeak" },
|
{ RbSettings::Tts, "tts", "espeak" },
|
||||||
#endif
|
#endif
|
||||||
|
{ RbSettings::UseTtsCorrections, "use_tts_corrections", "true" },
|
||||||
{ RbSettings::LastTalkedFolder, "last_talked_folder", "" },
|
{ RbSettings::LastTalkedFolder, "last_talked_folder", "" },
|
||||||
{ RbSettings::VoiceLanguage, "voicelanguage", "" },
|
{ RbSettings::VoiceLanguage, "voicelanguage", "" },
|
||||||
{ RbSettings::TtsLanguage, ":tts:/language", "" },
|
{ RbSettings::TtsLanguage, ":tts:/language", "" },
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ class RbSettings : public QObject
|
||||||
Platform,
|
Platform,
|
||||||
Language,
|
Language,
|
||||||
Tts,
|
Tts,
|
||||||
|
UseTtsCorrections,
|
||||||
LastTalkedFolder,
|
LastTalkedFolder,
|
||||||
VoiceLanguage,
|
VoiceLanguage,
|
||||||
TtsLanguage,
|
TtsLanguage,
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ bool TalkFileCreator::createTalkFiles()
|
||||||
// generate entries
|
// generate entries
|
||||||
{
|
{
|
||||||
TalkGenerator generator(this);
|
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(done(bool)),this,SIGNAL(done(bool)));
|
||||||
connect(&generator,SIGNAL(logItem(QString,int)),this,SIGNAL(logItem(QString,int)));
|
connect(&generator,SIGNAL(logItem(QString,int)),this,SIGNAL(logItem(QString,int)));
|
||||||
connect(&generator,SIGNAL(logProgress(int,int)),this,SIGNAL(logProgress(int,int)));
|
connect(&generator,SIGNAL(logProgress(int,int)),this,SIGNAL(logProgress(int,int)));
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
TalkGenerator::TalkGenerator(QObject* parent): QObject(parent), encFutureWatcher(this), ttsFutureWatcher(this)
|
TalkGenerator::TalkGenerator(QObject* parent): QObject(parent), encFutureWatcher(this), ttsFutureWatcher(this)
|
||||||
{
|
{
|
||||||
m_userAborted = false;
|
m_userAborted = false;
|
||||||
|
m_lang = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
//! \brief Creates Talkfiles.
|
//! \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.tts = m_tts;
|
||||||
(*list)[i].refs.wavtrim = wavtrimth;
|
(*list)[i].refs.wavtrim = wavtrimth;
|
||||||
(*list)[i].refs.generator = this;
|
(*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
|
// skip duplicated wav entries
|
||||||
if(!duplicates.contains(list->at(i).wavfilename))
|
if(!duplicates.contains(list->at(i).wavfilename))
|
||||||
|
|
@ -247,7 +253,7 @@ TalkGenerator::Status TalkGenerator::encodeList(QList<TalkEntry>* list)
|
||||||
TalkGenerators.*/
|
TalkGenerators.*/
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(&encFutureWatcher, SIGNAL(progressValueChanged(int)),
|
connect(&encFutureWatcher, SIGNAL(progressValueChanged(int)),
|
||||||
this, SLOT(encProgress(int)));
|
this, SLOT(encProgress(int)));
|
||||||
encFutureWatcher.setFuture(QtConcurrent::map(*list, &TalkGenerator::encEntryPoint));
|
encFutureWatcher.setFuture(QtConcurrent::map(*list, &TalkGenerator::encEntryPoint));
|
||||||
|
|
||||||
|
|
@ -302,3 +308,76 @@ void TalkGenerator::abort()
|
||||||
m_userAborted = true;
|
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();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,11 +67,13 @@ public:
|
||||||
|
|
||||||
TalkGenerator(QObject* parent);
|
TalkGenerator(QObject* parent);
|
||||||
Status process(QList<TalkEntry>* list,int wavtrimth = -1);
|
Status process(QList<TalkEntry>* list,int wavtrimth = -1);
|
||||||
|
QString correctString(QString s);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void abort();
|
void abort();
|
||||||
void encProgress(int value);
|
void encProgress(int value);
|
||||||
void ttsProgress(int value);
|
void ttsProgress(int value);
|
||||||
|
void setLang(QString name);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void done(bool);
|
void done(bool);
|
||||||
|
|
@ -95,6 +97,15 @@ private:
|
||||||
|
|
||||||
bool m_ttsWarnings;
|
bool m_ttsWarnings;
|
||||||
bool m_userAborted;
|
bool m_userAborted;
|
||||||
|
QString m_lang;
|
||||||
|
|
||||||
|
struct CorrectionItems
|
||||||
|
{
|
||||||
|
QString search;
|
||||||
|
QString replace;
|
||||||
|
QString modifier;
|
||||||
|
};
|
||||||
|
QList<struct CorrectionItems> m_corrections;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,7 @@ void VoiceFileCreator::downloadDone(bool error)
|
||||||
QString id, voice;
|
QString id, voice;
|
||||||
bool idfound = false;
|
bool idfound = false;
|
||||||
bool voicefound=false;
|
bool voicefound=false;
|
||||||
|
bool useCorrection = RbSettings::value(RbSettings::UseTtsCorrections).toBool();
|
||||||
while (!in.atEnd())
|
while (!in.atEnd())
|
||||||
{
|
{
|
||||||
QString line = in.readLine();
|
QString line = in.readLine();
|
||||||
|
|
@ -151,7 +152,8 @@ void VoiceFileCreator::downloadDone(bool error)
|
||||||
TalkGenerator::TalkEntry entry;
|
TalkGenerator::TalkEntry entry;
|
||||||
entry.toSpeak = voice;
|
entry.toSpeak = voice;
|
||||||
entry.wavfilename = m_path + "/" + id + ".wav";
|
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.voiced = false;
|
||||||
entry.encoded = false;
|
entry.encoded = false;
|
||||||
if(id == "VOICE_PAUSE")
|
if(id == "VOICE_PAUSE")
|
||||||
|
|
@ -178,6 +180,9 @@ void VoiceFileCreator::downloadDone(bool error)
|
||||||
// generate files
|
// generate files
|
||||||
{
|
{
|
||||||
TalkGenerator generator(this);
|
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(done(bool)),this,SIGNAL(done(bool)));
|
||||||
connect(&generator,SIGNAL(logItem(QString,int)),this,SIGNAL(logItem(QString,int)));
|
connect(&generator,SIGNAL(logItem(QString,int)),this,SIGNAL(logItem(QString,int)));
|
||||||
connect(&generator,SIGNAL(logProgress(int,int)),this,SIGNAL(logProgress(int,int)));
|
connect(&generator,SIGNAL(logProgress(int,int)),this,SIGNAL(logProgress(int,int)));
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ public:
|
||||||
bool createVoiceFile();
|
bool createVoiceFile();
|
||||||
|
|
||||||
void setMountPoint(QString mountpoint) {m_mountpoint =mountpoint; }
|
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;}
|
void setWavtrimThreshold(int th){m_wavtrimThreshold = th;}
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
@ -56,8 +56,9 @@ private slots:
|
||||||
void downloadDone(bool error);
|
void downloadDone(bool error);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void cleanup();
|
void cleanup();
|
||||||
|
|
||||||
HttpGet *getter;
|
HttpGet *getter;
|
||||||
QString filename; //the temporary file
|
QString filename; //the temporary file
|
||||||
QString m_mountpoint; //mountpoint of the device
|
QString m_mountpoint; //mountpoint of the device
|
||||||
|
|
|
||||||
|
|
@ -205,6 +205,7 @@ void Config::accept()
|
||||||
RbSettings::setValue(RbSettings::CacheOffline, ui.cacheOfflineMode->isChecked());
|
RbSettings::setValue(RbSettings::CacheOffline, ui.cacheOfflineMode->isChecked());
|
||||||
|
|
||||||
// tts settings
|
// tts settings
|
||||||
|
RbSettings::setValue(RbSettings::UseTtsCorrections, ui.ttsCorrections->isChecked());
|
||||||
int i = ui.comboTts->currentIndex();
|
int i = ui.comboTts->currentIndex();
|
||||||
RbSettings::setValue(RbSettings::Tts, ui.comboTts->itemData(i).toString());
|
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.cacheDisable->setChecked(RbSettings::value(RbSettings::CacheDisabled).toBool());
|
||||||
ui.cacheOfflineMode->setChecked(RbSettings::value(RbSettings::CacheOffline).toBool());
|
ui.cacheOfflineMode->setChecked(RbSettings::value(RbSettings::CacheOffline).toBool());
|
||||||
updateCacheInfo(RbSettings::value(RbSettings::CachePath).toString());
|
updateCacheInfo(RbSettings::value(RbSettings::CachePath).toString());
|
||||||
|
|
||||||
|
// TTS tab
|
||||||
|
ui.ttsCorrections->setChecked(RbSettings::value(RbSettings::UseTtsCorrections).toBool());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -449,6 +449,13 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="0" colspan="3">
|
||||||
|
<widget class="QCheckBox" name="ttsCorrections">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Use string corrections for TTS</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
<RCC>
|
<RCC>
|
||||||
<qresource prefix="/" >
|
<qresource prefix="/">
|
||||||
<file>../../docs/CREDITS</file>
|
<file>../../docs/CREDITS</file>
|
||||||
<file>../../docs/gpl-2.0.html</file>
|
<file>../../docs/gpl-2.0.html</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource>
|
<qresource>
|
||||||
<file alias="builtin/VOICE_PAUSE.wav">../../tools/VOICE_PAUSE.wav</file>
|
<file alias="builtin/VOICE_PAUSE.wav">../../tools/VOICE_PAUSE.wav</file>
|
||||||
|
<file alias="builtin/voice-corrections.txt">../../tools/voice-corrections.txt</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/" >
|
<qresource prefix="/">
|
||||||
<file>icons/audio-input-microphone.png</file>
|
<file>icons/audio-input-microphone.png</file>
|
||||||
<file>icons/bootloader_btn.png</file>
|
<file>icons/bootloader_btn.png</file>
|
||||||
<file>icons/dialog-error.png</file>
|
<file>icons/dialog-error.png</file>
|
||||||
|
|
@ -36,7 +37,7 @@
|
||||||
<file>icons/wizard.jpg</file>
|
<file>icons/wizard.jpg</file>
|
||||||
<file alias="icons/rockbox-clef.svg">../../docs/logo/rockbox-clef.svg</file>
|
<file alias="icons/rockbox-clef.svg">../../docs/logo/rockbox-clef.svg</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/ini" >
|
<qresource prefix="/ini">
|
||||||
<file>rbutil.ini</file>
|
<file>rbutil.ini</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue