From a8c1934c9d8d3618b78a2a15788f6d3cf5040ca4 Mon Sep 17 00:00:00 2001 From: Dominik Wenger Date: Fri, 4 Jun 2010 21:22:25 +0000 Subject: rbutil: Make TTS and encoders run on all cores \n FS#11160 by Delyan Kratunov git-svn-id: svn://svn.rockbox.org/rockbox/trunk@26558 a1c6a512-1295-4272-9138-f99709370657 --- rbutil/rbutilqt/base/encoders.cpp | 10 +- rbutil/rbutilqt/base/talkgenerator.cpp | 220 ++++++++++++++++++++------------- rbutil/rbutilqt/base/talkgenerator.h | 27 +++- rbutil/rbutilqt/base/ttsbase.h | 12 +- rbutil/rbutilqt/base/ttscarbon.cpp | 4 + rbutil/rbutilqt/base/ttscarbon.h | 2 + rbutil/rbutilqt/base/ttsexes.cpp | 5 + rbutil/rbutilqt/base/ttsexes.h | 1 + rbutil/rbutilqt/base/ttsfestival.cpp | 5 + rbutil/rbutilqt/base/ttsfestival.h | 1 + rbutil/rbutilqt/base/ttssapi.cpp | 13 +- rbutil/rbutilqt/base/ttssapi.h | 1 + 12 files changed, 197 insertions(+), 104 deletions(-) (limited to 'rbutil') diff --git a/rbutil/rbutilqt/base/encoders.cpp b/rbutil/rbutilqt/base/encoders.cpp index 3b9e1432ed..d763b96b4b 100644 --- a/rbutil/rbutilqt/base/encoders.cpp +++ b/rbutil/rbutilqt/base/encoders.cpp @@ -131,7 +131,7 @@ bool EncExes::encode(QString input,QString output) execstring.replace("%options",m_EncOpts); execstring.replace("%input",input); execstring.replace("%output",output); - qDebug() << execstring; + qDebug() << "[EncExes] cmd: " << execstring; int result = QProcess::execute(execstring); return (result == 0) ? true : false; } @@ -197,16 +197,16 @@ bool EncRbSpeex::start() bool EncRbSpeex::encode(QString input,QString output) { - qDebug() << "encoding " << input << " to "<< output; + qDebug() << "[RbSpeex] Encoding " << input << " to "<< output; char errstr[512]; FILE *fin,*fout; if ((fin = fopen(input.toLocal8Bit(), "rb")) == NULL) { - qDebug() << "Error: could not open input file\n"; + qDebug() << "[RbSpeex] Error: could not open input file\n"; return false; } if ((fout = fopen(output.toLocal8Bit(), "wb")) == NULL) { - qDebug() << "Error: could not open output file\n"; + qDebug() << "[RbSpeex] Error: could not open output file\n"; fclose(fin); return false; } @@ -218,7 +218,7 @@ bool EncRbSpeex::encode(QString input,QString output) if (!ret) { /* Attempt to delete unfinished output */ - qDebug() << "Error:" << errstr; + qDebug() << "[RbSpeex] Error:" << errstr; QFile(output).remove(); return false; } diff --git a/rbutil/rbutilqt/base/talkgenerator.cpp b/rbutil/rbutilqt/base/talkgenerator.cpp index 5c0f8e985b..bc7e5f18b0 100644 --- a/rbutil/rbutilqt/base/talkgenerator.cpp +++ b/rbutil/rbutilqt/base/talkgenerator.cpp @@ -22,7 +22,7 @@ #include "systeminfo.h" #include "wavtrim.h" -TalkGenerator::TalkGenerator(QObject* parent): QObject(parent) +TalkGenerator::TalkGenerator(QObject* parent): QObject(parent), encFutureWatcher(this), ttsFutureWatcher(this) { } @@ -31,7 +31,6 @@ TalkGenerator::TalkGenerator(QObject* parent): QObject(parent) //! TalkGenerator::Status TalkGenerator::process(QList* list,int wavtrimth) { - m_abort = false; QString errStr; bool warnings = false; @@ -104,136 +103,179 @@ TalkGenerator::Status TalkGenerator::process(QList* list,int wavtrimt //! TalkGenerator::Status TalkGenerator::voiceList(QList* list,int wavtrimth) { - int progressMax = list->size(); - int m_progress = 0; - emit logProgress(m_progress,progressMax); + emit logProgress(0, list->size()); - QStringList errors; - QStringList dublicates; + QStringList duplicates; - bool warnings = false; + m_ttsWarnings = false; for(int i=0; i < list->size(); i++) { - if(m_abort) - { - emit logItem(tr("Voicing aborted"), LOGERROR); - return eERROR; - } + (*list)[i].refs.tts = m_tts; + (*list)[i].refs.wavtrim = wavtrimth; + (*list)[i].refs.generator = this; - // skip dublicated wav entrys - if(!dublicates.contains(list->at(i).wavfilename)) - dublicates.append(list->at(i).wavfilename); + // skip duplicated wav entries + if(!duplicates.contains(list->at(i).wavfilename)) + duplicates.append(list->at(i).wavfilename); else { - qDebug() << "dublicate skipped"; + qDebug() << "[TalkGen] 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; - } + /* If the engine can't be parallelized, we use only 1 thread */ + int maxThreadCount = QThreadPool::globalInstance()->maxThreadCount(); + if ((m_tts->capabilities() & TTSBase::RunInParallel) == 0) + QThreadPool::globalInstance()->setMaxThreadCount(1); + + connect(&ttsFutureWatcher, SIGNAL(progressValueChanged(int)), + this, SLOT(ttsProgress(int))); + ttsFutureWatcher.setFuture(QtConcurrent::map(*list, &TalkGenerator::ttsEntryPoint)); + + /* We use this loop as an equivalent to ttsFutureWatcher.waitForFinished() + * since the latter blocks all events */ + while(ttsFutureWatcher.isRunning()) + QCoreApplication::processEvents(); + + /* Restore global settings, if we changed them */ + if ((m_tts->capabilities() & TTSBase::RunInParallel) == 0) + QThreadPool::globalInstance()->setMaxThreadCount(maxThreadCount); + + if(ttsFutureWatcher.isCanceled()) + return eERROR; + else if(m_ttsWarnings) + return eWARNING; + else + return eOK; +} - // voice entry +void TalkGenerator::ttsEntryPoint(TalkEntry& entry) +{ + if (!entry.voiced && !entry.toSpeak.isEmpty()) + { QString error; - qDebug() << "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) + qDebug() << "[TalkGen] voicing: " << entry.toSpeak << "to" << entry.wavfilename; + TTSStatus status = entry.refs.tts->voice(entry.toSpeak,entry.wavfilename, &error); + if (status == Warning || status == FatalError) { - warnings = true; - emit logItem(tr("Voicing of %1 failed: %2").arg(list->at(i).toSpeak).arg(error), - LOGWARNING); + entry.refs.generator->ttsFailEntry(entry, status, error); + return; } - else if (status == FatalError) - { - emit logItem(tr("Voicing of %1 failed: %2").arg(list->at(i).toSpeak).arg(error), - LOGERROR); - return eERROR; - } - else - (*list)[i].voiced = true; - - //wavetrim if needed - if(wavtrimth != -1) + if (entry.refs.wavtrim != -1) { char buffer[255]; - wavtrim(list->at(i).wavfilename.toLocal8Bit().data(),wavtrimth,buffer,255); + wavtrim(entry.wavfilename.toLocal8Bit().data(), entry.refs.wavtrim, buffer, 255); } + entry.voiced = true; + } +} - emit logProgress(++m_progress,progressMax); - QCoreApplication::processEvents(); +void TalkGenerator::ttsFailEntry(const TalkEntry& entry, TTSStatus status, QString error) +{ + if(status == Warning) + { + m_ttsWarnings = true; + emit logItem(tr("Voicing of %1 failed: %2").arg(entry.toSpeak).arg(error), + LOGWARNING); + } + else if (status == FatalError) + { + emit logItem(tr("Voicing of %1 failed: %2").arg(entry.toSpeak).arg(error), + LOGERROR); + abort(); } - if(warnings) - return eWARNING; - else - return eOK; } +void TalkGenerator::ttsProgress(int value) +{ + emit logProgress(value,ttsFutureWatcher.progressMaximum()); +} //! \brief Encodes a List of strings //! TalkGenerator::Status TalkGenerator::encodeList(QList* list) { - QStringList dublicates; + QStringList duplicates; - int progressMax = list->size(); - int m_progress = 0; - emit logProgress(m_progress,progressMax); + int itemsCount = list->size(); + emit logProgress(0, itemsCount); - for(int i=0; i < list->size(); i++) + /* Do some preprocessing and remove entries that have not been voiced. */ + for (int idx=0; idx < itemsCount; idx++) { - if(m_abort) - { - emit logItem(tr("Encoding aborted"), LOGERROR); - return eERROR; - } - - //skip non-voiced entrys - if(list->at(i).voiced == false) + if(list->at(idx).voiced == false) { - qDebug() << "non voiced entry" << list->at(i).toSpeak <<"detected"; - emit logProgress(++m_progress,progressMax); + qDebug() << "[TalkGen] unvoiced entry" << list->at(idx).toSpeak <<"detected"; + list->removeAt(idx); + itemsCount--; + idx--; continue; } - //skip dublicates - if(!dublicates.contains(list->at(i).talkfilename)) - dublicates.append(list->at(i).talkfilename); - else + if(duplicates.contains(list->at(idx).talkfilename)) { - qDebug() << "dublicate skipped"; - (*list)[i].encoded = true; - emit logProgress(++m_progress,progressMax); + (*list)[idx].encoded = true; /* make sure we skip this entry */ continue; } + duplicates.append(list->at(idx).talkfilename); + (*list)[idx].refs.encoder = m_enc; + (*list)[idx].refs.generator = this; /* not really needed, unless we end up + voicing and encoding with two different + TalkGenerators.*/ + } - //encode entry - qDebug() << "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(list->at(i).wavfilename), LOGERROR); - return eERROR; + connect(&encFutureWatcher, SIGNAL(progressValueChanged(int)), + this, SLOT(encProgress(int))); + encFutureWatcher.setFuture(QtConcurrent::map(*list, &TalkGenerator::encEntryPoint)); + + /* We use this loop as an equivalent to encFutureWatcher.waitForFinished() + * since the latter blocks all events */ + while (encFutureWatcher.isRunning()) + QCoreApplication::processEvents(QEventLoop::AllEvents); + + if (encFutureWatcher.isCanceled()) + return eERROR; + else + return eOK; +} + +void TalkGenerator::encEntryPoint(TalkEntry& entry) +{ + if(!entry.encoded) + { + bool res = entry.refs.encoder->encode(entry.wavfilename, entry.talkfilename); + entry.encoded = res; + if (!entry.encoded) + entry.refs.generator->encFailEntry(entry); } - (*list)[i].encoded = true; - emit logProgress(++m_progress,progressMax); - QCoreApplication::processEvents(); - } - return eOK; + return; +} + +void TalkGenerator::encProgress(int value) +{ + emit logProgress(value, encFutureWatcher.progressMaximum()); +} + +void TalkGenerator::encFailEntry(const TalkEntry& entry) +{ + emit logItem(tr("Encoding of %1 failed").arg(entry.wavfilename), LOGERROR); + abort(); } //! \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; + if (ttsFutureWatcher.isRunning()) + { + ttsFutureWatcher.cancel(); + emit logItem(tr("Voicing aborted"), LOGERROR); + } + if (encFutureWatcher.isRunning()) + { + encFutureWatcher.cancel(); + emit logItem(tr("Encoding aborted"), LOGERROR); + } } diff --git a/rbutil/rbutilqt/base/talkgenerator.h b/rbutil/rbutilqt/base/talkgenerator.h index b139c1879b..cca196bc2e 100644 --- a/rbutil/rbutilqt/base/talkgenerator.h +++ b/rbutil/rbutilqt/base/talkgenerator.h @@ -49,14 +49,29 @@ public: QString target; bool voiced; bool encoded; + + /* We need the following members because + * 1) the QtConcurrent entry points are all static methods (and we + * need to communicate with the TalkGenerator) + * 2) we are not guaranteed to go through the list in any + * particular order, so we can't use the progress slot + * for error checking */ + struct + { + EncBase* encoder; + TTSBase* tts; + TalkGenerator* generator; + int wavtrim; + } refs; }; TalkGenerator(QObject* parent); - Status process(QList* list,int wavtrimth = -1); public slots: void abort(); + void encProgress(int value); + void ttsProgress(int value); signals: void done(bool); @@ -64,13 +79,21 @@ signals: void logProgress(int, int); //! set progress bar. private: + QFutureWatcher encFutureWatcher; + QFutureWatcher ttsFutureWatcher; + void encFailEntry(const TalkEntry& entry); + void ttsFailEntry(const TalkEntry& entry, TTSStatus status, QString error); + Status voiceList(QList* list,int wavetrimth); Status encodeList(QList* list); + static void encEntryPoint(TalkEntry& entry); + static void ttsEntryPoint(TalkEntry& entry); + TTSBase* m_tts; EncBase* m_enc; - bool m_abort; + bool m_ttsWarnings; }; diff --git a/rbutil/rbutilqt/base/ttsbase.h b/rbutil/rbutilqt/base/ttsbase.h index 7c5932401f..f04016c85f 100644 --- a/rbutil/rbutilqt/base/ttsbase.h +++ b/rbutil/rbutilqt/base/ttsbase.h @@ -32,11 +32,13 @@ #include "encttssettings.h" enum TTSStatus{ FatalError, NoError, Warning }; - class TTSBase : public EncTtsSettingInterface { Q_OBJECT public: + enum Capability { None = 0, RunInParallel = 1 }; + Q_DECLARE_FLAGS(Capabilities, Capability) + TTSBase(QObject *parent); //! Child class should generate a clip virtual TTSStatus voice(QString text,QString wavfile, QString* errStr) =0; @@ -53,6 +55,8 @@ class TTSBase : public EncTtsSettingInterface //! Chlid class should commit the Settings to permanent storage virtual void saveSettings() = 0; + virtual Capabilities capabilities() = 0; + // static functions static TTSBase* getTTS(QObject* parent,QString ttsname); static QStringList getTTSList(); @@ -65,10 +69,6 @@ class TTSBase : public EncTtsSettingInterface protected: static QMap ttsList; }; - - - - - +Q_DECLARE_OPERATORS_FOR_FLAGS(TTSBase::Capabilities) #endif diff --git a/rbutil/rbutilqt/base/ttscarbon.cpp b/rbutil/rbutilqt/base/ttscarbon.cpp index f2e0b7dba3..63fb5315e3 100644 --- a/rbutil/rbutilqt/base/ttscarbon.cpp +++ b/rbutil/rbutilqt/base/ttscarbon.cpp @@ -34,6 +34,10 @@ TTSCarbon::TTSCarbon(QObject* parent) : TTSBase(parent) { } +TTSBase::Capabilities TTSCarbon::capabilities() +{ + return None; +} bool TTSCarbon::configOk() { diff --git a/rbutil/rbutilqt/base/ttscarbon.h b/rbutil/rbutilqt/base/ttscarbon.h index b2d39047a5..fd5f84849b 100644 --- a/rbutil/rbutilqt/base/ttscarbon.h +++ b/rbutil/rbutilqt/base/ttscarbon.h @@ -53,6 +53,8 @@ class TTSCarbon : public TTSBase //! Chlid class should commit the Settings to permanent storage void saveSettings(); + Capabilities capabilities(); + private: SpeechChannel m_channel; CFStringBuiltInEncodings m_voiceScript; diff --git a/rbutil/rbutilqt/base/ttsexes.cpp b/rbutil/rbutilqt/base/ttsexes.cpp index bd14e2a9ee..1818301220 100644 --- a/rbutil/rbutilqt/base/ttsexes.cpp +++ b/rbutil/rbutilqt/base/ttsexes.cpp @@ -31,6 +31,11 @@ TTSExes::TTSExes(QString name,QObject* parent) : TTSBase(parent) } +TTSBase::Capabilities TTSExes::capabilities() +{ + return RunInParallel; +} + void TTSExes::generateSettings() { QString exepath =RbSettings::subValue(m_name,RbSettings::TtsPath).toString(); diff --git a/rbutil/rbutilqt/base/ttsexes.h b/rbutil/rbutilqt/base/ttsexes.h index c03beb7595..04efb4ce50 100644 --- a/rbutil/rbutilqt/base/ttsexes.h +++ b/rbutil/rbutilqt/base/ttsexes.h @@ -38,6 +38,7 @@ class TTSExes : public TTSBase TTSStatus voice(QString text, QString wavfile, QString *errStr); bool start(QString *errStr); bool stop() {return true;} + Capabilities capabilities(); // for settings void generateSettings(); diff --git a/rbutil/rbutilqt/base/ttsfestival.cpp b/rbutil/rbutilqt/base/ttsfestival.cpp index 7cad16d3dd..7a9c854716 100644 --- a/rbutil/rbutilqt/base/ttsfestival.cpp +++ b/rbutil/rbutilqt/base/ttsfestival.cpp @@ -27,6 +27,11 @@ TTSFestival::~TTSFestival() stop(); } +TTSBase::Capabilities TTSFestival::capabilities() +{ + return RunInParallel; +} + void TTSFestival::generateSettings() { // server path diff --git a/rbutil/rbutilqt/base/ttsfestival.h b/rbutil/rbutilqt/base/ttsfestival.h index 8a687375bc..6c64c61532 100644 --- a/rbutil/rbutilqt/base/ttsfestival.h +++ b/rbutil/rbutilqt/base/ttsfestival.h @@ -42,6 +42,7 @@ class TTSFestival : public TTSBase bool start(QString *errStr); bool stop(); TTSStatus voice(QString text,QString wavfile, QString *errStr); + Capabilities capabilities(); // for settings bool configOk(); diff --git a/rbutil/rbutilqt/base/ttssapi.cpp b/rbutil/rbutilqt/base/ttssapi.cpp index 4f69de56ae..36ce16826f 100644 --- a/rbutil/rbutilqt/base/ttssapi.cpp +++ b/rbutil/rbutilqt/base/ttssapi.cpp @@ -30,6 +30,11 @@ TTSSapi::TTSSapi(QObject* parent) : TTSBase(parent) m_sapi4 =false; } +TTSBase::Capabilities TTSSapi::capabilities() +{ + return None; +} + void TTSSapi::generateSettings() { // language @@ -195,13 +200,17 @@ TTSStatus TTSSapi::voice(QString text,QString wavfile, QString *errStr) *voicestream << query; *voicestream << "SYNC\tbla\r\n"; voicestream->flush(); - voicescript->waitForReadyRead(); + char temp[20]; + + //we use this, because waitForReadyRead doesnt work from a different thread + while( voicescript->readLine(temp,20) == 0) + QCoreApplication::processEvents(); + return NoError; } bool TTSSapi::stop() { - *voicestream << "QUIT\r\n"; voicestream->flush(); voicescript->waitForFinished(); diff --git a/rbutil/rbutilqt/base/ttssapi.h b/rbutil/rbutilqt/base/ttssapi.h index 531f25679c..6070728a79 100644 --- a/rbutil/rbutilqt/base/ttssapi.h +++ b/rbutil/rbutilqt/base/ttssapi.h @@ -42,6 +42,7 @@ class TTSSapi : public TTSBase TTSStatus voice(QString text,QString wavfile, QString *errStr); bool start(QString *errStr); bool stop(); + Capabilities capabilities(); // for settings bool configOk(); -- cgit v1.2.3