Index: rbutil/rbutilqt/ttsgui.cpp
===================================================================
--- rbutil/rbutilqt/ttsgui.cpp (revision 20307)
+++ rbutil/rbutilqt/ttsgui.cpp (working copy)
@@ -181,3 +181,58 @@
}
}
+TTSFestivalGui::TTSFestivalGui(TTSFestival* api, QDialog* parent) :
+ QDialog(parent), festival(api)
+{
+ ui.setupUi(this);
+ this->hide();
+
+ connect(ui.refreshButton, SIGNAL(clicked()), this, SLOT(updateVoices()));
+ connect(ui.voicesBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(updateDescription(QString)));
+}
+
+void TTSFestivalGui::showCfg()
+{
+ // 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]);
+ }
+ //show dialog
+ this->exec();
+}
+
+void TTSFestivalGui::accept(void)
+{
+ //save settings in user config
+ settings->setTTSPath("festival", QString("%1:%2").arg(ui.serverPath->text().trimmed()).arg(ui.clientPath->text().trimmed()));
+ settings->setTTSVoice("festival", ui.voicesBox->currentText());
+
+ settings->sync();
+
+ this->done(0);
+}
+
+void TTSFestivalGui::reject(void)
+{
+ this->done(0);
+}
+
+void TTSFestivalGui::updateVoices()
+{
+ ui.voicesBox->clear();
+ ui.voicesBox->addItems(festival->getVoiceList());
+ ui.voicesBox->setCurrentIndex(ui.voicesBox->findText(settings->ttsVoice("festival")));
+}
+
+void TTSFestivalGui::updateDescription(QString value)
+{
+ ui.descriptionLabel->setText(tr("Querying festival"));
+ ui.descriptionLabel->setText(festival->getVoiceInfo(value));
+}
Index: rbutil/rbutilqt/ttsfestivalcfgform.ui
===================================================================
--- rbutil/rbutilqt/ttsfestivalcfgform.ui (revision 0)
+++ rbutil/rbutilqt/ttsfestivalcfgform.ui (revision 0)
@@ -0,0 +1,315 @@
+
+ TTSFestivalCfgFrm
+
+
+
+ 0
+ 0
+ 340
+ 314
+
+
+
+ Configuration
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+ false
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+ Executables
+
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ QLayout::SetMinimumSize
+
+
+ 6
+
+
+ 0
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Path to Festival server
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+
+ 215
+ 0
+
+
+
+
+ -
+
+
+ Browse
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Path to Festival client
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+
+ 215
+ 0
+
+
+
+
+ -
+
+
+ Browse
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 70
+ 20
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 153
+ 43
+
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+
+ 0
+ 0
+
+
+
+ Server voice
+
+
+
+ 6
+
+
+ 6
+
+
+ 6
+
+
+ 6
+
+
+ 0
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Select a voice
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ &Refresh
+
+
+
+ :/icons/view-refresh.png:/icons/view-refresh.png
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
+
+ buttonBox
+ execsBox
+ horizontalSpacer
+ verticalSpacer
+ groupBox2
+
+
+ serverPath
+ serverButton
+ clientPath
+ clientButton
+ refreshButton
+ voicesBox
+ buttonBox
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ TTSFestivalCfgFrm
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ TTSFestivalCfgFrm
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
Index: rbutil/rbutilqt/tts.cpp
===================================================================
--- rbutil/rbutilqt/tts.cpp (revision 20307)
+++ rbutil/rbutilqt/tts.cpp (working copy)
@@ -33,7 +33,9 @@
#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 @@
return ttsCache.value(ttsName);
TTSBase* tts;
+#if defined(Q_OS_WIN)
if(ttsName == "sapi")
{
tts = new TTSSapi();
@@ -51,7 +54,18 @@
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;
return tts;
@@ -92,7 +106,7 @@
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,7 +167,7 @@
}
}
-bool TTSExes::voice(QString text,QString wavfile)
+TTSStatus TTSExes::voice(QString text,QString wavfile, QString *errStr, bool ignoreNonAscii)
{
QString execstring = m_TTSTemplate;
@@ -163,7 +177,7 @@
execstring.replace("%text",text);
//qDebug() << "voicing" << execstring;
QProcess::execute(execstring);
- return true;
+ return NoError;
}
@@ -304,7 +318,7 @@
-bool TTSSapi::voice(QString text,QString wavfile)
+TTSStatus TTSSapi::voice(QString text,QString wavfile, QString *errStr, bool ignoreNonAscii)
{
QString query = "SPEAK\t"+wavfile+"\t"+text+"\r\n";
qDebug() << "voicing" << query;
@@ -312,7 +326,7 @@
*voicestream << "SYNC\tbla\r\n";
voicestream->flush();
voicescript->waitForReadyRead();
- return true;
+ return NoError;
}
bool TTSSapi::stop()
@@ -349,5 +363,325 @@
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();
+
+ QProgressDialog progressDialog(tr(""), tr(""), 0, 0);
+ progressDialog.setWindowTitle(tr("Starting festival"));
+ progressDialog.setModal(true);
+ progressDialog.setLabel(0);
+ progressDialog.setCancelButton(0);
+ progressDialog.show();
+
+ /* This loop tries to connect to festival in order to determine
+ * if festival has started. After it, festival has either crashed
+ * (state == NotRunning) or is ready to be used (state == Running)
+ */
+ /*while(serverProcess.state() == QProcess::Running)
+ {
+
+ QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
+ QTcpSocket socket;
+ socket.connectToHost("localhost", 1314);
+ socket.waitForConnected();
+
+ if (socket.state() == QAbstractSocket::ConnectedState)
+ {
+ // a quick way to see if Scheme is running
+ socket.write("(getpid)\n");
+ socket.waitForBytesWritten();
+ socket.waitForReadyRead();
+ QString response = socket.readAll().trimmed();
+ if(response != "LP")
+ break;
+ }
+
+ socket.abort();
+ socket.disconnectFromHost();
+ }*/
+
+ 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)
+ startServer();
+}
+
+bool TTSFestival::start(QString* errStr)
+{
+ ensureServerRunning();
+ if (!settings->ttsVoice("festival").isEmpty())
+ {
+ QTcpSocket setVoiceSocket;
+ setVoiceSocket.connectToHost("localhost", 1314);
+ setVoiceSocket.waitForConnected();
+ setVoiceSocket.write(QString("(voice.select '%1)\n").arg(settings->ttsVoice("festival")).toAscii());
+ setVoiceSocket.waitForBytesWritten();
+ setVoiceSocket.waitForReadyRead();
+ qDebug() << setVoiceSocket.readAll();
+ setVoiceSocket.disconnectFromHost();
+ }
+
+ return true;
+}
+
+bool TTSFestival::stop()
+{
+ serverProcess.terminate();
+ serverProcess.kill();
+
+ return true;
+}
+
+TTSStatus TTSFestival::voice(QString text, QString wavfile, QString* errStr, bool ignoreNonAscii)
+{
+ qDebug() << text << "->" << wavfile;
+ if(ignoreNonAscii && !isASCII(text))
+ {
+ *errStr = tr("ignored Non-ASCII text");
+ return Warning;
+ }
+ 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 FatalError;
+ }
+ 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)");
+
+ qDebug() << "voiceList: " << response;
+ // 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 "";
+
+ qDebug() << "voiceInfo for " << voice;
+ QString response = queryServer(QString("(voice.description '%1)").arg(voice), 3000);
+
+ if (response == "")
+ return tr("No description available");
+
+ 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 name of the voice
+
+ QString ret;
+ foreach(QString line, responseLines)
+ {
+ line = line.remove('(').trimmed();
+
+ QStringList tokens = line.split(' ', QString::SkipEmptyParts);
+ tokens[0] = tokens[0].toLower();
+ tokens[0][0] = tokens[0][0].toUpper(); // capitalize the key name
+
+ ret += tokens[0];
+
+ tokens.removeAt(0);
+ line = tokens.join(" ").toLower();
+ line[0] = line[0].toUpper();
+
+ ret += ": " + line + "\n";
+ }
+ return ret.trimmed();
+}
+
+void TTSFestival::setCfg(RbSettings* sett)
+{
+ // call function of base class
+ TTSBase::setCfg(sett);
+
+ // if the config isnt OK, try to autodetect
+ if(!configOk())
+ {
+ QString exepath;
+ //try autodetect tts
+#if defined(Q_OS_LINUX) || defined(Q_OS_MACX) || defined(Q_OS_OPENBSD)
+ QStringList path = QString(getenv("PATH")).split(":", QString::SkipEmptyParts);
+#elif defined(Q_OS_WIN)
+ QStringList path = QString(getenv("PATH")).split(";", QString::SkipEmptyParts);
+#endif
+ qDebug() << path;
+ for(int i = 0; i < path.size(); i++)
+ {
+ QString server = QDir::fromNativeSeparators(path.at(i)) + "/" + "festival";
+ QString client = QDir::fromNativeSeparators(path.at(i)) + "/" + "festival_client";
+#if defined(Q_OS_WIN)
+ server += ".exe";
+ client += ".exe";
+ QStringList ex = server.split("\"", QString::SkipEmptyParts);
+ server = ex.join("");
+ ex = client.split("\"", QString::SkipEmptyParts);
+ client = ex.join("");
+#endif
+ qDebug() << server << client;
+ if(QFileInfo(server).isExecutable() && QFileInfo(client).isExecutable())
+ {
+ exepath= QDir::toNativeSeparators(server) + ":" + QDir::toNativeSeparators(client);
+ break;
+ }
+ }
+ settings->setTTSPath("festival",exepath);
+ settings->sync();
+ }
+
+}
+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, (timeout > 0) ? timeout : 500);
+ 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")
+ break;
+ else
+ {
+ QDateTime endTime = QDateTime::currentDateTime().addMSecs(200);
+ while(QDateTime::currentDateTime() <= endTime)
+ QApplication::processEvents(QEventLoop::AllEvents, 200);
+ }
+ }
+ socket.abort();
+ socket.disconnectFromHost();
+
+ if(timeout > 0 && QDateTime::currentDateTime() >= endTime)
+ return "";
+ }
+ if(response == "nil")
+ return "";
+
+ QStringList lines = response.split('\n');
+ lines.removeFirst();
+ lines.removeLast();
+ return lines.join("\n");
+}
+bool TTSBase::isASCII(const QString str)
+{
+ QByteArray data = str.toUtf8();
+ for(int i=0; i 127
+ return false;
+ }
+ return true;
+}
Index: rbutil/rbutilqt/rbutilqt.pro
===================================================================
--- rbutil/rbutilqt/rbutilqt.pro (revision 20307)
+++ rbutil/rbutilqt/rbutilqt.pro (working copy)
@@ -2,11 +2,12 @@
CCACHE = $$system(which ccache)
!isEmpty(CCACHE) {
message("using ccache")
- QMAKE_CXX = ccache g++
- QMAKE_CC = ccache gcc
+ QMAKE_CXX = ccache \
+ g++
+ QMAKE_CC = ccache \
+ gcc
}
}
-
OBJECTS_DIR = build/o
UI_DIR = build/ui
MOC_DIR = build/moc
@@ -21,28 +22,33 @@
message("Qt version used:" $$VER)
# add a custom rule for pre-building librbspeex
-!mac {
-rbspeex.commands = @$(MAKE) -C ../../tools/rbspeex librbspeex.a
-}
-mac {
-rbspeex.commands = @$(MAKE) -C ../../tools/rbspeex librbspeex-universal
-}
+!mac:rbspeex.commands = @$(MAKE) \
+ -C \
+ ../../tools/rbspeex \
+ librbspeex.a
+mac:rbspeex.commands = @$(MAKE) \
+ -C \
+ ../../tools/rbspeex \
+ librbspeex-universal
QMAKE_EXTRA_TARGETS += rbspeex
PRE_TARGETDEPS += rbspeex
# rule for creating ctags file
-tags.commands = ctags -R --c++-kinds=+p --fields=+iaS --extra=+q $(SOURCES)
+tags.commands = ctags \
+ -R \
+ --c++-kinds=+p \
+ --fields=+iaS \
+ --extra=+q \
+ $(SOURCES)
tags.depends = $(SOURCES)
QMAKE_EXTRA_TARGETS += tags
# add a custom rule for making the translations
-lrelease.commands = $$[QT_INSTALL_BINS]/lrelease -silent rbutilqt.pro
+lrelease.commands = $$[QT_INSTALL_BINS]/lrelease \
+ -silent \
+ rbutilqt.pro
QMAKE_EXTRA_TARGETS += lrelease
-!dbg {
- PRE_TARGETDEPS += lrelease
-}
-
-
+!dbg:PRE_TARGETDEPS += lrelease
SOURCES += rbutilqt.cpp \
main.cpp \
install.cpp \
@@ -84,7 +90,6 @@
base/bootloaderinstallfile.cpp \
../../tools/mkboot.c \
../../tools/iriver.c
-
HEADERS += rbutilqt.h \
install.h \
base/httpget.h \
@@ -138,26 +143,36 @@
../../tools/iriver.h
# Needed by QT on Win
-INCLUDEPATH = . irivertools zip zlib ../ipodpatcher ../sansapatcher ../../tools/rbspeex ../../tools
+INCLUDEPATH = . \
+ irivertools \
+ zip \
+ zlib \
+ ../ipodpatcher \
+ ../sansapatcher \
+ ../../tools/rbspeex \
+ ../../tools
INCLUDEPATH += base
-
-LIBS += -L../../tools/rbspeex -lrbspeex
-
+LIBS += -L../../tools/rbspeex \
+ -lrbspeex
TEMPLATE = app
dbg {
- CONFIG += debug thread qt warn_on
+ CONFIG += debug \
+ thread \
+ qt \
+ warn_on
DEFINES -= QT_NO_DEBUG_OUTPUT
message("debug")
}
!dbg {
- CONFIG += release thread qt
+ CONFIG += release \
+ thread \
+ qt
DEFINES += QT_NO_DEBUG_OUTPUT
message("release")
}
-
TARGET = rbutilqt
-
-FORMS += rbutilqtfrm.ui \
+FORMS += ttsfestivalcfgform.ui \
+ rbutilqtfrm.ui \
aboutbox.ui \
installfrm.ui \
progressloggerfrm.ui \
@@ -173,15 +188,9 @@
sapicfgfrm.ui \
createvoicefrm.ui \
sysinfofrm.ui
-
RESOURCES += rbutilqt.qrc
-win32 {
- RESOURCES += rbutilqt-win.qrc
-}
-!dbg {
- RESOURCES += rbutilqt-lang.qrc
-}
-
+win32:RESOURCES += rbutilqt-win.qrc
+!dbg:RESOURCES += rbutilqt-lang.qrc
TRANSLATIONS += lang/rbutil_de.ts \
lang/rbutil_fi.ts \
lang/rbutil_fr.ts \
@@ -192,59 +201,63 @@
lang/rbutil_pt.ts \
lang/rbutil_tr.ts \
lang/rbutil_zh_CN.ts \
- lang/rbutil_zh_TW.ts \
-
-
+ lang/rbutil_zh_TW.ts
QT += network
-DEFINES += RBUTIL _LARGEFILE64_SOURCE
-
+DEFINES += RBUTIL \
+ _LARGEFILE64_SOURCE
win32 {
SOURCES += ../ipodpatcher/ipodio-win32.c
SOURCES += ../sansapatcher/sansaio-win32.c
RC_FILE = rbutilqt.rc
- LIBS += -lsetupapi -lnetapi32
+ LIBS += -lsetupapi \
+ -lnetapi32
}
-
unix {
SOURCES += ../ipodpatcher/ipodio-posix.c
SOURCES += ../sansapatcher/sansaio-posix.c
}
-unix:!static {
- LIBS += -lusb
-}
-unix:static {
- # force statically linking of libusb. Libraries that are appended
+unix:!static:LIBS += -lusb
+unix:static:# force statically linking of libusb. Libraries that are appended
+
# later will get linked dynamically again.
- LIBS += -Wl,-Bstatic -lusb -Wl,-Bdynamic
-}
-
+LIBS += -Wl,-Bstatic \
+ -lusb \
+ -Wl,-Bdynamic
macx {
QMAKE_MAC_SDK=/Developer/SDKs/MacOSX10.4u.sdk
- QMAKE_LFLAGS_PPC=-mmacosx-version-min=10.4 -arch ppc
- QMAKE_LFLAGS_X86=-mmacosx-version-min=10.4 -arch i386
- CONFIG+=x86 ppc
- LIBS += -L/usr/local/lib -framework IOKit
+ QMAKE_LFLAGS_PPC = -mmacosx-version-min=10.4 \
+ -arch \
+ ppc
+ QMAKE_LFLAGS_X86 = -mmacosx-version-min=10.4 \
+ -arch \
+ i386
+ CONFIG += x86 \
+ ppc
+ LIBS += -L/usr/local/lib \
+ -framework \
+ IOKit
INCLUDEPATH += /usr/local/include
QMAKE_INFO_PLIST = Info.plist
RC_FILE = icons/rbutilqt.icns
# rule for creating a dmg file
- dmg.commands = hdiutil create -ov -srcfolder rbutilqt.app/ rbutil.dmg
+ dmg.commands = hdiutil \
+ create \
+ -ov \
+ -srcfolder \
+ rbutilqt.app/ \
+ rbutil.dmg
QMAKE_EXTRA_TARGETS += dmg
}
-
static {
QTPLUGIN += qtaccessiblewidgets
- LIBS += -L$$(QT_BUILD_TREE)/plugins/accessible -lqtaccessiblewidgets
+ LIBS += -L$$(QT_BUILD_TREE)/plugins/accessible \
+ -lqtaccessiblewidgets
LIBS += -L.
DEFINES += STATIC
message("using static plugin")
}
-
unix {
target.path = /usr/local/bin
INSTALLS += target
}
-
-
-
Index: rbutil/rbutilqt/talkfile.h
===================================================================
--- rbutil/rbutilqt/talkfile.h (revision 20307)
+++ rbutil/rbutilqt/talkfile.h (working copy)
@@ -43,6 +43,7 @@
void setDir(QDir dir){m_dir = dir; }
void setMountPoint(QString mountpoint) {m_mountpoint =mountpoint; }
+ void setIgnoreNonAscii(bool ov) {m_ignoreNonAscii = ov;}
void setOverwriteTalk(bool ov) {m_overwriteTalk = ov;}
void setRecursive(bool ov) {m_recursive = ov;}
void setStripExtensions(bool ov) {m_stripExtensions = ov;}
@@ -58,8 +59,8 @@
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);
@@ -76,6 +77,7 @@
bool m_stripExtensions;
bool m_talkFolders;
bool m_talkFiles;
+ bool m_ignoreNonAscii;
ProgressloggerInterface* m_logger;
Index: rbutil/rbutilqt/voicefile.cpp
===================================================================
--- rbutil/rbutilqt/voicefile.cpp (revision 20307)
+++ rbutil/rbutilqt/voicefile.cpp (working copy)
@@ -244,7 +244,10 @@
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
Index: rbutil/rbutilqt/installtalkwindow.cpp
===================================================================
--- rbutil/rbutilqt/installtalkwindow.cpp (revision 20307)
+++ rbutil/rbutilqt/installtalkwindow.cpp (working copy)
@@ -96,6 +96,7 @@
talkcreator->setDir(QDir(folderToTalk));
talkcreator->setMountPoint(settings->mountpoint());
+ talkcreator->setIgnoreNonAscii(ui.IgnoreNonAscii->isChecked());
talkcreator->setOverwriteTalk(ui.OverwriteTalk->isChecked());
talkcreator->setRecursive(ui.recursive->isChecked());
talkcreator->setStripExtensions(ui.StripExtensions->isChecked());
Index: rbutil/rbutilqt/tts.h
===================================================================
--- rbutil/rbutilqt/tts.h (revision 20307)
+++ rbutil/rbutilqt/tts.h (working copy)
@@ -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, bool ignoreNonAscii = false)
+ { (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(){}
@@ -65,6 +73,7 @@
RbSettings* settings;
static QMap ttsList;
static QMap ttsCache;
+ bool isASCII(const QString str);
};
class TTSSapi : public TTSBase
@@ -72,7 +81,7 @@
Q_OBJECT
public:
TTSSapi();
- virtual bool voice(QString text,QString wavfile);
+ virtual TTSStatus voice(QString text,QString wavfile, QString *errStr, bool ignoreNonAscii = false);
virtual bool start(QString *errStr);
virtual bool stop();
virtual void showCfg();
@@ -99,7 +108,7 @@
Q_OBJECT
public:
TTSExes(QString name);
- virtual bool voice(QString text,QString wavfile);
+ virtual TTSStatus voice(QString text,QString wavfile, QString *errStr, bool ignoreNonAscii = false);
virtual bool start(QString *errStr);
virtual bool stop() {return true;}
virtual void showCfg();
@@ -115,4 +124,26 @@
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 void setCfg(RbSettings* sett);
+ virtual TTSStatus voice(QString text,QString wavfile, QString *errStr, bool ignoreNonAscii = false);
+
+ QStringList getVoiceList();
+ QString getVoiceInfo(QString voice);
+private:
+ void startServer();
+ void ensureServerRunning();
+ QString queryServer(QString query, int timeout = -1);
+ QProcess serverProcess;
+ QStringList voices;
+};
+
#endif
Index: rbutil/rbutilqt/installtalkfrm.ui
===================================================================
--- rbutil/rbutilqt/installtalkfrm.ui (revision 20307)
+++ rbutil/rbutilqt/installtalkfrm.ui (working copy)
@@ -9,7 +9,7 @@
0
0
600
- 450
+ 468
@@ -125,7 +125,7 @@
- -
+
-
Overwrite Talkfiles
@@ -158,14 +158,14 @@
+ -
+
+
+ Ignore filenames with non-ASCII characters
+
+
+
- recursive
- StripExtensions
- OverwriteTalk
- talkFolders
- talkFiles
- label_3
- fileFilter
-
@@ -230,6 +230,7 @@
talkFolders
recursive
StripExtensions
+ IgnoreNonAscii
OverwriteTalk
buttonOk
buttonCancel
Index: rbutil/rbutilqt/ttsgui.h
===================================================================
--- rbutil/rbutilqt/ttsgui.h (revision 20307)
+++ rbutil/rbutilqt/ttsgui.h (working copy)
@@ -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,26 @@
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 updateVoices();
+private:
+ Ui::TTSFestivalCfgFrm ui;
+ RbSettings* settings;
+ TTSFestival* festival;
+private slots:
+ void updateDescription(QString value);
+};
+
#endif
Index: rbutil/rbutilqt/talkfile.cpp
===================================================================
--- rbutil/rbutilqt/talkfile.cpp (revision 20307)
+++ rbutil/rbutilqt/talkfile.cpp (working copy)
@@ -34,7 +34,7 @@
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 @@
}
}
- // 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 @@
//! \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, m_ignoreNonAscii);
+ 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 @@
//! \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 @@
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 @@
}
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 @@
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);