From 8f2aaaf79d15614c2a6ebd30f461457fff2bf836 Mon Sep 17 00:00:00 2001 From: Dominik Wenger Date: Fri, 27 Mar 2009 19:18:14 +0000 Subject: 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 --- rbutil/rbutilqt/CREDITS | 1 + rbutil/rbutilqt/rbutilqt.pro | 1 + rbutil/rbutilqt/talkfile.cpp | 55 ++++++--- rbutil/rbutilqt/talkfile.h | 4 +- rbutil/rbutilqt/tts.cpp | 277 +++++++++++++++++++++++++++++++++++++++++- rbutil/rbutilqt/tts.h | 42 ++++++- rbutil/rbutilqt/ttsgui.cpp | 156 ++++++++++++++++++++++++ rbutil/rbutilqt/ttsgui.h | 30 +++++ rbutil/rbutilqt/voicefile.cpp | 5 +- 9 files changed, 540 insertions(+), 31 deletions(-) diff --git a/rbutil/rbutilqt/CREDITS b/rbutil/rbutilqt/CREDITS index 9c755d7b51..d76a7ded1c 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 392cf1e18f..72fead2c04 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 7980e7cbd0..43993be436 100644 --- a/rbutil/rbutilqt/talkfile.cpp +++ b/rbutil/rbutilqt/talkfile.cpp @@ -34,7 +34,7 @@ bool TalkFileCreator::createTalkFiles(ProgressloggerInterface* logger) QMultiMap fileList; QMultiMap 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,QMultiMapaddItem(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 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 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 d869c32880..08c076132d 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 *dirMap,QMultiMap *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 dirMap,QString* errString); bool copyTalkFileFiles(QMultiMap fileMap,QString* errString); diff --git a/rbutil/rbutilqt/tts.cpp b/rbutil/rbutilqt/tts.cpp index 252608f53e..48555cc114 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 (, ) + 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 7c21fd0d65..d225d46853 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 +#include +#include +#include +#include +#include #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 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 voiceDescriptions; +}; + #endif diff --git a/rbutil/rbutilqt/ttsgui.cpp b/rbutil/rbutilqt/ttsgui.cpp index 0a59b25d86..45dd3a86ef 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 68990b83c0..ce7be408ad 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 65c9bee8dc..6a04107cea 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 -- cgit v1.2.3