1
0
Fork 0
forked from len0rd/rockbox

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
This commit is contained in:
Dominik Wenger 2010-06-04 21:22:25 +00:00
parent 081bda8ab2
commit a8c1934c9d
12 changed files with 204 additions and 111 deletions

View file

@ -131,7 +131,7 @@ bool EncExes::encode(QString input,QString output)
execstring.replace("%options",m_EncOpts); execstring.replace("%options",m_EncOpts);
execstring.replace("%input",input); execstring.replace("%input",input);
execstring.replace("%output",output); execstring.replace("%output",output);
qDebug() << execstring; qDebug() << "[EncExes] cmd: " << execstring;
int result = QProcess::execute(execstring); int result = QProcess::execute(execstring);
return (result == 0) ? true : false; return (result == 0) ? true : false;
} }
@ -197,16 +197,16 @@ bool EncRbSpeex::start()
bool EncRbSpeex::encode(QString input,QString output) bool EncRbSpeex::encode(QString input,QString output)
{ {
qDebug() << "encoding " << input << " to "<< output; qDebug() << "[RbSpeex] Encoding " << input << " to "<< output;
char errstr[512]; char errstr[512];
FILE *fin,*fout; FILE *fin,*fout;
if ((fin = fopen(input.toLocal8Bit(), "rb")) == NULL) { 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; return false;
} }
if ((fout = fopen(output.toLocal8Bit(), "wb")) == NULL) { 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); fclose(fin);
return false; return false;
} }
@ -218,7 +218,7 @@ bool EncRbSpeex::encode(QString input,QString output)
if (!ret) { if (!ret) {
/* Attempt to delete unfinished output */ /* Attempt to delete unfinished output */
qDebug() << "Error:" << errstr; qDebug() << "[RbSpeex] Error:" << errstr;
QFile(output).remove(); QFile(output).remove();
return false; return false;
} }

View file

@ -22,7 +22,7 @@
#include "systeminfo.h" #include "systeminfo.h"
#include "wavtrim.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) TalkGenerator::Status TalkGenerator::process(QList<TalkEntry>* list,int wavtrimth)
{ {
m_abort = false;
QString errStr; QString errStr;
bool warnings = false; 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) TalkGenerator::Status TalkGenerator::voiceList(QList<TalkEntry>* list,int wavtrimth)
{ {
int progressMax = list->size(); emit logProgress(0, list->size());
int m_progress = 0;
emit logProgress(m_progress,progressMax);
QStringList errors; QStringList duplicates;
QStringList dublicates;
bool warnings = false; m_ttsWarnings = false;
for(int i=0; i < list->size(); i++) for(int i=0; i < list->size(); i++)
{ {
if(m_abort) (*list)[i].refs.tts = m_tts;
{ (*list)[i].refs.wavtrim = wavtrimth;
emit logItem(tr("Voicing aborted"), LOGERROR); (*list)[i].refs.generator = this;
return eERROR;
}
// skip dublicated wav entrys // skip duplicated wav entries
if(!dublicates.contains(list->at(i).wavfilename)) if(!duplicates.contains(list->at(i).wavfilename))
dublicates.append(list->at(i).wavfilename); duplicates.append(list->at(i).wavfilename);
else else
{ {
qDebug() << "dublicate skipped"; qDebug() << "[TalkGen] duplicate skipped";
(*list)[i].voiced = true; (*list)[i].voiced = true;
emit logProgress(++m_progress,progressMax);
continue; 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;
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)
{
warnings = true;
emit logItem(tr("Voicing of %1 failed: %2").arg(list->at(i).toSpeak).arg(error),
LOGWARNING);
}
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)
{
char buffer[255];
wavtrim(list->at(i).wavfilename.toLocal8Bit().data(),wavtrimth,buffer,255);
}
emit logProgress(++m_progress,progressMax);
QCoreApplication::processEvents();
} }
if(warnings)
/* 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; return eWARNING;
else else
return eOK; return eOK;
}
void TalkGenerator::ttsEntryPoint(TalkEntry& entry)
{
if (!entry.voiced && !entry.toSpeak.isEmpty())
{
QString error;
qDebug() << "[TalkGen] voicing: " << entry.toSpeak << "to" << entry.wavfilename;
TTSStatus status = entry.refs.tts->voice(entry.toSpeak,entry.wavfilename, &error);
if (status == Warning || status == FatalError)
{
entry.refs.generator->ttsFailEntry(entry, status, error);
return;
}
if (entry.refs.wavtrim != -1)
{
char buffer[255];
wavtrim(entry.wavfilename.toLocal8Bit().data(), entry.refs.wavtrim, buffer, 255);
}
entry.voiced = true;
}
} }
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();
}
}
void TalkGenerator::ttsProgress(int value)
{
emit logProgress(value,ttsFutureWatcher.progressMaximum());
}
//! \brief Encodes a List of strings //! \brief Encodes a List of strings
//! //!
TalkGenerator::Status TalkGenerator::encodeList(QList<TalkEntry>* list) TalkGenerator::Status TalkGenerator::encodeList(QList<TalkEntry>* list)
{ {
QStringList dublicates; QStringList duplicates;
int progressMax = list->size(); int itemsCount = list->size();
int m_progress = 0; emit logProgress(0, itemsCount);
emit logProgress(m_progress,progressMax);
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) if(list->at(idx).voiced == false)
{ {
emit logItem(tr("Encoding aborted"), LOGERROR); qDebug() << "[TalkGen] unvoiced entry" << list->at(idx).toSpeak <<"detected";
return eERROR; list->removeAt(idx);
} itemsCount--;
idx--;
//skip non-voiced entrys
if(list->at(i).voiced == false)
{
qDebug() << "non voiced entry" << list->at(i).toSpeak <<"detected";
emit logProgress(++m_progress,progressMax);
continue; continue;
} }
//skip dublicates if(duplicates.contains(list->at(idx).talkfilename))
if(!dublicates.contains(list->at(i).talkfilename))
dublicates.append(list->at(i).talkfilename);
else
{ {
qDebug() << "dublicate skipped"; (*list)[idx].encoded = true; /* make sure we skip this entry */
(*list)[i].encoded = true;
emit logProgress(++m_progress,progressMax);
continue; continue;
} }
duplicates.append(list->at(idx).talkfilename);
//encode entry (*list)[idx].refs.encoder = m_enc;
qDebug() << "encoding " << list->at(i).wavfilename << "to" << list->at(i).talkfilename; (*list)[idx].refs.generator = this; /* not really needed, unless we end up
if(!m_enc->encode(list->at(i).wavfilename,list->at(i).talkfilename)) voicing and encoding with two different
{ TalkGenerators.*/
emit logItem(tr("Encoding of %1 failed").arg(list->at(i).wavfilename), LOGERROR);
return eERROR;
}
(*list)[i].encoded = true;
emit logProgress(++m_progress,progressMax);
QCoreApplication::processEvents();
} }
return eOK;
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);
}
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 //! \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() 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);
}
} }

View file

@ -49,14 +49,29 @@ public:
QString target; QString target;
bool voiced; bool voiced;
bool encoded; 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); TalkGenerator(QObject* parent);
Status process(QList<TalkEntry>* list,int wavtrimth = -1); Status process(QList<TalkEntry>* list,int wavtrimth = -1);
public slots: public slots:
void abort(); void abort();
void encProgress(int value);
void ttsProgress(int value);
signals: signals:
void done(bool); void done(bool);
@ -64,13 +79,21 @@ signals:
void logProgress(int, int); //! set progress bar. void logProgress(int, int); //! set progress bar.
private: 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 voiceList(QList<TalkEntry>* list,int wavetrimth);
Status encodeList(QList<TalkEntry>* list); Status encodeList(QList<TalkEntry>* list);
static void encEntryPoint(TalkEntry& entry);
static void ttsEntryPoint(TalkEntry& entry);
TTSBase* m_tts; TTSBase* m_tts;
EncBase* m_enc; EncBase* m_enc;
bool m_abort; bool m_ttsWarnings;
}; };

View file

@ -32,11 +32,13 @@
#include "encttssettings.h" #include "encttssettings.h"
enum TTSStatus{ FatalError, NoError, Warning }; enum TTSStatus{ FatalError, NoError, Warning };
class TTSBase : public EncTtsSettingInterface class TTSBase : public EncTtsSettingInterface
{ {
Q_OBJECT Q_OBJECT
public: public:
enum Capability { None = 0, RunInParallel = 1 };
Q_DECLARE_FLAGS(Capabilities, Capability)
TTSBase(QObject *parent); TTSBase(QObject *parent);
//! Child class should generate a clip //! Child class should generate a clip
virtual TTSStatus voice(QString text,QString wavfile, QString* errStr) =0; 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 //! Chlid class should commit the Settings to permanent storage
virtual void saveSettings() = 0; virtual void saveSettings() = 0;
virtual Capabilities capabilities() = 0;
// static functions // static functions
static TTSBase* getTTS(QObject* parent,QString ttsname); static TTSBase* getTTS(QObject* parent,QString ttsname);
static QStringList getTTSList(); static QStringList getTTSList();
@ -65,10 +69,6 @@ class TTSBase : public EncTtsSettingInterface
protected: protected:
static QMap<QString,QString> ttsList; static QMap<QString,QString> ttsList;
}; };
Q_DECLARE_OPERATORS_FOR_FLAGS(TTSBase::Capabilities)
#endif #endif

View file

@ -34,6 +34,10 @@ TTSCarbon::TTSCarbon(QObject* parent) : TTSBase(parent)
{ {
} }
TTSBase::Capabilities TTSCarbon::capabilities()
{
return None;
}
bool TTSCarbon::configOk() bool TTSCarbon::configOk()
{ {

View file

@ -53,6 +53,8 @@ class TTSCarbon : public TTSBase
//! Chlid class should commit the Settings to permanent storage //! Chlid class should commit the Settings to permanent storage
void saveSettings(); void saveSettings();
Capabilities capabilities();
private: private:
SpeechChannel m_channel; SpeechChannel m_channel;
CFStringBuiltInEncodings m_voiceScript; CFStringBuiltInEncodings m_voiceScript;

View file

@ -31,6 +31,11 @@ TTSExes::TTSExes(QString name,QObject* parent) : TTSBase(parent)
} }
TTSBase::Capabilities TTSExes::capabilities()
{
return RunInParallel;
}
void TTSExes::generateSettings() void TTSExes::generateSettings()
{ {
QString exepath =RbSettings::subValue(m_name,RbSettings::TtsPath).toString(); QString exepath =RbSettings::subValue(m_name,RbSettings::TtsPath).toString();

View file

@ -38,6 +38,7 @@ class TTSExes : public TTSBase
TTSStatus voice(QString text, QString wavfile, QString *errStr); TTSStatus voice(QString text, QString wavfile, QString *errStr);
bool start(QString *errStr); bool start(QString *errStr);
bool stop() {return true;} bool stop() {return true;}
Capabilities capabilities();
// for settings // for settings
void generateSettings(); void generateSettings();

View file

@ -27,6 +27,11 @@ TTSFestival::~TTSFestival()
stop(); stop();
} }
TTSBase::Capabilities TTSFestival::capabilities()
{
return RunInParallel;
}
void TTSFestival::generateSettings() void TTSFestival::generateSettings()
{ {
// server path // server path

View file

@ -42,6 +42,7 @@ class TTSFestival : public TTSBase
bool start(QString *errStr); bool start(QString *errStr);
bool stop(); bool stop();
TTSStatus voice(QString text,QString wavfile, QString *errStr); TTSStatus voice(QString text,QString wavfile, QString *errStr);
Capabilities capabilities();
// for settings // for settings
bool configOk(); bool configOk();

View file

@ -30,6 +30,11 @@ TTSSapi::TTSSapi(QObject* parent) : TTSBase(parent)
m_sapi4 =false; m_sapi4 =false;
} }
TTSBase::Capabilities TTSSapi::capabilities()
{
return None;
}
void TTSSapi::generateSettings() void TTSSapi::generateSettings()
{ {
// language // language
@ -195,13 +200,17 @@ TTSStatus TTSSapi::voice(QString text,QString wavfile, QString *errStr)
*voicestream << query; *voicestream << query;
*voicestream << "SYNC\tbla\r\n"; *voicestream << "SYNC\tbla\r\n";
voicestream->flush(); 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; return NoError;
} }
bool TTSSapi::stop() bool TTSSapi::stop()
{ {
*voicestream << "QUIT\r\n"; *voicestream << "QUIT\r\n";
voicestream->flush(); voicestream->flush();
voicescript->waitForFinished(); voicescript->waitForFinished();

View file

@ -42,6 +42,7 @@ class TTSSapi : public TTSBase
TTSStatus voice(QString text,QString wavfile, QString *errStr); TTSStatus voice(QString text,QString wavfile, QString *errStr);
bool start(QString *errStr); bool start(QString *errStr);
bool stop(); bool stop();
Capabilities capabilities();
// for settings // for settings
bool configOk(); bool configOk();