summaryrefslogtreecommitdiffstats
path: root/kttsd/libkttsd
diff options
context:
space:
mode:
Diffstat (limited to 'kttsd/libkttsd')
-rw-r--r--kttsd/libkttsd/Makefile.am45
-rw-r--r--kttsd/libkttsd/filterconf.cpp166
-rw-r--r--kttsd/libkttsd/filterconf.h149
-rw-r--r--kttsd/libkttsd/filterproc.cpp153
-rw-r--r--kttsd/libkttsd/filterproc.h179
-rw-r--r--kttsd/libkttsd/kdeexportfix.h27
-rw-r--r--kttsd/libkttsd/kttsd_synthplugin.desktop8
-rw-r--r--kttsd/libkttsd/notify.cpp177
-rw-r--r--kttsd/libkttsd/notify.h85
-rw-r--r--kttsd/libkttsd/player.h76
-rw-r--r--kttsd/libkttsd/pluginconf.cpp245
-rw-r--r--kttsd/libkttsd/pluginconf.h378
-rw-r--r--kttsd/libkttsd/pluginproc.cpp292
-rw-r--r--kttsd/libkttsd/pluginproc.h466
-rw-r--r--kttsd/libkttsd/selecttalkerdlg.cpp365
-rw-r--r--kttsd/libkttsd/selecttalkerdlg.h110
-rw-r--r--kttsd/libkttsd/selecttalkerwidget.ui572
-rw-r--r--kttsd/libkttsd/stretcher.cpp99
-rw-r--r--kttsd/libkttsd/stretcher.h97
-rw-r--r--kttsd/libkttsd/talkercode.cpp517
-rw-r--r--kttsd/libkttsd/talkercode.h197
-rw-r--r--kttsd/libkttsd/testplayer.cpp209
-rw-r--r--kttsd/libkttsd/testplayer.h121
-rw-r--r--kttsd/libkttsd/utils.cpp132
-rw-r--r--kttsd/libkttsd/utils.h61
25 files changed, 4926 insertions, 0 deletions
diff --git a/kttsd/libkttsd/Makefile.am b/kttsd/libkttsd/Makefile.am
new file mode 100644
index 0000000..3b2c099
--- /dev/null
+++ b/kttsd/libkttsd/Makefile.am
@@ -0,0 +1,45 @@
+# Include paths.
+INCLUDES = $(all_includes)
+
+# Let automoc handle all of the metsource files (moc).
+METASOURCES = AUTO
+
+#########################################################################
+# LIBRARY SECTION
+#########################################################################
+# This is the library that gets installed. It's name is used for all
+# of the other Makefile.am variables.
+lib_LTLIBRARIES = libkttsd.la
+
+# The source, library search path, and link libraries.
+libkttsd_la_SOURCES = \
+ pluginproc.cpp \
+ pluginconf.cpp \
+ testplayer.cpp \
+ stretcher.cpp \
+ talkercode.cpp \
+ filterproc.cpp \
+ filterconf.cpp \
+ utils.cpp \
+ selecttalkerdlg.cpp \
+ selecttalkerwidget.ui \
+ notify.cpp
+
+libkttsd_la_LDFLAGS = -version-info 1:0:0 $(all_libraries) -no-undefined
+libkttsd_la_LIBADD = $(LIB_KIO)
+
+# Header files that should not be installed.
+noinst_HEADERS = \
+ pluginproc.h \
+ pluginconf.h \
+ player.h \
+ testplayer.h \
+ stretcher.h \
+ talkercode.h \
+ utils.h \
+ kdeexportfix.h \
+ selecttalkerdlg.h
+
+# This library is installed as a service.
+servicetypes_DATA = kttsd_synthplugin.desktop
+servicetypesdir = $(kde_servicetypesdir)
diff --git a/kttsd/libkttsd/filterconf.cpp b/kttsd/libkttsd/filterconf.cpp
new file mode 100644
index 0000000..43e62bc
--- /dev/null
+++ b/kttsd/libkttsd/filterconf.cpp
@@ -0,0 +1,166 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Filter Configuration class.
+ This is the interface definition for text filter configuration dialogs.
+ -------------------
+ 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.
+ ******************************************************************************/
+
+// C++ library includes.
+#include <stdlib.h>
+#include <sys/param.h>
+
+// Qt includes.
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qstring.h>
+
+// KDE includes.
+#include <kglobal.h>
+#include <klocale.h>
+
+// PluginConf includes.
+#include "filterconf.h"
+#include "filterconf.moc"
+
+/**
+* Constructor
+*/
+KttsFilterConf::KttsFilterConf( QWidget *parent, const char *name) : QWidget(parent, name){
+ // kdDebug() << "KttsFilterConf::KttsFilterConf: Running" << endl;
+ QString systemPath(getenv("PATH"));
+ // kdDebug() << "Path is " << systemPath << endl;
+ KGlobal::locale()->insertCatalogue("kttsd");
+ m_path = QStringList::split(":", systemPath);
+}
+
+/**
+* Destructor.
+*/
+KttsFilterConf::~KttsFilterConf(){
+ // kdDebug() << "KttsFilterConf::~KttsFilterConf: Running" << endl;
+}
+
+/**
+* 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 kttsmgr calls this when the plugin is
+* loaded, so it not necessary to call it in your constructor.
+* The plugin should read its configuration from the specified group
+* in the specified config file.
+* @param config Pointer to a KConfig object.
+* @param configGroup Call config->setGroup with this argument before
+* loading your configuration.
+*/
+void KttsFilterConf::load(KConfig* /*config*/, const QString& /*configGroup*/){
+ // kdDebug() << "KttsFilterConf::load: Running" << endl;
+}
+
+/**
+* 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". The plugin should save its configuration in the specified
+* group of the specified config file.
+* @param config Pointer to a KConfig object.
+* @param configGroup Call config->setGroup with this argument before
+* saving your configuration.
+*/
+void KttsFilterConf::save(KConfig* /*config*/, const QString& /*configGroup*/){
+ // kdDebug() << "KttsFilterConf::save: Running" << endl;
+}
+
+/**
+* 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. Note that defaults should
+* be applied to the on-screen widgets; not to the config file.
+*/
+void KttsFilterConf::defaults(){
+ // kdDebug() << "KttsFilterConf::defaults: Running" << endl;
+}
+
+/**
+ * Indicates whether the plugin supports multiple instances. Return
+ * False if only one instance of the plugin can be configured.
+ * @return True if multiple instances are possible.
+ */
+bool KttsFilterConf::supportsMultiInstance() { return false; }
+
+/**
+ * Returns the name of the plugin. Displayed in Filters tab of KTTSMgr.
+ * If there can be more than one instance of a filter, it should return
+ * a unique name for each instance. The name should be translated for
+ * the user if possible. If the plugin is not correctly configured,
+ * return an empty string.
+ * @return Filter instance name.
+ */
+QString KttsFilterConf::userPlugInName() { return QString::null; }
+
+/**
+ * Returns True if this filter is a Sentence Boundary Detector.
+ * @return True if this filter is a SBD.
+ */
+bool KttsFilterConf::isSBD() { return false; }
+
+/**
+* Return the full path to any program in the $PATH environmental variable
+* @param name The name of the file to search for.
+* @returns The path to the file on success, a blank QString
+* if its not found.
+*/
+QString KttsFilterConf::getLocation(const QString &name) {
+ // Iterate over the path and see if 'name' exists in it. Return the
+ // full path to it if it does. Else return an empty QString.
+ if (QFile::exists(name)) return name;
+ // kdDebug() << "KttsFilterConf::getLocation: Searching for " << name << " in the path.." << endl;
+ // kdDebug() << m_path << endl;
+ for(QStringList::iterator it = m_path.begin(); it != m_path.end(); ++it) {
+ QString fullName = *it;
+ fullName += "/";
+ fullName += name;
+ // The user either has the directory of the file in the path...
+ if(QFile::exists(fullName)) {
+ // kdDebug() << "KttsFilterConf:getLocation: " << fullName << endl;
+ return fullName;
+ }
+ // ....Or the file itself
+ else if(QFileInfo(*it).baseName().append(QString(".").append(QFileInfo(*it).extension())) == name) {
+ // kdDebug() << "KttsFilterConf:getLocation: " << fullName << endl;
+ return fullName;
+ }
+ }
+ return "";
+}
+
+/*static*/ QString KttsFilterConf::realFilePath(const QString &filename)
+{
+ char realpath_buffer[MAXPATHLEN + 1];
+ memset(realpath_buffer, 0, MAXPATHLEN + 1);
+
+ /* If the path contains symlinks, get the real name */
+ if (realpath( QFile::encodeName(filename).data(), realpath_buffer) != 0) {
+ //succes, use result from realpath
+ return QFile::decodeName(realpath_buffer);
+ }
+ return filename;
+}
diff --git a/kttsd/libkttsd/filterconf.h b/kttsd/libkttsd/filterconf.h
new file mode 100644
index 0000000..42055d8
--- /dev/null
+++ b/kttsd/libkttsd/filterconf.h
@@ -0,0 +1,149 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Filter Configuration class.
+ This is the interface definition for text filter configuration dialogs.
+ -------------------
+ 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.
+ ******************************************************************************/
+
+#ifndef _FILTERCONF_H_
+#define _FILTERCONF_H_
+
+// Qt includes.
+#include <qwidget.h>
+
+// KDE includes.
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kdemacros.h>
+#include "kdeexportfix.h"
+
+class KDE_EXPORT KttsFilterConf : public QWidget{
+ Q_OBJECT
+
+ public:
+ /**
+ * Constructor
+ */
+ KttsFilterConf( QWidget *parent = 0, const char *name = 0);
+
+ /**
+ * Destructor
+ */
+ virtual ~KttsFilterConf();
+
+ /**
+ * 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 KTTSMGR calls this when the plugin is
+ * loaded, so it not necessary to call it in your constructor.
+ * The plugin should read its configuration from the specified group
+ * in the specified config file.
+ * @param config Pointer to a KConfig object.
+ * @param configGroup Call config->setGroup with this argument before
+ * loading your configuration.
+ *
+ * When a plugin is first added to KTTSMGR, @e load will be called with
+ * a Null @e configGroup. In this case, the plugin will not have
+ * any instance-specific parameters to load, but it may still wish
+ * to load parameters that apply to all instances of the plugin.
+ */
+ virtual 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". The plugin should save its configuration in the specified
+ * group of the specified config file.
+ * @param config Pointer to a KConfig object.
+ * @param configGroup Call config->setGroup with this argument before
+ * saving your configuration.
+ */
+ virtual 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. Note that defaults should
+ * be applied to the on-screen widgets; not to the config file.
+ */
+ virtual void defaults();
+
+ /**
+ * Indicates whether the plugin supports multiple instances. Return
+ * False if only one instance of the plugin can be configured.
+ * @return True if multiple instances are possible.
+ */
+ virtual bool supportsMultiInstance();
+
+ /**
+ * Returns the name of the plugin. Displayed in Filters tab of KTTSMgr.
+ * If there can be more than one instance of a filter, it should return
+ * a unique name for each instance. The name should be translated for
+ * the user if possible. If the plugin is not correctly configured,
+ * return an empty string.
+ * @return Filter instance name.
+ */
+ virtual QString userPlugInName();
+
+ /**
+ * Returns True if this filter is a Sentence Boundary Detector.
+ * @return True if this filter is a SBD.
+ */
+ virtual bool isSBD();
+
+ static QString realFilePath(const QString &filename);
+
+ public slots:
+ /**
+ * This slot is used internally when the configuration is changed. It is
+ * typically connected to signals from the widgets of the configuration
+ * and should emit the @ref changed signal.
+ */
+ void configChanged(){
+ // kdDebug() << "KttsFilterConf::configChanged: Running"<< endl;
+ emit changed(true);
+ };
+
+ signals:
+ /**
+ * This signal indicates that the configuration has been changed.
+ * It should be emitted whenever user changes something in the configuration widget.
+ */
+ void changed(bool);
+
+ protected:
+ /**
+ * Searches the $PATH variable for any file. If that file exists in the PATH, or
+ * is contained in any directory in the PATH, it returns the full path to it.
+ * @param name The name of the file to search for.
+ * @returns The path to the file on success, a blank QString
+ * if its not found.
+ */
+ QString getLocation(const QString &name);
+
+ /// The system path in a QStringList.
+ QStringList m_path;
+};
+
+#endif //_FILTERCONF_H_
diff --git a/kttsd/libkttsd/filterproc.cpp b/kttsd/libkttsd/filterproc.cpp
new file mode 100644
index 0000000..178dec2
--- /dev/null
+++ b/kttsd/libkttsd/filterproc.cpp
@@ -0,0 +1,153 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Filter Processing class.
+ This is the interface definition for text filters.
+ -------------------
+ 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.
+ ******************************************************************************/
+
+// KDE includes.
+// #include <kdebug.h>
+
+// FilterProc includes.
+#include "filterproc.h"
+
+/**
+ * Constructor.
+ */
+KttsFilterProc::KttsFilterProc( QObject *parent, const char *name) :
+ QObject(parent, name)
+{
+ // kdDebug() << "KttsFilterProc::KttsFilterProc: Running" << endl;
+}
+
+/**
+ * Destructor.
+ */
+KttsFilterProc::~KttsFilterProc()
+{
+ // kdDebug() << "KttsFilterProc::~KttsFilterProc: Running" << endl;
+}
+
+/**
+ * Initialize the filter.
+ * @param config Settings object.
+ * @param configGroup Settings Group.
+ * @return False if filter is not ready to filter.
+ *
+ * Note: The parameters are for reading from kttsdrc file. Plugins may wish to maintain
+ * separate configuration files of their own.
+ */
+bool KttsFilterProc::init(KConfig* /*config*/, const QString& /*configGroup*/){
+ // kdDebug() << "PlugInProc::init: Running" << endl;
+ return false;
+}
+
+/**
+ * Returns True if this filter is a Sentence Boundary Detector.
+ * If so, the filter should implement @ref setSbRegExp() .
+ * @return True if this filter is a SBD.
+ */
+/*virtual*/ bool KttsFilterProc::isSBD() { return false; }
+
+/**
+ * Returns True if the plugin supports asynchronous processing,
+ * i.e., supports asyncConvert method.
+ * @return True if this plugin supports asynchronous processing.
+ *
+ * If the plugin returns True, it must also implement @ref getState .
+ * It must also emit @ref filteringFinished when filtering is completed.
+ * If the plugin returns True, it must also implement @ref stopFiltering .
+ * It must also emit @ref filteringStopped when filtering has been stopped.
+ */
+/*virtual*/ bool KttsFilterProc::supportsAsync() { return false; }
+
+/**
+ * Convert input, returning output. Runs synchronously.
+ * @param inputText Input text.
+ * @param talkerCode TalkerCode structure for the talker that KTTSD intends to
+ * use for synthing the text. Useful for extracting hints about
+ * how to filter the text. For example, languageCode.
+ * @param appId The DCOP appId of the application that queued the text.
+ * Also useful for hints about how to do the filtering.
+ */
+/*virtual*/ QString KttsFilterProc::convert(const QString& inputText, TalkerCode* /*talkerCode*/,
+ const QCString& /*appId*/)
+{
+ return inputText;
+}
+
+/**
+ * Convert input. Runs asynchronously.
+ * @param inputText Input text.
+ * @param talkerCode TalkerCode structure for the talker that KTTSD intends to
+ * use for synthing the text. Useful for extracting hints about
+ * how to filter the text. For example, languageCode.
+ * @param appId The DCOP appId of the application that queued the text.
+ * Also useful for hints about how to do the filtering.
+ * @return False if the filter cannot perform the conversion.
+ *
+ * When conversion is completed, emits signal @ref filteringFinished. Calling
+ * program may then call @ref getOutput to retrieve converted text. Calling
+ * program must call @ref ackFinished to acknowledge the conversion.
+ */
+/*virtual*/ bool KttsFilterProc::asyncConvert(const QString& /*inputText*/,
+ TalkerCode* /*talkerCode*/, const QCString& /*appId*/) { return false; }
+
+/**
+ * Waits for a previous call to asyncConvert to finish.
+ */
+/*virtual*/ void KttsFilterProc::waitForFinished() { }
+
+/**
+ * Returns the state of the Filter.
+ */
+/*virtual*/ int KttsFilterProc::getState() { return fsIdle; }
+
+/**
+ * Returns the filtered output.
+ */
+/*virtual*/ QString KttsFilterProc::getOutput() { return QString::null; }
+
+/**
+ * Acknowledges the finished filtering.
+ */
+/*virtual*/ void KttsFilterProc::ackFinished() { }
+
+/**
+ * Stops filtering. The filteringStopped signal will emit when filtering
+ * has in fact stopped and state returns to fsIdle;
+ */
+/*virtual*/ void KttsFilterProc::stopFiltering() { }
+
+/**
+ * Did this filter do anything? If the filter returns the input as output
+ * unmolested, it should return False when this method is called.
+ */
+/*virtual*/ bool KttsFilterProc::wasModified() { return true; }
+
+/**
+ * Set Sentence Boundary Regular Expression.
+ * This method will only be called if the application overrode the default.
+ *
+ * @param re The sentence delimiter regular expression.
+ */
+/*virtual*/ void KttsFilterProc::setSbRegExp(const QString& /*re*/) { }
+
+#include "filterproc.moc"
diff --git a/kttsd/libkttsd/filterproc.h b/kttsd/libkttsd/filterproc.h
new file mode 100644
index 0000000..ef7972c
--- /dev/null
+++ b/kttsd/libkttsd/filterproc.h
@@ -0,0 +1,179 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Filter Processing class.
+ This is the interface definition for text filters.
+ -------------------
+ 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.
+ ******************************************************************************/
+
+#ifndef _FILTERPROC_H_
+#define _FILTERPROC_H_
+
+// Qt includes.
+#include <qobject.h>
+#include <qstringlist.h>
+
+// KDE includes.
+#include <kdemacros.h>
+#include "kdeexportfix.h"
+
+class TalkerCode;
+class KConfig;
+
+class KDE_EXPORT KttsFilterProc : virtual public QObject
+{
+ Q_OBJECT
+
+public:
+ enum FilterState {
+ fsIdle = 0, // Not doing anything. Ready to filter.
+ fsFiltering = 1, // Filtering.
+ fsStopping = 2, // Stop of filtering is in progress.
+ fsFinished = 3 // Filtering finished.
+ };
+
+ /**
+ * Constructor.
+ */
+ KttsFilterProc( QObject *parent, const char *name );
+
+ /**
+ * Destructor.
+ */
+ virtual ~KttsFilterProc();
+
+ /**
+ * Initialize the filter.
+ * @param config Settings object.
+ * @param configGroup Settings Group.
+ * @return False if filter is not ready to filter.
+ *
+ * Note: The parameters are for reading from kttsdrc file. Plugins may wish to maintain
+ * separate configuration files of their own.
+ */
+ virtual bool init(KConfig *config, const QString &configGroup);
+
+ /**
+ * Returns True if this filter is a Sentence Boundary Detector.
+ * If so, the filter should implement @ref setSbRegExp() .
+ * @return True if this filter is a SBD.
+ */
+ virtual bool isSBD();
+
+ /**
+ * Returns True if the plugin supports asynchronous processing,
+ * i.e., supports asyncConvert method.
+ * @return True if this plugin supports asynchronous processing.
+ *
+ * If the plugin returns True, it must also implement @ref getState .
+ * It must also emit @ref filteringFinished when filtering is completed.
+ * If the plugin returns True, it must also implement @ref stopFiltering .
+ * It must also emit @ref filteringStopped when filtering has been stopped.
+ */
+ virtual bool supportsAsync();
+
+ /**
+ * Convert input, returning output. Runs synchronously.
+ * @param inputText Input text.
+ * @param talkerCode TalkerCode structure for the talker that KTTSD intends to
+ * use for synthing the text. Useful for extracting hints about
+ * how to filter the text. For example, languageCode.
+ * @param appId The DCOP appId of the application that queued the text.
+ * Also useful for hints about how to do the filtering.
+ */
+ virtual QString convert(const QString& inputText, TalkerCode* talkerCode, const QCString& appId);
+
+ /**
+ * Convert input. Runs asynchronously.
+ * @param inputText Input text.
+ * @param talkerCode TalkerCode structure for the talker that KTTSD intends to
+ * use for synthing the text. Useful for extracting hints about
+ * how to filter the text. For example, languageCode.
+ * @param appId The DCOP appId of the application that queued the text.
+ * Also useful for hints about how to do the filtering.
+ * @return False if the filter cannot perform the conversion.
+ *
+ * When conversion is completed, emits signal @ref filteringFinished. Calling
+ * program may then call @ref getOutput to retrieve converted text. Calling
+ * program must call @ref ackFinished to acknowledge the conversion.
+ */
+ virtual bool asyncConvert(const QString& inputText, TalkerCode* talkerCode, const QCString& appId);
+
+ /**
+ * Waits for a previous call to asyncConvert to finish.
+ */
+ virtual void waitForFinished();
+
+ /**
+ * Returns the state of the Filter.
+ */
+ virtual int getState();
+
+ /**
+ * Returns the filtered output.
+ */
+ virtual QString getOutput();
+
+ /**
+ * Acknowledges the finished filtering.
+ */
+ virtual void ackFinished();
+
+ /**
+ * Stops filtering. The filteringStopped signal will emit when filtering
+ * has in fact stopped and state returns to fsIdle;
+ */
+ virtual void stopFiltering();
+
+ /**
+ * Did this filter do anything? If the filter returns the input as output
+ * unmolested, it should return False when this method is called.
+ */
+ virtual bool wasModified();
+
+ /**
+ * Set Sentence Boundary Regular Expression.
+ * This method will only be called if the application overrode the default.
+ *
+ * @param re The sentence delimiter regular expression.
+ */
+ virtual void setSbRegExp(const QString& re);
+
+signals:
+ /**
+ * Emitted when asynchronous filtering has completed.
+ */
+ void filteringFinished();
+
+ /**
+ * Emitted when stopFiltering has been called and filtering has in fact stopped.
+ */
+ void filteringStopped();
+
+ /**
+ * If an error occurs, Filter should signal the error and return input as output in
+ * convert method. If Filter should not be called in the future, perhaps because
+ * it could not find its configuration file, return False for keepGoing.
+ * @param keepGoing False if the filter should not be called in the future.
+ * @param msg Error message.
+ */
+ void error(bool keepGoing, const QString &msg);
+};
+
+#endif // _FILTERPROC_H_
diff --git a/kttsd/libkttsd/kdeexportfix.h b/kttsd/libkttsd/kdeexportfix.h
new file mode 100644
index 0000000..7c80d9c
--- /dev/null
+++ b/kttsd/libkttsd/kdeexportfix.h
@@ -0,0 +1,27 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ kdelibs < 3.3.2 had a bug in the KDE_EXPORT macro. This file fixes this
+ by undefining it.
+ -------------------
+ 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#ifndef _KDEEXPORTFIX_H_
+#define _KDEEXPORTFIX_H_
+
+#include <kdeversion.h>
+#if KDE_VERSION < KDE_MAKE_VERSION (3,3,2)
+#undef KDE_EXPORT
+#define KDE_EXPORT
+#endif
+
+#endif // _KDEEXPORTFIX_H_
diff --git a/kttsd/libkttsd/kttsd_synthplugin.desktop b/kttsd/libkttsd/kttsd_synthplugin.desktop
new file mode 100644
index 0000000..c6367af
--- /dev/null
+++ b/kttsd/libkttsd/kttsd_synthplugin.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Name=KTTSD
+Name[zh_TW]=KTTSd
+Type=ServiceType
+X-KDE-ServiceType=KTTSD/SynthPlugin
+
+[PropertyDef::X-KDE-Languages]
+Type=QStringList
diff --git a/kttsd/libkttsd/notify.cpp b/kttsd/libkttsd/notify.cpp
new file mode 100644
index 0000000..fd28587
--- /dev/null
+++ b/kttsd/libkttsd/notify.cpp
@@ -0,0 +1,177 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Notification Action constants and utility functions.
+ -------------------
+ Copyright : (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+ Current Maintainer: 2004 by 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+// Qt includes.
+
+// Qt includes.
+#include <qstring.h>
+#include <qstringlist.h>
+
+// KDE includes.
+#include <kconfig.h>
+#include <klocale.h>
+#include <kstaticdeleter.h>
+
+// KTTS includes.
+#include "notify.h"
+
+static QStringList* s_actionNames = 0;
+static KStaticDeleter<QStringList> s_actionNames_sd;
+
+static QStringList* s_actionDisplayNames = 0;
+static KStaticDeleter<QStringList> s_actionDisplayNames_sd;
+
+static void notifyaction_init()
+{
+ if ( !s_actionNames )
+ {
+ s_actionNames_sd.setObject(s_actionNames, new QStringList);
+ s_actionNames->append( "SpeakEventName" );
+ s_actionNames->append( "SpeakMsg" );
+ s_actionNames->append( "DoNotSpeak" );
+ s_actionNames->append( "SpeakCustom" );
+
+ s_actionDisplayNames_sd.setObject(s_actionDisplayNames, new QStringList);
+ s_actionDisplayNames->append( i18n("Speak event name") );
+ s_actionDisplayNames->append( i18n("Speak the notification message") );
+ s_actionDisplayNames->append( i18n("Do not speak the notification") );
+ s_actionDisplayNames->append( i18n("Speak custom text:") );
+ }
+}
+
+/*static*/ int NotifyAction::count()
+{
+ notifyaction_init();
+ return s_actionNames->count();
+}
+
+/*static*/ QString NotifyAction::actionName( const int action )
+{
+ notifyaction_init();
+ return (*s_actionNames)[ action ];
+}
+
+/*static*/ int NotifyAction::action( const QString& actionName )
+{
+ notifyaction_init();
+ return s_actionNames->findIndex( actionName );
+}
+
+/*static*/ QString NotifyAction::actionDisplayName( const int action )
+{
+ notifyaction_init();
+ return (*s_actionDisplayNames)[ action ];
+}
+
+/*static*/ QString NotifyAction::actionDisplayName( const QString& actionName )
+{
+ notifyaction_init();
+ return (*s_actionDisplayNames)[ action( actionName ) ];
+}
+
+// --------------------------------------------------------------------
+
+static QStringList* s_presentNames = 0;
+static KStaticDeleter<QStringList> s_presentNames_sd;
+
+static QStringList* s_presentDisplayNames = 0;
+static KStaticDeleter<QStringList> s_presentDisplayNames_sd;
+
+static void notifypresent_init()
+{
+ if ( !s_presentNames )
+ {
+ s_presentNames_sd.setObject( s_presentNames, new QStringList );
+ s_presentNames->append( "None" );
+ s_presentNames->append( "Dialog" );
+ s_presentNames->append( "Passive" );
+ s_presentNames->append( "DialogAndPassive" );
+ s_presentNames->append( "All" );
+
+ s_presentDisplayNames_sd.setObject( s_presentDisplayNames, new QStringList );
+ s_presentDisplayNames->append( i18n("none") );
+ s_presentDisplayNames->append( i18n("notification dialogs") );
+ s_presentDisplayNames->append( i18n("passive popups") );
+ s_presentDisplayNames->append( i18n("notification dialogs and passive popups") );
+ s_presentDisplayNames->append( i18n("all notifications" ) );
+ }
+}
+
+/*static*/ int NotifyPresent::count()
+{
+ notifypresent_init();
+ return s_presentNames->count();
+}
+
+/*static*/ QString NotifyPresent::presentName( const int present )
+{
+ notifypresent_init();
+ return (*s_presentNames)[ present ];
+}
+
+/*static*/ int NotifyPresent::present( const QString& presentName )
+{
+ notifypresent_init();
+ return s_presentNames->findIndex( presentName );
+}
+
+/*static*/ QString NotifyPresent::presentDisplayName( const int present )
+{
+ notifypresent_init();
+ return (*s_presentDisplayNames)[ present ];
+}
+
+/*static*/ QString NotifyPresent::presentDisplayName( const QString& presentName )
+{
+ notifypresent_init();
+ return (*s_presentDisplayNames)[ present( presentName ) ];
+}
+
+// --------------------------------------------------------------------
+
+/**
+ * Retrieves the displayable name for an event source.
+ */
+/*static*/ QString NotifyEvent::getEventSrcName(const QString& eventSrc, QString& iconName)
+{
+ QString configFilename = eventSrc + QString::fromLatin1( "/eventsrc" );
+ KConfig* config = new KConfig( configFilename, true, false, "data" );
+ config->setGroup( QString::fromLatin1( "!Global!" ) );
+ QString appDesc = config->readEntry( "Comment", i18n("No description available") );
+ iconName = config->readEntry( "IconName" );
+ delete config;
+ return appDesc;
+}
+
+/**
+ * Retrieves the displayable name for an event from an event source.
+ */
+/*static*/ QString NotifyEvent::getEventName(const QString& eventSrc, const QString& event)
+{
+ QString eventName;
+ QString configFilename = eventSrc + QString::fromLatin1( "/eventsrc" );
+ KConfig* config = new KConfig( configFilename, true, false, "data" );
+ if ( config->hasGroup( event ) )
+ {
+ config->setGroup( event );
+ eventName = config->readEntry( QString::fromLatin1( "Comment" ),
+ config->readEntry( QString::fromLatin1( "Name" )));
+ }
+ delete config;
+ return eventName;
+}
+
diff --git a/kttsd/libkttsd/notify.h b/kttsd/libkttsd/notify.h
new file mode 100644
index 0000000..2a8162d
--- /dev/null
+++ b/kttsd/libkttsd/notify.h
@@ -0,0 +1,85 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Notification Action constants and utility functions.
+ -------------------
+ Copyright : (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+ Current Maintainer: 2004 by 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#ifndef _NOTIFYACTION_H
+#define _NOTIFYACTION_H
+
+#include <kdemacros.h>
+
+class QString;
+
+class KDE_EXPORT NotifyAction
+{
+
+public:
+
+ enum NotifyActions
+ {
+ SpeakEventName,
+ SpeakMsg,
+ DoNotSpeak,
+ SpeakCustom
+ };
+
+ static int count();
+ static QString actionName( const int action );
+ static int action( const QString& actionName );
+ static QString actionDisplayName( const int action );
+ static QString actionDisplayName( const QString& actionName );
+};
+
+// --------------------------------------------------------------------
+
+class KDE_EXPORT NotifyPresent
+{
+
+public:
+
+ enum NotifyPresentations
+ {
+ None,
+ Dialog,
+ Passive,
+ DialogAndPassive,
+ All
+ };
+
+ static int count();
+ static QString presentName( const int present );
+ static int present( const QString& presentName );
+ static QString presentDisplayName( const int present );
+ static QString presentDisplayName( const QString& presentName );
+};
+
+// --------------------------------------------------------------------
+
+class KDE_EXPORT NotifyEvent
+{
+
+public:
+ /**
+ * Retrieves the displayable name for an event source.
+ */
+ static QString getEventSrcName(const QString& eventSrc, QString& iconName);
+
+ /**
+ * Retrieves the displayable name for an event from an event source.
+ */
+ static QString getEventName(const QString& eventSrc, const QString& event);
+};
+
+#endif // _NOTIFYACTION_H
diff --git a/kttsd/libkttsd/player.h b/kttsd/libkttsd/player.h
new file mode 100644
index 0000000..ff4d240
--- /dev/null
+++ b/kttsd/libkttsd/player.h
@@ -0,0 +1,76 @@
+/***************************************************************************
+ begin : Sun Feb 17 2002
+ copyright : (C) 2002 - 2004 by Scott Wheeler
+ email : wheeler@kde.org
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifndef PLAYER_H
+#define PLAYER_H
+
+#include <qobject.h>
+#include <qstringlist.h>
+#include <kdemacros.h>
+#include "kdeexportfix.h"
+#include "kglobal.h"
+#include "klocale.h"
+
+// #include "filehandle.h"
+
+class KDE_EXPORT Player : virtual public QObject
+{
+ Q_OBJECT
+
+public:
+ virtual ~Player() {}
+
+// virtual void play(const FileHandle &file = FileHandle::null()) = 0;
+ virtual void startPlay(const QString& file) = 0;
+ virtual void pause() = 0;
+ virtual void stop() = 0;
+
+ virtual void setVolume(float volume = 1.0) = 0;
+ virtual float volume() const = 0;
+
+ virtual bool playing() const = 0;
+ virtual bool paused() const = 0;
+
+ virtual int totalTime() const = 0;
+ virtual int currentTime() const = 0;
+ virtual int position() const = 0; // in this case not really the percent
+
+ virtual void seek(int seekTime) = 0;
+ virtual void seekPosition(int position) = 0;
+
+ virtual QStringList getPluginList( const QCString& classname ) {
+ Q_UNUSED(classname);
+ return QStringList();
+ }
+ virtual void setSinkName(const QString &sinkName) { Q_UNUSED(sinkName); }
+ virtual bool requireVersion(uint major, uint minor, uint micro) {
+ Q_UNUSED(major);
+ Q_UNUSED(minor);
+ Q_UNUSED(micro);
+ return true;
+ }
+ virtual void setDebugLevel(uint level) { Q_UNUSED(level); }
+ virtual void setPeriodSize(uint periodSize) { Q_UNUSED(periodSize); }
+ virtual void setPeriods(uint periods) {Q_UNUSED(periods); }
+
+protected:
+ Player(QObject* parent = 0, const char* name = 0, const QStringList& args=QStringList() ) :
+ QObject(parent, name) {
+ Q_UNUSED(args);
+ }
+
+};
+
+#endif
diff --git a/kttsd/libkttsd/pluginconf.cpp b/kttsd/libkttsd/pluginconf.cpp
new file mode 100644
index 0000000..d2fb4b6
--- /dev/null
+++ b/kttsd/libkttsd/pluginconf.cpp
@@ -0,0 +1,245 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ This file is the templates for the configuration plug ins.
+ -------------------
+ Copyright : (C) 2002-2003 by José Pablo Ezequiel "Pupeno" Fernández
+ -------------------
+ Original author: José Pablo Ezequiel "Pupeno" Fernández <pupeno@kde.org>
+ Current Maintainer: 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+// C++ library includes.
+#include <stdlib.h>
+#include <sys/param.h>
+
+// Qt includes.
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qstring.h>
+
+// KDE includes.
+#include <kglobal.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+
+// PluginConf includes.
+#include "pluginconf.h"
+#include "pluginconf.moc"
+
+/**
+* Constructor
+*/
+PlugInConf::PlugInConf( QWidget *parent, const char *name) : QWidget(parent, name){
+ kdDebug() << "PlugInConf::PlugInConf: Running" << endl;
+ KGlobal::locale()->insertCatalogue("kttsd");
+ QString systemPath(getenv("PATH"));
+ // kdDebug() << "Path is " << systemPath << endl;
+ m_path = QStringList::split(":", systemPath);
+ m_player = 0;
+}
+
+/**
+* Destructor.
+*/
+PlugInConf::~PlugInConf(){
+ kdDebug() << "PlugInConf::~PlugInConf: Running" << endl;
+ delete m_player;
+}
+
+/**
+* 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 kttsmgr calls this when the plugin is
+* loaded, so it not necessary to call it in your constructor.
+* The plugin should read its configuration from the specified group
+* in the specified config file.
+* @param config Pointer to a KConfig object.
+* @param configGroup Call config->setGroup with this argument before
+* loading your configuration.
+*/
+void PlugInConf::load(KConfig* /*config*/, const QString& /*configGroup*/){
+ kdDebug() << "PlugInConf::load: Running" << endl;
+}
+
+/**
+* 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". The plugin should save its configuration in the specified
+* group of the specified config file.
+* @param config Pointer to a KConfig object.
+* @param configGroup Call config->setGroup with this argument before
+* saving your configuration.
+*/
+void PlugInConf::save(KConfig* /*config*/, const QString& /*configGroup*/){
+ kdDebug() << "PlugInConf::save: Running" << endl;
+}
+
+/**
+* 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. Note that defaults should
+* be applied to the on-screen widgets; not to the config file.
+*/
+void PlugInConf::defaults(){
+ kdDebug() << "PlugInConf::defaults: Running" << endl;
+}
+
+/**
+* Indicates whether the plugin supports multiple instances. Return
+* False if only one instance of the plugin can run at a time.
+* @return True if multiple instances are possible.
+*
+* It is assumed that most plugins can support multiple instances.
+* A plugin must override this method and return false if it
+* cannot support multiple instances.
+*/
+bool PlugInConf::supportsMultiInstance() { return true; }
+
+/**
+* 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.
+*/
+void PlugInConf::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 PlugInConf::getTalkerCode() { return QString::null; }
+
+/**
+* Return a list of all the languages currently supported by the plugin.
+* Note that as the user configures your plugin, the language choices may become
+* narrower. For example, once the user has picked a voice file, the language
+* may be determined. If your plugin has just been added and no configuration
+* choices have yet been made, return a list of all possible languages the
+* plugin might support.
+* If your plugin cannot yet determine the languages supported, return Null.
+* If your plugin can support any language, return Null.
+* @return A QStringList of supported language codes, or Null if unknown.
+*/
+QStringList PlugInConf::getSupportedLanguages() { return QStringList(); }
+
+/**
+* Return the full path to any program in the $PATH environmental variable
+* @param name The name of the file to search for.
+* @returns The path to the file on success, a blank QString
+* if its not found.
+*/
+QString PlugInConf::getLocation(const QString &name) {
+ // Iterate over the path and see if 'name' exists in it. Return the
+ // full path to it if it does. Else return an empty QString.
+
+ // If it's a file or a symlink pointing to a file, that's cool.
+ QFileInfo fileinfo(name);
+ if (fileinfo.isFile() || (fileinfo.isSymLink() && QFileInfo(fileinfo.readLink()).isFile()))
+ return name;
+ kdDebug() << "PluginConf::getLocation: Searching for " << name << " in the path.." << endl;
+ kdDebug() << m_path << endl;
+ for(QStringList::iterator it = m_path.begin(); it != m_path.end(); ++it) {
+ QString fullName = *it;
+
+ fullName += "/";
+ fullName += name;
+ fileinfo.setFile(fullName);
+ // The user either has the directory of the file in the path...
+ if(fileinfo.isFile() || (fileinfo.isSymLink() && QFileInfo(fileinfo.readLink()).isFile())) {
+ return fullName;
+// kdDebug() << "PluginConf:getLocation: " << fullName << endl;
+ }
+ // ....Or the file itself in the path (slightly freaky but hey.)
+ else if(QFileInfo(*it).baseName().append(QString(".").append(QFileInfo(*it).extension())) == name) {
+ return fullName;
+// kdDebug() << "PluginConf:getLocation: " << fullName << endl;
+ }
+ }
+ return "";
+}
+
+/**
+* Breaks a language code into the language code and country code (if any).
+* @param languageCode Language code.
+* @return countryCode Just the country code part (if any).
+* @return Just the language code part.
+*/
+QString PlugInConf::splitLanguageCode(const QString& languageCode, QString& countryCode)
+{
+ QString locale = languageCode;
+ QString langCode;
+ QString charSet;
+ KGlobal::locale()->splitLocale(locale, langCode, countryCode, charSet);
+ return langCode;
+}
+
+/*static*/ QString PlugInConf::realFilePath(const QString &filename)
+{
+ char realpath_buffer[MAXPATHLEN + 1];
+ memset(realpath_buffer, 0, MAXPATHLEN + 1);
+
+ /* If the path contains symlinks, get the real name */
+ if (realpath( QFile::encodeName(filename).data(), realpath_buffer) != 0) {
+ // succes, use result from realpath
+ return QFile::decodeName(realpath_buffer);
+ }
+ return filename;
+}
+
+/*static*/ QString PlugInConf::testMessage(const QString& languageCode)
+{
+ QString key = "Name[" + languageCode + "]";
+ QString result;
+ QString def;
+ QFile file(locate("data", "kttsd/kcmkttsd_testmessage.desktop"));
+ if (file.open(IO_ReadOnly))
+ {
+ QTextStream stream(&file);
+ stream.setEncoding(QTextStream::UnicodeUTF8);
+ while ( !stream.atEnd() ) {
+ QString line = stream.readLine(); // line of text excluding '\n'
+ QStringList keyAndValue = QStringList::split("=", line);
+ if (keyAndValue.count() == 2)
+ {
+ if (keyAndValue[0] == key)
+ {
+ result = keyAndValue[1];
+ break;
+ }
+ if (keyAndValue[0] == "Name") def = keyAndValue[1];
+ }
+ }
+ file.close();
+ }
+ if (result.isEmpty())
+ {
+ result = def;
+ if (result.isEmpty()) result = "The text-to-speech system seems to be functioning properly.";
+ }
+ return result;
+}
+
+/**
+* Player object that can be used by the plugin for testing playback of synthed files.
+*/
+void PlugInConf::setPlayer(TestPlayer* player) { m_player = player; }
+TestPlayer* PlugInConf::getPlayer() { return m_player; }
+
diff --git a/kttsd/libkttsd/pluginconf.h b/kttsd/libkttsd/pluginconf.h
new file mode 100644
index 0000000..4c4647c
--- /dev/null
+++ b/kttsd/libkttsd/pluginconf.h
@@ -0,0 +1,378 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ This file is the template for the configuration plug ins.
+ -------------------
+ Copyright : (C) 2002-2003 by José Pablo Ezequiel "Pupeno" Fernández
+ -------------------
+ Original author: José Pablo Ezequiel "Pupeno" Fernández <pupeno@kde.org>
+ Current Maintainer: 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#ifndef _PLUGINCONF_H_
+#define _PLUGINCONF_H_
+
+// Qt includes.
+#include <qwidget.h>
+
+// KDE includes.
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kdemacros.h>
+#include "kdeexportfix.h"
+
+// KTTS includes.
+#include "testplayer.h"
+
+/**
+* @interface PlugInConf
+*
+* pluginconf - the KDE Text-to-Speech Deamon Plugin Configuration API.
+*
+* @version 1.0 Draft 2
+*
+* This class defines the interface that plugins to KTTSMGR must implement.
+*
+* @warning The pluginconf interface is still being developed and is likely
+* to change in the future.
+*
+* A KTTSD Plugin interfaces between KTTSD and a speech engine.
+* A PlugInConf provides an on-screen widget for configuring the plugin for use
+* with KTTSD.
+*
+* @section guidelines General Guidelines
+*
+* - The configuration widget should be no larger than TODO pixels.
+* - Do not supply Load, Save, Cancel, or OK buttons. Those are provided by KTTSMGR.
+* - Try to supply a Test button so that users can test the configuration before
+* saving it.
+* - Your configuration widget will be running inside a KPart.
+* - Whenever the user changes something in your on-screen widget, emit the
+* @ref changed signal.
+* - If a plugin can automatically configure itself, i.e., locate voice files,
+* set default values, etc., it should do so when it is first added to KTTSMGR.
+*
+* @section multiinstance Multiple Instances
+*
+* If it is possible to run multiple simultaneous instances of your synthesis engine,
+* return True from the @ref supportsMultiInstance method. The user will be able to
+* configure multiple instances of your plugin, each with a different set of
+* talker attributes.
+*
+* If you cannot run multiple simultaneous instances of your synthesis engine,
+* or your plugin has a fixed set of talker attributes (only one language, voice,
+* gender, volume, and speed), return False from @ref supportsMultiInstance.
+*
+* @section language Language Support
+*
+* Some plugins support only one language. For them, return the appropriate language
+* code when @ref getSupportedLanguages is called.
+*
+* If your plugin can support multiple languages, your task is a little more
+* complicated. The best way to handle this is to install a @e voices file with
+* your plugin that lists all the supported languages, voice files, genders, etc.
+* that are possible. When your plugin is added to KTTSMGR,
+* @ref getSupportedLanguages will be called. Return a list of all the possible
+* languages supported, even if the user hasn't yet picked a voice file in your
+* configuration, or even told your where the voice files are.
+*
+* There are three ways that users and applications pick a language code for your
+* plugin:
+* - The user picks a code from among the languages you returned in
+* @ref getSupportedLanguages, or
+* - The user picks your plugin and uses your configuration widget to pick a voice
+* file or other configuration option that determines the language, or
+* - An application requests a plugin with specific language support.
+*
+* If possible, avoid making the user pick a language code in your plugin.
+*
+* In the first and third cases, the chosen language code will be passed to your
+* plugin when @ref setDesiredLanguage is called. If you can satisfy this
+* language code, good, but it is possible that once the user has begun
+* configuring your plugin, you find that you cannot support the desired
+* language. Perhaps a needed voice file is missing. That is OK.
+* You'll inform KTTSMGR of the actual language code when KTTSMGR
+* calls @ref getTalkerCode (see below). Note that you should not limit the
+* users choices based on the @ref setDesiredLanguage. A user might start
+* out configuring your plugin for one language, and then change his or her
+* mind to a different language.
+*
+* Also note that language codes may also include an appended country code.
+* For example, "en_GB" for British English. When @ref getSupportedLanguages is
+* called, you should return as specific a list as possible. For example,
+* if your plugin supports both American and British English, your returned
+* list would include "en_GB" and "en_US". When @ref setDesiredLanguage is
+* called, a country code may or may not be included. If included and your
+* plugin supports the language, but not the specific country variant,
+* your plugin should nevertheless attempt to satisfy the request, returning
+* the actual supported language and country when @ref getTalkerCode is called.
+*
+* @section talkercodes Talker Codes
+*
+* Review the section on Talkers in kspeech.h.
+*
+* When your plugin is added to the KTTSMGR, @ref getSupportedLanguages
+* will be called followed by @ref setDesiredLanguage and @ref load.
+* Note that the configuration file will most likely be empty when
+* @ref load is called.
+
+* Next, @ref getTalkerCode
+* will be called. If your plugin can automatically configure itself to the desired
+* language, it should do so and return a fully-specified talker code. If your
+* plugin is not yet ready and requires user help, return QString::null. Note that
+* @ref setDesiredLanguage may be Null, in which case, you should allow the
+* user to configure your plugin to any of your supported languages.
+*
+* When your plugin has been configured enough to begin synthesis, return a
+* fully-specified talker code in @ref getTalkerCode().
+*
+* Here is guidance for what you should return for each of the talker attributes
+* when @ref getTalkerCode is called:
+*
+* - @e lang. If user has completed configuring your plugin, i.e., it is
+* ready to begin synthesizing, return the ISO 639-1 language code
+* for the language it can synthesize. If your plugin is not yet
+* fully configured, you should return QString::null for the entire
+* talker code. If your plugin supports a specific national version
+* of a language, that should also be included using the ISO 3166
+* country code separated from the language code by underscore (_).
+* For example, if your plugin synthesizes American English, you would
+* return "en_US". If British English, "en_BR". And if
+* non-specific English, just "en".
+* - @e synthesizer. The name of your plugin. Keep short, but long enough to
+* distinquish different implementations. For example,
+* Festival Int, Flite, Hadifix. Use only letters, numbers
+* spaces, and underscores (_) in your plugin name.
+* - @e gender. May be "male", "female", or "neutral".
+* - @e name. The voice code. If your plugin does not support voices,
+* return "fixed".
+* - @e volume. May be "medium", "loud", or "soft". If your plugin does not support
+* configurable volume, return "medium".
+* - @e rate. May be "medium", "fast", or "slow". If your plugin does not support
+* configurable speed, return "medium".
+*
+* The order of the attributes you return does not matter. Here is an example of
+* a fully-specified talker code.
+*
+* lang="en" name="Kal" gender="male" volume="soft" rate="fast"
+* synthesizer="Festival Interactive"
+*
+* Do not return translated values for the Talker Code attributes. All English.
+*
+* Each time your plugin emits the @ref changed signal, @ref getTalkerCode will be called.
+* The configuration dialog OK button will be disabled until you return a non-null
+* Talker Code.
+*
+* It is possible that your plugin does not know the language supported. The generic
+* Command plugin is example of such a case, since the user enters an arbitrary command.
+* In this case, return the value from the @ref setDesiredLanguage call. It is possible
+* that @ref setDesiredLanguage is Null. That is OK. In this case, KTTSMGR will prompt
+* the user for the language code.
+*
+* @section loadandsavemethods Load and Save Methods
+*
+* The @ref load and @ref save methods are called by KTTSMGR so that your plugin
+* can load and save configuration options from the configuration file.
+* These methods have two parameters, a @e config object and a @e configGroup string.
+*
+* Plugins that do not support multiple instances (return False from
+* @ref supportsMultiInstance), should simply call config->setGroup(configGroup)
+* before loading or saving their configuration.
+*
+* If your plugin supports multiple instances, it is slightly more complicated.
+* Typically, there will be configuration options that apply to all instances
+* of the plugin and there will be options that apply only to the specific
+* configured instance of the plugin. To load or save the instance-specific
+* options, call config->setGroup(configGroup). For options that apply
+* to all instances of the plugin, call config->setGroup() with a group name
+* that contains your plugin's name. For example,
+* config->setGroup("Festival Defaults").
+*
+* For example, when first added to KTTSMGR, the Festival plugin needs to know the path to
+* the directory containing all the installed voice files. It is best for a plugin
+* to try to locate these resources automatically, but if it can't find them,
+* when the user has told it where they are, it is a good idea to save this information
+* in the all-instances group. In this way, the next time the plugin
+* is added to KTTSMGR, or another instance is added, it will be able to find them
+* automatically.
+*
+* @ref setDesiredLanguage is always called just prior to @ref load, therefore
+* it is not necessary to save the language code, unless your plugin needs it in
+* order to synthesize speech.
+*/
+
+class KDE_EXPORT PlugInConf : public QWidget{
+ Q_OBJECT
+
+ public:
+ /**
+ * Constructor
+ */
+ PlugInConf( QWidget *parent = 0, const char *name = 0);
+
+ /**
+ * Destructor
+ */
+ virtual ~PlugInConf();
+
+ /**
+ * 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 KTTSMGR calls this when the plugin is
+ * loaded, so it not necessary to call it in your constructor.
+ * The plugin should read its configuration from the specified group
+ * in the specified config file.
+ * @param config Pointer to a KConfig object.
+ * @param configGroup Call config->setGroup with this argument before
+ * loading your configuration.
+ *
+ * When a plugin is first added to KTTSMGR, @e load will be called with
+ * a Null @e configGroup. In this case, the plugin will not have
+ * any instance-specific parameters to load, but it may still wish
+ * to load parameters that apply to all instances of the plugin.
+ *
+ * @see loadandsavemethods
+ */
+ virtual 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". The plugin should save its configuration in the specified
+ * group of the specified config file.
+ * @param config Pointer to a KConfig object.
+ * @param configGroup Call config->setGroup with this argument before
+ * saving your configuration.
+ *
+ * @ref setDesiredLanguage is always called just prior to @ref load, therefore
+ * it is not necessary to save the language code, unless your plugin needs it in
+ * order to synthesize speech.
+ */
+ virtual 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. Note that defaults should
+ * be applied to the on-screen widgets; not to the config file.
+ */
+ virtual void defaults();
+
+ /**
+ * Indicates whether the plugin supports multiple instances. Return
+ * False if only one instance of the plugin can run at a time, or
+ * if your plugin is limited to a single language, voice, gender, volume,
+ * and speed.
+ * @return True if multiple instances are possible.
+ */
+ virtual bool supportsMultiInstance();
+
+ /**
+ * 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.
+ */
+ virtual 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.
+ */
+ virtual QString getTalkerCode();
+
+ /**
+ * Return a list of all the languages possibly supported by the plugin.
+ * If your plugin can support any language, return Null.
+ * @return A QStringList of supported language and optional country
+ * codes, or Null if any.
+ *
+ * The languge codes are given in ISO 639-1. Lowercase should be used.
+ * If your plugin supports various national forms of a language, ISO 3166
+ * country codes should also be include in upperase and separated from
+ * the language code with underscore (_). Examples:
+ * en
+ * en_US
+ * en_GB
+ * es
+ * es_CL
+ * The list you return should be as specific as practicable.
+ */
+ virtual QStringList getSupportedLanguages();
+
+ /**
+ * Player object that can be used by the plugin for testing playback of synthed files.
+ */
+ void setPlayer(TestPlayer* player);
+ TestPlayer* getPlayer();
+
+ static QString realFilePath(const QString &filename);
+
+ static QString testMessage(const QString& languageCode);
+
+ public slots:
+ /**
+ * This slot is used internally when the configuration is changed. It is
+ * typically connected to signals from the widgets of the configuration
+ * and should emit the @ref changed signal.
+ */
+ void configChanged(){
+ kdDebug() << "PlugInConf::configChanged: Running"<< endl;
+ emit changed(true);
+ };
+
+ signals:
+ /**
+ * This signal indicates that the configuration has been changed.
+ * It should be emitted whenever user changes something in the configuration widget.
+ */
+ void changed(bool);
+
+ protected:
+ /**
+ * Searches the $PATH variable for any file. If that file exists in the PATH, or
+ * is contained in any directory in the PATH, it returns the full path to it.
+ * @param name The name of the file to search for.
+ * @returns The path to the file on success, a blank QString
+ * if its not found.
+ */
+ QString getLocation(const QString &name);
+
+ /**
+ * Breaks a language code into the language code and country code (if any).
+ * @param languageCode Language code.
+ * @return countryCode Just the country code part (if any).
+ * @return Just the language code part.
+ */
+ QString splitLanguageCode(const QString& languageCode, QString& countryCode);
+
+ /// The system path in a QStringList.
+ QStringList m_path;
+
+ TestPlayer* m_player;
+};
+
+#endif //_PLUGINCONF_H_
diff --git a/kttsd/libkttsd/pluginproc.cpp b/kttsd/libkttsd/pluginproc.cpp
new file mode 100644
index 0000000..5c835a6
--- /dev/null
+++ b/kttsd/libkttsd/pluginproc.cpp
@@ -0,0 +1,292 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ This file is the template for the processing plug ins.
+ -------------------
+ Copyright : (C) 2002-2003 by José Pablo Ezequiel "Pupeno" Fernández
+ -------------------
+ Original author: José Pablo Ezequiel "Pupeno" Fernández <pupeno@kde.org>
+ Current Maintainer: 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+// Qt includes.
+#include <qstring.h>
+#include <qtextcodec.h>
+
+// KDE includes.
+#include <kdebug.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+
+// PlugInProc includes.
+#include "pluginproc.h"
+#include "pluginproc.moc"
+
+/**
+* Constructor
+*/
+PlugInProc::PlugInProc( QObject *parent, const char *name) : QObject(parent, name){
+ // kdDebug() << "PlugInProc::PlugInProc: Running" << endl;
+}
+
+/**
+* Destructor
+*/
+PlugInProc::~PlugInProc(){
+ // kdDebug() << "PlugInProc::~PlugInProc: Running" << endl;
+}
+
+/**
+* Initializate the speech plugin.
+*/
+bool PlugInProc::init(KConfig* /*config*/, const QString& /*configGroup*/){
+ // kdDebug() << "PlugInProc::init: Running" << endl;
+ return false;
+}
+
+/**
+* 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 PlugInProc::sayText(const QString& /*text*/){
+ // kdDebug() << "PlugInProc::sayText: Running" << endl;
+}
+
+/**
+* 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 PlugInProc::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.
+*/
+QString PlugInProc::getFilename() { return QString::null; }
+
+/**
+* Stop current operation (saying or synthesizing text).
+* This function only makes sense in asynchronus modes.
+* The plugin should return to the psIdle state.
+*/
+void PlugInProc::stopText(){
+ // kdDebug() << "PlugInProc::stopText: Running" << endl;
+}
+
+/**
+* Return the current state of the plugin.
+* This function only makes sense in asynchronous mode.
+* @return The pluginState of the plugin.
+*
+* @ref pluginState
+*/
+pluginState PlugInProc::getState() { return psIdle; }
+
+/**
+* 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 PlugInProc::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.
+*/
+bool PlugInProc::supportsAsync() { return false; }
+
+/**
+* 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 PlugInProc::supportsSynth() { return false; }
+
+/**
+* Returns the name of an XSLT stylesheet that will convert a valid SSML file
+* into a format that can be processed by the synth. For example,
+* The Festival plugin returns a stylesheet that will convert SSML into
+* SABLE. Any tags the synth cannot handle should be stripped (leaving
+* their text contents though). The default stylesheet strips all
+* tags and converts the file to plain text.
+* @return Name of the XSLT file.
+*/
+QString PlugInProc::getSsmlXsltFilename()
+{
+ return KGlobal::dirs()->resourceDirs("data").last() + "kttsd/xslt/SSMLtoPlainText.xsl";
+}
+
+/**
+* Given the name of a codec, returns the QTextCodec for the name.
+* Handles the following "special" codec names:
+* Local The user's current Locale codec.
+* Latin1 Latin1 (ISO 8859-1)
+* Unicode UTF-16
+* @param codecName Name of desired codec.
+* @return The codec object. Calling program must not delete this object
+* as it is a reference to an existing QTextCodec object.
+*
+* Caution: Do not pass translated codec names to this routine.
+*/
+/*static*/ QTextCodec* PlugInProc::codecNameToCodec(const QString &codecName)
+{
+ QTextCodec* codec = 0;
+ if (codecName == "Local")
+ codec = QTextCodec::codecForLocale();
+ else if (codecName == "Latin1")
+ codec = QTextCodec::codecForName("ISO8859-1");
+ else if (codecName == "Unicode")
+ codec = QTextCodec::codecForName("utf16");
+ else
+ codec = QTextCodec::codecForName(codecName.latin1());
+ if (!codec)
+ {
+ kdDebug() << "PluginProc::codecNameToCodec: Invalid codec name " << codecName << endl;
+ kdDebug() << "PluginProc::codecNameToCodec: Defaulting to ISO 8859-1" << endl;
+ codec = QTextCodec::codecForName("ISO8859-1");
+ }
+ return codec;
+}
+
+/**
+* Builds a list of codec names, suitable for display in a QComboBox.
+* The list includes the 3 special codec names (translated) at the top:
+* Local The user's current Locale codec.
+* Latin1 Latin1 (ISO 8859-1)
+* Unicode UTF-16
+*/
+/*static*/ QStringList PlugInProc::buildCodecList()
+{
+ // kdDebug() << "PlugInConf::buildCodecList: Running" << endl;
+ QStringList codecList;
+ QString local = i18n("Local")+" (";
+ local += QTextCodec::codecForLocale()->name();
+ local += ")";
+ codecList.append(local);
+ codecList.append(i18n("Latin1"));
+ codecList.append(i18n("Unicode"));
+ for (int i = 0; (QTextCodec::codecForIndex(i)); ++i )
+ codecList.append(QTextCodec::codecForIndex(i)->name());
+ return codecList;
+}
+
+/**
+* Given the name of a codec, returns index into the codec list.
+* Handles the following "special" codec names:
+* Local The user's current Locale codec.
+* Latin1 Latin1 (ISO 8859-1)
+* Unicode UTF-16
+* @param codecName Name of the codec.
+* @param codecList List of codec names. The first 3 entries may be translated names.
+* @return QTextCodec object. Caller must not delete this object.
+*
+* Caution: Do not pass translated codec names to this routine in codecName parameter.
+*/
+/*static*/ int PlugInProc::codecNameToListIndex(const QString &codecName, const QStringList &codecList)
+{
+ int codec;
+ if (codecName == "Local")
+ codec = PlugInProc::Local;
+ else if (codecName == "Latin1")
+ codec = PlugInProc::Latin1;
+ else if (codecName == "Unicode")
+ codec = PlugInProc::Unicode;
+ else {
+ codec = PlugInProc::Local;
+ const uint codecListCount = codecList.count();
+ for (uint i = PlugInProc::UseCodec; i < codecListCount; ++i )
+ if (codecName == codecList[i])
+ codec = i;
+ }
+ return codec;
+}
+
+/**
+* Given index into codec list, returns the codec object.
+* @param codecNum Index of the codec.
+* @param codecList List of codec names. The first 3 entries may be translated names.
+* @return QTextCodec object. Caller must not delete this object.
+*/
+/*static*/ QTextCodec* PlugInProc::codecIndexToCodec(int codecNum, const QStringList &codecList)
+{
+ QTextCodec* codec = 0;
+ switch (codecNum) {
+ case PlugInProc::Local:
+ codec = QTextCodec::codecForLocale();
+ break;
+ case PlugInProc::Latin1:
+ codec = QTextCodec::codecForName("ISO8859-1");
+ break;
+ case PlugInProc::Unicode:
+ codec = QTextCodec::codecForName("utf16");
+ break;
+ default:
+ codec = QTextCodec::codecForName(codecList[codecNum].latin1());
+ break;
+ }
+ if (!codec)
+ {
+ kdDebug() << "PlugInProc::codecIndexToCodec: Invalid codec index " << codecNum << endl;
+ kdDebug() << "PlugInProc::codecIndexToCodec: Defaulting to ISO 8859-1" << endl;
+ codec = QTextCodec::codecForName("ISO8859-1");
+ }
+ return codec;
+}
+
+/**
+* Given index into codec list, returns the codec Name.
+* Handles the following "special" codec names:
+* Local The user's current Locale codec.
+* Latin1 Latin1 (ISO 8859-1)
+* Unicode UTF-16
+* @param codecNum Index of the codec.
+* @param codecList List of codec names. The first 3 entries may be translated names.
+* @return Untranslated name of the codec.
+*/
+/*static*/ QString PlugInProc::codecIndexToCodecName(int codecNum, const QStringList &codecList)
+{
+ QString codecName;
+ switch (codecNum) {
+ case PlugInProc::Local:
+ codecName = "Local";
+ break;
+ case PlugInProc::Latin1:
+ codecName = "Latin1";
+ break;
+ case PlugInProc::Unicode:
+ codecName = "Unicode";
+ break;
+ default:
+ if ((uint)codecNum < codecList.count())
+ codecName = codecList[codecNum];
+ else
+ {
+ kdDebug() << "PlugInProc::codecIndexToCodec: Invalid codec index " << codecNum << endl;
+ kdDebug() << "PlugInProc::codecIndexToCodec: Defaulting to ISO 8859-1" << endl;
+ codecName = "ISO8859-1";
+ }
+ }
+ return codecName;
+}
diff --git a/kttsd/libkttsd/pluginproc.h b/kttsd/libkttsd/pluginproc.h
new file mode 100644
index 0000000..c271ad3
--- /dev/null
+++ b/kttsd/libkttsd/pluginproc.h
@@ -0,0 +1,466 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ This file is the template for the processing plug ins.
+ -------------------
+ Copyright : (C) 2002-2003 by José Pablo Ezequiel "Pupeno" Fernández
+ Copyright : (C) 2004 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: José Pablo Ezequiel "Pupeno" Fernández <pupeno@kde.org>
+ Current Maintainer: 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#ifndef _PLUGINPROC_H_
+#define _PLUGINPROC_H_
+
+#include <qobject.h>
+#include <qstring.h>
+#include <kdemacros.h>
+#include "kdeexportfix.h"
+#include <kconfig.h>
+
+/**
+* @interface PlugInProc
+*
+* pluginproc - the KDE Text-to-Speech Deamon Plugin API.
+*
+* @version 1.0 Draft 1
+*
+* This class defines the interface that plugins to KTTSD must implement.
+*
+* @warning The pluginproc interface is still being developed and is likely
+* to change in the future.
+*
+* A KTTSD Plugin interfaces between KTTSD and a speech engine. It provides the methods
+* used by KTTSD to synthesize and/or audibilize text into speech.
+*
+* @section Goals
+*
+* The ideal plugin has the following features (listed most important to least important):
+* - It can synthesize text into an audio file without sending the audio to
+* the audio device. If the plugin can do this, the @ref supportsSynth function
+* should return True.
+* - When @ref stopText is called, is able to immediately stop an in-progress
+* synthesis or saying operation.
+* - It can operate asynchronously, i.e., returns immediately from a
+* @ref sayText or @ref synthText call and emits signals @ref sayFinished or
+* @ref synthFinished when completed. If the plugin can do this, the @ref supportsAsync
+* function should return True.
+*
+* As a plugin author, your goal is to provide all 3 of these features. However,
+* the speech engine you are working with might not be able to support all three
+* features.
+*
+* If a plugin cannot do all 3 of the features above, the next best combinations
+* are (from best to worst):
+*
+* - @ref supportsSynth returns True, @ref supportsAsync returns False, and
+* @stopText is able to immediately stop synthesis.
+* - @ref supportsSynth returns True, @ref supportsAsync returns False, and
+* @stopText returns immediately without stopping synthesis.
+* - @ref supportsAsync returns True, @ref supportsSynth returns False, and
+* @ref stopText is able to immediately stop saying.
+* - Both @ref supportsSynth and @ref supportsAsync both return False, and
+* @ref stopText is able to immediately stop saying.
+* - @ref supportsAsync returns True, @ref supportsSynth returns False, and
+* @ref stopText returns immediately without stopping saying.
+* - Both @ref supportsSynth and @ref supportsAsync both return False, and
+* @ref stopText returns immediately without stopping saying.
+*
+* Notice that aynchronous support is not essential because KTTSD is able to
+* provide aynchronous support by running the plugin in a separate thread.
+* The ability to immediately stop audio output (or support separate synthesis
+* only) is more important.
+*
+* @section Minimum Implementations
+*
+* All plugins should implement @ref init in order to initialize the speech engine,
+* set language codes, etc.
+*
+* If @ref supportsSynth return False, a plugin must implement @ref sayText .
+*
+* If @ref supportsSynth returns True, a plugin must implement the following methods:
+* - @ref synthText
+* - @ref getFilename
+* - @ref ackFinished
+* The plugin need not implement @ref sayText .
+*
+* If @ref supportsAsync returns True, the plugin must implement @ref getState .
+*
+* If @ref supportsAsync returns True, and @ref supportsSynth returns True,
+* a plugin must emit @ref synthFinished signal when synthesis is completed.
+*
+* If @ref supportsAsync returns True, and @ref supportsSynth returns False,
+* a plugin must emit @ref sayFinished signal when saying is completed.
+*
+* If @ref supportsAsync returns False, do not emit signals @ref sayFinished
+* or @ref synthFinished .
+*
+* @section Implementation Guidelines
+*
+* In no case, will a plugin need to perform more than one @ref sayText or
+* @ref synthText at a time. In other words, in asynchronous mode, KTTSD will
+* not call @ref sayText or @ref synthText again until @ref @getState returns
+* psFinished.
+*
+* If @ref supportsAsync returns False, KTTSD will run the plugin in a separate
+* QThread. As a consequence, the plugin must not make use of the KDE Library,
+* when @ref sayText or @ref synthText is called,
+* with the exception of KProcess and family (KProcIO, KShellProcess).
+* This restriction comes about because the KDE Libraries make use of the
+* main Qt event loop, which unfortunately, runs only in the main thread.
+* This restriction will likely be lifted in Qt 4 and later.
+*
+* Since the KDE library is not available from the @ref sayText and @ref synthText methods,
+* it is best if the plugin reads configuration settings in the @ref init method.
+* The KConfig object is passed as an argument to @ref init .
+*
+* If the synthesis engine requires a long initialization time (more than a second),
+* it is best if the plugin loads the speech engine from the @ref init method.
+* Otherwise, it will be more memory efficient to wait until @ref sayText or
+* @ref synthText is called, because it is possible that the plugin will be created
+* and initialized, but never used.
+*
+* All plugins, whether @ref supportsAsync returns True or not, should try to
+* implement @ref stopText . If a plugin returns False from @ref supportsAsync,
+* @ref stopText will be called from the main thread, while @ref sayText and/or
+* @ref synthText will be called from a separate thread. Hence, it will be
+* possible for @ref stopText to be called while @ref sayText or @ref synthText is
+* running. Keep this in mind when implementing the code.
+*
+* If the plugin returns True from @ref supportsAsync, you will of course
+* need to deal with similar issues. If you have to use QThreads
+* to implement asynchronous support, do not be concerned about emitting
+* the @ref sayFinished or @ref synthFinished signals from your threads, since
+* KTTSD will convert the received signals into postEvents and
+* return immediately.
+*
+* If it is not possible for @ref stopText to stop an in-progress operation, it
+* must not wait for the operation to complete, since this would block KTTSD.
+* Instead, simply return immediately. Usually, KTTSD will perform other operations
+* while waiting for the plugin to complete its operation. (See threadedplugin.cpp.)
+*
+* If the @ref stopText implementation returns before the operation has actually
+* completed, it must emit the @ref stopped() signal when it is actually completed.
+*
+* If a plugin returns True from @ref supportsAsync, and @ref stopText is called,
+* when the plugin has stopped or completed the operation, it must return psIdle
+* on the next call to @ref getState ; not psFinished. The following state diagram
+* might be helpful to understand this:
+*
+ @verbatim
+ psIdle <<----------------------------------------------------------
+ / \ ^
+ psSaying psSynthing --- stopText called and operation completed -->> ^
+ \ / ^
+ psFinished --- ackFinished called ------------------------------->> ^
+ @endverbatim
+*
+* If your plugin can't immediately stop an in-progress operation, the easiest
+* way to handle this is to set a flag when stopText is called, and then in your
+* getState() implementation, if the operation has completed, change the
+* psFinished state to psIdle, if the flag is set. See the flite plugin for
+* example code.
+*
+* If a plugin returns True from @ref supportsSynth, KTTSD will pass a suggested
+* filename in the @ref synthText call. The plugin should synthesize the text
+* into an audio file with the suggested name. If the synthesis engine does not
+* permit this, i.e., it will pick a filename of its own, that is OK. In either
+* case, the actual filename produced should be returned in @ref getFilename .
+* In no case may the plugin re-use this filename once @ref getFilename has been
+* called. If for some reason the synthesis engine cannot support this, the
+* plugin should copy the file to the suggested filename. The file must not be
+* locked when @ref getFilename is called. The file will be deleted when
+* KTTSD is finished using it.
+*
+* The preferred audio file format is wave, since this is the only format
+* guaranteed to be supported by KDE (aRts). Other formats may or may not be
+* supported on a user's machine.
+*
+* The plugin destructor should take care of terminating the speech engine.
+*
+* @section Error-handling Error Handling
+*
+* Plugins may emit the @ref error signal when an error occurs.
+*
+* When an error occurs, plugins should attempt to recover as best they can and
+* continue accepting @ref sayText or @ref synthText calls. For example,
+* if a speech engine emits an error in response to certain characters embedded
+* in synthesizing text, the plugin should discard the text and
+* emit signal @ref error with True as the first argument and the speech
+* engine's error message as the second argument. The plugin should then
+* treat the operation as a completed operation, i.e., return psFinished when
+* @ref getState is called.
+*
+* If the speech engine crashes, the plugin should emit signal @ref error with
+* True as the first argument and then attempt to restart the speech engine.
+* The plugin will need to implement some protection against an infinite
+* restart loop and emit the @ref error signal with False as the first argument
+* if this occurs.
+*
+* If a plugin emits the @ref error signal with False as the first argument,
+* KTTSD will no longer call the plugin.
+*
+* @section PlugInConf
+*
+* The plugin should implement a configuration dialog where the user can specify
+* options specific to the speech engine. This dialog is displayed in the KDE
+* Control Center and also in the KTTS Manager (kttsmgr). See pluginconf.h.
+*
+* If the user changes any of the settings while the plugin is created,
+* the plugin will be destroyed and re-created.
+*/
+
+class QTextCodec;
+
+enum pluginState
+{
+ psIdle = 0, /**< Plugin is not doing anything. */
+ psSaying = 1, /**< Plugin is synthesizing and audibilizing. */
+ psSynthing = 2, /**< Plugin is synthesizing. */
+ psFinished = 3 /**< Plugin has finished synthesizing. Audio file is ready. */
+};
+
+class KDE_EXPORT PlugInProc : virtual public QObject{
+ Q_OBJECT
+
+ public:
+ enum CharacterCodec {
+ Local = 0,
+ Latin1 = 1,
+ Unicode = 2,
+ UseCodec = 3
+ };
+
+ /**
+ * Constructor.
+ */
+ PlugInProc( QObject *parent = 0, const char *name = 0);
+
+ /**
+ * Destructor.
+ * Plugin must terminate the speech engine.
+ */
+ virtual ~PlugInProc();
+
+ /**
+ * Initialize the speech engine.
+ * @param config Settings object.
+ * @param configGroup Settings Group.
+ *
+ * Sample code for reading configuration:
+ *
+ @verbatim
+ config->setGroup(configGroup);
+ m_fliteExePath = config->readEntry("FliteExePath", "flite");
+ kdDebug() << "FliteProc::init: path to flite: " << m_fliteExePath << endl;
+ config->setGroup(configGroup);
+ @endverbatim
+ */
+ virtual bool init(KConfig *config, const QString &configGroup);
+
+ /**
+ * Say a text. Synthesize and audibilize it.
+ * @param text The text to be spoken.
+ *
+ * If the plugin supports asynchronous operation, it should return immediately
+ * and emit sayFinished signal when synthesis and audibilizing is finished.
+ * It must also implement the @ref getState method, which must return
+ * psFinished, when saying is completed.
+ */
+ 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
+ * and emit @ref synthFinished signal when synthesis is completed.
+ * It must also implement the @ref getState method, which must return
+ * psFinished, when synthesis is completed.
+ */
+ virtual void synthText(const QString &text, const QString &suggestedFilename);
+
+ /**
+ * Get the generated audio filename from call to @ref synthText.
+ * @return Name of the audio file the plugin generated.
+ * Null if no such file.
+ *
+ * The plugin must not re-use or delete the filename. The file may not
+ * be locked when this method is called. The file will be deleted when
+ * KTTSD is finished using it.
+ */
+ 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.
+ *
+ * If the plugin returns True, it must also implement the following methods:
+ * - @ref synthText
+ * - @ref getFilename
+ * - @ref ackFinished
+ *
+ * If the plugin returns True, it need not implement @ref sayText .
+ */
+ virtual bool supportsSynth();
+
+ /**
+ * Returns the name of an XSLT stylesheet that will convert a valid SSML file
+ * into a format that can be processed by the synth. For example,
+ * The Festival plugin returns a stylesheet that will convert SSML into
+ * SABLE. Any tags the synth cannot handle should be stripped (leaving
+ * their text contents though). The default stylesheet strips all
+ * tags and converts the file to plain text.
+ * @return Name of the XSLT file.
+ */
+ virtual QString getSsmlXsltFilename();
+
+ /**
+ * Given the name of a codec, returns the QTextCodec for the name.
+ * Handles the following "special" codec names:
+ * Local The user's current Locale codec.
+ * Latin1 Latin1 (ISO 8859-1)
+ * Unicode UTF-16
+ * @param codecName Name of desired codec.
+ * @return The codec object. Calling program must not delete this object
+ * as it is a reference to an existing QTextCodec object.
+ *
+ * Caution: Do not pass translated codec names to this routine.
+ */
+ static QTextCodec* codecNameToCodec(const QString &codecName);
+
+ /**
+ * Builds a list of codec names, suitable for display in a QComboBox.
+ * The list includes the 3 special codec names (translated) at the top:
+ * Local The user's current Locale codec.
+ * Latin1 Latin1 (ISO 8859-1)
+ * Unicode UTF-16
+ */
+ static QStringList buildCodecList();
+
+ /**
+ * Given the name of a codec, returns index into the codec list.
+ * Handles the following "special" codec names:
+ * Local The user's current Locale codec.
+ * Latin1 Latin1 (ISO 8859-1)
+ * Unicode UTF-16
+ * @param codecName Name of the codec.
+ * @param codecList List of codec names. The first 3 entries may be translated names.
+ * @return QTextCodec object. Caller must not delete this object.
+ *
+ * Caution: Do not pass translated codec names to this routine in codecName parameter.
+ */
+ static int codecNameToListIndex(const QString &codecName, const QStringList &codecList);
+
+ /**
+ * Given index into codec list, returns the codec object.
+ * @param codecNum Index of the codec.
+ * @param codecList List of codec names. The first 3 entries may be translated names.
+ * @return QTextCodec object. Caller must not delete this object.
+ */
+ static QTextCodec* codecIndexToCodec(int codecNum, const QStringList &codecList);
+
+ /**
+ * Given index into codec list, returns the codec Name.
+ * Handles the following "special" codec names:
+ * Local The user's current Locale codec.
+ * Latin1 Latin1 (ISO 8859-1)
+ * Unicode UTF-16
+ * @param codecNum Index of the codec.
+ * @param codecList List of codec names. The first 3 entries may be translated names.
+ * @return Untranslated name of the codec.
+ */
+ static QString codecIndexToCodecName(int codecNum, const QStringList &codecList);
+
+ signals:
+ /**
+ * Emitted when synthText() finishes and plugin supports asynchronous mode.
+ */
+ void synthFinished();
+ /**
+ * Emitted when sayText() finishes and plugin supports asynchronous mode.
+ */
+ void sayFinished();
+ /**
+ * Emitted when stopText() has been called and plugin stops asynchronously.
+ */
+ void stopped();
+ /**
+ * Emitted if an error occurs.
+ * @param keepGoing True if the plugin can continue processing.
+ * False if the plugin cannot continue, for example,
+ * the speech engine could not be started.
+ * @param msg Error message.
+ *
+ * When an error occurs, plugins should attempt to recover as best they can
+ * and continue accepting @ref sayText or @ref synthText calls. For example,
+ * if the speech engine emits an error while synthesizing text, the plugin
+ * should return True along with error message.
+ *
+ * @see Error-handling
+ *
+ */
+ void error(bool keepGoing, const QString &msg);
+};
+
+#endif // _PLUGINPROC_H_
diff --git a/kttsd/libkttsd/selecttalkerdlg.cpp b/kttsd/libkttsd/selecttalkerdlg.cpp
new file mode 100644
index 0000000..d74339e
--- /dev/null
+++ b/kttsd/libkttsd/selecttalkerdlg.cpp
@@ -0,0 +1,365 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Description:
+ A dialog for user to select a Talker, either by specifying
+ selected Talker attributes, or by specifying all attributes
+ of an existing configured Talker.
+
+ 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.
+ ******************************************************************************/
+
+// Qt includes.
+#include <qcheckbox.h>
+#include <qradiobutton.h>
+#include <qhbox.h>
+#include <qgroupbox.h>
+
+// KDE includes.
+#include <kcombobox.h>
+#include <ktrader.h>
+#include <kpushbutton.h>
+#include <klistview.h>
+#include <klineedit.h>
+#include <kconfig.h>
+#include <kdebug.h>
+
+// KTTS includes.
+#include "utils.h"
+#include "selecttalkerdlg.h"
+#include "selecttalkerdlg.moc"
+
+SelectTalkerDlg::SelectTalkerDlg(
+ QWidget* parent,
+ const char* name,
+ const QString& caption,
+ const QString& talkerCode,
+ bool runningTalkers) :
+
+ KDialogBase(
+ parent,
+ name,
+ true,
+ caption,
+ KDialogBase::Ok|KDialogBase::Cancel,
+ KDialogBase::Ok)
+{
+ m_widget = new SelectTalkerWidget( this );
+ // TODO: How do I do this in a general way and still get KDialogBase to properly resize?
+ m_widget->setMinimumSize( QSize(700,500) );
+ // setInitialSize( QSize(700,600) );
+ setMainWidget( m_widget );
+ m_runningTalkers = runningTalkers;
+ m_talkerCode = TalkerCode( talkerCode, false );
+
+ // Fill combo boxes.
+ KComboBox* cb = m_widget->genderComboBox;
+ cb->insertItem( QString::null );
+ cb->insertItem( TalkerCode::translatedGender("male") );
+ cb->insertItem( TalkerCode::translatedGender("female") );
+ cb->insertItem( TalkerCode::translatedGender("neutral") );
+
+ cb = m_widget->volumeComboBox;
+ cb->insertItem( QString::null );
+ cb->insertItem( TalkerCode::translatedVolume("medium") );
+ cb->insertItem( TalkerCode::translatedVolume("loud") );
+ cb->insertItem( TalkerCode::translatedVolume("soft") );
+
+ cb = m_widget->rateComboBox;
+ cb->insertItem( QString::null );
+ cb->insertItem( TalkerCode::translatedRate("medium") );
+ cb->insertItem( TalkerCode::translatedRate("fast") );
+ cb->insertItem( TalkerCode::translatedRate("slow") );
+
+ cb = m_widget->synthComboBox;
+ cb->insertItem( QString::null );
+ KTrader::OfferList offers = KTrader::self()->query("KTTSD/SynthPlugin");
+ for(unsigned int i=0; i < offers.count() ; ++i)
+ cb->insertItem(offers[i]->name());
+
+ // Fill List View with list of Talkers.
+ m_widget->talkersListView->setSorting( -1 );
+ loadTalkers( m_runningTalkers );
+
+ // Set initial radio button state.
+ if ( talkerCode.isEmpty() )
+ m_widget->useDefaultRadioButton->setChecked(true);
+ else
+ {
+ QString dummy;
+ if (talkerCode == TalkerCode::normalizeTalkerCode(talkerCode, dummy))
+ m_widget->useSpecificTalkerRadioButton->setChecked(true);
+ else
+ m_widget->useClosestMatchRadioButton->setChecked(true);
+ }
+
+ applyTalkerCodeToControls();
+ enableDisableControls();
+
+ connect(m_widget->useDefaultRadioButton, SIGNAL(clicked()),
+ this, SLOT(configChanged()));
+ connect(m_widget->useClosestMatchRadioButton, SIGNAL(clicked()),
+ this, SLOT(configChanged()));
+ connect(m_widget->useSpecificTalkerRadioButton, SIGNAL(clicked()),
+ this, SLOT(configChanged()));
+
+ connect(m_widget->languageBrowseButton, SIGNAL(clicked()),
+ this, SLOT(slotLanguageBrowseButton_clicked()));
+
+ connect(m_widget->synthComboBox, SIGNAL(activated(const QString&)),
+ this, SLOT(configChanged()));
+ connect(m_widget->genderComboBox, SIGNAL(activated(const QString&)),
+ this, SLOT(configChanged()));
+ connect(m_widget->volumeComboBox, SIGNAL(activated(const QString&)),
+ this, SLOT(configChanged()));
+ connect(m_widget->rateComboBox, SIGNAL(activated(const QString&)),
+ this, SLOT(configChanged()));
+
+ connect(m_widget->synthCheckBox, SIGNAL(toggled(bool)),
+ this, SLOT(configChanged()));
+ connect(m_widget->genderCheckBox, SIGNAL(toggled(bool)),
+ this, SLOT(configChanged()));
+ connect(m_widget->volumeCheckBox, SIGNAL(toggled(bool)),
+ this, SLOT(configChanged()));
+ connect(m_widget->rateCheckBox, SIGNAL(toggled(bool)),
+ this, SLOT(configChanged()));
+
+ connect(m_widget->talkersListView, SIGNAL(selectionChanged()),
+ this, SLOT(slotTalkersListView_selectionChanged()));
+
+ m_widget->talkersListView->setMinimumHeight( 120 );
+}
+
+SelectTalkerDlg::~SelectTalkerDlg() { }
+
+QString SelectTalkerDlg::getSelectedTalkerCode()
+{
+ return m_talkerCode.getTalkerCode();
+}
+
+QString SelectTalkerDlg::getSelectedTranslatedDescription()
+{
+ return m_talkerCode.getTranslatedDescription();
+}
+
+void SelectTalkerDlg::slotLanguageBrowseButton_clicked()
+{
+ // Create a QHBox to host KListView.
+ QHBox* hBox = new QHBox(m_widget, "SelectLanguage_hbox");
+ // Create a KListView and fill with all known languages.
+ KListView* langLView = new KListView(hBox, "SelectLanguage_lview");
+ langLView->addColumn(i18n("Language"));
+ langLView->addColumn(i18n("Code"));
+ langLView->setSelectionMode(QListView::Single);
+ QStringList allLocales = KGlobal::locale()->allLanguagesTwoAlpha();
+ QString locale;
+ QString language;
+ // Blank line so user can select no language.
+ // Note: Don't use QString::null, which gets displayed at bottom of list, rather than top.
+ QListViewItem* item = new KListViewItem(langLView, "", "");
+ if (m_talkerCode.languageCode().isEmpty()) item->setSelected(true);
+ int allLocalesCount = allLocales.count();
+ for (int ndx=0; ndx < allLocalesCount; ++ndx)
+ {
+ locale = allLocales[ndx];
+ language = TalkerCode::languageCodeToLanguage(locale);
+ item = new KListViewItem(langLView, language, locale);
+ if (m_talkerCode.fullLanguageCode() == locale) item->setSelected(true);
+ }
+ // Sort by language.
+ langLView->setSorting(0);
+ langLView->sort();
+ // Display the box in a dialog.
+ KDialogBase* dlg = new KDialogBase(
+ KDialogBase::Swallow,
+ i18n("Select Languages"),
+ KDialogBase::Help|KDialogBase::Ok|KDialogBase::Cancel,
+ KDialogBase::Cancel,
+ m_widget,
+ "SelectLanguage_dlg",
+ true,
+ true);
+ dlg->setMainWidget(hBox);
+ dlg->setHelp("", "kttsd");
+ dlg->setInitialSize(QSize(300, 500), false);
+ // TODO: This isn't working. Furthermore, item appears selected but is not.
+ langLView->ensureItemVisible(langLView->selectedItem());
+ int dlgResult = dlg->exec();
+ language = QString::null;
+ if (dlgResult == QDialog::Accepted)
+ {
+ if (langLView->selectedItem())
+ {
+ language = langLView->selectedItem()->text(0);
+ m_talkerCode.setFullLanguageCode( langLView->selectedItem()->text(1) );
+ }
+ }
+ delete dlg;
+ m_widget->languageLineEdit->setText(language);
+ m_widget->languageCheckBox->setChecked( !language.isEmpty() );
+ configChanged();
+}
+
+void SelectTalkerDlg::slotTalkersListView_selectionChanged()
+{
+ QListViewItem* item = m_widget->talkersListView->selectedItem();
+ if ( !item ) return;
+ if (!m_widget->useSpecificTalkerRadioButton->isChecked()) return;
+ configChanged();
+}
+
+void SelectTalkerDlg::configChanged()
+{
+ applyControlsToTalkerCode();
+ applyTalkerCodeToControls();
+ enableDisableControls();
+}
+
+void SelectTalkerDlg::applyTalkerCodeToControls()
+{
+ bool preferred = false;
+ QString code = m_talkerCode.getTalkerCode();
+
+ // TODO: Need to display translated Synth names.
+ KttsUtils::setCbItemFromText(m_widget->synthComboBox,
+ TalkerCode::stripPrefer( m_talkerCode.plugInName(), preferred) );
+ m_widget->synthCheckBox->setEnabled( !m_talkerCode.plugInName().isEmpty() );
+ m_widget->synthCheckBox->setChecked( preferred );
+
+ KttsUtils::setCbItemFromText(m_widget->genderComboBox,
+ TalkerCode::translatedGender( TalkerCode::stripPrefer( m_talkerCode.gender(), preferred ) ) );
+ m_widget->genderCheckBox->setEnabled( !m_talkerCode.gender().isEmpty() );
+ m_widget->genderCheckBox->setChecked( preferred );
+
+ KttsUtils::setCbItemFromText(m_widget->volumeComboBox,
+ TalkerCode::translatedVolume( TalkerCode::stripPrefer( m_talkerCode.volume(), preferred ) ) );
+ m_widget->volumeCheckBox->setEnabled( !m_talkerCode.volume().isEmpty() );
+ m_widget->volumeCheckBox->setChecked( preferred );
+
+ KttsUtils::setCbItemFromText(m_widget->rateComboBox,
+ TalkerCode::translatedRate( TalkerCode::stripPrefer( m_talkerCode.rate(), preferred ) ) );
+ m_widget->rateCheckBox->setEnabled( !m_talkerCode.rate().isEmpty() );
+ m_widget->rateCheckBox->setChecked( preferred );
+
+ // Select closest matching specific Talker.
+ int talkerIndex = TalkerCode::findClosestMatchingTalker(m_talkers, m_talkerCode.getTalkerCode(), false);
+ KListView* lv = m_widget->talkersListView;
+ QListViewItem* item = lv->firstChild();
+ if ( item )
+ {
+ while ( talkerIndex > 0 )
+ {
+ item = item->nextSibling();
+ --talkerIndex;
+ }
+ lv->setSelected( item, true );
+ }
+}
+
+void SelectTalkerDlg::applyControlsToTalkerCode()
+{
+ if ( m_widget->useDefaultRadioButton->isChecked() )
+ m_talkerCode = TalkerCode(QString::null, false);
+ else if ( m_widget->useClosestMatchRadioButton->isChecked() )
+ {
+ // Language already stored in talker code.
+
+ QString t = m_widget->synthComboBox->currentText();
+ if ( !t.isEmpty() && m_widget->synthCheckBox->isChecked() ) t.prepend("*");
+ m_talkerCode.setPlugInName( t );
+
+ t = TalkerCode::untranslatedGender( m_widget->genderComboBox->currentText() );
+ if ( !t.isEmpty() && m_widget->genderCheckBox->isChecked() ) t.prepend("*");
+ m_talkerCode.setGender( t );
+
+ t = TalkerCode::untranslatedVolume( m_widget->volumeComboBox->currentText() );
+ if ( !t.isEmpty() && m_widget->volumeCheckBox->isChecked() ) t.prepend("*");
+ m_talkerCode.setVolume( t );
+
+ t = TalkerCode::untranslatedRate( m_widget->rateComboBox->currentText() );
+ if ( !t.isEmpty() && m_widget->rateCheckBox->isChecked() ) t.prepend("*");
+ m_talkerCode.setRate( t );
+ }
+ else if (m_widget->useSpecificTalkerRadioButton->isChecked() )
+ {
+ QListViewItem* item = m_widget->talkersListView->selectedItem();
+ if ( item )
+ {
+ int itemIndex = -1;
+ while ( item )
+ {
+ item = item->itemAbove();
+ itemIndex++;
+ }
+ m_talkerCode = TalkerCode( &(m_talkers[itemIndex]), false );
+ }
+ }
+}
+
+void SelectTalkerDlg::loadTalkers(bool /*runningTalkers*/)
+{
+ m_talkers.clear();
+ KListView* lv = m_widget->talkersListView;
+ lv->clear();
+ QListViewItem* item;
+ KConfig* config = new KConfig("kttsdrc");
+ config->setGroup("General");
+ QStringList talkerIDsList = config->readListEntry("TalkerIDs", ',');
+ if (!talkerIDsList.isEmpty())
+ {
+ QStringList::ConstIterator itEnd(talkerIDsList.constEnd());
+ for( QStringList::ConstIterator it = talkerIDsList.constBegin(); it != itEnd; ++it )
+ {
+ QString talkerID = *it;
+ config->setGroup("Talker_" + talkerID);
+ QString talkerCode = config->readEntry("TalkerCode", QString::null);
+ // Parse and normalize the talker code.
+ TalkerCode talker = TalkerCode(talkerCode, true);
+ m_talkers.append(talker);
+ QString desktopEntryName = config->readEntry("DesktopEntryName", QString::null);
+ QString synthName = TalkerCode::TalkerDesktopEntryNameToName(desktopEntryName);
+ // Display in List View using translated strings.
+ item = new KListViewItem(lv, item);
+ QString fullLanguageCode = talker.fullLanguageCode();
+ QString language = TalkerCode::languageCodeToLanguage(fullLanguageCode);
+ item->setText(tlvcLanguage, language);
+ // Don't update the Synthesizer name with plugInName. The former is a translated
+ // name; the latter an English name.
+ // if (!plugInName.isEmpty()) talkerItem->setText(tlvcSynthName, plugInName);
+ if (!synthName.isEmpty())
+ item->setText(tlvcSynthName, synthName);
+ if (!talker.voice().isEmpty())
+ item->setText(tlvcVoice, talker.voice());
+ if (!talker.gender().isEmpty())
+ item->setText(tlvcGender, TalkerCode::translatedGender(talker.gender()));
+ if (!talker.volume().isEmpty())
+ item->setText(tlvcVolume, TalkerCode::translatedVolume(talker.volume()));
+ if (!talker.rate().isEmpty())
+ item->setText(tlvcRate, TalkerCode::translatedRate(talker.rate()));
+ }
+ }
+ delete config;
+}
+
+void SelectTalkerDlg::enableDisableControls()
+{
+ bool enableClosest = ( m_widget->useClosestMatchRadioButton->isChecked() );
+ bool enableSpecific = ( m_widget->useSpecificTalkerRadioButton->isChecked() );
+ m_widget->closestMatchGroupBox->setEnabled( enableClosest );
+ m_widget->talkersListView->setEnabled( enableSpecific );
+}
diff --git a/kttsd/libkttsd/selecttalkerdlg.h b/kttsd/libkttsd/selecttalkerdlg.h
new file mode 100644
index 0000000..bc236e2
--- /dev/null
+++ b/kttsd/libkttsd/selecttalkerdlg.h
@@ -0,0 +1,110 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Description:
+ A dialog for user to select a Talker, either by specifying
+ selected Talker attributes, or by specifying all attributes
+ of an existing configured Talker.
+
+ 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.
+ ******************************************************************************/
+
+#ifndef _SELECTTALKERDLG_H_
+#define _SELECTTALKERDLG_H_
+
+// Qt includes.
+#include <qvaluelist.h>
+
+// KDE includes.
+#include <kdialogbase.h>
+#include <klocale.h>
+#include <kdemacros.h>
+
+// KTTS includes.
+#include "talkercode.h"
+#include "selecttalkerwidget.h"
+
+class KDE_EXPORT SelectTalkerDlg : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Constructor.
+ * @param parent The parent for this dialog.
+ * @param name Name for this dialog.
+ * @param caption Displayed title for this dialog.
+ * @param talkerCode A suggested starting Talker Code.
+ * @param runningTalkers If true, lists configured and Applied Talkers in the running
+ * KTTSD. If false, lists Talkers in the KTTSMgr Talker tab
+ * (which may not yet have been Applied).
+ */
+ SelectTalkerDlg(
+ QWidget* parent = 0,
+ const char* name = "selecttalkerdialog",
+ const QString& caption = i18n("Select Talker"),
+ const QString& talkerCode = QString::null,
+ bool runningTalkers = false);
+
+ /**
+ * Destructor.
+ */
+ ~SelectTalkerDlg();
+
+ /**
+ * Returns the Talker Code user chose. QString::null if default Talker chosen.
+ * Note that if user did not choose a specific Talker, this will be a partial Talker Code.
+ */
+ QString getSelectedTalkerCode();
+ /**
+ * Returns the Talker user chose in a translated displayable format.
+ */
+ QString getSelectedTranslatedDescription();
+
+ private slots:
+ void slotLanguageBrowseButton_clicked();
+ void slotTalkersListView_selectionChanged();
+ void configChanged();
+
+ private:
+ enum TalkerListViewColumn
+ {
+ tlvcLanguage,
+ tlvcSynthName,
+ tlvcVoice,
+ tlvcGender,
+ tlvcVolume,
+ tlvcRate
+ };
+
+ void applyTalkerCodeToControls();
+ void applyControlsToTalkerCode();
+ void loadTalkers(bool runningTalkers);
+ void enableDisableControls();
+
+ // Main dialog widget.
+ SelectTalkerWidget* m_widget;
+ // True if list of Talkers should be taken from config file.
+ bool m_runningTalkers;
+ // Current Talker Code.
+ TalkerCode m_talkerCode;
+ // List of parsed talker codes for the configured Talkers.
+ TalkerCode::TalkerCodeList m_talkers;
+};
+
+#endif // _SELECTTALKERDLG_H_
diff --git a/kttsd/libkttsd/selecttalkerwidget.ui b/kttsd/libkttsd/selecttalkerwidget.ui
new file mode 100644
index 0000000..adcc8a9
--- /dev/null
+++ b/kttsd/libkttsd/selecttalkerwidget.ui
@@ -0,0 +1,572 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>SelectTalkerWidget</class>
+<author>Gary Cramblitt &lt;garycramblitt@comcast.net&gt;</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>SelectTalkerWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>558</width>
+ <height>447</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="caption">
+ <string>Select Talker</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="title">
+ <string></string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>useDefaultRadioButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Use default Talker</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>When checked, will use the default Talker, which is the topmost Talker listed in the Talkers tab.</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>useClosestMatchRadioButton</cstring>
+ </property>
+ <property name="text">
+ <string>Use closest &amp;matching Talker having</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>When checked, will use a configured Talker most closely matching the attributes you choose. Attributes with checks next to them will be preferred over unchecked attributes. Language is always preferred.</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer15</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>30</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>closestMatchGroupBox</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="title">
+ <string></string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="3" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>preferredLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Checked items are preferred over unchecked items.</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout11_2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>synthLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Synthesizer:</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter|AlignLeft</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>synthComboBox</cstring>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <property name="name">
+ <cstring>synthComboBox</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>synthCheckBox</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="2">
+ <property name="name">
+ <cstring>layout12</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>genderLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Gender:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genderCheckBox</cstring>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <property name="name">
+ <cstring>genderComboBox</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>genderCheckBox</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="Line" row="0" column="1" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>line1</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>VLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>2</number>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>layout14</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>volumeLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Volume:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>volumeCheckBox</cstring>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <property name="name">
+ <cstring>volumeComboBox</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>volumeCheckBox</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="2">
+ <property name="name">
+ <cstring>layout13</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>rateLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Rate:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>rateCheckBox</cstring>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <property name="name">
+ <cstring>rateComboBox</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>rateCheckBox</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>layout10</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>languageLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Language:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>languageLineEdit</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>languageLineEdit</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="paletteForegroundColor">
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This filter is applied to text jobs of the specified language. You may select more than one language by clicking the browse button and Ctrl-clicking on more than one in the list. If blank, the filter applies to all text jobs of any language.</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>languageBrowseButton</cstring>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to select one or more languages. This filter will be applied to text jobs of those languages.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>languageCheckBox</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>useSpecificTalkerRadioButton</cstring>
+ </property>
+ <property name="text">
+ <string>Use specific &amp;Talker</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>When checked, will use the specific Talker (if it is still configured), otherwise the Talker most closely matching.</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout10</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer15_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>30</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Language</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Speech Synthesizer</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Voice</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Gender</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Volume</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Rate</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>talkersListView</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="resizeMode">
+ <enum>LastColumn</enum>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This is a list of all the configured Talkers. A Talker is a speech synthesizer that has been configured with a language, voice, gender, speaking rate, and volume. Talkers higher in the list have higher priority. The topmost Talker will be used when no talker attributes have been specified by an application.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<tabstops>
+ <tabstop>useDefaultRadioButton</tabstop>
+ <tabstop>useClosestMatchRadioButton</tabstop>
+ <tabstop>synthComboBox</tabstop>
+ <tabstop>synthCheckBox</tabstop>
+ <tabstop>genderComboBox</tabstop>
+ <tabstop>genderCheckBox</tabstop>
+ <tabstop>volumeComboBox</tabstop>
+ <tabstop>volumeCheckBox</tabstop>
+ <tabstop>rateComboBox</tabstop>
+ <tabstop>rateCheckBox</tabstop>
+ <tabstop>languageLineEdit</tabstop>
+ <tabstop>languageBrowseButton</tabstop>
+ <tabstop>languageCheckBox</tabstop>
+ <tabstop>useSpecificTalkerRadioButton</tabstop>
+ <tabstop>talkersListView</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="0"/>
+<includehints>
+ <includehint>kcombobox.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klistview.h</includehint>
+</includehints>
+</UI>
diff --git a/kttsd/libkttsd/stretcher.cpp b/kttsd/libkttsd/stretcher.cpp
new file mode 100644
index 0000000..d3a3984
--- /dev/null
+++ b/kttsd/libkttsd/stretcher.cpp
@@ -0,0 +1,99 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Description:
+ Speeds up or slows down an audio file by stretching the audio stream.
+ Uses the sox program to do the stretching.
+
+ 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.
+ ******************************************************************************/
+
+// KDE includes.
+#include <kprocess.h>
+#include <kdebug.h>
+
+// Stretcher includes.
+#include "stretcher.h"
+#include "stretcher.moc"
+
+/**
+ * Constructor.
+ */
+Stretcher::Stretcher(QObject *parent, const char *name) :
+ QObject(parent, name)
+{
+ m_state = 0;
+ m_stretchProc = 0;
+}
+
+/**
+ * Destructor.
+ */
+Stretcher::~Stretcher()
+{
+ delete m_stretchProc;
+}
+
+/**
+ * Stretch the given input file to an output file.
+ * @param inFilename Name of input audio file.
+ * @param outFilename Name of output audio file.
+ * @param stretchFactor Amount to stretch. 2.0 is twice as slow. 0.5 is twice as fast.
+ * @return False if an error occurs.
+ */
+bool Stretcher::stretch(const QString &inFilename, const QString &outFilename, float stretchFactor)
+{
+ if (m_stretchProc) return false;
+ m_outFilename = outFilename;
+ m_stretchProc = new KProcess;
+ QString stretchStr = QString("%1").arg(stretchFactor, 0, 'f', 3);
+ *m_stretchProc << "sox" << inFilename << outFilename << "stretch" << stretchStr;
+ connect(m_stretchProc, SIGNAL(processExited(KProcess*)),
+ this, SLOT(slotProcessExited(KProcess*)));
+ if (!m_stretchProc->start(KProcess::NotifyOnExit, KProcess::NoCommunication))
+ {
+ kdDebug() << "Stretcher::stretch: Error starting audio stretcher process. Is sox installed?" << endl;
+ return false;
+ }
+ m_state = ssStretching;
+ return true;
+}
+
+void Stretcher::slotProcessExited(KProcess*)
+{
+ m_stretchProc->deleteLater();
+ m_stretchProc = 0;
+ m_state = ssFinished;
+ emit stretchFinished();
+}
+
+/**
+ * Returns the state of the Stretcher.
+ */
+int Stretcher::getState() { return m_state; }
+
+/**
+ * Returns the output filename (as given in call to stretch).
+ */
+QString Stretcher::getOutFilename() { return m_outFilename; }
+
+/**
+ * Acknowledges the finished stretching.
+ */
+void Stretcher::ackFinished() { m_state = ssIdle; }
+
diff --git a/kttsd/libkttsd/stretcher.h b/kttsd/libkttsd/stretcher.h
new file mode 100644
index 0000000..e4d8686
--- /dev/null
+++ b/kttsd/libkttsd/stretcher.h
@@ -0,0 +1,97 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Description:
+ Speeds up or slows down an audio file by stretching the audio stream.
+ Uses the sox program to do the stretching.
+
+ 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 _STRETCHER_H_
+#define _STRETCHER_H_
+
+#include <kdemacros.h>
+#include "kdeexportfix.h"
+
+class KProcess;
+
+class KDE_EXPORT Stretcher : public QObject{
+ Q_OBJECT
+
+ public:
+ /**
+ * Constructor.
+ */
+ Stretcher(QObject *parent = 0, const char *name = 0);
+
+ /**
+ * Destructor.
+ */
+ ~Stretcher();
+
+ enum StretcherState {
+ ssIdle = 0, // Not doing anything. Ready to stretch.
+ ssStretching = 1, // Stretching.
+ ssFinished = 2 // Stretching finished.
+ };
+
+ /**
+ * Stretch the given input file to an output file.
+ * @param inFilename Name of input audio file.
+ * @param outFilename Name of output audio file.
+ * @param stretchFactor Amount to stretch. 2.0 is twice as slow. 0.5 is twice as fast.
+ */
+ bool stretch(const QString &inFilename, const QString &outFilename, float stretchFactor);
+
+ /**
+ * Returns the state of the Stretcher.
+ */
+ int getState();
+
+ /**
+ * Returns the output filename (as given in call to stretch).
+ */
+ QString getOutFilename();
+
+ /**
+ * Acknowledges the finished stretching.
+ */
+ void ackFinished();
+
+ signals:
+ /**
+ * Emitted whenever stretching is completed.
+ */
+ void stretchFinished();
+
+ private slots:
+ void slotProcessExited(KProcess* proc);
+
+ private:
+ // Stretcher state.
+ int m_state;
+
+ // Sox process.
+ KProcess* m_stretchProc;
+
+ // Output file name.
+ QString m_outFilename;
+};
+
+#endif // _STRETCHER_H_
diff --git a/kttsd/libkttsd/talkercode.cpp b/kttsd/libkttsd/talkercode.cpp
new file mode 100644
index 0000000..d25cf7f
--- /dev/null
+++ b/kttsd/libkttsd/talkercode.cpp
@@ -0,0 +1,517 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Convenience object for manipulating Talker Codes.
+ For an explanation of what a Talker Code is, see kspeech.h.
+ -------------------
+ 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.
+ ******************************************************************************/
+
+// KDE includes.
+#include <kglobal.h>
+#include <klocale.h>
+#include <ktrader.h>
+#include <kdebug.h>
+
+// TalkerCode includes.
+#include "talkercode.h"
+
+/**
+ * Constructor.
+ */
+TalkerCode::TalkerCode(const QString &code/*=QString::null*/, bool normal /*=false*/)
+{
+ if (!code.isEmpty())
+ parseTalkerCode(code);
+ if (normal) normalize();
+}
+
+/**
+ * Copy Constructor.
+ */
+TalkerCode::TalkerCode(TalkerCode* talker, bool normal /*=false*/)
+{
+ m_languageCode = talker->languageCode();
+ m_countryCode = talker->countryCode();
+ m_voice = talker->voice();
+ m_gender = talker->gender();
+ m_volume = talker->volume();
+ m_rate = talker->rate();
+ m_plugInName = talker->plugInName();
+ if (normal) normalize();
+}
+
+/**
+ * Destructor.
+ */
+TalkerCode::~TalkerCode() { }
+
+/**
+ * Properties.
+ */
+QString TalkerCode::languageCode() const { return m_languageCode; }
+QString TalkerCode::countryCode() const { return m_countryCode; }
+QString TalkerCode::voice() const { return m_voice; }
+QString TalkerCode::gender() const { return m_gender; }
+QString TalkerCode::volume() const { return m_volume; }
+QString TalkerCode::rate() const { return m_rate; }
+QString TalkerCode::plugInName() const { return m_plugInName; }
+
+void TalkerCode::setLanguageCode(const QString &languageCode) { m_languageCode = languageCode; }
+void TalkerCode::setCountryCode(const QString &countryCode) { m_countryCode = countryCode; }
+void TalkerCode::setVoice(const QString &voice) { m_voice = voice; }
+void TalkerCode::setGender(const QString &gender) { m_gender = gender; }
+void TalkerCode::setVolume(const QString &volume) { m_volume = volume; }
+void TalkerCode::setRate(const QString &rate) { m_rate = rate; }
+void TalkerCode::setPlugInName(const QString plugInName) { m_plugInName = plugInName; }
+
+/**
+ * Sets the language code and country code (if given).
+ */
+void TalkerCode::setFullLanguageCode(const QString &fullLanguageCode)
+{
+ splitFullLanguageCode(fullLanguageCode, m_languageCode, m_countryCode);
+}
+
+/**
+ * Returns the language code plus country code (if any).
+ */
+QString TalkerCode::fullLanguageCode() const
+{
+ if (!m_countryCode.isEmpty())
+ return m_languageCode + "_" + m_countryCode;
+ else
+ return m_languageCode;
+}
+
+/**
+ * The Talker Code returned in XML format.
+ */
+QString TalkerCode::getTalkerCode() const
+{
+ QString code;
+ QString languageCode = m_languageCode;
+ if (!m_countryCode.isEmpty()) languageCode += "_" + m_countryCode;
+ if (!languageCode.isEmpty()) code = "lang=\"" + languageCode + "\" ";
+ if (!m_voice.isEmpty()) code += "name=\"" + m_voice + "\" ";
+ if (!m_gender.isEmpty()) code += "gender=\"" + m_gender + "\" ";
+ if (!code.isEmpty()) code = "<voice " + code + "/>";
+ QString prosody;
+ if (!m_volume.isEmpty()) prosody = "volume=\"" + m_volume + "\" ";
+ if (!m_rate.isEmpty()) prosody += "rate=\"" + m_rate + "\" ";
+ if (!prosody.isEmpty()) code += "<prosody " + prosody + "/>";
+ if (!m_plugInName.isEmpty()) code += "<kttsd synthesizer=\"" + m_plugInName + "\" />";
+ return code;
+}
+
+/**
+ * The Talker Code translated for display.
+ */
+QString TalkerCode::getTranslatedDescription() const
+{
+ QString code;
+ bool prefer;
+ QString fullLangCode = fullLanguageCode();
+ if (!fullLangCode.isEmpty()) code = languageCodeToLanguage( fullLangCode );
+ // TODO: The PlugInName is always English. Need a way to convert this to a translated
+ // name (possibly via DesktopEntryNameToName, but to do that, we need the desktopEntryName
+ // from the config file).
+ if (!m_plugInName.isEmpty()) code += " " + stripPrefer(m_plugInName, prefer);
+ if (!m_voice.isEmpty()) code += " " + stripPrefer(m_voice, prefer);
+ if (!m_gender.isEmpty()) code += " " + translatedGender(stripPrefer(m_gender, prefer));
+ if (!m_volume.isEmpty()) code += " " + translatedVolume(stripPrefer(m_volume, prefer));
+ if (!m_rate.isEmpty()) code += " " + translatedRate(stripPrefer(m_rate, prefer));
+ code = code.stripWhiteSpace();
+ if (code.isEmpty()) code = i18n("default");
+ return code;
+}
+
+/**
+ * Normalizes the Talker Code by filling in defaults.
+ */
+void TalkerCode::normalize()
+{
+ if (m_voice.isEmpty()) m_voice = "fixed";
+ if (m_gender.isEmpty()) m_gender = "neutral";
+ if (m_volume.isEmpty()) m_volume = "medium";
+ if (m_rate.isEmpty()) m_rate = "medium";
+}
+
+/**
+ * Given a talker code, normalizes it into a standard form and also returns
+ * the language code.
+ * @param talkerCode Unnormalized talker code.
+ * @return fullLanguageCode Language code from the talker code (including country code if any).
+ * @return Normalized talker code.
+ */
+/*static*/ QString TalkerCode::normalizeTalkerCode(const QString &talkerCode, QString &fullLanguageCode)
+{
+ TalkerCode tmpTalkerCode(talkerCode);
+ tmpTalkerCode.normalize();
+ fullLanguageCode = tmpTalkerCode.fullLanguageCode();
+ return tmpTalkerCode.getTalkerCode();
+}
+
+/**
+ * Given a language code that might contain a country code, splits the code into
+ * the two letter language code and country code.
+ * @param fullLanguageCode Language code to be split.
+ * @return languageCode Just the language part of the code.
+ * @return countryCode The country code part (if any).
+ *
+ * If the input code begins with an asterisk, it is ignored and removed from the returned
+ * languageCode.
+ */
+/*static*/ void TalkerCode::splitFullLanguageCode(const QString &lang, QString &languageCode, QString &countryCode)
+{
+ QString language = lang;
+ if (language.left(1) == "*") language = language.mid(1);
+ QString charSet;
+ KGlobal::locale()->splitLocale(language, languageCode, countryCode, charSet);
+}
+
+/**
+ * Given a full language code and plugin name, returns a normalized default talker code.
+ * @param fullLanguageCode Language code.
+ * @param plugInName Name of the Synthesizer plugin.
+ * @return Full normalized talker code.
+ *
+ * Example returned from defaultTalkerCode("en", "Festival")
+ * <voice lang="en" name="fixed" gender="neutral"/>
+ * <prosody volume="medium" rate="medium"/>
+ * <kttsd synthesizer="Festival" />
+ */
+/*static*/ QString TalkerCode::defaultTalkerCode(const QString &fullLanguageCode, const QString &plugInName)
+{
+ TalkerCode tmpTalkerCode;
+ tmpTalkerCode.setFullLanguageCode(fullLanguageCode);
+ tmpTalkerCode.setPlugInName(plugInName);
+ tmpTalkerCode.normalize();
+ return tmpTalkerCode.getTalkerCode();
+}
+
+/**
+ * Converts a language code plus optional country code to language description.
+ */
+/*static*/ QString TalkerCode::languageCodeToLanguage(const QString &languageCode)
+{
+ QString twoAlpha;
+ QString countryCode;
+ QString language;
+ if (languageCode == "other")
+ language = i18n("Other");
+ else
+ {
+ splitFullLanguageCode(languageCode, twoAlpha, countryCode);
+ language = KGlobal::locale()->twoAlphaToLanguageName(twoAlpha);
+ }
+ if (!countryCode.isEmpty())
+ {
+ QString countryName = KGlobal::locale()->twoAlphaToCountryName(countryCode);
+ // Some abbreviations to save screen space.
+ if (countryName == i18n("full country name", "United States of America"))
+ countryName = i18n("abbreviated country name", "USA");
+ if (countryName == i18n("full country name", "United Kingdom"))
+ countryName = i18n("abbreviated country name", "UK");
+ language += " (" + countryName + ")";
+ }
+ return language;
+}
+
+/**
+ * These functions return translated Talker Code attributes.
+ */
+/*static*/ QString TalkerCode::translatedGender(const QString &gender)
+{
+ if (gender == "male")
+ return i18n("male");
+ else if (gender == "female")
+ return i18n("female");
+ else if (gender == "neutral")
+ return i18n("neutral gender", "neutral");
+ else return gender;
+}
+/*static*/ QString TalkerCode::untranslatedGender(const QString &gender)
+{
+ if (gender == i18n("male"))
+ return "male";
+ else if (gender == i18n("female"))
+ return "female";
+ else if (gender == i18n("neutral gender", "neutral"))
+ return "neutral";
+ else return gender;
+}
+/*static*/ QString TalkerCode::translatedVolume(const QString &volume)
+{
+ if (volume == "medium")
+ return i18n("medium sound", "medium");
+ else if (volume == "loud")
+ return i18n("loud sound", "loud");
+ else if (volume == "soft")
+ return i18n("soft sound", "soft");
+ else return volume;
+}
+/*static*/ QString TalkerCode::untranslatedVolume(const QString &volume)
+{
+ if (volume == i18n("medium sound", "medium"))
+ return "medium";
+ else if (volume == i18n("loud sound", "loud"))
+ return "loud";
+ else if (volume == i18n("soft sound", "soft"))
+ return "soft";
+ else return volume;
+}
+/*static*/ QString TalkerCode::translatedRate(const QString &rate)
+{
+ if (rate == "medium")
+ return i18n("medium speed", "medium");
+ else if (rate == "fast")
+ return i18n("fast speed", "fast");
+ else if (rate == "slow")
+ return i18n("slow speed", "slow");
+ else return rate;
+}
+/*static*/ QString TalkerCode::untranslatedRate(const QString &rate)
+{
+ if (rate == i18n("medium speed", "medium"))
+ return "medium";
+ else if (rate == i18n("fast speed", "fast"))
+ return "fast";
+ else if (rate == i18n("slow speed", "slow"))
+ return "slow";
+ else return rate;
+}
+
+/**
+ * Given a talker code, parses out the attributes.
+ * @param talkerCode The talker code.
+ */
+void TalkerCode::parseTalkerCode(const QString &talkerCode)
+{
+ QString fullLanguageCode;
+ if (talkerCode.contains("\""))
+ {
+ fullLanguageCode = talkerCode.section("lang=", 1, 1);
+ fullLanguageCode = fullLanguageCode.section('"', 1, 1);
+ }
+ else
+ fullLanguageCode = talkerCode;
+ QString languageCode;
+ QString countryCode;
+ splitFullLanguageCode(fullLanguageCode, languageCode, countryCode);
+ m_languageCode = languageCode;
+ if (fullLanguageCode.left(1) == "*") countryCode = "*" + countryCode;
+ m_countryCode = countryCode;
+ m_voice = talkerCode.section("name=", 1, 1);
+ m_voice = m_voice.section('"', 1, 1);
+ m_gender = talkerCode.section("gender=", 1, 1);
+ m_gender = m_gender.section('"', 1, 1);
+ m_volume = talkerCode.section("volume=", 1, 1);
+ m_volume = m_volume.section('"', 1, 1);
+ m_rate = talkerCode.section("rate=", 1, 1);
+ m_rate = m_rate.section('"', 1, 1);
+ m_plugInName = talkerCode.section("synthesizer=", 1, 1);
+ m_plugInName = m_plugInName.section('"', 1, 1);
+}
+
+/**
+ * Given a list of parsed talker codes and a desired talker code, finds the closest
+ * matching talker in the list.
+ * @param talkers The list of parsed talker codes.
+ * @param talker The desired talker code.
+ * @param assumeDefaultLang If true, and desired talker code lacks a language code,
+ * the default language is assumed.
+ * @return Index into talkers of the closest matching talker.
+ */
+/*static*/ int TalkerCode::findClosestMatchingTalker(
+ const TalkerCodeList& talkers,
+ const QString& talker,
+ bool assumeDefaultLang)
+{
+ // kdDebug() << "TalkerCode::findClosestMatchingTalker: matching on talker code " << talker << endl;
+ // If nothing to match on, winner is top in the list.
+ if (talker.isEmpty()) return 0;
+ // Parse the given talker.
+ TalkerCode parsedTalkerCode(talker);
+ // If no language code specified, use the language code of the default talker.
+ if (assumeDefaultLang)
+ {
+ if (parsedTalkerCode.languageCode().isEmpty()) parsedTalkerCode.setLanguageCode(
+ talkers[0].languageCode());
+ }
+ // The talker that matches on the most priority attributes wins.
+ int talkersCount = int(talkers.count());
+ QMemArray<int> priorityMatch(talkersCount);
+ for (int ndx = 0; ndx < talkersCount; ++ndx)
+ {
+ priorityMatch[ndx] = 0;
+ // kdDebug() << "Comparing language code " << parsedTalkerCode.languageCode() << " to " << m_loadedPlugIns[ndx].parsedTalkerCode.languageCode() << endl;
+ if (parsedTalkerCode.languageCode() == talkers[ndx].languageCode())
+ {
+ ++priorityMatch[ndx];
+ // kdDebug() << "TalkerCode::findClosestMatchingTalker: Match on language " << parsedTalkerCode.languageCode() << endl;
+ }
+ if (parsedTalkerCode.countryCode().left(1) == "*")
+ if (parsedTalkerCode.countryCode().mid(1) ==
+ talkers[ndx].countryCode())
+ ++priorityMatch[ndx];
+ if (parsedTalkerCode.voice().left(1) == "*")
+ if (parsedTalkerCode.voice().mid(1) == talkers[ndx].voice())
+ ++priorityMatch[ndx];
+ if (parsedTalkerCode.gender().left(1) == "*")
+ if (parsedTalkerCode.gender().mid(1) == talkers[ndx].gender())
+ ++priorityMatch[ndx];
+ if (parsedTalkerCode.volume().left(1) == "*")
+ if (parsedTalkerCode.volume().mid(1) == talkers[ndx].volume())
+ ++priorityMatch[ndx];
+ if (parsedTalkerCode.rate().left(1) == "*")
+ if (parsedTalkerCode.rate().mid(1) == talkers[ndx].rate())
+ ++priorityMatch[ndx];
+ if (parsedTalkerCode.plugInName().left(1) == "*")
+ if (parsedTalkerCode.plugInName().mid(1) ==
+ talkers[ndx].plugInName())
+ ++priorityMatch[ndx];
+ }
+ // Determine the maximum number of priority attributes that were matched.
+ int maxPriority = -1;
+ for (int ndx = 0; ndx < talkersCount; ++ndx)
+ {
+ if (priorityMatch[ndx] > maxPriority) maxPriority = priorityMatch[ndx];
+ }
+ // Find the talker(s) that matched on most priority attributes.
+ int winnerCount = 0;
+ int winner = -1;
+ for (int ndx = 0; ndx < talkersCount; ++ndx)
+ {
+ if (priorityMatch[ndx] == maxPriority)
+ {
+ ++winnerCount;
+ winner = ndx;
+ }
+ }
+ // kdDebug() << "Priority phase: winnerCount = " << winnerCount
+ // << " winner = " << winner
+ // << " maxPriority = " << maxPriority << endl;
+ // If a tie, the one that matches on the most priority and preferred attributes wins.
+ // If there is still a tie, the one nearest the top of the kttsmgr display
+ // (first configured) will be chosen.
+ if (winnerCount > 1)
+ {
+ QMemArray<int> preferredMatch(talkersCount);
+ for (int ndx = 0; ndx < talkersCount; ++ndx)
+ {
+ preferredMatch[ndx] = 0;
+ if (priorityMatch[ndx] == maxPriority)
+ {
+ if (parsedTalkerCode.countryCode().left(1) != "*")
+ if (!talkers[ndx].countryCode().isEmpty())
+ if (parsedTalkerCode.countryCode() == talkers[ndx].countryCode())
+ ++preferredMatch[ndx];
+ if (parsedTalkerCode.voice().left(1) != "*")
+ if (parsedTalkerCode.voice() == talkers[ndx].voice())
+ ++preferredMatch[ndx];
+ if (parsedTalkerCode.gender().left(1) != "*")
+ if (parsedTalkerCode.gender() == talkers[ndx].gender())
+ ++preferredMatch[ndx];
+ if (parsedTalkerCode.volume().left(1) != "*")
+ if (parsedTalkerCode.volume() == talkers[ndx].volume())
+ ++preferredMatch[ndx];
+ if (parsedTalkerCode.rate().left(1) != "*")
+ if (parsedTalkerCode.rate() == talkers[ndx].rate())
+ ++preferredMatch[ndx];
+ if (parsedTalkerCode.plugInName().left(1) != "*")
+ if (parsedTalkerCode.plugInName() ==
+ talkers[ndx].plugInName())
+ ++preferredMatch[ndx];
+ }
+ }
+ // Determine the maximum number of preferred attributes that were matched.
+ int maxPreferred = -1;
+ for (int ndx = 0; ndx < talkersCount; ++ndx)
+ {
+ if (preferredMatch[ndx] > maxPreferred) maxPreferred = preferredMatch[ndx];
+ }
+ winner = -1;
+ winnerCount = 0;
+ // Find the talker that matched on most priority and preferred attributes.
+ // Work bottom to top so topmost wins in a tie.
+ for (int ndx = talkersCount-1; ndx >= 0; --ndx)
+ {
+ if (priorityMatch[ndx] == maxPriority)
+ {
+ if (preferredMatch[ndx] == maxPreferred)
+ {
+ ++winnerCount;
+ winner = ndx;
+ }
+ }
+ }
+ // kdDebug() << "Preferred phase: winnerCount = " << winnerCount
+ // << " winner = " << winner
+ // << " maxPreferred = " << maxPreferred << endl;
+ }
+ // If no winner found, use the first talker.
+ if (winner < 0) winner = 0;
+ // kdDebug() << "TalkerCode::findClosestMatchingTalker: returning winner = " << winner << endl;
+ return winner;
+}
+
+/*static*/ QString TalkerCode::stripPrefer( const QString& code, bool& preferred)
+{
+ if ( code.left(1) == "*" )
+ {
+ preferred = true;
+ return code.mid(1);
+ } else {
+ preferred = false;
+ return code;
+ }
+}
+
+/**
+* Uses KTrader to convert a translated Synth Plugin Name to DesktopEntryName.
+* @param name The translated plugin name. From Name= line in .desktop file.
+* @return DesktopEntryName. The name of the .desktop file (less .desktop).
+* QString::null if not found.
+*/
+/*static*/ QString TalkerCode::TalkerNameToDesktopEntryName(const QString& name)
+{
+ if (name.isEmpty()) return QString::null;
+ KTrader::OfferList offers = KTrader::self()->query("KTTSD/SynthPlugin");
+ for (uint ndx = 0; ndx < offers.count(); ++ndx)
+ if (offers[ndx]->name() == name) return offers[ndx]->desktopEntryName();
+ return QString::null;
+}
+
+/**
+* Uses KTrader to convert a DesktopEntryName into a translated Synth Plugin Name.
+* @param desktopEntryName The DesktopEntryName.
+* @return The translated Name of the plugin, from Name= line in .desktop file.
+*/
+/*static*/ QString TalkerCode::TalkerDesktopEntryNameToName(const QString& desktopEntryName)
+{
+ if (desktopEntryName.isEmpty()) return QString::null;
+ KTrader::OfferList offers = KTrader::self()->query("KTTSD/SynthPlugin",
+ QString("DesktopEntryName == '%1'").arg(desktopEntryName));
+
+ if (offers.count() == 1)
+ return offers[0]->name();
+ else
+ return QString::null;
+}
+
diff --git a/kttsd/libkttsd/talkercode.h b/kttsd/libkttsd/talkercode.h
new file mode 100644
index 0000000..45469af
--- /dev/null
+++ b/kttsd/libkttsd/talkercode.h
@@ -0,0 +1,197 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Object containing a Talker Code and providing convenience
+ functions for manipulating Talker Codes.
+ For an explanation of what a Talker Code is, see speech.h.
+ -------------------
+ 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.
+ ******************************************************************************/
+
+#ifndef _TALKERCODE_H_
+#define _TALKERCODE_H_
+
+// Qt includes.
+#include <qstring.h>
+#include <kdemacros.h>
+#include "kdeexportfix.h"
+#include <qvaluelist.h>
+
+class KDE_EXPORT TalkerCode
+{
+ public:
+ /**
+ * Constructor.
+ */
+ TalkerCode(const QString &code=QString::null, bool normal=false);
+ /**
+ * Copy Constructor.
+ */
+ TalkerCode(TalkerCode* talker, bool normal=false);
+
+ /**
+ * Destructor.
+ */
+ ~TalkerCode();
+
+ typedef QValueList<TalkerCode> TalkerCodeList;
+
+ /**
+ * Properties.
+ */
+ QString languageCode() const; /* lang="xx" */
+ QString countryCode() const; /* lang="yy_xx */
+ QString voice() const; /* name="xxx" */
+ QString gender() const; /* gender="xxx" */
+ QString volume() const; /* volume="xxx" */
+ QString rate() const; /* rate="xxx" */
+ QString plugInName() const; /* synthesizer="xxx" */
+
+ /**
+ * Returns the language code plus country code (if any).
+ */
+ QString fullLanguageCode() const;
+
+ void setLanguageCode(const QString &languageCode);
+ void setCountryCode(const QString &countryCode);
+ void setVoice(const QString &voice);
+ void setGender(const QString &gender);
+ void setVolume(const QString &volume);
+ void setRate(const QString &rate);
+ void setPlugInName(const QString plugInName);
+
+ /**
+ * Sets the language code and country code (if given).
+ */
+ void setFullLanguageCode(const QString &fullLanguageCode);
+
+ /**
+ * The Talker Code returned in XML format.
+ */
+ QString getTalkerCode() const;
+
+ /**
+ * The Talker Code translated for display.
+ */
+ QString getTranslatedDescription() const;
+
+ /**
+ * Normalizes the Talker Code by filling in defaults.
+ */
+ void normalize();
+
+ /**
+ * Given a talker code, normalizes it into a standard form and also returns
+ * the full language code.
+ * @param talkerCode Unnormalized talker code.
+ * @return fullLanguageCode Language code from the talker code (including country code if any).
+ * @return Normalized talker code.
+ */
+ static QString normalizeTalkerCode(const QString &talkerCode, QString &fullLanguageCode);
+
+ /**
+ * Given a language code that might contain a country code, splits the code into
+ * the two letter language code and country code.
+ * @param fullLanguageCode Language code to be split.
+ * @return languageCode Just the language part of the code.
+ * @return countryCode The country code part (if any).
+ *
+ * If the input code begins with an asterisk, it is ignored and removed from the returned
+ * languageCode.
+ */
+ static void splitFullLanguageCode(const QString &lang, QString &languageCode, QString &countryCode);
+
+ /**
+ * Given a language code and plugin name, returns a normalized default talker code.
+ * @param fullLanguageCode Language code.
+ * @param plugInName Name of the Synthesizer plugin.
+ * @return Full normalized talker code.
+ *
+ * Example returned from defaultTalkerCode("en", "Festival")
+ * <voice lang="en" name="fixed" gender="neutral"/>
+ * <prosody volume="medium" rate="medium"/>
+ * <kttsd synthesizer="Festival" />
+ */
+ static QString defaultTalkerCode(const QString &fullLanguageCode, const QString &plugInName);
+
+ /**
+ * Converts a language code plus optional country code to language description.
+ */
+ static QString languageCodeToLanguage(const QString &languageCode);
+
+ /**
+ * These functions return translated Talker Code attributes.
+ */
+ static QString translatedGender(const QString &gender);
+ static QString translatedVolume(const QString &volume);
+ static QString translatedRate(const QString &rate);
+ static QString untranslatedGender(const QString &gender);
+ static QString untranslatedVolume(const QString &volume);
+ static QString untranslatedRate(const QString &rate);
+
+ /**
+ * Given a list of parsed talker codes and a desired talker code, finds the closest
+ * matching talker in the list.
+ * @param talkers The list of parsed talker codes.
+ * @param talker The desired talker code.
+ * @param assumeDefaultLang If true, and desired talker code lacks a language code,
+ * the default language is assumed.
+ * @return Index into talkers of the closest matching talker.
+ */
+ static int findClosestMatchingTalker(
+ const TalkerCodeList& talkers,
+ const QString& talker,
+ bool assumeDefaultLang = true);
+
+ /**
+ * Strips leading * from a code.
+ */
+ static QString stripPrefer( const QString& code, bool& preferred);
+
+ /**
+ * Uses KTrader to convert a translated Synth Plugin Name to DesktopEntryName.
+ * @param name The translated plugin name. From Name= line in .desktop file.
+ * @return DesktopEntryName. The name of the .desktop file (less .desktop).
+ * QString::null if not found.
+ */
+ static QString TalkerNameToDesktopEntryName(const QString& name);
+
+ /**
+ * Uses KTrader to convert a DesktopEntryName into a translated Synth Plugin Name.
+ * @param desktopEntryName The DesktopEntryName.
+ * @return The translated Name of the plugin, from Name= line in .desktop file.
+ */
+ static QString TalkerDesktopEntryNameToName(const QString& desktopEntryName);
+
+ private:
+ /**
+ * Given a talker code, parses out the attributes.
+ * @param talkerCode The talker code.
+ */
+ void parseTalkerCode(const QString &talkerCode);
+
+ QString m_languageCode; /* lang="xx" */
+ QString m_countryCode; /* lang="yy_xx */
+ QString m_voice; /* name="xxx" */
+ QString m_gender; /* gender="xxx" */
+ QString m_volume; /* volume="xxx" */
+ QString m_rate; /* rate="xxx" */
+ QString m_plugInName; /* synthesizer="xxx" */
+};
+
+#endif // _TALKERCODE_H_
diff --git a/kttsd/libkttsd/testplayer.cpp b/kttsd/libkttsd/testplayer.cpp
new file mode 100644
index 0000000..06d05a7
--- /dev/null
+++ b/kttsd/libkttsd/testplayer.cpp
@@ -0,0 +1,209 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Player Object for playing synthesized audio files. Plays them
+ synchronously.
+ -------------------
+ 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.
+ ******************************************************************************/
+
+// Qt includes.
+#include <qfile.h>
+
+// KDE includes.
+#include <kapplication.h>
+#include <ktempfile.h>
+#include <kstandarddirs.h>
+#include <kparts/componentfactory.h>
+#include <ktrader.h>
+#include <kdebug.h>
+
+// KTTS includes.
+#include "player.h"
+#include "stretcher.h"
+#include "pluginconf.h"
+
+// TestPlayer includes.
+#include "testplayer.h"
+
+/**
+ * Constructor.
+ */
+TestPlayer::TestPlayer(QObject *parent, const char *name,
+ const int playerOption, const float audioStretchFactor, const QString &sinkName) :
+ QObject(parent, name)
+{
+ m_playerOption = playerOption;
+ m_audioStretchFactor = audioStretchFactor;
+ m_stretcher = 0;
+ m_player = 0;
+ m_sinkName = sinkName;
+}
+
+/**
+ * Destructor.
+ */
+TestPlayer::~TestPlayer()
+{
+ delete m_stretcher;
+ delete m_player;
+}
+
+/**
+ * Sets which audio player to use.
+ * 0 = aRts
+ * 1 = gstreamer
+ * 2 = ALSA
+ * 3 = aKode
+ */
+void TestPlayer::setPlayerOption(const int playerOption) { m_playerOption = playerOption; }
+
+/**
+ * Sets the audio stretch factor (Speed adjustment).
+ * 1.0 = normal
+ * 0.5 = twice as fast
+ * 2.0 = twice as slow
+ */
+void TestPlayer::setAudioStretchFactor(const float audioStretchFactor)
+ { m_audioStretchFactor = audioStretchFactor; }
+
+void TestPlayer::setSinkName(const QString &sinkName) { m_sinkName = sinkName; }
+
+/**
+ * Plays the specifified audio file and waits for completion.
+ * The audio file speed is adjusted according to the stretch factor.
+ * @param waveFile Name of the audio file to play.
+ */
+void TestPlayer::play(const QString &waveFile)
+{
+ // kdDebug() << "TestPlayer::play: running" << endl;
+ // Create a Stretcher object to adjust the audio Speed.
+ QString playFile = waveFile;
+ QString tmpFile;
+ if (m_audioStretchFactor != 1.0)
+ {
+ tmpFile = makeSuggestedFilename();
+ // kdDebug() << "TestPlayer::play: stretching file " << playFile << " by " << m_audioStretchFactor
+ // << " to file " << tmpFile << endl;
+ m_stretcher = new Stretcher();
+ if (m_stretcher->stretch(playFile, tmpFile, m_audioStretchFactor))
+ {
+ while (m_stretcher->getState() != Stretcher::ssFinished) qApp->processEvents();
+ playFile = m_stretcher->getOutFilename();
+ }
+ delete m_stretcher;
+ m_stretcher = 0;
+ }
+
+ // Create player object based on player option.
+ // kdDebug() << "TestPlayer::play: creating Player object with playerOption " << m_playerOption << endl;
+ m_player = createPlayerObject(m_playerOption);
+ // If player object could not be created, avoid crash is the best we can do!
+ if (!m_player) return;
+ // kdDebug() << "TestPlayer::play: starting playback." << endl;
+ m_player->startPlay(playFile);
+
+ // TODO: The following hunk of code would ideally be unnecessary. We would just
+ // return at this point and let take care of
+ // cleaning up the play object. However, because we've been called from DCOP,
+ // this seems to be necessary. The call to processEvents is problematic because
+ // it can cause re-entrancy.
+ while (m_player->playing()) qApp->processEvents();
+ // kdDebug() << "TestPlayer::play: stopping playback." << endl;
+ m_player->stop();
+ delete m_player;
+ m_player = 0;
+ if (!tmpFile.isEmpty()) QFile::remove(tmpFile);
+}
+
+/**
+ * Creates and returns a player object based on user option.
+ */
+Player* TestPlayer::createPlayerObject(int playerOption)
+{
+ Player* player = 0;
+ QString plugInName;
+ switch(playerOption)
+ {
+ case 1 :
+ {
+ plugInName = "kttsd_gstplugin";
+ break;
+ }
+ case 2 :
+ {
+ plugInName = "kttsd_alsaplugin";
+ break;
+ }
+ case 3 :
+ {
+ plugInName = "kttsd_akodeplugin";
+ break;
+ }
+ default:
+ {
+ plugInName = "kttsd_artsplugin";
+ break;
+ }
+ }
+ KTrader::OfferList offers = KTrader::self()->query(
+ "KTTSD/AudioPlugin", QString("DesktopEntryName == '%1'").arg(plugInName));
+
+ if(offers.count() == 1)
+ {
+ // kdDebug() << "TestPlayer::createPlayerObject: Loading " << offers[0]->library() << endl;
+ KLibFactory *factory = KLibLoader::self()->factory(offers[0]->library().latin1());
+ if (factory)
+ player =
+ KParts::ComponentFactory::createInstanceFromLibrary<Player>(
+ offers[0]->library().latin1(), this, offers[0]->library().latin1());
+ else
+ kdDebug() << "TestPlayer::createPlayerObject: Could not create factory." << endl;
+ }
+ if (player == 0)
+ kdDebug() << "TestPlayer::createPlayerObject: Could not load " + plugInName +
+ ". Is KDEDIRS set correctly?" << endl;
+ else
+ // Must have GStreamer >= 0.8.7.
+ if (playerOption == 1)
+ {
+ if (!player->requireVersion(0, 8, 7))
+ {
+ delete player;
+ player = 0;
+ }
+ }
+ if (player) player->setSinkName(m_sinkName);
+ return player;
+}
+
+/**
+ * Constructs a temporary filename for plugins to use as a suggested filename
+ * for synthesis to write to.
+ * @return Full pathname of suggested file.
+ */
+QString TestPlayer::makeSuggestedFilename()
+{
+ KTempFile tempFile (locateLocal("tmp", "kttsmgr-"), ".wav");
+ QString waveFile = tempFile.file()->name();
+ tempFile.close();
+ QFile::remove(waveFile);
+ // kdDebug() << "TestPlayer::makeSuggestedFilename: Suggesting filename: " << waveFile << endl;
+ return PlugInConf::realFilePath(waveFile);
+}
+
diff --git a/kttsd/libkttsd/testplayer.h b/kttsd/libkttsd/testplayer.h
new file mode 100644
index 0000000..2e7339c
--- /dev/null
+++ b/kttsd/libkttsd/testplayer.h
@@ -0,0 +1,121 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Player Object for playing synthesized audio files. Plays them
+ synchronously.
+ -------------------
+ 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 _TESTPLAYER_H_
+#define _TESTPLAYER_H_
+
+#include <kdemacros.h>
+#include "kdeexportfix.h"
+
+class Player;
+class Stretcher;
+
+// TODO: Make this work asynchronously.
+
+class KDE_EXPORT TestPlayer : public QObject{
+ public:
+ /**
+ * Constructor.
+ * @param playerOption
+ * @param audioStretchFactor
+ */
+ TestPlayer(QObject *parent = 0, const char *name = 0,
+ const int playerOption = 0, const float audioStretchFactor = 1.0,
+ const QString &sinkName = QString::null);
+
+ /**
+ * Destructor.
+ */
+ ~TestPlayer();
+
+ /**
+ * Sets which audio player to use.
+ * 0 = aRts
+ * 1 = gstreamer
+ */
+ void setPlayerOption(const int playerOption);
+
+ /**
+ * Sets the audio stretch factor (Speed adjustment).
+ * 1.0 = normal
+ * 0.5 = twice as fast
+ * 2.0 = twice as slow
+ */
+ void setAudioStretchFactor(const float audioStretchFactor);
+
+ /**
+ * Plays the specifified audio file and waits for completion.
+ * The audio file speed is adjusted according to the stretch factor.
+ * @param waveFile Name of the audio file to play.
+ */
+ void play(const QString &waveFile);
+
+ /**
+ * Sets the GStreamer Sink Name. Examples: "alsasink", "osssink", "nassink".
+ */
+ void setSinkName(const QString &sinkName);
+
+ /**
+ * Creates and returns a player object based on user option.
+ */
+ Player* createPlayerObject(int playerOption);
+
+ private:
+
+ /**
+ * Constructs a temporary filename for plugins to use as a suggested filename
+ * for synthesis to write to.
+ * @return Full pathname of suggested file.
+ */
+ QString makeSuggestedFilename();
+
+ /**
+ * Which audio player to use.
+ * 0 = aRts
+ * 1 = gstreamer
+ */
+ int m_playerOption;
+
+ /**
+ * Audio stretch factor (Speed).
+ */
+ float m_audioStretchFactor;
+
+ /**
+ * GStreamer sink name.
+ */
+ QString m_sinkName;
+
+ /**
+ * Stretcher object.
+ */
+ Stretcher* m_stretcher;
+
+ /**
+ * Player object.
+ */
+ Player* m_player;
+};
+
+#endif // _TESTPLAYER_H_
diff --git a/kttsd/libkttsd/utils.cpp b/kttsd/libkttsd/utils.cpp
new file mode 100644
index 0000000..dd9f98c
--- /dev/null
+++ b/kttsd/libkttsd/utils.cpp
@@ -0,0 +1,132 @@
+/***************************************************************************
+ Class of utility functions.
+ -------------------
+ Copyright : (C) 2004 Paul Giannaros
+ -------------------
+ Original author: Paul Giannaros <ceruleanblaze@gmail.com>
+ Current Maintainer: Paul Giannaros <ceruleanblaze@gmail.com>
+ ****************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#include <qstring.h>
+#include <kdebug.h>
+#include <qcombobox.h>
+
+#include "utils.h"
+
+KttsUtils::KttsUtils() {
+}
+
+
+KttsUtils::~KttsUtils() {
+}
+
+/**
+ * Check if an XML document has a certain root element.
+ * @param xmldoc The document to check for the element.
+ * @param elementName The element to check for in the document.
+ * @returns True if the root element exists in the document, false otherwise.
+*/
+bool KttsUtils::hasRootElement(const QString &xmldoc, const QString &elementName) {
+ // Strip all whitespace and go from there.
+ QString doc = xmldoc.simplifyWhiteSpace();
+ // Take off the <?xml...?> if it exists
+ if(doc.startsWith("<?xml")) {
+ // Look for ?> and strip everything off from there to the start - effectively removing
+ // <?xml...?>
+ int xmlStatementEnd = doc.find("?>");
+ if(xmlStatementEnd == -1) {
+ kdDebug() << "KttsUtils::hasRootElement: Bad XML file syntax\n";
+ return false;
+ }
+ xmlStatementEnd += 2; // len '?>' == 2
+ doc = doc.right(doc.length() - xmlStatementEnd);
+ }
+ // Take off leading comments, if they exist.
+ while(doc.startsWith("<!--") || doc.startsWith(" <!--")) {
+ int commentStatementEnd = doc.find("-->");
+ if(commentStatementEnd == -1) {
+ kdDebug() << "KttsUtils::hasRootElement: Bad XML file syntax\n";
+ return false;
+ }
+ commentStatementEnd += 3; // len '>' == 2
+ doc = doc.right(doc.length() - commentStatementEnd);
+ }
+ // Take off the doctype statement if it exists.
+ while(doc.startsWith("<!DOCTYPE") || doc.startsWith(" <!DOCTYPE")) {
+ int doctypeStatementEnd = doc.find(">");
+ if(doctypeStatementEnd == -1) {
+ kdDebug() << "KttsUtils::hasRootElement: Bad XML file syntax\n";
+ return false;
+ }
+ doctypeStatementEnd += 1; // len '>' == 2
+ doc = doc.right(doc.length() - doctypeStatementEnd);
+ }
+ // We should (hopefully) be left with the root element.
+ return (doc.startsWith("<" + elementName) || doc.startsWith(" <" + elementName));
+}
+
+/**
+ * Check if an XML document has a certain DOCTYPE.
+ * @param xmldoc The document to check for the doctype.
+ * @param name The doctype name to check for. Pass QString::null to not check the name.
+ * @param publicId The public ID to check for. Pass QString::null to not check the ID.
+ * @param systemId The system ID to check for. Pass QString::null to not check the ID.
+ * @returns True if the parameters match the doctype, false otherwise.
+*/
+bool KttsUtils::hasDoctype(const QString &xmldoc, const QString &name/*, const QString &publicId, const QString &systemId*/) {
+ // Strip all whitespace and go from there.
+ QString doc = xmldoc.stripWhiteSpace();
+ // Take off the <?xml...?> if it exists
+ if(doc.startsWith("<?xml")) {
+ // Look for ?> and strip everything off from there to the start - effectively removing
+ // <?xml...?>
+ int xmlStatementEnd = doc.find("?>");
+ if(xmlStatementEnd == -1) {
+ kdDebug() << "KttsUtils::hasDoctype: Bad XML file syntax\n";
+ return false;
+ }
+ xmlStatementEnd += 2; // len '?>' == 2
+ doc = doc.right(doc.length() - xmlStatementEnd);
+ doc = doc.stripWhiteSpace();
+ }
+ // Take off leading comments, if they exist.
+ while(doc.startsWith("<!--")) {
+ int commentStatementEnd = doc.find("-->");
+ if(commentStatementEnd == -1) {
+ kdDebug() << "KttsUtils::hasDoctype: Bad XML file syntax\n";
+ return false;
+ }
+ commentStatementEnd += 3; // len '>' == 2
+ doc = doc.right(doc.length() - commentStatementEnd);
+ doc = doc.stripWhiteSpace();
+ }
+ // Match the doctype statement if it exists.
+ // kdDebug() << "KttsUtils::hasDoctype: searching " << doc.left(20) << "... for " << "<!DOCTYPE " << name << endl;
+ return (doc.startsWith("<!DOCTYPE " + name));
+}
+
+/**
+ * Sets the current item in the given combobox to the item with the given text.
+ * If item with the text not found, does nothing.
+ */
+/*static*/ void KttsUtils::setCbItemFromText(QComboBox* cb, const QString& text)
+{
+ const int itemCount = cb->count();
+ for (int ndx = 0; ndx < itemCount; ++ndx)
+ {
+ if (cb->text(ndx) == text)
+ {
+ cb->setCurrentItem(ndx);
+ return;
+ }
+ }
+}
+
diff --git a/kttsd/libkttsd/utils.h b/kttsd/libkttsd/utils.h
new file mode 100644
index 0000000..63e95bc
--- /dev/null
+++ b/kttsd/libkttsd/utils.h
@@ -0,0 +1,61 @@
+/***************************************************************************
+ Class of utility functions.
+ -------------------
+ Copyright : (C) 2004 Paul Giannaros
+ -------------------
+ Original author: Paul Giannaros <ceruleanblaze@gmail.com>
+ Current Maintainer: Paul Giannaros <ceruleanblaze@gmail.com>
+ ****************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <kdemacros.h>
+#include "kdeexportfix.h"
+
+class QString;
+class QComboBox;
+
+class KDE_EXPORT KttsUtils {
+
+public:
+ /// Constructor
+ KttsUtils();
+ /// Destructor
+ ~KttsUtils();
+
+ /**
+ * Check if an XML document has a certain root element.
+ * @param xmldoc The document to check for the element.
+ * @param elementName The element to check for in the document.
+ * @returns true if the root element exists in the document, false otherwise.
+ */
+ static bool hasRootElement(const QString &xmldoc, const QString &elementName);
+
+ /**
+ * Check if an XML document has a certain DOCTYPE.
+ * @param xmldoc The document to check for the doctype.
+ * @param name The doctype name to check for. Pass QString::null to not check the name.
+ * @param publicId The public ID to check for. Pass QString::null to not check the ID.
+ * @param systemId The system ID to check for. Pass QString::null to not check the ID.
+ * @returns true if the parameters match the doctype, false otherwise.
+ */
+ static bool hasDoctype(const QString &xmldoc, const QString &name/*, const QString &publicId, const QString &systemId*/);
+
+ /**
+ * Sets the current item in the given combobox to the item with the given text.
+ * If item with the text not found, does nothing.
+ */
+ static void setCbItemFromText(QComboBox* cb, const QString& text);
+
+};
+
+#endif