summaryrefslogtreecommitdiffstats
path: root/rbutil
diff options
context:
space:
mode:
authorDominik Wenger <domonoky@googlemail.com>2010-06-04 21:22:25 +0000
committerDominik Wenger <domonoky@googlemail.com>2010-06-04 21:22:25 +0000
commita8c1934c9d8d3618b78a2a15788f6d3cf5040ca4 (patch)
tree4d0b742e0cae5230529f9bdb933321f3b9336347 /rbutil
parent081bda8ab258c763e654067740f365be68269340 (diff)
downloadrockbox-a8c1934c9d8d3618b78a2a15788f6d3cf5040ca4.tar.gz
rockbox-a8c1934c9d8d3618b78a2a15788f6d3cf5040ca4.tar.bz2
rockbox-a8c1934c9d8d3618b78a2a15788f6d3cf5040ca4.zip
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
Diffstat (limited to 'rbutil')
-rw-r--r--rbutil/rbutilqt/base/encoders.cpp10
-rw-r--r--rbutil/rbutilqt/base/talkgenerator.cpp220
-rw-r--r--rbutil/rbutilqt/base/talkgenerator.h27
-rw-r--r--rbutil/rbutilqt/base/ttsbase.h12
-rw-r--r--rbutil/rbutilqt/base/ttscarbon.cpp4
-rw-r--r--rbutil/rbutilqt/base/ttscarbon.h2
-rw-r--r--rbutil/rbutilqt/base/ttsexes.cpp5
-rw-r--r--rbutil/rbutilqt/base/ttsexes.h1
-rw-r--r--rbutil/rbutilqt/base/ttsfestival.cpp5
-rw-r--r--rbutil/rbutilqt/base/ttsfestival.h1
-rw-r--r--rbutil/rbutilqt/base/ttssapi.cpp13
-rw-r--r--rbutil/rbutilqt/base/ttssapi.h1
12 files changed, 197 insertions, 104 deletions
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<TalkEntry>* list,int wavtrimth)
{
- m_abort = false;
QString errStr;
bool warnings = false;
@@ -104,136 +103,179 @@ TalkGenerator::Status TalkGenerator::process(QList<TalkEntry>* list,int wavtrimt
//!
TalkGenerator::Status TalkGenerator::voiceList(QList<TalkEntry>* 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<TalkEntry>* 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<TalkEntry>* 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<void> encFutureWatcher;
+ QFutureWatcher<void> ttsFutureWatcher;
+ void encFailEntry(const TalkEntry& entry);
+ void ttsFailEntry(const TalkEntry& entry, TTSStatus status, QString error);
+
Status voiceList(QList<TalkEntry>* list,int wavetrimth);
Status encodeList(QList<TalkEntry>* 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<QString,QString> 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();