diff options
Diffstat (limited to 'kttsd/plugins/epos')
-rw-r--r-- | kttsd/plugins/epos/Makefile.am | 20 | ||||
-rw-r--r-- | kttsd/plugins/epos/README | 3 | ||||
-rw-r--r-- | kttsd/plugins/epos/configure.in.bot | 15 | ||||
-rw-r--r-- | kttsd/plugins/epos/configure.in.in | 22 | ||||
-rw-r--r-- | kttsd/plugins/epos/eposconf.cpp | 319 | ||||
-rw-r--r-- | kttsd/plugins/epos/eposconf.h | 141 | ||||
-rw-r--r-- | kttsd/plugins/epos/eposconfwidget.ui | 610 | ||||
-rw-r--r-- | kttsd/plugins/epos/eposplugin.cpp | 31 | ||||
-rw-r--r-- | kttsd/plugins/epos/eposproc.cpp | 389 | ||||
-rw-r--r-- | kttsd/plugins/epos/eposproc.h | 241 | ||||
-rw-r--r-- | kttsd/plugins/epos/kttsd_eposplugin.desktop | 87 |
11 files changed, 1878 insertions, 0 deletions
diff --git a/kttsd/plugins/epos/Makefile.am b/kttsd/plugins/epos/Makefile.am new file mode 100644 index 0000000..8f40ca7 --- /dev/null +++ b/kttsd/plugins/epos/Makefile.am @@ -0,0 +1,20 @@ +INCLUDES = \ + -I$(top_srcdir)/kttsd/libkttsd -I$(top_builddir)/kttsd/libkttsd \ + $(all_includes) + +METASOURCES = AUTO + +kde_module_LTLIBRARIES = libkttsd_eposplugin.la + +libkttsd_eposplugin_la_SOURCES = \ + eposconfwidget.ui \ + eposconf.cpp \ + eposproc.cpp \ + eposplugin.cpp +libkttsd_eposplugin_la_LDFLAGS = $(KDE_PLUGIN) $(all_libraries) +libkttsd_eposplugin_la_LIBADD = $(top_builddir)/kttsd/libkttsd/libkttsd.la + +services_DATA = kttsd_eposplugin.desktop +servicesdir = $(kde_servicesdir) + +noinst_HEADERS = eposproc.h eposconf.h eposconfwidget.h diff --git a/kttsd/plugins/epos/README b/kttsd/plugins/epos/README new file mode 100644 index 0000000..2f61a9c --- /dev/null +++ b/kttsd/plugins/epos/README @@ -0,0 +1,3 @@ +This is the directory containing the Epos plug in. +This plugin is developed and maintained by Gary Cramblitt. +<garycramblitt@comcast.net> diff --git a/kttsd/plugins/epos/configure.in.bot b/kttsd/plugins/epos/configure.in.bot new file mode 100644 index 0000000..59f4fdc --- /dev/null +++ b/kttsd/plugins/epos/configure.in.bot @@ -0,0 +1,15 @@ +if test "x$epos_bindir" = "xno"; then + if test "$compile_epos_plugin" = "yes"; then + echo "" + echo "=======================================================" + echo "The Epos program does not appear to be" + echo "installed on this system. The epos plugin will" + echo "be built, but you need to install epos before you" + echo "can use it. You can get it at" + echo " http://epos.ure.cas.cz/" + echo "Debian users: apt-get install epos" + echo "======================================================" + all_tests=bad + fi +fi + diff --git a/kttsd/plugins/epos/configure.in.in b/kttsd/plugins/epos/configure.in.in new file mode 100644 index 0000000..5a0f655 --- /dev/null +++ b/kttsd/plugins/epos/configure.in.in @@ -0,0 +1,22 @@ +dnl ========================================= +dnl checks for Festival Lite (epos) Plug In +dnl ========================================= + +AC_ARG_ENABLE(kttsd-epos, + AC_HELP_STRING([--enable-kttsd-epos], + [build KTTSD Epos plugin [default=yes]]), + epos_plugin=$enableval, + epos_plugin=yes) + +compile_epos_plugin="yes" + +if test "x$epos_plugin" = "xno"; then + compile_epos_plugin="no" +fi + +dnl Check for epos executable. +dnl Note that epos plugin is always built, unless +dnl user overrides on configure command line. +AC_PATH_PROG(epos_bindir, "epos", "no") + +AM_CONDITIONAL(include_kttsd_epos, test "x$compile_epos_plugin" != "xno") diff --git a/kttsd/plugins/epos/eposconf.cpp b/kttsd/plugins/epos/eposconf.cpp new file mode 100644 index 0000000..063c9f9 --- /dev/null +++ b/kttsd/plugins/epos/eposconf.cpp @@ -0,0 +1,319 @@ +/***************************************************** vim:set ts=4 sw=4 sts=4: + Configuration widget and functions for Epos plug in + ------------------- + Copyright: + (C) 2004 by Gary Cramblitt <garycramblitt@comcast.net> + ------------------- + Original author: Gary Cramblitt <garycramblitt@comcast.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + ******************************************************************************/ + +// C++ includes. +#include <math.h> + +// Qt includes. +#include <qfile.h> +#include <qapplication.h> +#include <qtextcodec.h> +#include <qlayout.h> +#include <qslider.h> + +// KDE includes. +#include <kdialog.h> +#include <ktempfile.h> +#include <kstandarddirs.h> +#include <kcombobox.h> +#include <klocale.h> +#include <knuminput.h> + +// KTTS includes. +#include <testplayer.h> + +// Epos Plugin includes. +#include "eposproc.h" +#include "eposconf.h" +#include "eposconf.moc" + +/** Constructor */ +EposConf::EposConf( QWidget* parent, const char* name, const QStringList& /*args*/) : + PlugInConf(parent, name) +{ + // kdDebug() << "EposConf::EposConf: Running" << endl; + m_eposProc = 0; + m_progressDlg = 0; + + QVBoxLayout *layout = new QVBoxLayout(this, KDialog::marginHint(), + KDialog::spacingHint(), "EposConfigWidgetLayout"); + layout->setAlignment (Qt::AlignTop); + m_widget = new EposConfWidget(this, "EposConfigWidget"); + layout->addWidget(m_widget); + + // Build codec list and fill combobox. + m_codecList = PlugInProc::buildCodecList(); + m_widget->characterCodingBox->clear(); + m_widget->characterCodingBox->insertStringList(m_codecList); + + defaults(); + + connect(m_widget->eposServerPath, SIGNAL(textChanged(const QString&)), + this, SLOT(configChanged())); + connect(m_widget->eposClientPath, SIGNAL(textChanged(const QString&)), + this, SLOT(configChanged())); + connect(m_widget->timeBox, SIGNAL(valueChanged(int)), + this, SLOT(timeBox_valueChanged(int))); + connect(m_widget->frequencyBox, SIGNAL(valueChanged(int)), + this, SLOT(frequencyBox_valueChanged(int))); + connect(m_widget->timeSlider, SIGNAL(valueChanged(int)), + this, SLOT(timeSlider_valueChanged(int))); + connect(m_widget->frequencySlider, SIGNAL(valueChanged(int)), + this, SLOT(frequencySlider_valueChanged(int))); + connect(m_widget->timeBox, SIGNAL(valueChanged(int)), this, SLOT(configChanged())); + connect(m_widget->timeSlider, SIGNAL(valueChanged(int)), this, SLOT(configChanged())); + connect(m_widget->frequencyBox, SIGNAL(valueChanged(int)), this, SLOT(configChanged())); + connect(m_widget->frequencySlider, SIGNAL(valueChanged(int)), this, SLOT(configChanged())); + connect(m_widget->characterCodingBox, SIGNAL(activated(const QString&)), + this, SLOT(configChanged())); + connect(m_widget->eposServerOptions, SIGNAL(textChanged(const QString&)), + this, SLOT(configChanged())); + connect(m_widget->eposClientOptions, SIGNAL(textChanged(const QString&)), + this, SLOT(configChanged())); + connect(m_widget->eposTest, SIGNAL(clicked()), + this, SLOT(slotEposTest_clicked())); +} + +/** Destructor */ +EposConf::~EposConf(){ + // kdDebug() << "Running: EposConf::~EposConf()" << endl; + if (!m_waveFile.isNull()) QFile::remove(m_waveFile); + delete m_eposProc; + delete m_progressDlg; +} + +void EposConf::load(KConfig *config, const QString &configGroup){ + // kdDebug() << "EposConf::load: Running " << endl; + + config->setGroup(configGroup); + m_widget->eposServerPath->setURL(config->readEntry("EposServerExePath", "eposd")); + m_widget->eposClientPath->setURL(config->readEntry("EposClientExePath", "say-epos")); + m_widget->eposServerOptions->setText(config->readEntry("EposServerOptions", "")); + m_widget->eposClientOptions->setText(config->readEntry("EposClientOptions", "")); + QString codecString = config->readEntry("Codec", "ISO 8859-2"); + int codec = PlugInProc::codecNameToListIndex(codecString, m_codecList); + m_widget->timeBox->setValue(config->readNumEntry("time", 100)); + m_widget->frequencyBox->setValue(config->readNumEntry("pitch", 100)); + m_widget->characterCodingBox->setCurrentItem(codec); +} + +/** +* Converts a language code into the language setting passed to Epos synth. +*/ +QString EposConf::languageCodeToEposLanguage(const QString &languageCode) +{ + QString eposLanguage; + if (languageCode.left(2) == "cs") eposLanguage = "czech"; + if (languageCode.left(2) == "sk") eposLanguage = "slovak"; + return eposLanguage; +} + +void EposConf::save(KConfig *config, const QString &configGroup){ + // kdDebug() << "EposConf::save: Running" << endl; + + config->setGroup("Epos"); + config->writeEntry("EposServerExePath", + realFilePath(m_widget->eposServerPath->url())); + config->writeEntry("EposClientExePath", + realFilePath(m_widget->eposClientPath->url())); + config->writeEntry("Language", languageCodeToEposLanguage(m_languageCode)); + config->setGroup(configGroup); + config->writeEntry("EposServerExePath", + realFilePath(m_widget->eposServerPath->url())); + config->writeEntry("EposClientExePath", + realFilePath(m_widget->eposClientPath->url())); + config->writeEntry("EposServerOptions", m_widget->eposServerOptions->text()); + config->writeEntry("EposClientOptions", m_widget->eposClientOptions->text()); + config->writeEntry("time", m_widget->timeBox->value()); + config->writeEntry("pitch", m_widget->frequencyBox->value()); + int codec = m_widget->characterCodingBox->currentItem(); + config->writeEntry("Codec", PlugInProc::codecIndexToCodecName(codec, m_codecList)); +} + +void EposConf::defaults(){ + // kdDebug() << "EposConf::defaults: Running" << endl; + // Epos server command changed from epos to eposd. Epos client command changed from + // say to say-epos. These changes appeared around Epos v2.5.35. Try for these automatically. + QString exeName = "eposd"; + if (realFilePath(exeName).isEmpty()) + if (!realFilePath("epos").isEmpty()) + exeName = "epos"; + m_widget->eposServerPath->setURL(exeName); + exeName = "say-epos"; + if (realFilePath(exeName).isEmpty()) + if (!realFilePath("say").isEmpty()) + exeName = "say"; + m_widget->eposClientPath->setURL(exeName); + m_widget->eposServerOptions->setText(""); + m_widget->eposClientOptions->setText(""); + m_widget->timeBox->setValue(100); + timeBox_valueChanged(100); + m_widget->frequencyBox->setValue(100); + frequencyBox_valueChanged(100); + int codec = PlugInProc::codecNameToListIndex("ISO 8859-2", m_codecList); + m_widget->characterCodingBox->setCurrentItem(codec); +} + +void EposConf::setDesiredLanguage(const QString &lang) +{ + m_languageCode = lang; +} + +QString EposConf::getTalkerCode() +{ + QString eposServerExe = realFilePath(m_widget->eposServerPath->url()); + QString eposClientExe = realFilePath(m_widget->eposClientPath->url()); + if (!eposServerExe.isEmpty() && !eposClientExe.isEmpty()) + { + if (!getLocation(eposServerExe).isEmpty() && !getLocation(eposClientExe).isEmpty()) + { + QString rate = "medium"; + if (m_widget->timeBox->value() < 75) rate = "slow"; + if (m_widget->timeBox->value() > 125) rate = "fast"; + return QString( + "<voice lang=\"%1\" name=\"%2\" gender=\"%3\" />" + "<prosody volume=\"%4\" rate=\"%5\" />" + "<kttsd synthesizer=\"%6\" />") + .arg(m_languageCode) + .arg("fixed") + .arg("neutral") + .arg("medium") + .arg(rate) + .arg("Epos TTS Synthesis System"); + } + } + return QString::null; +} + +void EposConf::slotEposTest_clicked() +{ + // kdDebug() << "EposConf::slotEposTest_clicked(): Running" << endl; + // If currently synthesizing, stop it. + if (m_eposProc) + m_eposProc->stopText(); + else + { + m_eposProc = new EposProc(); + connect (m_eposProc, SIGNAL(stopped()), this, SLOT(slotSynthStopped())); + } + // Create a temp file name for the wave file. + KTempFile tempFile (locateLocal("tmp", "eposplugin-"), ".wav"); + QString tmpWaveFile = tempFile.file()->name(); + tempFile.close(); + + // Get test message in the language of the voice. + QString testMsg = testMessage(m_languageCode); + + // Tell user to wait. + m_progressDlg = new KProgressDialog(m_widget, "kttsmgr_epos_testdlg", + i18n("Testing"), + i18n("Testing."), + true); + m_progressDlg->progressBar()->hide(); + m_progressDlg->setAllowCancel(true); + + // TODO: Whenever server options change, the server must be restarted. + // TODO: Do codec names contain non-ASCII characters? + connect (m_eposProc, SIGNAL(synthFinished()), this, SLOT(slotSynthFinished())); + m_eposProc->synth( + testMsg, + tmpWaveFile, + realFilePath(m_widget->eposServerPath->url()), + realFilePath(m_widget->eposClientPath->url()), + m_widget->eposServerOptions->text(), + m_widget->eposClientOptions->text(), + PlugInProc::codecIndexToCodec(m_widget->characterCodingBox->currentItem(), m_codecList), + languageCodeToEposLanguage(m_languageCode), + m_widget->timeBox->value(), + m_widget->frequencyBox->value() + ); + + // Display progress dialog modally. Processing continues when plugin signals synthFinished, + // or if user clicks Cancel button. + m_progressDlg->exec(); + disconnect (m_eposProc, SIGNAL(synthFinished()), this, SLOT(slotSynthFinished())); + if (m_progressDlg->wasCancelled()) m_eposProc->stopText(); + delete m_progressDlg; + m_progressDlg = 0; +} + +void EposConf::slotSynthFinished() +{ + // If user canceled, progress dialog is gone, so exit. + if (!m_progressDlg) + { + m_eposProc->ackFinished(); + return; + } + // Hide the Cancel button so user can't cancel in the middle of playback. + m_progressDlg->showCancelButton(false); + // Get new wavefile name. + m_waveFile = m_eposProc->getFilename(); + // Tell synth we're done. + m_eposProc->ackFinished(); + // Play the wave file (possibly adjusting its Speed). + // Player object deletes the wave file when done. + if (m_player) m_player->play(m_waveFile); + QFile::remove(m_waveFile); + m_waveFile = QString::null; + if (m_progressDlg) m_progressDlg->close(); +} + +void EposConf::slotSynthStopped() +{ + // Clean up after canceling test. + QString filename = m_eposProc->getFilename(); + if (!filename.isNull()) QFile::remove(filename); +} + +// Basically the slider values are logarithmic (0,...,1000) whereas percent +// values are linear (50%,...,200%). +// +// slider = alpha * (log(percent)-log(50)) +// with alpha = 1000/(log(200)-log(50)) + +int EposConf::percentToSlider(int percentValue) { + double alpha = 1000 / (log(200) - log(50)); + return (int)floor (0.5 + alpha * (log(percentValue)-log(50))); +} + +int EposConf::sliderToPercent(int sliderValue) { + double alpha = 1000 / (log(200) - log(50)); + return (int)floor(0.5 + exp (sliderValue/alpha + log(50))); +} + +void EposConf::timeBox_valueChanged(int percentValue) { + m_widget->timeSlider->setValue (percentToSlider (percentValue)); +} + +void EposConf::frequencyBox_valueChanged(int percentValue) { + m_widget->frequencySlider->setValue(percentToSlider(percentValue)); +} + +void EposConf::timeSlider_valueChanged(int sliderValue) { + m_widget->timeBox->setValue (sliderToPercent (sliderValue)); +} + +void EposConf::frequencySlider_valueChanged(int sliderValue) { + m_widget->frequencyBox->setValue(sliderToPercent(sliderValue)); +} diff --git a/kttsd/plugins/epos/eposconf.h b/kttsd/plugins/epos/eposconf.h new file mode 100644 index 0000000..5c3fbe3 --- /dev/null +++ b/kttsd/plugins/epos/eposconf.h @@ -0,0 +1,141 @@ +/***************************************************** vim:set ts=4 sw=4 sts=4: + Configuration widget and functions for Epos plug in + ------------------- + Copyright: + (C) 2004 by Gary Cramblitt <garycramblitt@comcast.net> + ------------------- + Original author: Gary Cramblitt <garycramblitt@comcast.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + ******************************************************************************/ + +#ifndef _EPOSCONF_H_ +#define _EPOSCONF_H_ + +// Qt includes. +#include <qstring.h> + +// KDE includes. +#include <kconfig.h> +#include <kdebug.h> +#include <kprogress.h> + +// KTTS includes. +#include <pluginconf.h> + +// Epos plugin includes. +#include "eposconfwidget.h" + +class EposProc; +class KProgressDialog; + +class EposConf : public PlugInConf { + Q_OBJECT + + public: + /** Constructor */ + EposConf( QWidget* parent = 0, const char* name = 0, const QStringList &args = QStringList()); + + /** Destructor */ + ~EposConf(); + + /** This method is invoked whenever the module should read its + * configuration (most of the times from a config file) and update the + * user interface. This happens when the user clicks the "Reset" button in + * the control center, to undo all of his changes and restore the currently + * valid settings. NOTE that this is not called after the modules is loaded, + * so you probably want to call this method in the constructor. + */ + void load(KConfig *config, const QString &configGroup); + + /** This function gets called when the user wants to save the settings in + * the user interface, updating the config files or wherever the + * configuration is stored. The method is called when the user clicks "Apply" + * or "Ok". + */ + void save(KConfig *config, const QString &configGroup); + + /** This function is called to set the settings in the module to sensible + * default values. It gets called when hitting the "Default" button. The + * default values should probably be the same as the ones the application + * uses when started without a config file. + */ + void defaults(); + + /** + * This function informs the plugin of the desired language to be spoken + * by the plugin. The plugin should attempt to adapt itself to the + * specified language code, choosing sensible defaults if necessary. + * If the passed-in code is QString::null, no specific language has + * been chosen. + * @param lang The desired language code or Null if none. + * + * If the plugin is unable to support the desired language, that is OK. + * Language codes are given by ISO 639-1 and are in lowercase. + * The code may also include an ISO 3166 country code in uppercase + * separated from the language code by underscore (_). For + * example, en_GB. If your plugin supports the given language, but + * not the given country, treat it as though the country + * code were not specified, i.e., adapt to the given language. + */ + void setDesiredLanguage(const QString &lang); + + /** + * Return fully-specified talker code for the configured plugin. This code + * uniquely identifies the configured instance of the plugin and distinquishes + * one instance from another. If the plugin has not been fully configured, + * i.e., cannot yet synthesize, return QString::null. + * @return Fully-specified talker code. + */ + QString getTalkerCode(); + + private slots: + void configChanged(){ + kdDebug() << "EposConf::configChanged: Running" << endl; + emit changed(true); + }; + void slotEposTest_clicked(); + void slotSynthFinished(); + void slotSynthStopped(); + void timeBox_valueChanged(int percentValue); + void frequencyBox_valueChanged(int percentValue); + void timeSlider_valueChanged(int sliderValue); + void frequencySlider_valueChanged(int sliderValue); + + private: + /** + * Converts a language code into the language setting passed to Epos synth. + */ + QString languageCodeToEposLanguage(const QString &languageCode); + + int percentToSlider(int percentValue); + int sliderToPercent(int sliderValue); + + // Language code. + QString m_languageCode; + + // Configuration widget. + EposConfWidget* m_widget; + + // Epos synthesizer. + EposProc* m_eposProc; + // Synthesized wave file name. + QString m_waveFile; + // Progress dialog. + KProgressDialog* m_progressDlg; + // List of displayed codec names. + QStringList m_codecList; +}; +#endif // _EPOSCONF_H_ diff --git a/kttsd/plugins/epos/eposconfwidget.ui b/kttsd/plugins/epos/eposconfwidget.ui new file mode 100644 index 0000000..23e47da --- /dev/null +++ b/kttsd/plugins/epos/eposconfwidget.ui @@ -0,0 +1,610 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>EposConfWidget</class> +<author>Gary Cramblitt</author> +<widget class="QWidget"> + <property name="name"> + <cstring>EposConfWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>535</width> + <height>381</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="caption"> + <string>Epos Config UI</string> + </property> + <property name="whatsThis" stdset="0"> + <string>This is the configuration dialog for the Epos Czech and Slovak speech synthesizer.</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox" row="0" column="0"> + <property name="name"> + <cstring>eposConfigurationBox</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>GroupBoxPanel</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="title"> + <string>E&pos Configuration</string> + </property> + <property name="whatsThis" stdset="0"> + <string>This is the configuration dialog for the Epos Czech and Slovak speech synthesizer.</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLayoutWidget" row="2" column="0"> + <property name="name"> + <cstring>layout13</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>characterCodingLabel</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Character &encoding:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>characterCodingBox</cstring> + </property> + </widget> + <widget class="KComboBox"> + <property name="name"> + <cstring>characterCodingBox</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="whatsThis" stdset="0"> + <string>Specifies which character encoding is used for passing the text.</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget" row="1" column="0"> + <property name="name"> + <cstring>layout17</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout14</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>timeLabel</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Speed:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>timeBox</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Sets the speed of speech. Slide the slider to the left to slow speech down; to the right to increase talking speed. Anything less than 75 percent is considered "slow", and anything greater than 125 percent is considered "fast".</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>frequencyLabel</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Pitch:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>frequencyBox</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Sets the tone (frequency) of speech. Slide the slider to the left to lower the voice tone; to the right to increase tone. Anything less than 75 percent is considered "low", and anything greater than 125 percent is considered "high".</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout15</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KIntSpinBox"> + <property name="name"> + <cstring>timeBox</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="suffix"> + <string> %</string> + </property> + <property name="maxValue"> + <number>200</number> + </property> + <property name="minValue"> + <number>50</number> + </property> + <property name="value"> + <number>100</number> + </property> + <property name="whatsThis" stdset="0"> + <string>Sets the speed of speech. Slide the slider to the left to slow speech down; to the right to increase talking speed. Anything less than 75 percent is considered "slow", and anything greater than 125 percent is considered "fast".</string> + </property> + </widget> + <widget class="KIntSpinBox"> + <property name="name"> + <cstring>frequencyBox</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="suffix"> + <string> %</string> + </property> + <property name="maxValue"> + <number>200</number> + </property> + <property name="minValue"> + <number>50</number> + </property> + <property name="value"> + <number>100</number> + </property> + <property name="whatsThis" stdset="0"> + <string>Sets the tone (frequency) of speech. Slide the slider to the left to lower the voice tone; to the right to increase tone. Anything less than 75 percent is considered "low", and anything greater than 125 percent is considered "high".</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout16</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QSlider"> + <property name="name"> + <cstring>timeSlider</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="focusPolicy"> + <enum>NoFocus</enum> + </property> + <property name="maxValue"> + <number>1000</number> + </property> + <property name="lineStep"> + <number>10</number> + </property> + <property name="pageStep"> + <number>100</number> + </property> + <property name="value"> + <number>500</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="whatsThis" stdset="0"> + <string>Sets the speed of speech. Slide the slider to the left to slow speech down; to the right to increase talking speed. Anything less than 75 percent is considered "slow", and anything greater than 125 percent is considered "fast".</string> + </property> + </widget> + <widget class="QSlider"> + <property name="name"> + <cstring>frequencySlider</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="focusPolicy"> + <enum>NoFocus</enum> + </property> + <property name="maxValue"> + <number>1000</number> + </property> + <property name="lineStep"> + <number>10</number> + </property> + <property name="pageStep"> + <number>100</number> + </property> + <property name="value"> + <number>500</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="whatsThis" stdset="0"> + <string>Sets the tone (frequency) of speech. Slide the slider to the left to lower the voice tone; to the right to increase tone. Anything less than 75 percent is considered "low", and anything greater than 125 percent is considered "high".</string> + </property> + </widget> + </vbox> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout13</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout11</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>eposServerPathLabel</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Epos server executable path:</string> + </property> + <property name="alignment"> + <set>AlignVCenter</set> + </property> + <property name="buddy" stdset="0"> + <cstring>festivalVoicesPath</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>If the Epos server program will be found due to your PATH environment variable, simply enter "epos", otherwise enter the full path to the Epos server executable program.</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>eposClientPathLabel</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Epos client executable path:</string> + </property> + <property name="alignment"> + <set>AlignVCenter</set> + </property> + <property name="buddy" stdset="0"> + <cstring>festivalVoicesPath</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>If the Epos client program will be found due to the PATH environment variable, simply enter "say" here. Otherwise, specify the full path to the Epos client program.</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout12</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KURLRequester"> + <property name="name"> + <cstring>eposServerPath</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="url" stdset="0"> + <string>epos</string> + </property> + <property name="whatsThis" stdset="0"> + <string>If the Epos server program will be found due to your PATH environment variable, simply enter "epos", otherwise enter the full path to the Epos server executable program.</string> + </property> + </widget> + <widget class="KURLRequester"> + <property name="name"> + <cstring>eposClientPath</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="url" stdset="0"> + <string>say</string> + </property> + <property name="whatsThis" stdset="0"> + <string>If the Epos client program will be found due to the PATH environment variable, simply enter "say" here. Otherwise, specify the full path to the Epos client program.</string> + </property> + </widget> + </vbox> + </widget> + </hbox> + </widget> + <widget class="QGroupBox" row="3" column="0"> + <property name="name"> + <cstring>advancedGroupBox</cstring> + </property> + <property name="title"> + <string>Additional Options (advanced)</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout14</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget" row="0" column="1"> + <property name="name"> + <cstring>layout13</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLineEdit"> + <property name="name"> + <cstring>eposServerOptions</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Optional. Enter any server command line options here. To see available options, enter "epos -h" in a terminal. Do not use "-o".</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>eposClientOptions</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Specify options to be passed to Epos client. To see available options, enter "say -h" in a terminal. Do not use "-o".</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout12</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>eposServerOptionsLabel</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Epos server:</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignLeft</set> + </property> + <property name="buddy" stdset="0"> + <cstring>festivalVoicesPath</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Optional. Enter any server command line options here. To see available options, enter "epos -h" in a terminal. Do not use "-o".</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>eposClientOptionsLabel</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Epos client:</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignLeft</set> + </property> + <property name="buddy" stdset="0"> + <cstring>festivalVoicesPath</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Specify options to be passed to Epos client. Do not use -o. To see available options, enter "say -h" in a terminal. Do not use "-o".</string> + </property> + </widget> + </vbox> + </widget> + </grid> + </widget> + </grid> + </widget> + <widget class="QLayoutWidget" row="4" column="0"> + <property name="name"> + <cstring>layout5</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>410</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>eposTest</cstring> + </property> + <property name="text"> + <string>&Test</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Click to test the configuration. If correct, you will hear a sentence spoken.</string> + </property> + </widget> + </hbox> + </widget> + </grid> + </widget> + </grid> +</widget> +<customwidgets> +</customwidgets> +<tabstops> + <tabstop>eposServerPath</tabstop> + <tabstop>eposClientPath</tabstop> + <tabstop>characterCodingBox</tabstop> + <tabstop>eposTest</tabstop> +</tabstops> +<includes> + <include location="global" impldecl="in declaration">kurlrequester.h</include> + <include location="global" impldecl="in implementation">kurlrequester.h</include> +</includes> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kcombobox.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>kurlrequester.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kurlrequester.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kpushbutton.h</includehint> +</includehints> +</UI> diff --git a/kttsd/plugins/epos/eposplugin.cpp b/kttsd/plugins/epos/eposplugin.cpp new file mode 100644 index 0000000..9bc0bee --- /dev/null +++ b/kttsd/plugins/epos/eposplugin.cpp @@ -0,0 +1,31 @@ +/***************************************************** vim:set ts=4 sw=4 sts=4: + Generating the factories so Epos can be used as plug in. + ------------------- + Copyright: + (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net> + ------------------- + Original author: Gary Cramblitt <garycramblitt@comcast.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + ******************************************************************************/ + +#include <kgenericfactory.h> + +#include "eposconf.h" +#include "eposproc.h" + +typedef K_TYPELIST_2( EposProc, EposConf ) Epos; +K_EXPORT_COMPONENT_FACTORY( libkttsd_eposplugin, KGenericFactory<Epos>("kttsd_epos") ) + diff --git a/kttsd/plugins/epos/eposproc.cpp b/kttsd/plugins/epos/eposproc.cpp new file mode 100644 index 0000000..35bc1d3 --- /dev/null +++ b/kttsd/plugins/epos/eposproc.cpp @@ -0,0 +1,389 @@ +/***************************************************** vim:set ts=4 sw=4 sts=4: + eposproc.cpp + Main speaking functions for the Epos Plug in + ------------------- + Copyright: + (C) 2004 by Gary Cramblitt <garycramblitt@comcast.net> + ------------------- + Original author: Gary Cramblitt <garycramblitt@comcast.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + ******************************************************************************/ + +// C++ includes. +#include <math.h> + +// Qt includes. +#include <qstring.h> +#include <qstringlist.h> +#include <qtextcodec.h> +#include <qfile.h> + +// KDE includes. +#include <kdebug.h> +#include <kconfig.h> +#include <ktempfile.h> +#include <kstandarddirs.h> +#include <kprocess.h> + +// Epos Plugin includes. +#include "eposproc.h" +#include "eposproc.moc" + +/** Constructor */ +EposProc::EposProc( QObject* parent, const char* name, const QStringList& ) : + PlugInProc( parent, name ){ + kdDebug() << "EposProc::EposProc: Running" << endl; + m_state = psIdle; + m_waitingStop = false; + m_eposServerProc = 0; + m_eposProc = 0; +} + +/** Destructor */ +EposProc::~EposProc(){ + kdDebug() << "EposProc::~EposProc:: Running" << endl; + if (m_eposProc) + { + stopText(); + delete m_eposProc; + } + delete m_eposServerProc; +} + +/** Initialize the speech */ +bool EposProc::init(KConfig* config, const QString& configGroup) +{ + // kdDebug() << "EposProc::init: Running" << endl; + // kdDebug() << "Initializing plug in: Epos" << endl; + // Retrieve path to epos executable. + config->setGroup(configGroup); + m_eposServerExePath = config->readEntry("EposServerExePath", "epos"); + m_eposClientExePath = config->readEntry("EposClientExePath", "say"); + m_eposLanguage = config->readEntry("Language", QString::null); + m_time = config->readNumEntry("time", 100); + m_pitch = config->readNumEntry("pitch", 100); + m_eposServerOptions = config->readEntry("EposServerOptions", QString::null); + m_eposClientOptions = config->readEntry("EposClientOptions", QString::null); + kdDebug() << "EposProc::init: path to epos server: " << m_eposServerExePath << endl; + kdDebug() << "EposProc::init: path to epos client: " << m_eposClientExePath << endl; + + QString codecString = config->readEntry("Codec", "Local"); + m_codec = codecNameToCodec(codecString); + // Start the Epos server if not already started. + if (!m_eposServerProc) + { + m_eposServerProc = new KProcess; + *m_eposServerProc << m_eposServerExePath; + if (!m_eposServerOptions.isEmpty()) + *m_eposServerProc << m_eposServerOptions; + connect(m_eposServerProc, SIGNAL(receivedStdout(KProcess*, char*, int)), + this, SLOT(slotReceivedStdout(KProcess*, char*, int))); + connect(m_eposServerProc, SIGNAL(receivedStderr(KProcess*, char*, int)), + this, SLOT(slotReceivedStderr(KProcess*, char*, int))); + m_eposServerProc->start(KProcess::DontCare, KProcess::AllOutput); + } + + kdDebug() << "EposProc::init: Initialized with codec: " << codecString << endl; + + return true; +} + +/** +* Say a text. Synthesize and audibilize it. +* @param text The text to be spoken. +* +* If the plugin supports asynchronous operation, it should return immediately. +*/ +void EposProc::sayText(const QString &text) +{ + synth(text, QString::null, m_eposServerExePath, m_eposClientExePath, + m_eposServerOptions, m_eposClientOptions, + m_codec, m_eposLanguage, m_time, m_pitch); +} + +/** +* Synthesize text into an audio file, but do not send to the audio device. +* @param text The text to be synthesized. +* @param suggestedFilename Full pathname of file to create. The plugin +* may ignore this parameter and choose its own +* filename. KTTSD will query the generated +* filename using getFilename(). +* +* If the plugin supports asynchronous operation, it should return immediately. +*/ +void EposProc::synthText(const QString& text, const QString& suggestedFilename) +{ + synth(text, suggestedFilename, m_eposServerExePath, m_eposClientExePath, + m_eposServerOptions, m_eposClientOptions, + m_codec, m_eposLanguage, m_time, m_pitch); +} + +/** +* Say or Synthesize text. +* @param text The text to be synthesized. +* @param suggestedFilename If not Null, synthesize only to this filename, otherwise +* synthesize and audibilize the text. +* @param eposServerExePath Path to the Epos server executable. +* @param eposClientExePath Path to the Epos client executable. +* @param eposServerOptions Options passed to Epos server executable. +* @param eposClientOptions Options passed to Epos client executable (don't include -o). +* @param codec Codec for encoding of text. +* @param eposLanguage Epos language setting. "czech", "slovak", +* or null (default language). +* @param time Speed percentage. 50 to 200. 200% = 2x normal. +* @param pitch Pitch persentage. 50 to 200. +*/ +void EposProc::synth( + const QString &text, + const QString &suggestedFilename, + const QString& eposServerExePath, + const QString& eposClientExePath, + const QString& eposServerOptions, + const QString& eposClientOptions, + QTextCodec *codec, + const QString& eposLanguage, + int time, + int pitch) +{ + // kdDebug() << "Running: EposProc::synth(const QString &text)" << endl; + + if (m_eposProc) + { + if (m_eposProc->isRunning()) m_eposProc->kill(); + delete m_eposProc; + m_eposProc = 0; + } + // Start the Epos server if not already started. + if (!m_eposServerProc) + { + m_eposServerProc = new KProcess; + *m_eposServerProc << eposServerExePath; + if (!eposServerOptions.isEmpty()) + *m_eposServerProc << eposServerOptions; + connect(m_eposServerProc, SIGNAL(receivedStdout(KProcess*, char*, int)), + this, SLOT(slotReceivedStdout(KProcess*, char*, int))); + connect(m_eposServerProc, SIGNAL(receivedStderr(KProcess*, char*, int)), + this, SLOT(slotReceivedStderr(KProcess*, char*, int))); + m_eposServerProc->start(KProcess::DontCare, KProcess::AllOutput); + kdDebug() << "EposProc:: Epos server process started" << endl; + } + +// // Encode the text. +// // 1.a) encode the text +// m_encText = QCString(); +// QTextStream ts (m_encText, IO_WriteOnly); +// ts.setCodec(codec); +// ts << text; +// ts << endl; // Some synths need this, eg. flite. + + if (codec) + m_encText = codec->fromUnicode(text); + else + m_encText = text.latin1(); // Should not happen, but just in case. + + // kdDebug()<< "EposProc::synth: Creating Epos object" << endl; + m_eposProc = new KProcess; + m_eposProc->setUseShell(true); + QString languageCode; + if (eposLanguage == "czech") + languageCode == "cz"; + else if (eposLanguage == "slovak") + languageCode == "sk"; + if (!languageCode.isEmpty()) + { + m_eposProc->setEnvironment("LANG", languageCode + "." + codec->mimeName()); + m_eposProc->setEnvironment("LC_CTYPE", languageCode + "." + codec->mimeName()); + } + *m_eposProc << eposClientExePath; + // Language. + if (!eposLanguage.isEmpty()) + *m_eposProc << QString("--language=%1").arg(eposLanguage); + // Rate (speed). + // Map 50% to 200% onto 0 to 1000. + // slider = alpha * (log(percent)-log(50)) + // with alpha = 1000/(log(200)-log(50)) + double alpha = 1000 / (log(200) - log(50)); + int slider = (int)floor (0.5 + alpha * (log(time)-log(50))); + // Center at 0. + slider = slider - 500; + // Map -500 to 500 onto 45 to -45 then shift to 130 to 40 (85 midpoint). + float stretchValue = (-float(slider) * 45.0 / 500.0) + 85.0; + QString timeMsg = QString("--init_t=%1").arg(stretchValue, 0, 'f', 3); + *m_eposProc << timeMsg; + // Pitch. Map 50% to 200% onto 50 to 200. easy. + QString pitchMsg = QString("--init_f=%1").arg(pitch); + *m_eposProc << pitchMsg; + // Output file. + if (!suggestedFilename.isEmpty()) + *m_eposProc << "-o"; + if (!eposClientOptions.isEmpty()) + *m_eposProc << eposClientOptions; + *m_eposProc << "-"; // Read from StdIn. + if (!suggestedFilename.isEmpty()) + *m_eposProc << " >" + suggestedFilename; + connect(m_eposProc, SIGNAL(processExited(KProcess*)), + this, SLOT(slotProcessExited(KProcess*))); + connect(m_eposProc, SIGNAL(receivedStdout(KProcess*, char*, int)), + this, SLOT(slotReceivedStdout(KProcess*, char*, int))); + connect(m_eposProc, SIGNAL(receivedStderr(KProcess*, char*, int)), + this, SLOT(slotReceivedStderr(KProcess*, char*, int))); + connect(m_eposProc, SIGNAL(wroteStdin(KProcess*)), + this, SLOT(slotWroteStdin(KProcess* ))); + if (suggestedFilename.isEmpty()) + m_state = psSaying; + else + m_state = psSynthing; + + // Ok, let's rock. + m_synthFilename = suggestedFilename; + // kdDebug() << "EposProc::synth: Synthing text: '" << text << "' using Epos plug in" << endl; + if (!m_eposProc->start(KProcess::NotifyOnExit, KProcess::All)) + { + kdDebug() << "EposProc::synth: Error starting Epos process. Is epos in the PATH?" << endl; + m_state = psIdle; + return; + } + // kdDebug() << "EposProc:synth: Epos initialized" << endl; + // kdDebug() << "EposProc::synth: m_encText.length() = " << m_encText.length() << " text.length() = " + // << text.length() << endl; + if (!m_eposProc->writeStdin(m_encText, m_encText.length())) + kdDebug() << "EposProc::synth: Error writing to Epos client StdIn." << endl; +} + +/** +* Get the generated audio filename from synthText. +* @return Name of the audio file the plugin generated. +* Null if no such file. +* +* The plugin must not re-use the filename. +*/ +QString EposProc::getFilename() +{ + kdDebug() << "EposProc::getFilename: returning " << m_synthFilename << endl; + return m_synthFilename; +} + +/** +* Stop current operation (saying or synthesizing text). +* Important: This function may be called from a thread different from the +* one that called sayText or synthText. +* If the plugin cannot stop an in-progress @ref sayText or +* @ref synthText operation, it must not block waiting for it to complete. +* Instead, return immediately. +* +* If a plugin returns before the operation has actually been stopped, +* the plugin must emit the @ref stopped signal when the operation has +* actually stopped. +* +* The plugin should change to the psIdle state after stopping the +* operation. +*/ +void EposProc::stopText(){ + kdDebug() << "EposProc::stopText:: Running" << endl; + if (m_eposProc) + { + if (m_eposProc->isRunning()) + { + kdDebug() << "EposProc::stopText: killing Epos." << endl; + m_waitingStop = true; + m_eposProc->kill(); + } else m_state = psIdle; + } else m_state = psIdle; + kdDebug() << "EposProc::stopText: Epos stopped." << endl; +} + +void EposProc::slotProcessExited(KProcess*) +{ + // kdDebug() << "EposProc:slotProcessExited: Epos process has exited." << endl; + pluginState prevState = m_state; + if (m_waitingStop) + { + m_waitingStop = false; + m_state = psIdle; + emit stopped(); + } else { + m_state = psFinished; + if (prevState == psSaying) + emit sayFinished(); + else + if (prevState == psSynthing) + emit synthFinished(); + } +} + +void EposProc::slotReceivedStdout(KProcess*, char* buffer, int buflen) +{ + QString buf = QString::fromLatin1(buffer, buflen); + kdDebug() << "EposProc::slotReceivedStdout: Received output from Epos: " << buf << endl; +} + +void EposProc::slotReceivedStderr(KProcess*, char* buffer, int buflen) +{ + QString buf = QString::fromLatin1(buffer, buflen); + kdDebug() << "EposProc::slotReceivedStderr: Received error from Epos: " << buf << endl; +} + +void EposProc::slotWroteStdin(KProcess*) +{ + // kdDebug() << "EposProc::slotWroteStdin: closing Stdin" << endl; + m_eposProc->closeStdin(); + m_encText = QCString(); +} + +/** +* Return the current state of the plugin. +* This function only makes sense in asynchronous mode. +* @return The pluginState of the plugin. +* +* @see pluginState +*/ +pluginState EposProc::getState() { return m_state; } + +/** +* Acknowledges a finished state and resets the plugin state to psIdle. +* +* If the plugin is not in state psFinished, nothing happens. +* The plugin may use this call to do any post-processing cleanup, +* for example, blanking the stored filename (but do not delete the file). +* Calling program should call getFilename prior to ackFinished. +*/ +void EposProc::ackFinished() +{ + if (m_state == psFinished) + { + m_state = psIdle; + m_synthFilename = QString::null; + } +} + +/** +* Returns True if the plugin supports asynchronous processing, +* i.e., returns immediately from sayText or synthText. +* @return True if this plugin supports asynchronous processing. +* +* If the plugin returns True, it must also implement @ref getState . +* It must also emit @ref sayFinished or @ref synthFinished signals when +* saying or synthesis is completed. +*/ +bool EposProc::supportsAsync() { return true; } + +/** +* Returns True if the plugin supports synthText method, +* i.e., is able to synthesize text to a sound file without +* audibilizing the text. +* @return True if this plugin supports synthText method. +*/ +bool EposProc::supportsSynth() { return true; } diff --git a/kttsd/plugins/epos/eposproc.h b/kttsd/plugins/epos/eposproc.h new file mode 100644 index 0000000..b2d1d90 --- /dev/null +++ b/kttsd/plugins/epos/eposproc.h @@ -0,0 +1,241 @@ +/***************************************************** vim:set ts=4 sw=4 sts=4: + eposproc.h + Main speaking functions for the Epos Plug in + ------------------- + Copyright: + (C) 2004 by Gary Cramblitt <garycramblitt@comcast.net> + ------------------- + Original author: Gary Cramblitt <garycramblitt@comcast.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + ******************************************************************************/ + +#ifndef _EPOSPROC_H_ +#define _EPOSPROC_H_ + +// Qt includes. +#include <qstringlist.h> +#include <qmutex.h> + +// KTTS includes. +#include <pluginproc.h> + +class KProcess; +class QTextCodec; + +class EposProc : public PlugInProc{ + Q_OBJECT + + public: + /** + * Constructor + */ + EposProc( QObject* parent = 0, const char* name = 0, const QStringList &args = QStringList()); + + /** + * Destructor + */ + virtual ~EposProc(); + + /** + * Initializate the speech engine. + * @param config Settings object. + * @param configGroup Settings group. + */ + virtual bool init(KConfig *config, const QString &configGroup); + + /** + * Say a text string. + * @param text The text to speak. + */ + virtual void sayText(const QString &text); + + /** + * Synthesize text into an audio file, but do not send to the audio device. + * @param text The text to be synthesized. + * @param suggestedFilename Full pathname of file to create. The plugin + * may ignore this parameter and choose its own + * filename. KTTSD will query the generated + * filename using getFilename(). + * + * If the plugin supports asynchronous operation, it should return immediately. + */ + virtual void synthText(const QString& text, const QString& suggestedFilename); + + /** + * Get the generated audio filename from synthText. + * @return Name of the audio file the plugin generated. + * Null if no such file. + * + * The plugin must not re-use the filename. + */ + virtual QString getFilename(); + + /** + * Stop current operation (saying or synthesizing text). + * Important: This function may be called from a thread different from the + * one that called sayText or synthText. + * If the plugin cannot stop an in-progress @ref sayText or + * @ref synthText operation, it must not block waiting for it to complete. + * Instead, return immediately. + * + * If a plugin returns before the operation has actually been stopped, + * the plugin must emit the @ref stopped signal when the operation has + * actually stopped. + * + * The plugin should change to the psIdle state after stopping the + * operation. + */ + virtual void stopText(); + + /** + * Return the current state of the plugin. + * This function only makes sense in asynchronous mode. + * @return The pluginState of the plugin. + * + * @see pluginState + */ + virtual pluginState getState(); + + /** + * Acknowledges a finished state and resets the plugin state to psIdle. + * + * If the plugin is not in state psFinished, nothing happens. + * The plugin may use this call to do any post-processing cleanup, + * for example, blanking the stored filename (but do not delete the file). + * Calling program should call getFilename prior to ackFinished. + */ + virtual void ackFinished(); + + /** + * Returns True if the plugin supports asynchronous processing, + * i.e., returns immediately from sayText or synthText. + * @return True if this plugin supports asynchronous processing. + * + * If the plugin returns True, it must also implement @ref getState . + * It must also emit @ref sayFinished or @ref synthFinished signals when + * saying or synthesis is completed. + */ + virtual bool supportsAsync(); + + /** + * Returns True if the plugin supports synthText method, + * i.e., is able to synthesize text to a sound file without + * audibilizing the text. + * @return True if this plugin supports synthText method. + */ + virtual bool supportsSynth(); + + /** + * Say or Synthesize text. + * @param text The text to be synthesized. + * @param suggestedFilename If not Null, synthesize only to this filename, otherwise + * synthesize and audibilize the text. + * @param eposServerExePath Path to the Epos server executable. + * @param eposClientExePath Path to the Epos client executable. + * @param eposServerOptions Options passed to Epos server executable. + * @param eposClientOptions Options passed to Epos client executable (don't include -o). + * @param codec Codec for encoding of text. + * @param eposLanguage Epos language setting. "czech", "slovak", + * or null (default language). + * @param time Speed percentage. 50 to 200. 200% = 2x normal. + * @param pitch Pitch persentage. 50 to 200. + */ + void synth( + const QString &text, + const QString &suggestedFilename, + const QString& eposServerExePath, + const QString& eposClientExePath, + const QString& eposServerOptions, + const QString& eposClientOptions, + QTextCodec *codec, + const QString& eposLanguage, + int time, + int pitch); + + private slots: + void slotProcessExited(KProcess* proc); + void slotReceivedStdout(KProcess* proc, char* buffer, int buflen); + void slotReceivedStderr(KProcess* proc, char* buffer, int buflen); + void slotWroteStdin(KProcess* proc); + + private: + + /** + * Path to epos executables (from config). + */ + QString m_eposServerExePath; + QString m_eposClientExePath; + + /** + * User options passed to executables (from config). + */ + QString m_eposServerOptions; + QString m_eposClientOptions; + + /** + * Epos Server process. + */ + KProcess* m_eposServerProc; + + /** + * Epos Client process + */ + KProcess* m_eposProc; + + /** + * Epos language setting. "czech", "slovak", or Null (use default language). + */ + QString m_eposLanguage; + + /** + * Rate (speed) from config file. + */ + int m_time; + + /** + * Pitch from the config file. + */ + int m_pitch; + + /** + * Codec. + */ + QTextCodec* m_codec; + + /** + * Encoded buffer to be sent to Epos client. + */ + QCString m_encText; + + /** + * Synthesis filename. + */ + QString m_synthFilename; + + /** + * Plugin state. + */ + pluginState m_state; + + /** + * True when stopText has been called. Used to force transition to psIdle when + * Epos exits. + */ + bool m_waitingStop; + +}; + +#endif // _EPOSPROC_H_ diff --git a/kttsd/plugins/epos/kttsd_eposplugin.desktop b/kttsd/plugins/epos/kttsd_eposplugin.desktop new file mode 100644 index 0000000..7673929 --- /dev/null +++ b/kttsd/plugins/epos/kttsd_eposplugin.desktop @@ -0,0 +1,87 @@ +[Desktop Entry] +Name=Epos TTS Synthesis System +Name[ca]=Sistema de síntesi Epos TTS +Name[cs]=Epos TTS +Name[da]=Epos TTS Synthesis-system +Name[de]=Epos TTS-Synthese-System +Name[el]=Σύστημα σύνθεσης Epos TTS +Name[es]=Sistema de síntesis Epos TTS +Name[et]=Kõnesünteesisüsteem Epos TTS +Name[eu]=Epos TTS sintesi-sistema +Name[fa]=سیستم ترکیبدهی Epos TTS +Name[fi]=Epos TTS -syntetisoijasysteemi +Name[fr]=Système de synthèse Epos TTS +Name[ga]=Córas Sintéise TTS Epos +Name[gl]=Sistema de Síntese de TTS Epos +Name[hu]=Epos szövegfelolvasó motor +Name[it]=Sistema di sintesi TTS Epos +Name[ja]=Epos TTS シンセサイズシステム +Name[ka]=Epos TTS სინთეზის სისტემა +Name[km]=ប្រព័ន្ធសង្គ្រោះ Epos TTS +Name[mk]=Epos TTS систем за синтеза +Name[ms]=Sistem Sintesis Epos TTS +Name[nb]=Epos TTT syntesesystem +Name[nds]=Epos Blicksnuut +Name[ne]=Epos TTS सिन्थेसिस प्रणाली +Name[nl]=Epos TTS Synthesis-systeem +Name[pa]=Epos TTS ਸੰਸਲੇਸ਼ਣ ਸਿਸਟਮ +Name[pl]=System syntezy mowy Epos +Name[pt]=Sistema de Síntese Epos TTS +Name[pt_BR]=Sistema de Sintetizador de Fala Epos +Name[sk]=Systém Epos TTS Synthesis +Name[sl]=Sistem sinteze besedila v govor Epos +Name[sr]=Систем за синтезу Epos TTS +Name[sr@Latn]=Sistem za sintezu Epos TTS +Name[sv]=Epos TTS syntessystem +Name[ta]=Epos TTS கூட்டிணைப்பு அமைப்பு +Name[tg]=Системаи Синтезиси Epos TTS +Name[tr]=Epos TTS Sentezleme Sistemi +Name[vi]=Hệ thống Tổng hợp Văn bản sang Tiếng nói Epos +Name[zh_TW]=Epos TTS 合成系統 +Comment=Epos TTS speech synthesizer +Comment[bg]=Синтезатор на глас Epos TTS +Comment[ca]=Sintetitzador de veu Epos TTS +Comment[cs]=Hlasový syntetizér Epos TTS +Comment[da]=Epos TTS tale-synthesizer +Comment[de]=Epos TTS-Sprachsynthesizer +Comment[el]=Συνθέτης ομιλίας Epos TTS +Comment[es]=Sintetizador de texto a voz Epos TTS +Comment[et]=Kõnesüntesaator Epos TTS +Comment[eu]=Epos TTS hizketa-sintetizadorea +Comment[fa]=ترکیبدهندۀ گفتار Epos TTS +Comment[fi]=Epos TTS -puhesyntetisaattori +Comment[fr]=Synthèse vocale Epos TTS +Comment[ga]=Sintéiseoir cainte TTS Epos +Comment[gl]=Sintetizador de fala TTS Epos +Comment[hu]=Epos TTS beszédszintetizátor +Comment[is]=Epos TTS talgerfill +Comment[it]=Sintetizzatore vocale TTS Epos +Comment[ja]=Epos TTS スピーチシンセサイザ +Comment[ka]=Epos TTS სიტყვის სინთეზატორი +Comment[km]=កម្មវិធីសង្គ្រោះការនិយាយ Epos TTS +Comment[mk]=Epos TTS синтетизатор на говор +Comment[ms]=Pensintesis tutur Epos TTS +Comment[nb]=Epos TTT talesyntetisering +Comment[nds]=Blicksnuut vun Epos +Comment[ne]=Epos TTS संवाद सिन्थेसाइजर +Comment[nl]=Epos TTS spraaksynthesizer +Comment[pa]=Epos TTS ਬੋਲੀ ਸੰਸਲੇਸ਼ਣ +Comment[pl]=Syntezator mowy Epos +Comment[pt]=O sintetizador de fala Epos TTS +Comment[pt_BR]=Sistema de Sintetizador de Fala Epos +Comment[ru]=Синтезатор речи Epos TTS +Comment[sk]=Syntetizátor reči Epos TTS +Comment[sl]=Sintetizator besedila v govor Epos +Comment[sr]=Синтетизатор говора Epos TTS +Comment[sr@Latn]=Sintetizator govora Epos TTS +Comment[sv]=Epos TTS talsyntes +Comment[ta]=Epos TTS பேச்சு கூட்டிணைப்பான் +Comment[tg]=Таҳлилгари овози Epos TTS +Comment[tr]=Epos TTS konuşma sentezleyicisi +Comment[uk]=Синтезатор мовлення Epos TTS +Comment[vi]=Trình tổng hợp Văn bản sang Tiếng nói Epos +Comment[zh_TW]=Epos TTS 語音合成器 +Type=Service +ServiceTypes=KTTSD/SynthPlugin +X-KDE-Library=libkttsd_eposplugin +X-KDE-Languages=cs,sk |