Index: rbutil/rbutilqt/ttsgui.cpp =================================================================== --- rbutil/rbutilqt/ttsgui.cpp (revision 20206) +++ rbutil/rbutilqt/ttsgui.cpp (working copy) @@ -16,13 +16,13 @@ * KIND, either express or implied. * ****************************************************************************/ - + #include "ttsgui.h" #include "rbsettings.h" #include "tts.h" #include "browsedirtree.h" - + TTSSapiGui::TTSSapiGui(TTSSapi* sapi,QDialog* parent) : QDialog(parent) { m_sapi= sapi; @@ -31,46 +31,46 @@ connect(ui.reset,SIGNAL(clicked()),this,SLOT(reset())); connect(ui.languagecombo,SIGNAL(currentIndexChanged(QString)),this,SLOT(updateVoices(QString))); connect(ui.usesapi4,SIGNAL(stateChanged(int)),this,SLOT(useSapi4Changed(int))); -} +} void TTSSapiGui::showCfg() { // try to get config from settings - ui.ttsoptions->setText(settings->ttsOptions("sapi")); + ui.ttsoptions->setText(settings->ttsOptions("sapi")); QString selLang = settings->ttsLang("sapi"); - QString selVoice = settings->ttsVoice("sapi"); + QString selVoice = settings->ttsVoice("sapi"); ui.speed->setValue(settings->ttsSpeed("sapi")); if(settings->ttsUseSapi4()) ui.usesapi4->setCheckState(Qt::Checked); else ui.usesapi4->setCheckState(Qt::Unchecked); - + // fill in language combobox QStringList languages = settings->allLanguages(); - + languages.sort(); ui.languagecombo->clear(); ui.languagecombo->addItems(languages); - + // set saved lang ui.languagecombo->setCurrentIndex(ui.languagecombo->findText(selLang)); - // fill in voice combobox + // fill in voice combobox updateVoices(selLang); - + // set saved lang - ui.voicecombo->setCurrentIndex(ui.voicecombo->findText(selVoice)); - + ui.voicecombo->setCurrentIndex(ui.voicecombo->findText(selVoice)); + //show dialog this->exec(); - + } void TTSSapiGui::reset() { - ui.ttsoptions->setText(""); - ui.languagecombo->setCurrentIndex(ui.languagecombo->findText("english")); + ui.ttsoptions->setText(""); + ui.languagecombo->setCurrentIndex(ui.languagecombo->findText("english")); } @@ -101,7 +101,7 @@ { QStringList Voices = m_sapi->getVoiceList(language); ui.voicecombo->clear(); - ui.voicecombo->addItems(Voices); + ui.voicecombo->addItems(Voices); } @@ -114,7 +114,7 @@ // sync settings settings->sync(); updateVoices(ui.languagecombo->currentText()); - + } TTSExesGui::TTSExesGui(QDialog* parent) : QDialog(parent) @@ -129,7 +129,7 @@ void TTSExesGui::reset() { ui.ttspath->setText(""); - ui.ttsoptions->setText(""); + ui.ttsoptions->setText(""); } void TTSExesGui::showCfg(QString name) @@ -137,12 +137,12 @@ m_name = name; // try to get config from settings QString exepath =settings->ttsPath(m_name); - ui.ttsoptions->setText(settings->ttsOptions(m_name)); + ui.ttsoptions->setText(settings->ttsOptions(m_name)); ui.ttspath->setText(exepath); - + //show dialog this->exec(); - + } void TTSExesGui::accept(void) @@ -152,7 +152,7 @@ settings->setTTSOptions(m_name,ui.ttsoptions->text()); // sync settings settings->sync(); - + this->done(0); } @@ -181,3 +181,55 @@ } } + +TTSFestivalGui::TTSFestivalGui(TTSFestival* api, QDialog* parent) : + QDialog(parent), festival(api) +{ + ui.setupUi(this); + this->hide(); + + connect(ui.refreshButton, SIGNAL(clicked()), this, SLOT(updateVoices())); +} + +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]); + } + ui.voicesBox->setCurrentIndex(ui.voicesBox->findText(settings->ttsVoice("festival"))); + + //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"))); +} Index: rbutil/rbutilqt/ttsfestivalcfgform.ui =================================================================== --- rbutil/rbutilqt/ttsfestivalcfgform.ui (revision 0) +++ rbutil/rbutilqt/ttsfestivalcfgform.ui (revision 0) @@ -0,0 +1,300 @@ + + TTSFestivalCfgFrm + + + + 0 + 0 + 340 + 314 + + + + Configuration + + + + + + + 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 + + + + + + + label + serverPath + serverButton + label_2 + layoutWidget + groupBox2 + + + + + + Qt::Vertical + + + + 153 + 43 + + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + false + + + + + + + Qt::Horizontal + + + + 70 + 20 + + + + + + + + + 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 + + + + + + + + + buttonBox + execsBox + verticalSpacer + horizontalSpacer + verticalSpacer + groupBox2 + + + + + + + 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 20206) +++ rbutil/rbutilqt/tts.cpp (working copy) @@ -33,7 +33,7 @@ #if defined(Q_OS_WIN) ttsList["sapi"] = "Sapi TTS Engine"; #endif - + ttsList["festival"] = "Festival TTS Engine"; } // function to get a specific encoder @@ -47,11 +47,17 @@ if(ttsName == "sapi") { tts = new TTSSapi(); - ttsCache[ttsName] = tts; + ttsCache[ttsName] = tts; return tts; } - else + else if (ttsName == "festival") { + tts = new TTSFestival(); + ttsCache[ttsName] = tts; + return tts; + } + else + { tts = new TTSExes(ttsName); ttsCache[ttsName] = tts; return tts; @@ -90,30 +96,30 @@ 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\""; - + } void TTSExes::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 + //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++) + for(int i = 0; i < path.size(); i++) { QString executable = QDir::fromNativeSeparators(path.at(i)) + "/" + m_name; #if defined(Q_OS_WIN) @@ -131,14 +137,14 @@ settings->setTTSPath(m_name,exepath); settings->sync(); } - + } bool TTSExes::start(QString *errStr) { m_TTSexec = settings->ttsPath(m_name); m_TTSOpts = settings->ttsOptions(m_name); - + m_TTSTemplate = m_TemplateMap.value(m_name); QFileInfo tts(m_TTSexec); @@ -153,8 +159,10 @@ } } -bool TTSExes::voice(QString text,QString wavfile) +TTSStatus TTSExes::voice(QString text,QString wavfile, QString *errStr, bool ignoreNonAscii) { + TTSBase::voice(text, wavfile, errStr, ignoreNonAscii); + QString execstring = m_TTSTemplate; execstring.replace("%exe",m_TTSexec); @@ -163,7 +171,7 @@ execstring.replace("%text",text); //qDebug() << "voicing" << execstring; QProcess::execute(execstring); - return true; + return NoError; } @@ -181,10 +189,10 @@ bool TTSExes::configOk() { QString path = settings->ttsPath(m_name); - + if (QFileInfo(path).exists()) return true; - + return false; } @@ -200,24 +208,24 @@ bool TTSSapi::start(QString *errStr) -{ +{ m_TTSOpts = settings->ttsOptions("sapi"); m_TTSLanguage =settings->ttsLang("sapi"); m_TTSVoice=settings->ttsVoice("sapi"); m_TTSSpeed=QString("%1").arg(settings->ttsSpeed("sapi")); m_sapi4 = settings->ttsUseSapi4(); - + QFile::remove(QDir::tempPath() +"/sapi_voice.vbs"); QFile::copy(":/builtin/sapi_voice.vbs",QDir::tempPath() + "/sapi_voice.vbs"); m_TTSexec = QDir::tempPath() +"/sapi_voice.vbs"; - + QFileInfo tts(m_TTSexec); if(!tts.exists()) { *errStr = tr("Could not copy the Sapi-script"); return false; - } + } // create the voice process QString execstring = m_TTSTemplate; execstring.replace("%exe",m_TTSexec); @@ -225,31 +233,31 @@ execstring.replace("%lang",m_TTSLanguage); execstring.replace("%voice",m_TTSVoice); execstring.replace("%speed",m_TTSSpeed); - + if(m_sapi4) execstring.append(" /sapi4 "); - - qDebug() << "init" << execstring; + + qDebug() << "init" << execstring; voicescript = new QProcess(NULL); //connect(voicescript,SIGNAL(readyReadStandardError()),this,SLOT(error())); - + voicescript->start(execstring); if(!voicescript->waitForStarted()) { *errStr = tr("Could not start the Sapi-script"); return false; } - - if(!voicescript->waitForReadyRead(300)) + + if(!voicescript->waitForReadyRead(300)) { *errStr = voicescript->readAllStandardError(); if(*errStr != "") - return false; + return false; } - + voicestream = new QTextStream(voicescript); voicestream->setCodec("UTF16-LE"); - + return true; } @@ -257,31 +265,31 @@ QStringList TTSSapi::getVoiceList(QString language) { QStringList result; - + QFile::copy(":/builtin/sapi_voice.vbs",QDir::tempPath() + "/sapi_voice.vbs"); m_TTSexec = QDir::tempPath() +"/sapi_voice.vbs"; - + QFileInfo tts(m_TTSexec); if(!tts.exists()) return result; - + // create the voice process QString execstring = "cscript //nologo \"%exe\" /language:%lang /listvoices"; execstring.replace("%exe",m_TTSexec); execstring.replace("%lang",language); - + if(settings->ttsUseSapi4()) execstring.append(" /sapi4 "); - - qDebug() << "init" << execstring; + + qDebug() << "init" << execstring; voicescript = new QProcess(NULL); voicescript->start(execstring); - qDebug() << "wait for started"; + qDebug() << "wait for started"; if(!voicescript->waitForStarted()) return result; voicescript->closeWriteChannel(); voicescript->waitForReadyRead(); - + QString dataRaw = voicescript->readAllStandardError().data(); result = dataRaw.split(",",QString::SkipEmptyParts); result.sort(); @@ -290,40 +298,41 @@ { result[i] = result.at(i).simplified(); } - - + + delete voicescript; - QFile::setPermissions(QDir::tempPath() +"/sapi_voice.vbs",QFile::ReadOwner |QFile::WriteOwner|QFile::ExeOwner + QFile::setPermissions(QDir::tempPath() +"/sapi_voice.vbs",QFile::ReadOwner |QFile::WriteOwner|QFile::ExeOwner |QFile::ReadUser| QFile::WriteUser| QFile::ExeUser |QFile::ReadGroup |QFile::WriteGroup |QFile::ExeGroup |QFile::ReadOther |QFile::WriteOther |QFile::ExeOther ); QFile::remove(QDir::tempPath() +"/sapi_voice.vbs"); - + return result; } -bool TTSSapi::voice(QString text,QString wavfile) +TTSStatus TTSSapi::voice(QString text,QString wavfile, QString *errStr, bool ignoreNonAscii) { + TTSBase::voice(text, wavfile, errStr, ignoreNonAscii); 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() -{ - +{ + *voicestream << "QUIT\r\n"; voicestream->flush(); voicescript->waitForFinished(); delete voicestream; delete voicescript; - QFile::setPermissions(QDir::tempPath() +"/sapi_voice.vbs",QFile::ReadOwner |QFile::WriteOwner|QFile::ExeOwner + QFile::setPermissions(QDir::tempPath() +"/sapi_voice.vbs",QFile::ReadOwner |QFile::WriteOwner|QFile::ExeOwner |QFile::ReadUser| QFile::WriteUser| QFile::ExeUser |QFile::ReadGroup |QFile::WriteGroup |QFile::ExeGroup |QFile::ReadOther |QFile::WriteOther |QFile::ExeOther ); @@ -351,3 +360,236 @@ } +/********************************************************************** + * TSSFestival - client-server wrapper + **********************************************************************/ +TTSFestival::TTSFestival() +{ +} + +void TTSFestival::startServer() +{ + if(!configOk()) + return; + + QStringList paths = settings->ttsPath("festival").split(":"); + + serverProcess.start(QString("%1 --server").arg(paths[0])); + serverProcess.waitForStarted(); + serverProcess.waitForReadyRead(); + + QString response(serverProcess.readAll()); + if (!response.isEmpty()) + qDebug() << "Festival is up and running"; + else + qDebug() << "Festival failed to start"; + + // at this point the server has entered the Scheme engine + // but we still cannot send it commands + // serverProcess.waitForReadyRead(2000); +} + +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_%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) +{ + TTSBase::voice(text, wavfile, errStr, ignoreNonAscii); + 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("nist")) + { + *errStr = tr("engine could not voice string"); + return FatalError; + } + clientProcess.closeReadChannel(QProcess::StandardError); + clientProcess.closeReadChannel(QProcess::StandardOutput); + clientProcess.terminate(); + clientProcess.kill(); + + QFileInfo info(wavfile); + do + { + info.refresh(); + } + while(!info.exists()); + + 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; + } + ensureServerRunning(); + + QTcpSocket socket; + QStringList responseLines; + QString response; + size_t retry = 0; + do + { + socket.connectToHost("localhost", 1314); + socket.waitForConnected(); + + if (socket.state() != QAbstractSocket::ConnectedState) + { + ensureServerRunning(); + qDebug() << "Error: " << socket.errorString(); + continue; + } + socket.write(QString("(voice.list)\n\n").toAscii()); + socket.waitForBytesWritten(); + socket.waitForReadyRead(); + + response = socket.readAll(); + response = response.trimmed(); + responseLines = response.split('\n'); + qDebug() << responseLines; + + socket.disconnectFromHost(); + + retry += 1; + if (retry >= 10) + { + qDebug() << "Could not get voices list"; + return QStringList(); + } + }while(responseLines.size() < 2); + + if (!responseLines[0].startsWith("LP")) + return QStringList(); + + // responseLine should be (, ) + voices = responseLines[1].mid(1, responseLines[1].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; +} + + +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(); + } + +} + +bool TTSBase::isASCII(const QString str) +{ + QByteArray data = str.toUtf8(); + for(int i=0; i 127 + return false; + } + return true; +} Index: rbutil/rbutilqt/ttsexescfgfrm.ui =================================================================== --- rbutil/rbutilqt/ttsexescfgfrm.ui (revision 20206) +++ rbutil/rbutilqt/ttsexescfgfrm.ui (working copy) @@ -5,8 +5,8 @@ 0 0 - 463 - 214 + 389 + 206 @@ -22,7 +22,7 @@ - + 0 255 @@ -61,7 +61,7 @@ Qt::Vertical - + 20 40 @@ -83,7 +83,7 @@ Qt::Horizontal - + 40 20 @@ -97,7 +97,8 @@ &Ok - :/icons/go-next.png + + :/icons/go-next.png:/icons/go-next.png @@ -107,7 +108,8 @@ &Cancel - :/icons/process-stop.png + + :/icons/process-stop.png:/icons/process-stop.png Index: rbutil/rbutilqt/rbutilqt.pro =================================================================== --- rbutil/rbutilqt/rbutilqt.pro (revision 20206) +++ rbutil/rbutilqt/rbutilqt.pro (working copy) @@ -1,12 +1,13 @@ -unix:!mac { +unix:!mac { CCACHE = $$system(which ccache) - !isEmpty(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 @@ -14,237 +15,249 @@ # check version of Qt installation VER = $$find(QT_VERSION, ^4\.[3-9]+.*) -isEmpty(VER) { +isEmpty(VER) { !isEmpty(QT_VERSION) error("Qt found:" $$[QT_VERSION]) error("Qt >= 4.3 required!") } 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 \ - base/httpget.cpp \ - configure.cpp \ - zip/zip.cpp \ - zip/unzip.cpp \ - installzip.cpp \ - progressloggergui.cpp \ - installtalkwindow.cpp \ - talkfile.cpp \ - base/autodetection.cpp \ - ../ipodpatcher/ipodpatcher.c \ - ../sansapatcher/sansapatcher.c \ - browsedirtree.cpp \ - installthemes.cpp \ - base/uninstall.cpp \ - uninstallwindow.cpp \ - base/utils.cpp \ - preview.cpp \ - encoders.cpp \ - encodersgui.cpp \ - tts.cpp \ - ttsgui.cpp \ - ../../tools/wavtrim.c \ - ../../tools/voicefont.c \ - voicefile.cpp \ - createvoicewindow.cpp \ - rbsettings.cpp \ - base/rbunzip.cpp \ - base/rbzip.cpp \ - base/detect.cpp \ - sysinfo.cpp \ - base/bootloaderinstallbase.cpp \ - base/bootloaderinstallmi4.cpp \ - base/bootloaderinstallhex.cpp \ - base/bootloaderinstallipod.cpp \ - base/bootloaderinstallsansa.cpp \ - base/bootloaderinstallfile.cpp \ - ../../tools/mkboot.c \ - ../../tools/iriver.c - + main.cpp \ + install.cpp \ + base/httpget.cpp \ + configure.cpp \ + zip/zip.cpp \ + zip/unzip.cpp \ + installzip.cpp \ + progressloggergui.cpp \ + installtalkwindow.cpp \ + talkfile.cpp \ + base/autodetection.cpp \ + ../ipodpatcher/ipodpatcher.c \ + ../sansapatcher/sansapatcher.c \ + browsedirtree.cpp \ + installthemes.cpp \ + base/uninstall.cpp \ + uninstallwindow.cpp \ + base/utils.cpp \ + preview.cpp \ + encoders.cpp \ + encodersgui.cpp \ + tts.cpp \ + ttsgui.cpp \ + ../../tools/wavtrim.c \ + ../../tools/voicefont.c \ + voicefile.cpp \ + createvoicewindow.cpp \ + rbsettings.cpp \ + base/rbunzip.cpp \ + base/rbzip.cpp \ + base/detect.cpp \ + sysinfo.cpp \ + base/bootloaderinstallbase.cpp \ + base/bootloaderinstallmi4.cpp \ + base/bootloaderinstallhex.cpp \ + base/bootloaderinstallipod.cpp \ + base/bootloaderinstallsansa.cpp \ + base/bootloaderinstallfile.cpp \ + ../../tools/mkboot.c \ + ../../tools/iriver.c HEADERS += rbutilqt.h \ - install.h \ - base/httpget.h \ - configure.h \ - zip/zip.h \ - zip/unzip.h \ - zip/zipentry_p.h \ - zip/unzip_p.h \ - zip/zip_p.h \ - version.h \ - installzip.h \ - installtalkwindow.h \ - talkfile.h \ - base/autodetection.h \ - progressloggerinterface.h \ - progressloggergui.h \ - ../ipodpatcher/ipodpatcher.h \ - ../ipodpatcher/ipodio.h \ - ../ipodpatcher/parttypes.h \ - ../sansapatcher/sansapatcher.h \ - ../sansapatcher/sansaio.h \ - irivertools/h100sums.h \ - irivertools/h120sums.h \ - irivertools/h300sums.h \ - browsedirtree.h \ - installthemes.h \ - base/uninstall.h \ - uninstallwindow.h \ - base/utils.h \ - preview.h \ - encoders.h \ - encodersgui.h \ - tts.h \ - ttsgui.h \ - ../../tools/wavtrim.h \ - ../../tools/voicefont.h \ - voicefile.h \ - createvoicewindow.h \ - rbsettings.h \ - base/rbunzip.h \ - base/rbzip.h \ - sysinfo.h \ - base/detect.h \ - base/bootloaderinstallbase.h \ - base/bootloaderinstallmi4.h \ - base/bootloaderinstallhex.h \ - base/bootloaderinstallipod.h \ - base/bootloaderinstallsansa.h \ - base/bootloaderinstallfile.h \ - ../../tools/mkboot.h \ - ../../tools/iriver.h + install.h \ + base/httpget.h \ + configure.h \ + zip/zip.h \ + zip/unzip.h \ + zip/zipentry_p.h \ + zip/unzip_p.h \ + zip/zip_p.h \ + version.h \ + installzip.h \ + installtalkwindow.h \ + talkfile.h \ + base/autodetection.h \ + progressloggerinterface.h \ + progressloggergui.h \ + ../ipodpatcher/ipodpatcher.h \ + ../ipodpatcher/ipodio.h \ + ../ipodpatcher/parttypes.h \ + ../sansapatcher/sansapatcher.h \ + ../sansapatcher/sansaio.h \ + irivertools/h100sums.h \ + irivertools/h120sums.h \ + irivertools/h300sums.h \ + browsedirtree.h \ + installthemes.h \ + base/uninstall.h \ + uninstallwindow.h \ + base/utils.h \ + preview.h \ + encoders.h \ + encodersgui.h \ + tts.h \ + ttsgui.h \ + ../../tools/wavtrim.h \ + ../../tools/voicefont.h \ + voicefile.h \ + createvoicewindow.h \ + rbsettings.h \ + base/rbunzip.h \ + base/rbzip.h \ + sysinfo.h \ + base/detect.h \ + base/bootloaderinstallbase.h \ + base/bootloaderinstallmi4.h \ + base/bootloaderinstallhex.h \ + base/bootloaderinstallipod.h \ + base/bootloaderinstallsansa.h \ + base/bootloaderinstallfile.h \ + ../../tools/mkboot.h \ + ../../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 +dbg { + CONFIG += debug \ + thread \ + qt \ + warn_on DEFINES -= QT_NO_DEBUG_OUTPUT message("debug") } -!dbg { - CONFIG += release thread qt +!dbg { + CONFIG += release \ + thread \ + qt DEFINES += QT_NO_DEBUG_OUTPUT message("release") } - TARGET = rbutilqt - -FORMS += rbutilqtfrm.ui \ - aboutbox.ui \ - installfrm.ui \ - progressloggerfrm.ui \ - configurefrm.ui \ - browsedirtreefrm.ui \ - installtalkfrm.ui \ - installthemesfrm.ui \ - uninstallfrm.ui \ - previewfrm.ui \ - rbspeexcfgfrm.ui \ - encexescfgfrm.ui \ - ttsexescfgfrm.ui \ - sapicfgfrm.ui \ - createvoicefrm.ui \ - sysinfofrm.ui - +FORMS += ttsfestivalcfgform.ui \ + rbutilqtfrm.ui \ + aboutbox.ui \ + installfrm.ui \ + progressloggerfrm.ui \ + configurefrm.ui \ + browsedirtreefrm.ui \ + installtalkfrm.ui \ + installthemesfrm.ui \ + uninstallfrm.ui \ + previewfrm.ui \ + rbspeexcfgfrm.ui \ + encexescfgfrm.ui \ + ttsexescfgfrm.ui \ + 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 \ - lang/rbutil_gr.ts \ - lang/rbutil_he.ts \ - lang/rbutil_ja.ts \ - lang/rbutil_nl.ts \ - lang/rbutil_pt.ts \ - lang/rbutil_tr.ts \ - lang/rbutil_zh_CN.ts \ - lang/rbutil_zh_TW.ts \ - - + lang/rbutil_fi.ts \ + lang/rbutil_fr.ts \ + lang/rbutil_gr.ts \ + lang/rbutil_he.ts \ + lang/rbutil_ja.ts \ + lang/rbutil_nl.ts \ + lang/rbutil_pt.ts \ + lang/rbutil_tr.ts \ + lang/rbutil_zh_CN.ts \ + lang/rbutil_zh_TW.ts QT += network -DEFINES += RBUTIL _LARGEFILE64_SOURCE - -win32 { - SOURCES += ../ipodpatcher/ipodio-win32.c - SOURCES += ../sansapatcher/sansaio-win32.c +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 { + 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 - # later will get linked dynamically again. - LIBS += -Wl,-Bstatic -lusb -Wl,-Bdynamic -} +unix:!static:LIBS += -lusb +unix:static:# force statically linking of libusb. Libraries that are appended -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 +# later will get linked dynamically again. +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 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 { +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 { +unix { target.path = /usr/local/bin INSTALLS += target } - - - Index: rbutil/rbutilqt/progressloggerinterface.h =================================================================== --- rbutil/rbutilqt/progressloggerinterface.h (revision 20206) +++ rbutil/rbutilqt/progressloggerinterface.h (working copy) @@ -24,7 +24,7 @@ #include -enum { +enum LOGLEVEL { LOGNOICON, LOGOK, LOGINFO, LOGWARNING, LOGERROR }; Index: rbutil/rbutilqt/talkfile.h =================================================================== --- rbutil/rbutilqt/talkfile.h (revision 20206) +++ rbutil/rbutilqt/talkfile.h (working copy) @@ -39,16 +39,17 @@ bool createTalkFiles(ProgressloggerInterface* logger); void setSettings(RbSettings* sett) { settings = sett;} - + 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;} - void setTalkFolders(bool ov) {m_talkFolders = ov;} + void setTalkFolders(bool ov) {m_talkFolders = ov;} void setTalkFiles(bool ov) {m_talkFiles = ov;} - + private slots: void abort(); @@ -58,24 +59,25 @@ 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); - + TTSBase* m_tts; EncBase* m_enc; RbSettings* settings; - + QDir m_dir; QString m_mountpoint; int m_progress; - + bool m_overwriteTalk; bool m_recursive; 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 20206) +++ rbutil/rbutilqt/voicefile.cpp (working copy) @@ -39,15 +39,15 @@ m_abort = false; m_logger = logger; m_logger->addItem(tr("Starting Voicefile generation"),LOGINFO); - + // test if tempdir exists if(!QDir(QDir::tempPath()+"/rbvoice/").exists()) { QDir(QDir::tempPath()).mkdir("rbvoice"); } - - m_path = QDir::tempPath() + "/rbvoice/"; - + + m_path = QDir::tempPath() + "/rbvoice/"; + // read rockbox-info.txt QFile info(m_mountpoint+"/.rockbox/rockbox-info.txt"); if(!info.open(QIODevice::ReadOnly)) @@ -57,11 +57,11 @@ emit done(false); return false; } - + QString target, features,version; while (!info.atEnd()) { QString line = info.readLine(); - + if(line.contains("Target:")) { target = line.remove("Target:").trimmed(); @@ -77,13 +77,13 @@ } } info.close(); - + //prepare download url QUrl genlangUrl = settings->genlangUrl() +"?lang=" +m_lang+"&t="+target+"&rev="+version+"&f="+features; - + qDebug() << "downloading " << genlangUrl; - - //download the correct genlang output + + //download the correct genlang output QTemporaryFile *downloadFile = new QTemporaryFile(this); downloadFile->open(); filename = downloadFile->fileName(); @@ -91,11 +91,11 @@ // get the real file. getter = new HttpGet(this); getter->setFile(downloadFile); - + connect(getter, SIGNAL(done(bool)), this, SLOT(downloadDone(bool))); connect(getter, SIGNAL(dataReadProgress(int, int)), this, SLOT(updateDataReadProgress(int, int))); connect(m_logger, SIGNAL(aborted()), getter, SLOT(abort())); - + getter->getFile(genlangUrl); return true; } @@ -127,9 +127,9 @@ } else m_logger->addItem(tr("Download finished."),LOGOK); QCoreApplication::processEvents(); - - - m_logger->setProgressMax(0); + + + m_logger->setProgressMax(0); //open downloaded file QFile genlang(filename); if(!genlang.open(QIODevice::ReadOnly)) @@ -138,12 +138,12 @@ m_logger->abort(); emit done(false); return; - } + } //tts - m_tts = TTSBase::getTTS(settings->curTTS()); + m_tts = TTSBase::getTTS(settings->curTTS()); m_tts->setCfg(settings); - + QString errStr; if(!m_tts->start(&errStr)) { @@ -155,9 +155,9 @@ } // Encoder - m_enc = EncBase::getEncoder(settings->curEncoder()); + m_enc = EncBase::getEncoder(settings->curEncoder()); m_enc->setCfg(settings); - + if(!m_enc->start()) { m_logger->addItem(tr("Init of Encoder engine failed"),LOGERROR); @@ -169,15 +169,15 @@ QCoreApplication::processEvents(); connect(m_logger,SIGNAL(aborted()),this,SLOT(abort())); - + //read in downloaded file - QList > voicepairs; + QList > voicepairs; QTextStream in(&genlang); in.setCodec("UTF-8"); QString id, voice; bool idfound = false; bool voicefound=false; - while (!in.atEnd()) + while (!in.atEnd()) { QString line = in.readLine(); if(line.contains("id:")) //ID found @@ -190,82 +190,85 @@ voice = line.remove("voice:").remove('"').trimmed(); voicefound=true; } - + if(idfound && voicefound) { voicepairs.append(QPair(id,voice)); idfound=false; voicefound=false; - } + } } genlang.close(); - + // check for empty list if(voicepairs.size() == 0) { - m_logger->addItem(tr("The downloaded file was empty!"),LOGERROR); + m_logger->addItem(tr("The downloaded file was empty!"),LOGERROR); m_logger->abort(); m_tts->stop(); emit done(false); return; } - + m_logger->setProgressMax(voicepairs.size()); m_logger->setProgressValue(0); - + // create voice clips QStringList mp3files; for(int i=0; i< voicepairs.size(); i++) - { + { if(m_abort) { - m_logger->addItem("aborted.",LOGERROR); + m_logger->addItem("aborted.",LOGERROR); m_logger->abort(); m_tts->stop(); emit done(false); return; - } - + } + m_logger->setProgressValue(i); - + QString wavname = m_path + "/" + voicepairs.at(i).first + ".wav"; QString toSpeak = voicepairs.at(i).second; QString encodedname = m_path + "/" + voicepairs.at(i).first +".mp3"; - + // todo PAUSE if(voicepairs.at(i).first == "VOICE_PAUSE") { QFile::copy(":/builtin/builtin/VOICE_PAUSE.wav",m_path + "/VOICE_PAUSE.wav"); - + } else - { + { if(toSpeak == "") continue; - + 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 char buffer[255]; - + wavtrim((char*)qPrintable(wavname),m_wavtrimThreshold,buffer,255); - - // encode wav + + // encode wav m_enc->encode(wavname,encodedname); - // remove the wav file + // remove the wav file QFile::remove(wavname); // remember the mp3 file for later removing mp3files << encodedname; } - - - //make voicefile + + + //make voicefile FILE* ids2 = fopen(filename.toUtf8(), "r"); if (ids2 == NULL) { - m_logger->addItem(tr("Error opening downloaded file"),LOGERROR); + m_logger->addItem(tr("Error opening downloaded file"),LOGERROR); m_logger->abort(); emit done(false); return; @@ -274,32 +277,32 @@ FILE* output = fopen(QString(m_mountpoint + "/.rockbox/langs/" + m_lang + ".voice").toUtf8(), "wb"); if (output == NULL) { - m_logger->addItem(tr("Error opening output file"),LOGERROR); - emit done(false); + m_logger->addItem(tr("Error opening output file"),LOGERROR); + emit done(false); return; } - + voicefont(ids2,m_targetid,(char*)(const char*)m_path.toUtf8(), output); - + //remove .mp3 files for(int i=0;i< mp3files.size(); i++) { QFile::remove(mp3files.at(i)); - } + } // Add Voice file to the install log QSettings installlog(m_mountpoint + "/.rockbox/rbutil.log", QSettings::IniFormat, 0); - installlog.beginGroup("selfcreated Voice"); + installlog.beginGroup("selfcreated Voice"); installlog.setValue("/.rockbox/langs/" + m_lang + ".voice",QDate::currentDate().toString("yyyyMMdd")); installlog.endGroup(); installlog.sync(); m_logger->setProgressMax(100); m_logger->setProgressValue(100); - m_logger->addItem(tr("successfully created."),LOGOK); - m_logger->abort(); + m_logger->addItem(tr("successfully created."),LOGOK); + m_logger->abort(); - emit done(true); + emit done(true); } void VoiceFileCreator::updateDataReadProgress(int read, int total) Index: rbutil/rbutilqt/installtalkwindow.cpp =================================================================== --- rbutil/rbutilqt/installtalkwindow.cpp (revision 20206) +++ rbutil/rbutilqt/installtalkwindow.cpp (working copy) @@ -65,22 +65,22 @@ { Config *cw = new Config(this,4); cw->setSettings(settings); - + connect(cw, SIGNAL(settingsUpdated()), this, SLOT(updateSettings())); - + cw->show(); } void InstallTalkWindow::accept() { logger = new ProgressLoggerGui(this); - + connect(logger,SIGNAL(closed()),this,SLOT(close())); logger->show(); - + QString folderToTalk = ui.lineTalkFolder->text(); - + if(!QFileInfo(folderToTalk).isDir()) { logger->addItem(tr("The Folder to Talk is wrong!"),LOGERROR); @@ -95,13 +95,14 @@ talkcreator->setSettings(settings); 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()); talkcreator->setTalkFolders(ui.talkFolders->isChecked()); talkcreator->setTalkFiles(ui.talkFiles->isChecked()); - + talkcreator->createTalkFiles(logger); } @@ -124,7 +125,7 @@ else ui.labelTtsProfile->setText(tr("Selected TTS engine: %1") .arg("Invalid TTS configuration!")); - + QString encoder = settings->curEncoder(); EncBase* enc = EncBase::getEncoder(encoder); if(enc != NULL) { Index: rbutil/rbutilqt/tts.h =================================================================== --- rbutil/rbutilqt/tts.h (revision 20206) +++ rbutil/rbutilqt/tts.h (working copy) @@ -19,13 +19,15 @@ * ****************************************************************************/ - + #ifndef TTS_H #define TTS_H #include "rbsettings.h" #include +#include +#include #ifndef CONSOLE #include "ttsgui.h" @@ -33,30 +35,38 @@ #include "ttsguicli.h" #endif - +enum TTSStatus{ FatalError, NoError, Warning }; 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) + { + if(ignoreNonAscii && !isASCII(text)) + { + *errStr = tr("Ignored non-ASCII text."); + return Warning; + } + + (void)wavfile; (void) errStr; return FatalError; + } virtual bool start(QString *errStr) { (void)errStr; return false; } virtual bool stop() { return false; } virtual void showCfg(){} virtual bool configOk() { return false; } virtual void setCfg(RbSettings* sett) { settings = sett; } - + static TTSBase* getTTS(QString ttsname); static QStringList getTTSList(); static QString getTTSName(QString tts); - + public slots: virtual void accept(void){} virtual void reject(void){} virtual void reset(void){} - + private: //inits the tts List static void initTTSList(); @@ -65,6 +75,7 @@ RbSettings* settings; static QMap ttsList; static QMap ttsCache; + bool isASCII(const QString str); }; class TTSSapi : public TTSBase @@ -72,18 +83,18 @@ 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(); virtual bool configOk(); - + QStringList getVoiceList(QString language); private: QProcess* voicescript; QTextStream* voicestream; QString defaultLanguage; - + QString m_TTSexec; QString m_TTSOpts; QString m_TTSTemplate; @@ -99,14 +110,14 @@ 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(); virtual bool configOk(); virtual void setCfg(RbSettings* sett); - + private: QString m_name; QString m_TTSexec; @@ -115,4 +126,23 @@ 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(); +private: + void startServer(); + void ensureServerRunning(); + QProcess serverProcess; + QStringList voices; +}; #endif Index: rbutil/rbutilqt/installtalkfrm.ui =================================================================== --- rbutil/rbutilqt/installtalkfrm.ui (revision 20206) +++ rbutil/rbutilqt/installtalkfrm.ui (working copy) @@ -125,7 +125,7 @@ - + Overwrite Talkfiles @@ -158,14 +158,14 @@ + + + + Ignore filenames with non-ASCII characters + + + - recursive - StripExtensions - OverwriteTalk - talkFolders - talkFiles - label_3 - fileFilter Index: rbutil/rbutilqt/ttsgui.h =================================================================== --- rbutil/rbutilqt/ttsgui.h (revision 20206) +++ rbutil/rbutilqt/ttsgui.h (working copy) @@ -26,16 +26,18 @@ #include "ui_ttsexescfgfrm.h" #include "ui_sapicfgfrm.h" +#include "ui_ttsfestivalcfgform.h" class RbSettings; class TTSSapi; +class TTSFestival; class TTSSapiGui : public QDialog { Q_OBJECT public: TTSSapiGui(TTSSapi* sapi,QDialog* parent = NULL); - + void showCfg(); void setCfg(RbSettings* sett){settings = sett;} public slots: @@ -56,14 +58,14 @@ Q_OBJECT public: TTSExesGui(QDialog* parent = NULL); - + void showCfg(QString m_name); void setCfg(RbSettings* sett){settings = sett;} public slots: virtual void accept(void); virtual void reject(void); - virtual void reset(void); + virtual void reset(void); void browse(void); private: Ui::TTSExesCfgFrm ui; @@ -71,4 +73,23 @@ 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; +}; #endif Index: rbutil/rbutilqt/talkfile.cpp =================================================================== --- rbutil/rbutilqt/talkfile.cpp (revision 20206) +++ rbutil/rbutilqt/talkfile.cpp (working copy) @@ -24,25 +24,25 @@ } -//! \brief Creates Talkfiles. +//! \brief Creates Talkfiles. //! //! \param logger A pointer to a Loggerobject bool TalkFileCreator::createTalkFiles(ProgressloggerInterface* logger) { m_abort = false; m_logger = logger; - + QMultiMap fileList; QMultiMap dirList; - QStringList toSpeakList; + QStringList toSpeakList, voicedEntries, encodedEntries; QString errStr; - + m_logger->addItem(tr("Starting Talk file generation"),LOGINFO); - + //tts - m_tts = TTSBase::getTTS(settings->curTTS()); + m_tts = TTSBase::getTTS(settings->curTTS()); m_tts->setCfg(settings); - + if(!m_tts->start(&errStr)) { m_logger->addItem(errStr.trimmed(),LOGERROR); @@ -52,9 +52,9 @@ } // Encoder - m_enc = EncBase::getEncoder(settings->curEncoder()); + m_enc = EncBase::getEncoder(settings->curEncoder()); m_enc->setCfg(settings); - + if(!m_enc->start()) { m_logger->addItem(tr("Init of Encoder engine failed"),LOGERROR); @@ -67,7 +67,7 @@ connect(logger,SIGNAL(aborted()),this,SLOT(abort())); m_logger->setProgressMax(0); - + // read in Maps of paths - file/dirnames m_logger->addItem(tr("Reading Filelist..."),LOGINFO); if(createDirAndFileMaps(m_dir,&dirList,&fileList) == false) @@ -76,21 +76,21 @@ doAbort(toSpeakList); return false; } - + // create List of all Files/Dirs to speak QMapIterator dirIt(dirList); - while (dirIt.hasNext()) + while (dirIt.hasNext()) { dirIt.next(); // insert only non dublicate dir entries into list if(!toSpeakList.contains(dirIt.value())) { qDebug() << "toSpeaklist dir:" << dirIt.value(); - toSpeakList.append(dirIt.value()); + toSpeakList.append(dirIt.value()); } } QMapIterator fileIt(fileList); - while (fileIt.hasNext()) + while (fileIt.hasNext()) { fileIt.next(); // insert only non- dublictae file entries into list @@ -99,46 +99,45 @@ if(m_stripExtensions) toSpeakList.append(stripExtension(fileIt.value())); else - toSpeakList.append(fileIt.value()); + toSpeakList.append(fileIt.value()); } } - - // 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); + 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; } - - // Copying talk files + + // Copying talk files m_logger->addItem(tr("Copying Talkfile for Dirs..."),LOGINFO); if(copyTalkDirFiles(dirList,&errStr) == false) { m_logger->addItem(errStr,LOGERROR); - doAbort(toSpeakList); + doAbort(toSpeakList); return false; } - + //Copying file talk files m_logger->addItem(tr("Copying Talkfile for Files..."),LOGINFO); if(copyTalkFileFiles(fileList,&errStr) == false) { m_logger->addItem(errStr,LOGERROR); - doAbort(toSpeakList); + doAbort(toSpeakList); return false; } - + // Deleting left overs if( !cleanup(toSpeakList)) return false; @@ -185,7 +184,7 @@ m_logger->setProgressValue(0); m_logger->abort(); m_tts->stop(); - m_enc->stop(); + m_enc->stop(); } //! \brief Creates MultiMaps (paths -> File/dir names) of all Dirs and Files in a Folder. @@ -195,16 +194,16 @@ //! \param startDir The dir where it beginns scanning //! \param dirMap The MulitMap where the dirs are stored //! \param filMap The MultiMap where Files are stored -//! \returns true on Success, false if User aborted. +//! \returns true on Success, false if User aborted. bool TalkFileCreator::createDirAndFileMaps(QDir startDir,QMultiMap *dirMap,QMultiMap *fileMap) { // create Iterator QDirIterator::IteratorFlags flags = QDirIterator::NoIteratorFlags; if(m_recursive) flags = QDirIterator::Subdirectories; - + QDirIterator it(startDir,flags); - + // read in Maps of paths - file/dirnames while (it.hasNext()) { @@ -213,14 +212,14 @@ { return false; } - + QFileInfo fileInf = it.fileInfo(); - + // its a dir if(fileInf.isDir()) { QDir dir = fileInf.dir(); - + // insert into List if(!dir.dirName().isEmpty() && m_talkFolders) { @@ -239,7 +238,7 @@ } QCoreApplication::processEvents(); } - return true; + return true; } //! \brief Voices a List of string to the temp dir. Progress is handled inside. @@ -247,59 +246,76 @@ //! \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; } //! \brief Encodes a List of strings from/to the temp dir. Progress is handled inside. -//! It expects the inputfile in the temp dir with the name in the List appended with ".wav" +//! It expects the inputfile in the temp dir with the name in the List appended with ".wav" //! //! \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; } - + QString wavfilename = QDir::tempPath()+ "/"+ toEncode[i] + ".wav"; QString filename = QDir::tempPath()+ "/"+ toEncode[i] + ".talk"; - + 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(); + QCoreApplication::processEvents(); } return true; } @@ -308,16 +324,16 @@ //! //! \param dirMap a MultiMap of Paths -> Dirnames //! \param errString Pointer to a QString where the error cause is written. -//! \returns true on success, false on error or user abort +//! \returns true on success, false on error or user abort bool TalkFileCreator::copyTalkDirFiles(QMultiMap dirMap,QString* errString) { resetProgress(dirMap.size()); - + QSettings installlog(m_mountpoint + "/.rockbox/rbutil.log", QSettings::IniFormat, 0); installlog.beginGroup("talkfiles"); - + QMapIterator it(dirMap); - while (it.hasNext()) + while (it.hasNext()) { it.next(); if(m_abort) @@ -325,47 +341,51 @@ *errString = tr("Talk file creation aborted"); return false; } - + 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 if(m_overwriteTalk && QFile::exists(target)) QFile::remove(target); - + // copying if(!QFile::copy(source,target)) { *errString = tr("Copying of %1 to %2 failed").arg(source).arg(target); return false; - } - + } + // add to installlog QString now = QDate::currentDate().toString("yyyyMMdd"); installlog.setValue(target.remove(0,m_mountpoint.length()),now); - + m_logger->setProgressValue(++m_progress); QCoreApplication::processEvents(); } installlog.endGroup(); - installlog.sync(); - return true; + installlog.sync(); + return true; } //! \brief copys Talkfile for Files from the temp dir to the target. Progress and installlog is handled inside //! //! \param fileMap a MultiMap of Paths -> Filenames //! \param errString Pointer to a QString where the error cause is written. -//! \returns true on success, false on error or user abort +//! \returns true on success, false on error or user abort bool TalkFileCreator::copyTalkFileFiles(QMultiMap fileMap,QString* errString) { resetProgress(fileMap.size()); - + QSettings installlog(m_mountpoint + "/.rockbox/rbutil.log", QSettings::IniFormat, 0); installlog.beginGroup("talkfiles"); - + QMapIterator it(fileMap); - while (it.hasNext()) + while (it.hasNext()) { it.next(); if(m_abort) @@ -373,55 +393,58 @@ *errString = tr("Talk file creation aborted"); return false; } - + QString source; QString target = it.key() + "/" + it.value() + ".talk"; - - // correct source if we hav stripExtension enabled + + // correct source if we hav stripExtension enabled if(m_stripExtensions) - source = QDir::tempPath()+ "/"+ stripExtension(it.value()) + ".talk"; + source = QDir::tempPath()+ "/"+ stripExtension(it.value()) + ".talk"; 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); - + // copy file qDebug() << "copying: " << source << " to " << target; if(!QFile::copy(source,target)) { *errString = tr("Copying of %1 to %2 failed").arg(source).arg(target); return false; - } - + } + // add to Install log QString now = QDate::currentDate().toString("yyyyMMdd"); installlog.setValue(target.remove(0,m_mountpoint.length()),now); - + m_logger->setProgressValue(++m_progress); QCoreApplication::processEvents(); } installlog.endGroup(); - installlog.sync(); - return true; + installlog.sync(); + return true; } //! \brief Cleans up Files potentially left in the temp dir -//! +//! //! \param list List of file to try to delete in the temp dir. Function appends ".wav" and ".talk" to the filenames bool TalkFileCreator::cleanup(QStringList list) { m_logger->addItem(tr("Cleaning up.."),LOGINFO); - + for(int i=0; i < list.size(); i++) - { + { if(QFile::exists(QDir::tempPath()+ "/"+ list[i] + ".wav")) QFile::remove(QDir::tempPath()+ "/"+ list[i] + ".wav"); if(QFile::exists(QDir::tempPath()+ "/"+ list[i] + ".talk")) QFile::remove(QDir::tempPath()+ "/"+ list[i] + ".talk"); - + QCoreApplication::processEvents(); } return true;