summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDominik Wenger <domonoky@googlemail.com>2009-03-27 19:18:14 +0000
committerDominik Wenger <domonoky@googlemail.com>2009-03-27 19:18:14 +0000
commit8f2aaaf79d15614c2a6ebd30f461457fff2bf836 (patch)
treed6515790f343df31050f6ae28b1defb50e4b24ac
parent75a5b9321bc9c5da20de023f84dd96dcc5297f1a (diff)
downloadrockbox-8f2aaaf79d15614c2a6ebd30f461457fff2bf836.zip
rockbox-8f2aaaf79d15614c2a6ebd30f461457fff2bf836.tar.gz
rockbox-8f2aaaf79d15614c2a6ebd30f461457fff2bf836.tar.bz2
rockbox-8f2aaaf79d15614c2a6ebd30f461457fff2bf836.tar.xz
rbutil: commit FS#9983 by Delyan Kratunov. It adds support for the Festival TTS on Linux.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@20559 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--rbutil/rbutilqt/CREDITS1
-rw-r--r--rbutil/rbutilqt/rbutilqt.pro1
-rw-r--r--rbutil/rbutilqt/talkfile.cpp55
-rw-r--r--rbutil/rbutilqt/talkfile.h4
-rw-r--r--rbutil/rbutilqt/tts.cpp277
-rw-r--r--rbutil/rbutilqt/tts.h42
-rw-r--r--rbutil/rbutilqt/ttsgui.cpp156
-rw-r--r--rbutil/rbutilqt/ttsgui.h30
-rw-r--r--rbutil/rbutilqt/voicefile.cpp5
9 files changed, 540 insertions, 31 deletions
diff --git a/rbutil/rbutilqt/CREDITS b/rbutil/rbutilqt/CREDITS
index 9c755d7..d76a7de 100644
--- a/rbutil/rbutilqt/CREDITS
+++ b/rbutil/rbutilqt/CREDITS
@@ -8,3 +8,4 @@ Tomer Shalev
Yoshihisa Uchida
Alexander Spyridakis
Rui Araújo
+Delyan Kratunov \ No newline at end of file
diff --git a/rbutil/rbutilqt/rbutilqt.pro b/rbutil/rbutilqt/rbutilqt.pro
index 392cf1e..72fead2 100644
--- a/rbutil/rbutilqt/rbutilqt.pro
+++ b/rbutil/rbutilqt/rbutilqt.pro
@@ -171,6 +171,7 @@ FORMS += rbutilqtfrm.ui \
encexescfgfrm.ui \
ttsexescfgfrm.ui \
sapicfgfrm.ui \
+ ttsfestivalcfgform.ui \
createvoicefrm.ui \
sysinfofrm.ui
diff --git a/rbutil/rbutilqt/talkfile.cpp b/rbutil/rbutilqt/talkfile.cpp
index 7980e7c..43993be 100644
--- a/rbutil/rbutilqt/talkfile.cpp
+++ b/rbutil/rbutilqt/talkfile.cpp
@@ -34,7 +34,7 @@ bool TalkFileCreator::createTalkFiles(ProgressloggerInterface* logger)
QMultiMap<QString,QString> fileList;
QMultiMap<QString,QString> dirList;
- QStringList toSpeakList;
+ QStringList toSpeakList, voicedEntries, encodedEntries;
QString errStr;
m_logger->addItem(tr("Starting Talk file generation"),LOGINFO);
@@ -103,20 +103,19 @@ bool TalkFileCreator::createTalkFiles(ProgressloggerInterface* logger)
}
}
- // Voice entryies
+ // Voice entries
m_logger->addItem(tr("Voicing entries..."),LOGINFO);
- if(voiceList(toSpeakList,&errStr) == false)
+ TTSStatus voiceStatus= voiceList(toSpeakList,voicedEntries);
+ if(voiceStatus == FatalError)
{
- m_logger->addItem(errStr,LOGERROR);
doAbort(toSpeakList);
return false;
}
// Encoding Entries
m_logger->addItem(tr("Encoding files..."),LOGINFO);
- if(encodeList(toSpeakList,&errStr) == false)
+ if(encodeList(voicedEntries,encodedEntries) == false)
{
- m_logger->addItem(errStr,LOGERROR);
doAbort(toSpeakList);
return false;
}
@@ -247,29 +246,45 @@ bool TalkFileCreator::createDirAndFileMaps(QDir startDir,QMultiMap<QString,QStri
//! \param toSpeak QStringList with the Entries to voice.
//! \param errString pointer to where the Error cause is written
//! \returns true on success, false on error or user abort
-bool TalkFileCreator::voiceList(QStringList toSpeak,QString* errString)
+TTSStatus TalkFileCreator::voiceList(QStringList toSpeak,QStringList& voicedEntries)
{
resetProgress(toSpeak.size());
+ QStringList errors;
+ bool warnings = false;
for(int i=0; i < toSpeak.size(); i++)
{
if(m_abort)
{
- *errString = tr("Talk file creation aborted");
- return false;
+ m_logger->addItem(tr("Talk file creation aborted"), LOGERROR);
+ return FatalError;
}
QString filename = QDir::tempPath()+ "/"+ toSpeak[i] + ".wav";
- if(!m_tts->voice(toSpeak[i],filename))
+ QString error;
+ TTSStatus status = m_tts->voice(toSpeak[i],filename, &error);
+ if(status == Warning)
{
- *errString =tr("Voicing of %s failed").arg(toSpeak[i]);
- return false;
+ warnings = true;
+ m_logger->addItem(tr("Voicing of %1 failed: %2").arg(toSpeak[i]).arg(error),
+ LOGWARNING);
+ }
+ else if (status == FatalError)
+ {
+ m_logger->addItem(tr("Voicing of %1 failed: %2").arg(toSpeak[i]).arg(error),
+ LOGERROR);
+ return FatalError;
}
+ else
+ voicedEntries.append(toSpeak[i]);
m_logger->setProgressValue(++m_progress);
QCoreApplication::processEvents();
}
- return true;
+ if(warnings)
+ return Warning;
+ else
+ return NoError;
}
@@ -279,14 +294,14 @@ bool TalkFileCreator::voiceList(QStringList toSpeak,QString* errString)
//! \param toSpeak QStringList with the Entries to encode.
//! \param errString pointer to where the Error cause is written
//! \returns true on success, false on error or user abort
-bool TalkFileCreator::encodeList(QStringList toEncode,QString* errString)
+bool TalkFileCreator::encodeList(QStringList toEncode,QStringList& encodedEntries)
{
resetProgress(toEncode.size());
for(int i=0; i < toEncode.size(); i++)
{
if(m_abort)
{
- *errString = tr("Talk file creation aborted");
+ m_logger->addItem(tr("Talk file creation aborted"), LOGERROR);
return false;
}
@@ -295,9 +310,10 @@ bool TalkFileCreator::encodeList(QStringList toEncode,QString* errString)
if(!m_enc->encode(wavfilename,filename))
{
- *errString =tr("Encoding of %1 failed").arg(filename);
+ m_logger->addItem(tr("Encoding of %1 failed").arg(filename), LOGERROR);
return false;
}
+ encodedEntries.append(toEncode[i]);
m_logger->setProgressValue(++m_progress);
QCoreApplication::processEvents();
}
@@ -327,6 +343,10 @@ bool TalkFileCreator::copyTalkDirFiles(QMultiMap<QString,QString> dirMap,QString
}
QString source = QDir::tempPath()+ "/"+ it.value() + ".talk";
+
+ if(!QFileInfo(source).exists())
+ continue; // this file was skipped in one of the previous steps
+
QString target = it.key() + "/" + "_dirname.talk";
// remove target if it exists, and if we should overwrite it
@@ -383,6 +403,9 @@ bool TalkFileCreator::copyTalkFileFiles(QMultiMap<QString,QString> fileMap,QStri
else
source = QDir::tempPath()+ "/"+ it.value() + ".talk";
+ if(!QFileInfo(source).exists())
+ continue; // this file was skipped in one of the previous steps
+
// remove target if it exists, and if we should overwrite it
if(m_overwriteTalk && QFile::exists(target))
QFile::remove(target);
diff --git a/rbutil/rbutilqt/talkfile.h b/rbutil/rbutilqt/talkfile.h
index d869c32..08c0761 100644
--- a/rbutil/rbutilqt/talkfile.h
+++ b/rbutil/rbutilqt/talkfile.h
@@ -58,8 +58,8 @@ private:
void doAbort(QStringList cleanupList);
void resetProgress(int max);
bool createDirAndFileMaps(QDir startDir,QMultiMap<QString,QString> *dirMap,QMultiMap<QString,QString> *fileMap);
- bool voiceList(QStringList toSpeak,QString* errString);
- bool encodeList(QStringList toEncode,QString* errString);
+ TTSStatus voiceList(QStringList toSpeak,QStringList& voicedEntries);
+ bool encodeList(QStringList toEncode,QStringList& encodedEntries);
bool copyTalkDirFiles(QMultiMap<QString,QString> dirMap,QString* errString);
bool copyTalkFileFiles(QMultiMap<QString,QString> fileMap,QString* errString);
diff --git a/rbutil/rbutilqt/tts.cpp b/rbutil/rbutilqt/tts.cpp
index 252608f..48555cc 100644
--- a/rbutil/rbutilqt/tts.cpp
+++ b/rbutil/rbutilqt/tts.cpp
@@ -33,7 +33,9 @@ void TTSBase::initTTSList()
#if defined(Q_OS_WIN)
ttsList["sapi"] = "Sapi TTS Engine";
#endif
-
+#if defined(Q_OS_LINUX)
+ ttsList["festival"] = "Festival TTS Engine";
+#endif
}
// function to get a specific encoder
@@ -44,6 +46,7 @@ TTSBase* TTSBase::getTTS(QString ttsName)
return ttsCache.value(ttsName);
TTSBase* tts;
+#if defined(Q_OS_WIN)
if(ttsName == "sapi")
{
tts = new TTSSapi();
@@ -51,6 +54,17 @@ TTSBase* TTSBase::getTTS(QString ttsName)
return tts;
}
else
+#endif
+#if defined(Q_OS_LINUX)
+ if (ttsName == "festival")
+ {
+ tts = new TTSFestival();
+ ttsCache[ttsName] = tts;
+ return tts;
+ }
+ else
+#endif
+ if (true) // fix for OS other than WIN or LINUX
{
tts = new TTSExes(ttsName);
ttsCache[ttsName] = tts;
@@ -92,7 +106,7 @@ TTSExes::TTSExes(QString name) : TTSBase()
m_name = name;
m_TemplateMap["espeak"] = "\"%exe\" %options -w \"%wavfile\" \"%text\"";
- m_TemplateMap["flite"] = "\"%exe\" %options -o \"%wavfile\" \"%text\"";
+ m_TemplateMap["flite"] = "\"%exe\" %options -o \"%wavfile\" -t \"%text\"";
m_TemplateMap["swift"] = "\"%exe\" %options -o \"%wavfile\" \"%text\"";
}
@@ -153,8 +167,9 @@ bool TTSExes::start(QString *errStr)
}
}
-bool TTSExes::voice(QString text,QString wavfile)
+TTSStatus TTSExes::voice(QString text,QString wavfile, QString *errStr)
{
+ (void) errStr;
QString execstring = m_TTSTemplate;
execstring.replace("%exe",m_TTSexec);
@@ -163,7 +178,7 @@ bool TTSExes::voice(QString text,QString wavfile)
execstring.replace("%text",text);
//qDebug() << "voicing" << execstring;
QProcess::execute(execstring);
- return true;
+ return NoError;
}
@@ -304,15 +319,16 @@ QStringList TTSSapi::getVoiceList(QString language)
-bool TTSSapi::voice(QString text,QString wavfile)
+TTSStatus TTSSapi::voice(QString text,QString wavfile, QString *errStr)
{
+ (void) errStr;
QString query = "SPEAK\t"+wavfile+"\t"+text+"\r\n";
qDebug() << "voicing" << query;
*voicestream << query;
*voicestream << "SYNC\tbla\r\n";
voicestream->flush();
voicescript->waitForReadyRead();
- return true;
+ return NoError;
}
bool TTSSapi::stop()
@@ -349,5 +365,254 @@ bool TTSSapi::configOk()
return false;
return true;
}
+/**********************************************************************
+ * TSSFestival - client-server wrapper
+ **********************************************************************/
+TTSFestival::~TTSFestival()
+{
+ stop();
+}
+
+void TTSFestival::startServer()
+{
+ if(!configOk())
+ return;
+
+ QStringList paths = settings->ttsPath("festival").split(":");
+
+ serverProcess.start(QString("%1 --server").arg(paths[0]));
+ serverProcess.waitForStarted();
+
+ queryServer("(getpid)");
+ if(serverProcess.state() == QProcess::Running)
+ qDebug() << "Festival is up and running";
+ else
+ qDebug() << "Festival failed to start";
+}
+
+void TTSFestival::ensureServerRunning()
+{
+ if(serverProcess.state() != QProcess::Running)
+ {
+ // least common denominator for all the server startup code paths
+ QProgressDialog progressDialog(tr(""), tr(""), 0, 0);
+ progressDialog.setWindowTitle(tr("Starting festival"));
+ progressDialog.setModal(true);
+ progressDialog.setLabel(0);
+ progressDialog.setCancelButton(0);
+ progressDialog.show();
+
+ QApplication::processEvents(); // actually show the dialog
+
+ startServer();
+ }
+}
+
+bool TTSFestival::start(QString* errStr)
+{
+ (void) errStr;
+ ensureServerRunning();
+ if (!settings->ttsVoice("festival").isEmpty())
+ queryServer(QString("(voice.select '%1)").arg(settings->ttsVoice("festival")));
+
+ return true;
+}
+bool TTSFestival::stop()
+{
+ serverProcess.terminate();
+ serverProcess.kill();
+
+ return true;
+}
+
+TTSStatus TTSFestival::voice(QString text, QString wavfile, QString* errStr)
+{
+ qDebug() << text << "->" << wavfile;
+
+ QStringList paths = settings->ttsPath("festival").split(":");
+ QString cmd = QString("%1 --server localhost --otype riff --ttw --withlisp --output \"%2\" - ").arg(paths[1]).arg(wavfile);
+ qDebug() << cmd;
+
+ QProcess clientProcess;
+ clientProcess.start(cmd);
+ clientProcess.write(QString("%1.\n").arg(text).toAscii());
+ clientProcess.waitForBytesWritten();
+ clientProcess.closeWriteChannel();
+ clientProcess.waitForReadyRead();
+ QString response = clientProcess.readAll();
+ response = response.trimmed();
+ if(!response.contains("Utterance"))
+ {
+ qDebug() << "Could not voice string: " << response;
+ *errStr = tr("engine could not voice string");
+ return Warning;
+ /* do not stop the voicing process because of a single string
+ TODO: needs proper settings */
+ }
+ clientProcess.closeReadChannel(QProcess::StandardError);
+ clientProcess.closeReadChannel(QProcess::StandardOutput);
+ clientProcess.terminate();
+ clientProcess.kill();
+
+ return NoError;
+}
+
+bool TTSFestival::configOk()
+{
+ QStringList paths = settings->ttsPath("festival").split(":");
+ if(paths.size() != 2)
+ return false;
+ bool ret = QFileInfo(paths[0]).isExecutable() &&
+ QFileInfo(paths[1]).isExecutable();
+ if(settings->ttsVoice("festival").size() > 0 && voices.size() > 0)
+ ret = ret && (voices.indexOf(settings->ttsVoice("festival")) != -1);
+ return ret;
+}
+
+void TTSFestival::showCfg()
+{
+#ifndef CONSOLE
+ TTSFestivalGui gui(this);
+#endif
+ gui.setCfg(settings);
+ gui.showCfg();
+}
+
+QStringList TTSFestival::getVoiceList()
+{
+ if(!configOk())
+ return QStringList();
+
+ if(voices.size() > 0)
+ {
+ qDebug() << "Using voice cache";
+ return voices;
+ }
+ QString response = queryServer("(voice.list)");
+
+ // get the 2nd line. It should be (<voice_name>, <voice_name>)
+ response = response.mid(response.indexOf('\n') + 1, -1);
+ response = response.left(response.indexOf('\n')).trimmed();
+
+ voices = response.mid(1, response.size()-2).split(' ');
+
+ voices.sort();
+ if (voices.size() == 1 && voices[0].size() == 0)
+ voices.removeAt(0);
+ if (voices.size() > 0)
+ qDebug() << "Voices: " << voices;
+ else
+ qDebug() << "No voices.";
+ return voices;
+}
+
+QString TTSFestival::getVoiceInfo(QString voice)
+{
+ if(!configOk())
+ return "";
+
+ if(!getVoiceList().contains(voice))
+ return "";
+
+ if(voiceDescriptions.contains(voice))
+ return voiceDescriptions[voice];
+
+ QString response = queryServer(QString("(voice.description '%1)").arg(voice), 3000);
+
+ if (response == "")
+ {
+ voiceDescriptions[voice]=tr("No description available");
+ }
+ else
+ {
+ response = response.remove(QRegExp("(description \"*\")", Qt::CaseInsensitive, QRegExp::Wildcard));
+ qDebug() << "voiceInfo w/o descr: " << response;
+ response = response.remove(')');
+ QStringList responseLines = response.split('(', QString::SkipEmptyParts);
+ responseLines.removeAt(0); // the voice name itself
+
+ QString description;
+ foreach(QString line, responseLines)
+ {
+ line = line.remove('(');
+ line = line.simplified();
+
+ line[0] = line[0].toUpper(); // capitalize the key
+
+ int firstSpace = line.indexOf(' ');
+ if (firstSpace > 0)
+ {
+ line = line.insert(firstSpace, ':'); // add a colon between the key and the value
+ line[firstSpace+2] = line[firstSpace+2].toUpper(); // capitalize the value
+ }
+
+ description += line + "\n";
+ }
+ voiceDescriptions[voice] = description.trimmed();
+ }
+ return voiceDescriptions[voice];
+}
+
+QString TTSFestival::queryServer(QString query, int timeout)
+{
+ if(!configOk())
+ return "";
+
+ ensureServerRunning();
+
+ qDebug() << "queryServer with " << query;
+ QString response;
+
+ QDateTime endTime;
+ if(timeout > 0)
+ endTime = QDateTime::currentDateTime().addMSecs(timeout);
+
+ /* Festival is *extremely* unreliable. Although at this
+ * point we are sure that SIOD is accepting commands,
+ * we might end up with an empty response. Hence, the loop.
+ */
+ while(true)
+ {
+ QApplication::processEvents(QEventLoop::AllEvents, 50);
+ QTcpSocket socket;
+
+ socket.connectToHost("localhost", 1314);
+ socket.waitForConnected();
+
+ if(socket.state() == QAbstractSocket::ConnectedState)
+ {
+ socket.write(QString("%1\n").arg(query).toAscii());
+ socket.waitForBytesWritten();
+ socket.waitForReadyRead();
+
+ response = socket.readAll().trimmed();
+
+ if (response != "LP" && response != "")
+ break;
+ }
+ socket.abort();
+ socket.disconnectFromHost();
+
+ if(timeout > 0 && QDateTime::currentDateTime() >= endTime)
+ return "";
+
+ /* make sure we wait a little as we don't want to flood the server with requests */
+ QDateTime tmpEndTime = QDateTime::currentDateTime().addMSecs(500);
+ while(QDateTime::currentDateTime() < tmpEndTime)
+ QApplication::processEvents(QEventLoop::AllEvents);
+ }
+ if(response == "nil")
+ return "";
+
+ QStringList lines = response.split('\n');
+ if(lines.size() > 2)
+ {
+ lines.removeFirst();
+ lines.removeLast();
+ }
+ else
+ qDebug() << "Response too short: " << response;
+ return lines.join("\n");
+}
diff --git a/rbutil/rbutilqt/tts.h b/rbutil/rbutilqt/tts.h
index 7c21fd0..d225d46 100644
--- a/rbutil/rbutilqt/tts.h
+++ b/rbutil/rbutilqt/tts.h
@@ -23,9 +23,13 @@
#ifndef TTS_H
#define TTS_H
-
#include "rbsettings.h"
#include <QtCore>
+#include <QProcess>
+#include <QProgressDialog>
+#include <QDateTime>
+#include <QRegExp>
+#include <QTcpSocket>
#ifndef CONSOLE
#include "ttsgui.h"
@@ -33,14 +37,18 @@
#include "ttsguicli.h"
#endif
-
+enum TTSStatus{ FatalError, NoError, Warning };
+class TTSSapi;
+#if defined(Q_OS_LINUX)
+class TTSFestival;
+#endif
class TTSBase : public QObject
{
Q_OBJECT
public:
TTSBase();
- virtual bool voice(QString text,QString wavfile)
- { (void)text; (void)wavfile; return false; }
+ virtual TTSStatus voice(QString text,QString wavfile, QString* errStr)
+ { (void) text; (void) wavfile; (void) errStr; return FatalError;}
virtual bool start(QString *errStr) { (void)errStr; return false; }
virtual bool stop() { return false; }
virtual void showCfg(){}
@@ -72,7 +80,7 @@ class TTSSapi : public TTSBase
Q_OBJECT
public:
TTSSapi();
- virtual bool voice(QString text,QString wavfile);
+ virtual TTSStatus voice(QString text,QString wavfile, QString *errStr);
virtual bool start(QString *errStr);
virtual bool stop();
virtual void showCfg();
@@ -99,7 +107,7 @@ class TTSExes : public TTSBase
Q_OBJECT
public:
TTSExes(QString name);
- virtual bool voice(QString text,QString wavfile);
+ virtual TTSStatus voice(QString text,QString wavfile, QString *errStr);
virtual bool start(QString *errStr);
virtual bool stop() {return true;}
virtual void showCfg();
@@ -115,4 +123,26 @@ class TTSExes : public TTSBase
QMap<QString,QString> m_TemplateMap;
};
+class TTSFestival : public TTSBase
+{
+ Q_OBJECT
+public:
+ ~TTSFestival();
+ virtual bool configOk();
+ virtual bool start(QString *errStr);
+ virtual bool stop();
+ virtual void showCfg();
+ virtual TTSStatus voice(QString text,QString wavfile, QString *errStr);
+
+ QStringList getVoiceList();
+ QString getVoiceInfo(QString voice);
+private:
+ inline void startServer();
+ inline void ensureServerRunning();
+ QString queryServer(QString query, int timeout = -1);
+ QProcess serverProcess;
+ QStringList voices;
+ QMap<QString, QString> voiceDescriptions;
+};
+
#endif
diff --git a/rbutil/rbutilqt/ttsgui.cpp b/rbutil/rbutilqt/ttsgui.cpp
index 0a59b25..45dd3a8 100644
--- a/rbutil/rbutilqt/ttsgui.cpp
+++ b/rbutil/rbutilqt/ttsgui.cpp
@@ -181,3 +181,159 @@ void TTSExesGui::browse()
}
}
+TTSFestivalGui::TTSFestivalGui(TTSFestival* api, QDialog* parent) :
+ QDialog(parent), festival(api)
+{
+ ui.setupUi(this);
+ this->setModal(true);
+ this->setDisabled(true);
+ this->show();
+
+ connect(ui.clientButton, SIGNAL(clicked()), this, SLOT(onBrowseClient()));
+ connect(ui.serverButton, SIGNAL(clicked()), this, SLOT(onBrowseServer()));
+
+ connect(ui.refreshButton, SIGNAL(clicked()), this, SLOT(onRefreshButton()));
+ connect(ui.voicesBox, SIGNAL(activated(QString)), this, SLOT(updateDescription(QString)));
+ connect(ui.showDescriptionCheckbox, SIGNAL(stateChanged(int)), this, SLOT(onShowDescription(int)));
+}
+
+void TTSFestivalGui::showCfg()
+{
+ qDebug() << "show\tpaths: " << settings->ttsPath("festival") << "\n"
+ << "\tvoice: " << settings->ttsVoice("festival");
+
+ // will populate the voices if the paths are correct,
+ // otherwise, it will require the user to press Refresh
+ updateVoices();
+
+ // try to get config from settings
+ QStringList paths = settings->ttsPath("festival").split(":");
+ if(paths.size() == 2)
+ {
+ ui.serverPath->setText(paths[0]);
+ ui.clientPath->setText(paths[1]);
+ }
+
+ this->setEnabled(true);
+ this->exec();
+}
+
+void TTSFestivalGui::accept(void)
+{
+ //save settings in user config
+ QString newPath = QString("%1:%2").arg(ui.serverPath->text().trimmed()).arg(ui.clientPath->text().trimmed());
+ qDebug() << "set\tpaths: " << newPath << "\n\tvoice: " << ui.voicesBox->currentText();
+ settings->setTTSPath("festival", newPath);
+ settings->setTTSVoice("festival", ui.voicesBox->currentText());
+
+ settings->sync();
+
+ this->done(0);
+}
+
+void TTSFestivalGui::reject(void)
+{
+ this->done(0);
+}
+
+void TTSFestivalGui::onBrowseClient()
+{
+ BrowseDirtree browser(this);
+ browser.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
+
+ QFileInfo currentPath(ui.clientPath->text().trimmed());
+ if(currentPath.isDir())
+ {
+ browser.setDir(ui.clientPath->text());
+ }
+ else if (currentPath.isFile())
+ {
+ browser.setDir(currentPath.dir().absolutePath());
+ }
+ if(browser.exec() == QDialog::Accepted)
+ {
+ qDebug() << browser.getSelected();
+ QString exe = browser.getSelected();
+ if(!QFileInfo(exe).isExecutable())
+ return;
+ ui.clientPath->setText(exe);
+ }
+}
+
+void TTSFestivalGui::onBrowseServer()
+{
+ BrowseDirtree browser(this);
+ browser.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
+
+ QFileInfo currentPath(ui.serverPath->text().trimmed());
+ if(currentPath.isDir())
+ {
+ browser.setDir(ui.serverPath->text());
+ }
+ else if (currentPath.isFile())
+ {
+ browser.setDir(currentPath.dir().absolutePath());
+ }
+ if(browser.exec() == QDialog::Accepted)
+ {
+ qDebug() << browser.getSelected();
+ QString exe = browser.getSelected();
+ if(!QFileInfo(exe).isExecutable())
+ return;
+ ui.serverPath->setText(exe);
+ }
+}
+
+void TTSFestivalGui::onRefreshButton()
+{
+ /* Temporarily commit the settings so that we get the new path when we check for voices */
+ QString newPath = QString("%1:%2").arg(ui.serverPath->text().trimmed()).arg(ui.clientPath->text().trimmed());
+ QString oldPath = settings->ttsPath("festival");
+ qDebug() << "new path: " << newPath << "\n" << "old path: " << oldPath << "\nuse new: " << (newPath != oldPath);
+
+ if(newPath != oldPath)
+ {
+ qDebug() << "Using new paths for getVoiceList";
+ settings->setTTSPath("festival", newPath);
+ settings->sync();
+ }
+
+ updateVoices();
+
+ if(newPath != oldPath)
+ {
+ settings->setTTSPath("festival", oldPath);
+ settings->sync();
+ }
+}
+
+void TTSFestivalGui::onShowDescription(int state)
+{
+ if(state == Qt::Unchecked)
+ ui.descriptionLabel->setText("");
+ else
+ updateDescription(ui.voicesBox->currentText());
+}
+
+void TTSFestivalGui::updateVoices()
+{
+ ui.voicesBox->clear();
+ ui.voicesBox->addItem(tr("Loading.."));
+
+ QStringList voiceList = festival->getVoiceList();
+ ui.voicesBox->clear();
+ ui.voicesBox->addItems(voiceList);
+
+ ui.voicesBox->setCurrentIndex(ui.voicesBox->findText(settings->ttsVoice("festival")));
+
+ updateDescription(settings->ttsVoice("festival"));
+}
+
+void TTSFestivalGui::updateDescription(QString value)
+{
+ if(ui.showDescriptionCheckbox->checkState() == Qt::Checked)
+ {
+ ui.descriptionLabel->setText(tr("Querying festival"));
+ ui.descriptionLabel->setText(festival->getVoiceInfo(value));
+ }
+}
diff --git a/rbutil/rbutilqt/ttsgui.h b/rbutil/rbutilqt/ttsgui.h
index 68990b8..ce7be40 100644
--- a/rbutil/rbutilqt/ttsgui.h
+++ b/rbutil/rbutilqt/ttsgui.h
@@ -26,9 +26,11 @@
#include "ui_ttsexescfgfrm.h"
#include "ui_sapicfgfrm.h"
+#include "ui_ttsfestivalcfgform.h"
class RbSettings;
class TTSSapi;
+class TTSFestival;
class TTSSapiGui : public QDialog
{
@@ -71,4 +73,32 @@ private:
QString m_name;
};
+class TTSFestivalGui : public QDialog
+{
+ Q_OBJECT
+public:
+ TTSFestivalGui(TTSFestival* festival, QDialog* parent = NULL);
+
+ void showCfg();
+ void setCfg(RbSettings* sett){settings = sett;}
+
+public slots:
+ virtual void accept(void);
+ virtual void reject(void);
+ //virtual void reset(void);
+
+ void onRefreshButton();
+ void onShowDescription(int state);
+ void onBrowseServer();
+ void onBrowseClient();
+private:
+ Ui::TTSFestivalCfgFrm ui;
+ RbSettings* settings;
+ TTSFestival* festival;
+
+ void updateVoices();
+private slots:
+ void updateDescription(QString value);
+};
+
#endif
diff --git a/rbutil/rbutilqt/voicefile.cpp b/rbutil/rbutilqt/voicefile.cpp
index 65c9bee..6a04107 100644
--- a/rbutil/rbutilqt/voicefile.cpp
+++ b/rbutil/rbutilqt/voicefile.cpp
@@ -230,7 +230,10 @@ void VoiceFileCreator::downloadDone(bool error)
m_logger->addItem(tr("creating ")+toSpeak,LOGINFO);
QCoreApplication::processEvents();
- m_tts->voice(toSpeak,wavname); // generate wav
+
+ // TODO: add support for aborting the operation
+ QString errStr;
+ m_tts->voice(toSpeak,wavname, &errStr); // generate wav
}
// todo strip