summaryrefslogtreecommitdiffstats
path: root/kttsd/plugins/hadifix
diff options
context:
space:
mode:
Diffstat (limited to 'kttsd/plugins/hadifix')
-rw-r--r--kttsd/plugins/hadifix/Makefile.am23
-rw-r--r--kttsd/plugins/hadifix/README3
-rw-r--r--kttsd/plugins/hadifix/SSMLtoTxt2pho.xsl164
-rw-r--r--kttsd/plugins/hadifix/configure.in.bot37
-rw-r--r--kttsd/plugins/hadifix/configure.in.in37
-rw-r--r--kttsd/plugins/hadifix/hadifixconf.cpp406
-rw-r--r--kttsd/plugins/hadifix/hadifixconf.h83
-rw-r--r--kttsd/plugins/hadifix/hadifixconfigui.ui692
-rw-r--r--kttsd/plugins/hadifix/hadifixconfigui.ui.h114
-rw-r--r--kttsd/plugins/hadifix/hadifixplugin.cpp23
-rw-r--r--kttsd/plugins/hadifix/hadifixproc.cpp411
-rw-r--r--kttsd/plugins/hadifix/hadifixproc.h203
-rw-r--r--kttsd/plugins/hadifix/initialconfig.h164
-rw-r--r--kttsd/plugins/hadifix/kttsd_hadifixplugin.desktop51
-rw-r--r--kttsd/plugins/hadifix/voicefileui.ui119
-rw-r--r--kttsd/plugins/hadifix/voicefileui.ui.h35
16 files changed, 2565 insertions, 0 deletions
diff --git a/kttsd/plugins/hadifix/Makefile.am b/kttsd/plugins/hadifix/Makefile.am
new file mode 100644
index 0000000..35ca445
--- /dev/null
+++ b/kttsd/plugins/hadifix/Makefile.am
@@ -0,0 +1,23 @@
+INCLUDES = \
+ -I$(top_srcdir)/kttsd/libkttsd -I$(top_builddir)/kttsd/libkttsd \
+ $(all_includes)
+
+METASOURCES = AUTO
+
+kde_module_LTLIBRARIES = libkttsd_hadifixplugin.la
+
+libkttsd_hadifixplugin_la_SOURCES = \
+ hadifixconf.cpp \
+ hadifixproc.cpp \
+ hadifixplugin.cpp \
+ hadifixconfigui.ui \
+ voicefileui.ui
+libkttsd_hadifixplugin_la_LDFLAGS = $(KDE_PLUGIN) $(all_libraries)
+libkttsd_hadifixplugin_la_LIBADD = $(LIB_KDECORE) $(LIB_KFILE) $(top_builddir)/kttsd/libkttsd/libkttsd.la
+
+services_DATA = kttsd_hadifixplugin.desktop
+servicesdir = $(kde_servicesdir)
+
+# Data files.
+festivalintxsltdatadir = $(kde_datadir)/kttsd/hadifix/xslt/
+festivalintxsltdata_DATA = SSMLtoTxt2pho.xsl
diff --git a/kttsd/plugins/hadifix/README b/kttsd/plugins/hadifix/README
new file mode 100644
index 0000000..f41b967
--- /dev/null
+++ b/kttsd/plugins/hadifix/README
@@ -0,0 +1,3 @@
+This is the directory containing the Hadifix plug in.
+This plug in is developed and maintained by Gunnar Schmi Dt.
+Rework by Gary Cramblitt <garycramblitt@comcast.net>
diff --git a/kttsd/plugins/hadifix/SSMLtoTxt2pho.xsl b/kttsd/plugins/hadifix/SSMLtoTxt2pho.xsl
new file mode 100644
index 0000000..5a81c8f
--- /dev/null
+++ b/kttsd/plugins/hadifix/SSMLtoTxt2pho.xsl
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+<xsl:output method="text" encoding="ISO-8859-1" indent="no"/>
+
+<!-- XSLT stylesheet to convert SSML into a format that can be processed by the
+ Hadifix txt2pho processor.
+
+ (c) 2004 by Gary Cramblitt
+
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+ 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; either version 2 of the License, or *
+ (at your option) any later version. *
+
+ The txt2pho processor permits special markup to be embedded in text to control
+ speech attributes such as speed (duration), pitch, etc.
+ The markup must be inside curly braces and separated from other text by a space.
+ Spaces within the markup are not permitted.
+ See the txt2pho README file for details.
+
+ Something the README does not say is that {Pitch} markup applies only to one
+ sentence and reverts back to normal when the sentence is completed.
+ It means that we must repeatedly ouput {Pitch} markup
+ for each sentence in the text. For example, the SSML
+
+ <prosody pitch="high">Sentence one. Sentence two.</prosody> Sentence three.
+
+ must be converted to
+
+ {Pitch:300} Sentence one. {Pitch:300} Sentence two. Sentence three.
+
+ {Duration} markup, on the other hand, is addative and stays in effect until
+ changed. For example, the SSML
+
+ <prosody rate="fast">Sentence one. Sentence two.</prosody> Sentence three.
+
+ must be converted to
+
+ {Duration:-0.5} Sentence one. Sentence two. {Duration:0.5} Sentence three.
+
+ txt2pho will also stop processing when it sees a newline. Therefore, we must take
+ care to strip all newlines and avoid inserting any newlines.
+ -->
+
+<!-- Strip all elements and attributes from output. -->
+<xsl:strip-space elements="*" />
+
+<xsl:template match="speak">
+ <xsl:apply-templates/>
+</xsl:template>
+
+<!-- Handle markup that maintains state. -->
+<xsl:template match="prosody">
+ <!-- Rate (speed), Rates are addative and stay in effect until changed. -->
+ <!-- TODO: SSML permits nesting of prosody elements. As coded below,
+ <prosody rate="slow">One<prosody rate="slow">Two</prosody</prosody>
+ will pronounce "Two" doubly slow, which it should not do. -->
+ <xsl:choose>
+ <xsl:when test="@rate='fast'">
+ <!-- Increase speed. -->
+ <xsl:value-of select="'{Duration:-0.5} '"/>
+ <!-- Continue processing. -->
+ <xsl:apply-templates/>
+ <!-- Decrease speed. -->
+ <xsl:value-of select="'{Duration:0.5} '"/>
+ </xsl:when>
+ <xsl:when test="@rate='slow'">
+ <!-- Decrease speed. -->
+ <xsl:value-of select="'{Duration:0.5} '"/>
+ <!-- Continue processing. -->
+ <xsl:apply-templates/>
+ <!-- Increase speed. -->
+ <xsl:value-of select="'{Duration:-0.5} '"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-templates/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<!-- Outputs a sentence with txt2pho markup.
+ Called in the context of a text node. Obtain markup by
+ extracting ancestor node attributes.
+ @param sentence The sentence to mark up.
+ @return Sentence preceeded by txt2pho markup.
+ -->
+<xsl:template name="output-sentence">
+ <xsl:param name="sentence"/>
+ <!-- Pitch -->
+ <xsl:if test="ancestor::prosody/@pitch='high'">
+ <xsl:value-of select="'{Pitch:300} '"/>
+ </xsl:if>
+ <xsl:if test="ancestor::prosody/@pitch='low'">
+ <xsl:value-of select="'{Pitch:-40} '"/>
+ </xsl:if>
+ <!-- Output the sentence itself. -->
+ <xsl:value-of select="$sentence"/>
+</xsl:template>
+
+<!-- Return the first sentence of argument.
+ Sentence delimiters are '.:;!?'
+ @param paragraph Paragraph from which to extract first sentence.
+ @return The first sentence, or if no such sentence, the paragraph.
+ -->
+<xsl:template name="parse-sentence">
+ <xsl:param name="paragraph"/>
+ <!-- Copy paragraph, replacing all delimeters with period. -->
+ <xsl:variable name="tmp">
+ <xsl:value-of select="translate($paragraph,':;!?','....')"/>
+ </xsl:variable>
+ <!-- Look for first period and space and extract corresponding substring from original. -->
+ <xsl:choose>
+ <xsl:when test="contains($tmp, '. ')">
+ <xsl:value-of select="substring($paragraph, 1, string-length(substring-before($tmp, '. '))+2)"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$paragraph"/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<!-- Process a paragraph, outputting each sentence with txt2pho markup.
+ @param paragraph The paragraph to process.
+ @return The paragraph with each sentence preceeded by txt2pho markup.
+ -->
+<xsl:template name="output-paragraph">
+ <xsl:param name="paragraph" />
+ <!-- Stop when no more sentences to output. -->
+ <xsl:choose>
+ <xsl:when test="normalize-space($paragraph)!=''">
+ <!-- Split the paragraph into first sentence and rest of paragraph (if any). -->
+ <xsl:variable name="sentence">
+ <xsl:call-template name="parse-sentence">
+ <xsl:with-param name="paragraph" select="$paragraph"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <!-- debug: <xsl:value-of select="concat('[sentence: ',$sentence,']')"/> -->
+ <xsl:variable name="rest">
+ <xsl:value-of select="substring-after($paragraph,$sentence)" />
+ </xsl:variable>
+ <!-- Output the sentence with markup. -->
+ <xsl:call-template name="output-sentence" >
+ <xsl:with-param name="sentence" select="$sentence" />
+ </xsl:call-template>
+ <!-- Recursively process the rest of the paragraph. -->
+ <xsl:call-template name="output-paragraph">
+ <xsl:with-param name="paragraph" select="$rest" />
+ </xsl:call-template>
+ </xsl:when>
+ </xsl:choose>
+</xsl:template>
+
+<!-- Process each text node. -->
+<xsl:template match="text()">
+ <!-- debug: <xsl:value-of select="concat('[paragraph: ',normalize-space(.),']')"/> -->
+ <xsl:call-template name="output-paragraph">
+ <xsl:with-param name="paragraph" select="concat(normalize-space(.),' ')"/>
+ </xsl:call-template>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/kttsd/plugins/hadifix/configure.in.bot b/kttsd/plugins/hadifix/configure.in.bot
new file mode 100644
index 0000000..ffada4f
--- /dev/null
+++ b/kttsd/plugins/hadifix/configure.in.bot
@@ -0,0 +1,37 @@
+if test "x$compile_hadifix_plugin" = "xyes"; then
+ if test "x$hadifix_inst" = "xno"; then
+ echo ""
+ echo "======================================================"
+ echo "Some of the tools needed to run Hadifix do not appear"
+ echo "to be installed on this system. (mbrola and txt2pho"
+ echo "were not found in the PATH or /etc/txt2pho was not"
+ echo "found). The Hadifix plugin will be built, but you need"
+ echo "to install the required tools before you can use it:"
+ echo ""
+ if test "x$hadifix_mbrola_bin" = "xno"; then
+ echo "- mbrola was not found. You can get it from"
+ echo " http://tcts.fpms.ac.be/synthesis/mbrola.html"
+ fi
+ if test "x$hadifix_txt2pho_bin" = "xno"; then
+ echo "- txt2pho was not found. You can get it from"
+ echo " http://www.ikp.uni-bonn.de/dt/forsch/phonetik/"
+ echo " hadifix/HADIFIXforMBROLA.html"
+ fi
+ echo ""
+ echo "Please read the KTTS Handbook for further information."
+ echo "====================================================="
+ all_tests=bad
+ fi
+ if test "x$hadifix_inst" = "xyes"; then
+ if test "x$hadifix_txt2pho" = "xno"; then
+ echo ""
+ echo "======================================================"
+ echo "The configuration file /etc/txt2pho was not found on"
+ echo "this system. This is no problem as long as all users"
+ echo "have a valid ~/.txt2phorc in their home directories if"
+ echo "they want to use the Hadifix plugin."
+ echo "======================================================"
+ fi
+ fi
+fi
+
diff --git a/kttsd/plugins/hadifix/configure.in.in b/kttsd/plugins/hadifix/configure.in.in
new file mode 100644
index 0000000..3c997c2
--- /dev/null
+++ b/kttsd/plugins/hadifix/configure.in.in
@@ -0,0 +1,37 @@
+dnl ==========================
+dnl checks for Hadifix plug in
+dnl ==========================
+
+AC_ARG_ENABLE(kttsd-hadifix,
+ AC_HELP_STRING([--enable-kttsd-hadifix],
+ [build KTTSD Hadifix Plugin [default=yes]]),
+ hadifix_plugin=$enableval,
+ hadifix_plugin=yes)
+
+compile_hadifix_plugin="no"
+
+if test "x$hadifix_plugin" = "xyes"; then
+ compile_hadifix_plugin="yes"
+fi
+
+if test "x$compile_hadifix_plugin" = "xyes"; then
+ dnl Check for Hadifix installation.
+ dnl Note that Hadifix plugin is always built,
+ dnl unless user overrides with -disable-kttsd-hadifix.
+ AC_PATH_PROG(hadifix_mbrola_bin, "mbrola", "no", [$PATH:])
+ AC_PATH_PROG(hadifix_txt2pho_bin, "txt2pho", "no", [$PATH:])
+
+ hadifix_inst="yes"
+ if test "x$hadifix_mbrola_bin" = "xno"; then
+ hadifix_inst="no"
+ fi
+ if test "x$hadifix_txt2pho_bin" = "xno"; then
+ hadifix_inst="no"
+ fi
+
+ if test "x$hadifix_inst" = "xyes"; then
+ AC_CHECK_FILE(/etc/txt2pho, [hadifix_txt2pho="yes"], [hadifix_txt2pho="no"])
+ fi
+fi
+
+AM_CONDITIONAL(include_kttsd_hadifix, test "x$compile_hadifix_plugin" = "xyes")
diff --git a/kttsd/plugins/hadifix/hadifixconf.cpp b/kttsd/plugins/hadifix/hadifixconf.cpp
new file mode 100644
index 0000000..a2c5547
--- /dev/null
+++ b/kttsd/plugins/hadifix/hadifixconf.cpp
@@ -0,0 +1,406 @@
+/***************************************************************************
+ begin : Mon Okt 14 2002
+ copyright : (C) 2002 by Gunnar Schmi Dt
+ email : gunnar@schmi-dt.de
+ current mainainer: : 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. *
+ * *
+ ***************************************************************************/
+
+// Qt includes.
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qgroupbox.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <qfile.h>
+
+// KDE includes.
+#include <ktempfile.h>
+#include <kaboutdata.h>
+#include <kaboutapplication.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kdialog.h>
+#include <kcombobox.h>
+#include <kstandarddirs.h>
+#include <kurlrequester.h>
+#include <kdialogbase.h>
+#include <klineedit.h>
+#include <knuminput.h>
+#include <kprogress.h>
+
+// KTTS includes.
+#include <pluginconf.h>
+#include <testplayer.h>
+#include <talkercode.h>
+
+// Hadifix includes.
+#include "hadifixproc.h"
+#include "voicefileui.h"
+#include "hadifixconfigui.h"
+#include "hadifixconf.h"
+#include "hadifixconf.moc"
+
+class HadifixConfPrivate {
+ friend class HadifixConf;
+ private:
+ HadifixConfPrivate() {
+ hadifixProc = 0;
+ progressDlg = 0;
+ findInitialConfig();
+ };
+
+ ~HadifixConfPrivate() {
+ if (hadifixProc) hadifixProc->stopText();
+ delete hadifixProc;
+ if (!waveFile.isNull()) QFile::remove(waveFile);
+ delete progressDlg;
+ };
+
+ #include "initialconfig.h"
+
+ void setConfiguration (QString hadifixExec, QString mbrolaExec,
+ QString voice, bool male,
+ int volume, int time, int pitch,
+ QString codecName)
+ {
+ configWidget->hadifixURL->setURL (hadifixExec);
+ configWidget->mbrolaURL->setURL (mbrolaExec);
+ configWidget->setVoice (voice, male);
+
+ configWidget->volumeBox->setValue (volume);
+ configWidget->timeBox->setValue (time);
+ configWidget->frequencyBox->setValue (pitch);
+ int codec = PlugInProc::codecNameToListIndex(codecName, codecList);
+ configWidget->characterCodingBox->setCurrentItem(codec);
+ }
+
+ void initializeVoices () {
+ QStringList::iterator it;
+ for (it = defaultVoices.begin(); it != defaultVoices.end(); ++it) {
+ HadifixProc::VoiceGender gender;
+ QString name = QFileInfo(*it).fileName();
+ gender = HadifixProc::determineGender(defaultMbrolaExec, *it);
+ if (gender == HadifixProc::MaleGender)
+ configWidget->addVoice(*it, true, i18n("Male voice \"%1\"").arg(name));
+ else if (gender == HadifixProc::FemaleGender)
+ configWidget->addVoice(*it, false, i18n("Female voice \"%1\"").arg(name));
+ else {
+ if (name == "de1")
+ configWidget->addVoice(*it, false, i18n("Female voice \"%1\"").arg(name));
+ else {
+ configWidget->addVoice(*it, true, i18n("Unknown voice \"%1\"").arg(name));
+ configWidget->addVoice(*it, false, i18n("Unknown voice \"%1\"").arg(name));
+ }
+ }
+ }
+ };
+
+ void initializeCharacterCodes() {
+ // Build codec list and fill combobox.
+ codecList = PlugInProc::buildCodecList();
+ configWidget->characterCodingBox->clear();
+ configWidget->characterCodingBox->insertStringList(codecList);
+ }
+
+ void setDefaultEncodingFromVoice() {
+ QString voiceFile = configWidget->getVoiceFilename();
+ QString voiceCode = QFileInfo(voiceFile).baseName(false);
+ voiceCode = voiceCode.left(2);
+ QString codecName = "Local";
+ if (voiceCode == "de") codecName = "ISO 8859-1";
+ if (voiceCode == "hu") codecName = "ISO 8859-2";
+ configWidget->characterCodingBox->setCurrentItem(PlugInProc::codecNameToListIndex(
+ codecName, codecList));
+}
+
+ void setDefaults () {
+ QStringList::iterator it = defaultVoices.begin();
+ // Find a voice that matches language code, if any.
+ if (!languageCode.isEmpty())
+ {
+ QString justLang = languageCode.left(2);
+ for (;it != defaultVoices.end();++it)
+ {
+ QString voiceCode = QFileInfo(*it).baseName(false).left(2);
+ if (voiceCode == justLang) break;
+ }
+ if (it == defaultVoices.end()) it = defaultVoices.begin();
+ }
+ HadifixProc::VoiceGender gender;
+ gender = HadifixProc::determineGender(defaultMbrolaExec, *it);
+
+ setConfiguration (defaultHadifixExec, defaultMbrolaExec,
+ *it, gender == HadifixProc::MaleGender,
+ 100, 100, 100, "Local");
+ };
+
+ void load (KConfig *config, const QString &configGroup) {
+ config->setGroup(configGroup);
+
+ QString voice = config->readEntry("voice", configWidget->getVoiceFilename());
+
+ HadifixProc::VoiceGender gender;
+ gender = HadifixProc::determineGender(defaultMbrolaExec, voice);
+ bool isMale = (gender == HadifixProc::MaleGender);
+
+ QString defaultCodecName = "Local";
+ // TODO: Need a better way to determine proper codec for each voice.
+ // This will do for now.
+ QString voiceCode = QFileInfo(voice).baseName(false);
+ if (voiceCode.left(2) == "de") defaultCodecName = "ISO 8859-1";
+ if (voiceCode.left(2) == "hu") defaultCodecName = "ISO 8859-2";
+
+ setConfiguration (
+ config->readEntry ("hadifixExec",defaultHadifixExec),
+ config->readEntry ("mbrolaExec", defaultMbrolaExec),
+ config->readEntry ("voice", voice),
+ config->readBoolEntry("gender", isMale),
+ config->readNumEntry ("volume", 100),
+ config->readNumEntry ("time", 100),
+ config->readNumEntry ("pitch", 100),
+ config->readEntry ("codec", defaultCodecName)
+ );
+ };
+
+ void save (KConfig *config, const QString &configGroup) {
+ config->setGroup(configGroup);
+ config->writeEntry ("hadifixExec", PlugInConf::realFilePath(configWidget->hadifixURL->url()));
+ config->writeEntry ("mbrolaExec", PlugInConf::realFilePath(configWidget->mbrolaURL->url()));
+ config->writeEntry ("voice", configWidget->getVoiceFilename());
+ config->writeEntry ("gender", configWidget->isMaleVoice());
+ config->writeEntry ("volume", configWidget->volumeBox->value());
+ config->writeEntry ("time", configWidget->timeBox->value());
+ config->writeEntry ("pitch", configWidget->frequencyBox->value());
+ config->writeEntry ("codec", PlugInProc::codecIndexToCodecName(
+ configWidget->characterCodingBox->currentItem(), codecList));
+ }
+
+ HadifixConfigUI *configWidget;
+
+ QString languageCode;
+ QString defaultHadifixExec;
+ QString defaultMbrolaExec;
+ QStringList defaultVoices;
+ QStringList codecList;
+
+ // Wave file playing on play object.
+ QString waveFile;
+ // Synthesizer.
+ HadifixProc* hadifixProc;
+ // Progress Dialog.
+ KProgressDialog* progressDlg;
+};
+
+/** Constructor */
+HadifixConf::HadifixConf( QWidget* parent, const char* name, const QStringList &) :
+ PlugInConf( parent, name ){
+ // kdDebug() << "HadifixConf::HadifixConf: Running" << endl;
+ QVBoxLayout *layout = new QVBoxLayout (this, KDialog::marginHint(), KDialog::spacingHint(), "CommandConfigWidgetLayout");
+ layout->setAlignment (Qt::AlignTop);
+
+ d = new HadifixConfPrivate();
+ d->configWidget = new HadifixConfigUI (this, "configWidget");
+
+ QString file = locate("data", "LICENSES/LGPL_V2");
+ i18n("This plugin is distributed under the terms of the GPL v2 or later.");
+
+ connect(d->configWidget->voiceButton, SIGNAL(clicked()), this, SLOT(voiceButton_clicked()));
+ connect(d->configWidget->testButton, SIGNAL(clicked()), this, SLOT(testButton_clicked()));
+ connect(d->configWidget, SIGNAL(changed(bool)), this, SLOT(configChanged (bool)));
+ connect(d->configWidget->characterCodingBox, SIGNAL(textChanged(const QString&)),
+ this, SLOT(configChanged()));
+ connect(d->configWidget->voiceCombo, SIGNAL(activated(int)), this, SLOT(voiceCombo_activated(int)));
+ d->initializeCharacterCodes();
+ d->initializeVoices();
+ d->setDefaults();
+ layout->addWidget (d->configWidget);
+}
+
+/** Destructor */
+HadifixConf::~HadifixConf(){
+ // kdDebug() << "HadifixConf::~HadifixConf: Running" << endl;
+ delete d;
+}
+
+void HadifixConf::load(KConfig *config, const QString &configGroup) {
+ // kdDebug() << "HadifixConf::load: Running" << endl;
+ d->setDefaults();
+ d->load (config, configGroup);
+}
+
+void HadifixConf::save(KConfig *config, const QString &configGroup) {
+ // kdDebug() << "HadifixConf::save: Running" << endl;
+ d->save (config, configGroup);
+}
+
+void HadifixConf::defaults() {
+ // kdDebug() << "HadifixConf::defaults: Running" << endl;
+ d->setDefaults();
+}
+
+void HadifixConf::setDesiredLanguage(const QString &lang)
+{
+ d->languageCode = lang;
+}
+
+QString HadifixConf::getTalkerCode()
+{
+ if (!d->configWidget->hadifixURL->url().isEmpty() && !d->configWidget->mbrolaURL->url().isEmpty())
+ {
+ QString voiceFile = d->configWidget->getVoiceFilename();
+ if (QFileInfo(voiceFile).exists())
+ {
+ // mbrola voice file names usually start with two-letter language code,
+ // but this is by no means guaranteed.
+ QString voiceCode = QFileInfo(voiceFile).baseName(false);
+ QString voiceLangCode = voiceCode.left(2);
+ if (d->languageCode.left(2) != voiceLangCode)
+ {
+ // Verify that first two letters of voice filename are a valid language code.
+ // If they are, switch to that language.
+ if (!TalkerCode::languageCodeToLanguage(voiceLangCode).isEmpty())
+ d->languageCode = voiceLangCode;
+ }
+ QString gender = "male";
+ if (!d->configWidget->isMaleVoice()) gender = "female";
+ QString volume = "medium";
+ if (d->configWidget->volumeBox->value() < 75) volume = "soft";
+ if (d->configWidget->volumeBox->value() > 125) volume = "loud";
+ QString rate = "medium";
+ if (d->configWidget->timeBox->value() < 75) rate = "slow";
+ if (d->configWidget->timeBox->value() > 125) rate = "fast";
+ return QString(
+ "<voice lang=\"%1\" name=\"%2\" gender=\"%3\" />"
+ "<prosody volume=\"%4\" rate=\"%5\" />"
+ "<kttsd synthesizer=\"%6\" />")
+ .arg(d->languageCode)
+ .arg(voiceCode)
+ .arg(gender)
+ .arg(volume)
+ .arg(rate)
+ .arg("Hadifix");
+ }
+ }
+ return QString::null;
+}
+
+void HadifixConf::voiceButton_clicked () {
+ KDialogBase *dialog = new KDialogBase (this, 0, true,
+ i18n("Voice File - Hadifix Plugin"),
+ KDialogBase::Ok|KDialogBase::Cancel,
+ KDialogBase::Ok, true);
+ VoiceFileWidget *widget = new VoiceFileWidget(dialog);
+ dialog->setMainWidget (widget);
+
+ widget->femaleOption->setChecked(!d->configWidget->isMaleVoice());
+ widget->maleOption->setChecked(d->configWidget->isMaleVoice());
+ widget->voiceFileURL->setURL(d->configWidget->getVoiceFilename());
+ widget->mbrola = d->defaultMbrolaExec;
+
+ if (dialog->exec() == QDialog::Accepted) {
+ d->configWidget->setVoice (widget->voiceFileURL->url(),
+ widget->maleOption->isChecked());
+ d->setDefaultEncodingFromVoice();
+ emit changed(true);
+ }
+
+ delete dialog;
+}
+
+void HadifixConf::voiceCombo_activated(int /*index*/)
+{
+ d->setDefaultEncodingFromVoice();
+}
+
+void HadifixConf::testButton_clicked () {
+ // If currently synthesizing, stop it.
+ if (d->hadifixProc)
+ d->hadifixProc->stopText();
+ else
+ {
+ d->hadifixProc = new HadifixProc();
+ connect (d->hadifixProc, SIGNAL(stopped()), this, SLOT(slotSynthStopped()));
+ }
+ // Create a temp file name for the wave file.
+ KTempFile tempFile (locateLocal("tmp", "hadifixplugin-"), ".wav");
+ QString tmpWaveFile = tempFile.file()->name();
+ tempFile.close();
+
+ // Tell user to wait.
+ d->progressDlg = new KProgressDialog(d->configWidget, "ktts_hadifix_testdlg",
+ i18n("Testing"),
+ i18n("Testing."),
+ true);
+ d->progressDlg->progressBar()->hide();
+ d->progressDlg->setAllowCancel(true);
+
+ // Speak a German sentence as hadifix is a German tts
+ // TODO: Actually, Hadifix does support English (and other languages?) as well,
+ // If you install the right voice files. The hard part is finding and installing
+ // a working txt2pho for the desired language. There seem to be some primitive french,
+ // italian, and a few others, written in perl, but they have many issues.
+ // Go to the mbrola website and click on "TTS" to learn more.
+
+ // QString testMsg = "K D E ist eine moderne grafische Arbeitsumgebung für UNIX-Computer.";
+ QString testMsg = testMessage(d->languageCode);
+ connect (d->hadifixProc, SIGNAL(synthFinished()), this, SLOT(slotSynthFinished()));
+ d->hadifixProc->synth (testMsg,
+ realFilePath(d->configWidget->hadifixURL->url()),
+ d->configWidget->isMaleVoice(),
+ realFilePath(d->configWidget->mbrolaURL->url()),
+ d->configWidget->getVoiceFilename(),
+ d->configWidget->volumeBox->value(),
+ d->configWidget->timeBox->value(),
+ d->configWidget->frequencyBox->value(),
+ PlugInProc::codecIndexToCodec(d->configWidget->characterCodingBox->currentItem(), d->codecList),
+ tmpWaveFile);
+
+ // Display progress dialog modally. Processing continues when plugin signals synthFinished,
+ // or if user clicks Cancel button.
+ d->progressDlg->exec();
+ disconnect (d->hadifixProc, SIGNAL(synthFinished()), this, SLOT(slotSynthFinished()));
+ if (d->progressDlg->wasCancelled()) d->hadifixProc->stopText();
+ delete d->progressDlg;
+ d->progressDlg = 0;
+}
+
+void HadifixConf::slotSynthFinished()
+{
+ // If user canceled, progress dialog is gone, so exit.
+ if (!d->progressDlg)
+ {
+ d->hadifixProc->ackFinished();
+ return;
+ }
+ // Hide the Cancel button so user can't cancel in the middle of playback.
+ d->progressDlg->showCancelButton(false);
+ // Get new wavefile name.
+ d->waveFile = d->hadifixProc->getFilename();
+ // Tell synth we're done.
+ d->hadifixProc->ackFinished();
+ // Play the wave file (possibly adjusting its Speed).
+ // Player object deletes the wave file when done.
+ if (m_player) m_player->play(d->waveFile);
+ QFile::remove(d->waveFile);
+ d->waveFile = QString::null;
+ if (d->progressDlg) d->progressDlg->close();
+}
+
+void HadifixConf::slotSynthStopped()
+{
+ // Clean up after canceling test.
+ QString filename = d->hadifixProc->getFilename();
+ // kdDebug() << "HadifixConf::slotSynthStopped: filename = " << filename << endl;
+ if (!filename.isNull()) QFile::remove(filename);
+}
diff --git a/kttsd/plugins/hadifix/hadifixconf.h b/kttsd/plugins/hadifix/hadifixconf.h
new file mode 100644
index 0000000..a53ecb1
--- /dev/null
+++ b/kttsd/plugins/hadifix/hadifixconf.h
@@ -0,0 +1,83 @@
+#ifndef _HADIFIXCONF_H_
+#define _HADIFIXCONF_H_
+
+#include <qstringlist.h>
+
+#include <kconfig.h>
+
+#include <pluginconf.h>
+
+class HadifixProc;
+class HadifixConfPrivate;
+
+class HadifixConf : public PlugInConf {
+ Q_OBJECT
+
+ public:
+ /** Constructor */
+ HadifixConf( QWidget* parent = 0, const char* name = 0, const QStringList &args = QStringList());
+
+ /** Destructor */
+ ~HadifixConf();
+
+ /** This method is invoked whenever the module should read its
+ configuration (most of the times from a config file) and update the
+ user interface. This happens when the user clicks the "Reset" button in
+ the control center, to undo all of his changes and restore the currently
+ valid settings. NOTE that this is not called after the modules is loaded,
+ so you probably want to call this method in the constructor.*/
+ void load(KConfig *config, const QString &configGroup);
+
+ /** This function gets called when the user wants to save the settings in
+ the user interface, updating the config files or wherever the
+ configuration is stored. The method is called when the user clicks "Apply"
+ or "Ok". */
+ void save(KConfig *config, const QString &configGroup);
+
+ /** This function is called to set the settings in the module to sensible
+ default values. It gets called when hitting the "Default" button. The
+ default values should probably be the same as the ones the application
+ uses when started without a config file. */
+ void defaults();
+
+ /**
+ * This function informs the plugin of the desired language to be spoken
+ * by the plugin. The plugin should attempt to adapt itself to the
+ * specified language code, choosing sensible defaults if necessary.
+ * If the passed-in code is QString::null, no specific language has
+ * been chosen.
+ * @param lang The desired language code or Null if none.
+ *
+ * If the plugin is unable to support the desired language, that is OK.
+ * Language codes are given by ISO 639-1 and are in lowercase.
+ * The code may also include an ISO 3166 country code in uppercase
+ * separated from the language code by underscore (_). For
+ * example, en_GB. If your plugin supports the given language, but
+ * not the given country, treat it as though the country
+ * code were not specified, i.e., adapt to the given language.
+ */
+ void setDesiredLanguage(const QString &lang);
+
+ /**
+ * Return fully-specified talker code for the configured plugin. This code
+ * uniquely identifies the configured instance of the plugin and distinquishes
+ * one instance from another. If the plugin has not been fully configured,
+ * i.e., cannot yet synthesize, return QString::null.
+ * @return Fully-specified talker code.
+ */
+ QString getTalkerCode();
+
+ public slots:
+ void configChanged(bool t = true){emit changed(t);};
+
+ private slots:
+ virtual void voiceButton_clicked();
+ virtual void testButton_clicked();
+ virtual void voiceCombo_activated(int index);
+ void slotSynthFinished();
+ void slotSynthStopped();
+
+ private:
+ HadifixConfPrivate *d;
+};
+#endif
diff --git a/kttsd/plugins/hadifix/hadifixconfigui.ui b/kttsd/plugins/hadifix/hadifixconfigui.ui
new file mode 100644
index 0000000..302c1de
--- /dev/null
+++ b/kttsd/plugins/hadifix/hadifixconfigui.ui
@@ -0,0 +1,692 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>HadifixConfigUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>HadifixConfigUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>578</width>
+ <height>388</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Hadifix Configuration</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This is the configuration dialog for the Hadifix (txt2pho and Mbrola) speech synthesizer.</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>GroupBox4</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Had&amp;ifix Configuration</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This is the configuration dialog for the Hadifix (txt2pho and Mbrola) speech synthesizer.</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QGroupBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>basicOptions</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>Box</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="title">
+ <string>&amp;Basic Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>voiceLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Voice file:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>voiceCombo</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Select a voice for speaking text. If no voices are listed, check your Mbrola configuration. You must install at least one voice.</string>
+ </property>
+ </widget>
+ <widget class="KComboBox" row="0" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>voiceCombo</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Select a voice for speaking text. If no voices are listed, check your Mbrola configuration. You must install at least one voice.</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="0" column="3">
+ <property name="name">
+ <cstring>voiceButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Select...</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>volumeLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Volume &amp;ratio:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>volumeBox</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Adjusts the volume of speech. Slide to left for softer speech; to the right for louder.</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="1" column="1">
+ <property name="name">
+ <cstring>volumeBox</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="suffix">
+ <string> %</string>
+ </property>
+ <property name="maxValue">
+ <number>200</number>
+ </property>
+ <property name="minValue">
+ <number>50</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Adjusts the volume of speech. Slide to left for softer speech; to the right for louder.</string>
+ </property>
+ </widget>
+ <widget class="QSlider" row="1" column="2" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>volumeSlider</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="focusPolicy">
+ <enum>NoFocus</enum>
+ </property>
+ <property name="minValue">
+ <number>0</number>
+ </property>
+ <property name="maxValue">
+ <number>1000</number>
+ </property>
+ <property name="lineStep">
+ <number>10</number>
+ </property>
+ <property name="pageStep">
+ <number>100</number>
+ </property>
+ <property name="value">
+ <number>500</number>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="tickmarks">
+ <enum>NoMarks</enum>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Adjusts the volume of speech. Slide to left for softer speech; to the right for louder.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>timeLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Speed:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>timeBox</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Adjusts the speed of speech. Slide to left for slower speech; to the right for faster.</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="2" column="1">
+ <property name="name">
+ <cstring>timeBox</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="suffix">
+ <string> %</string>
+ </property>
+ <property name="maxValue">
+ <number>200</number>
+ </property>
+ <property name="minValue">
+ <number>50</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Adjusts the speed of speech. Slide to left for slower speech; to the right for faster.</string>
+ </property>
+ </widget>
+ <widget class="QSlider" row="2" column="2" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>timeSlider</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="focusPolicy">
+ <enum>NoFocus</enum>
+ </property>
+ <property name="maxValue">
+ <number>1000</number>
+ </property>
+ <property name="lineStep">
+ <number>10</number>
+ </property>
+ <property name="pageStep">
+ <number>100</number>
+ </property>
+ <property name="value">
+ <number>500</number>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Adjusts the speed of speech. Slide to left for slower speech; to the right for faster.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>frequencyLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Pitch:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>frequencyBox</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Adjusts the pitch (tone) of speech. Slide to left for lower speech; to the right for higher.</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="3" column="1">
+ <property name="name">
+ <cstring>frequencyBox</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="suffix">
+ <string> %</string>
+ </property>
+ <property name="maxValue">
+ <number>200</number>
+ </property>
+ <property name="minValue">
+ <number>50</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Adjusts the pitch (tone) of speech. Slide to left for lower speech; to the right for higher.</string>
+ </property>
+ </widget>
+ <widget class="QSlider" row="3" column="2" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>frequencySlider</cstring>
+ </property>
+ <property name="focusPolicy">
+ <enum>NoFocus</enum>
+ </property>
+ <property name="maxValue">
+ <number>1000</number>
+ </property>
+ <property name="lineStep">
+ <number>10</number>
+ </property>
+ <property name="pageStep">
+ <number>100</number>
+ </property>
+ <property name="value">
+ <number>500</number>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Adjusts the pitch (tone) of speech. Slide to left for lower speech; to the right for higher.</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>advancedOptions</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>&amp;Advanced Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>hadifixBinLabel</cstring>
+ </property>
+ <property name="text">
+ <string>txt2pho e&amp;xecutable:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>hadifixURL</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If the txt2pho program is in your PATH environment variable, simply enter "txt2pho", otherwise specify the full path to the txt2pho executable program.</string>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="0" column="1">
+ <property name="name">
+ <cstring>hadifixURL</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If the txt2pho program is in your PATH environment variable, simply enter "txt2pho", otherwise specify the full path to the txt2pho executable program.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>mbrolaBinLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Mbrola executable:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mbrolaURL</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If the Mbrola program is in your PATH environment variable, simply enter "mbrola", otherwise specify the full path to the Mbrola executable program.</string>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="1" column="1">
+ <property name="name">
+ <cstring>mbrolaURL</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If the Mbrola program is in your PATH environment variable, simply enter "mbrola", otherwise specify the full path to the Mbrola executable program.</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>characterCodingLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Character &amp;encoding:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>characterCodingBox</cstring>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <property name="name">
+ <cstring>characterCodingBox</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This combo box specifies which character encoding is used for passing the text. For most western languages, use ISO-8859-1. For Hungarian, use ISO-8859-2.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+ </widget>
+ <spacer row="2" column="0">
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Preferred</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>240</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KPushButton" row="2" column="1">
+ <property name="name">
+ <cstring>testButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Test</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to test the configuration. You should hear a spoken sentence.</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>volumeBox</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>HadifixConfigUI</receiver>
+ <slot>volumeBox_valueChanged(int)</slot>
+ </connection>
+ <connection>
+ <sender>volumeSlider</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>HadifixConfigUI</receiver>
+ <slot>volumeSlider_valueChanged(int)</slot>
+ </connection>
+ <connection>
+ <sender>timeBox</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>HadifixConfigUI</receiver>
+ <slot>timeBox_valueChanged(int)</slot>
+ </connection>
+ <connection>
+ <sender>timeSlider</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>HadifixConfigUI</receiver>
+ <slot>timeSlider_valueChanged(int)</slot>
+ </connection>
+ <connection>
+ <sender>frequencyBox</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>HadifixConfigUI</receiver>
+ <slot>frequencyBox_valueChanged(int)</slot>
+ </connection>
+ <connection>
+ <sender>frequencySlider</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>HadifixConfigUI</receiver>
+ <slot>frequencySlider_valueChanged(int)</slot>
+ </connection>
+ <connection>
+ <sender>voiceCombo</sender>
+ <signal>activated(const QString&amp;)</signal>
+ <receiver>HadifixConfigUI</receiver>
+ <slot>changed(const QString&amp;)</slot>
+ </connection>
+ <connection>
+ <sender>volumeBox</sender>
+ <signal>valueChanged(const QString&amp;)</signal>
+ <receiver>HadifixConfigUI</receiver>
+ <slot>changed(const QString&amp;)</slot>
+ </connection>
+ <connection>
+ <sender>timeBox</sender>
+ <signal>valueChanged(const QString&amp;)</signal>
+ <receiver>HadifixConfigUI</receiver>
+ <slot>changed(const QString&amp;)</slot>
+ </connection>
+ <connection>
+ <sender>frequencyBox</sender>
+ <signal>valueChanged(const QString&amp;)</signal>
+ <receiver>HadifixConfigUI</receiver>
+ <slot>changed(const QString&amp;)</slot>
+ </connection>
+ <connection>
+ <sender>hadifixURL</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>HadifixConfigUI</receiver>
+ <slot>changed(const QString&amp;)</slot>
+ </connection>
+ <connection>
+ <sender>mbrolaURL</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>HadifixConfigUI</receiver>
+ <slot>changed(const QString&amp;)</slot>
+ </connection>
+</connections>
+<includes>
+ <include location="global" impldecl="in declaration">qradiobutton.h</include>
+ <include location="global" impldecl="in declaration">qpixmap.h</include>
+ <include location="global" impldecl="in declaration">kurl.h</include>
+ <include location="global" impldecl="in declaration">qmap.h</include>
+ <include location="global" impldecl="in implementation">kglobal.h</include>
+ <include location="global" impldecl="in implementation">qstringlist.h</include>
+ <include location="global" impldecl="in implementation">math.h</include>
+ <include location="global" impldecl="in implementation">kiconloader.h</include>
+</includes>
+<forwards>
+ <forward>class QStringList;</forward>
+</forwards>
+<variables>
+ <variable>QMap&lt;QString,int&gt; maleVoices;</variable>
+ <variable>QMap&lt;int,QString&gt; defaultVoices;</variable>
+ <variable>QPixmap female;</variable>
+ <variable>QPixmap male;</variable>
+ <variable>QMap&lt;QString,int&gt; femaleVoices;</variable>
+</variables>
+<signals>
+ <signal>changed(bool)</signal>
+</signals>
+<slots>
+ <slot access="protected" specifier="non virtual">volumeBox_valueChanged( int percentValue )</slot>
+ <slot access="protected" specifier="non virtual">timeBox_valueChanged( int percentValue )</slot>
+ <slot access="protected" specifier="non virtual">frequencyBox_valueChanged( int percentValue )</slot>
+ <slot access="protected" specifier="non virtual">volumeSlider_valueChanged( int sliderValue )</slot>
+ <slot access="protected" specifier="non virtual">timeSlider_valueChanged( int sliderValue )</slot>
+ <slot access="protected" specifier="non virtual">frequencySlider_valueChanged( int sliderValue )</slot>
+ <slot access="protected">changed( const QString &amp; )</slot>
+</slots>
+<functions>
+ <function access="protected" specifier="non virtual" returnType="int">percentToSlider( int percentValue )</function>
+ <function access="protected" specifier="non virtual" returnType="int">sliderToPercent( int sliderValue )</function>
+ <function access="private" specifier="non virtual">init()</function>
+ <function specifier="non virtual">addVoice( const QString &amp;filename, bool isMale )</function>
+ <function specifier="non virtual">addVoice( const QString &amp;filename, bool isMale, const QString &amp;displayname )</function>
+ <function specifier="non virtual">setVoice( const QString &amp;filename, bool isMale )</function>
+ <function specifier="non virtual" returnType="QString">getVoiceFilename()</function>
+ <function specifier="non virtual" returnType="bool">isMaleVoice()</function>
+</functions>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kcombobox.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kttsd/plugins/hadifix/hadifixconfigui.ui.h b/kttsd/plugins/hadifix/hadifixconfigui.ui.h
new file mode 100644
index 0000000..e3a7a04
--- /dev/null
+++ b/kttsd/plugins/hadifix/hadifixconfigui.ui.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename slots use Qt Designer which will
+** update this file, preserving your code. Create an init() slot in place of
+** a constructor, and a destroy() slot in place of a destructor.
+*****************************************************************************/
+
+// Basically the slider values are logarithmic (0,...,1000) whereas percent
+// values are linear (50%,...,200%).
+//
+// slider = alpha * (log(percent)-log(50))
+// with alpha = 1000/(log(200)-log(50))
+
+int HadifixConfigUI::percentToSlider (int percentValue) {
+ double alpha = 1000 / (log(200) - log(50));
+ return (int)floor (0.5 + alpha * (log(percentValue)-log(50)));
+}
+
+int HadifixConfigUI::sliderToPercent (int sliderValue) {
+ double alpha = 1000 / (log(200) - log(50));
+ return (int)floor(0.5 + exp (sliderValue/alpha + log(50)));
+}
+
+void HadifixConfigUI::volumeBox_valueChanged (int percentValue) {
+ volumeSlider->setValue (percentToSlider (percentValue));
+}
+
+void HadifixConfigUI::timeBox_valueChanged (int percentValue) {
+ timeSlider->setValue (percentToSlider (percentValue));
+}
+
+void HadifixConfigUI::frequencyBox_valueChanged (int percentValue) {
+ frequencySlider->setValue (percentToSlider (percentValue));
+}
+
+void HadifixConfigUI::volumeSlider_valueChanged (int sliderValue) {
+ volumeBox->setValue (sliderToPercent (sliderValue));
+}
+
+void HadifixConfigUI::timeSlider_valueChanged (int sliderValue) {
+ timeBox->setValue (sliderToPercent (sliderValue));
+}
+
+void HadifixConfigUI::frequencySlider_valueChanged (int sliderValue) {
+ frequencyBox->setValue (sliderToPercent (sliderValue));
+}
+
+void HadifixConfigUI::init () {
+ male = KGlobal::iconLoader()->loadIcon("male", KIcon::Small);
+ female = KGlobal::iconLoader()->loadIcon("female", KIcon::Small);
+}
+
+void HadifixConfigUI::addVoice (const QString &filename, bool isMale) {
+ if (isMale) {
+ if (!maleVoices.contains(filename)) {
+ int id = voiceCombo->count();
+ maleVoices.insert (filename, id);
+ voiceCombo->insertItem (male, filename, id);
+ }
+ }
+ else {
+ if (!femaleVoices.contains(filename)) {
+ int id = voiceCombo->count();
+ femaleVoices.insert (filename, id);
+ voiceCombo->insertItem (female, filename, id);
+ }
+ }
+}
+
+void HadifixConfigUI::addVoice (const QString &filename, bool isMale, const QString &displayname) {
+ addVoice (filename, isMale);
+
+ if (isMale) {
+ defaultVoices [maleVoices [filename]] = filename;
+ voiceCombo->changeItem (male, displayname, maleVoices [filename]);
+ }
+ else{
+ defaultVoices [femaleVoices [filename]] = filename;
+ voiceCombo->changeItem (female, displayname, femaleVoices [filename]);
+ }
+}
+
+void HadifixConfigUI::setVoice (const QString &filename, bool isMale) {
+ addVoice (filename, isMale);
+ if (isMale)
+ voiceCombo->setCurrentItem (maleVoices[filename]);
+ else
+ voiceCombo->setCurrentItem (femaleVoices[filename]);
+}
+
+QString HadifixConfigUI::getVoiceFilename() {
+ int curr = voiceCombo->currentItem();
+
+ QString filename = voiceCombo->text(curr);
+ if (defaultVoices.contains(curr))
+ filename = defaultVoices[curr];
+
+ return filename;
+}
+
+bool HadifixConfigUI::isMaleVoice() {
+ int curr = voiceCombo->currentItem();
+ QString filename = getVoiceFilename();
+
+ if (maleVoices.contains(filename))
+ return maleVoices[filename] == curr;
+ else
+ return false;
+}
+
+void HadifixConfigUI::changed (const QString &) {
+ emit changed (true);
+}
diff --git a/kttsd/plugins/hadifix/hadifixplugin.cpp b/kttsd/plugins/hadifix/hadifixplugin.cpp
new file mode 100644
index 0000000..b52052c
--- /dev/null
+++ b/kttsd/plugins/hadifix/hadifixplugin.cpp
@@ -0,0 +1,23 @@
+/***************************************************************************
+ begin : Mon Okt 14 2002
+ copyright : (C) 2002 by Gunnar Schmi Dt
+ email : gunnar@schmi-dt.de
+ current mainainer: : 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. *
+ * *
+ ***************************************************************************/
+
+#include <kgenericfactory.h>
+
+#include "hadifixconf.h"
+#include "hadifixproc.h"
+
+typedef K_TYPELIST_2( HadifixProc, HadifixConf ) Hadifix;
+K_EXPORT_COMPONENT_FACTORY( libkttsd_hadifixplugin, KGenericFactory<Hadifix>("kttsd_hadifix") )
diff --git a/kttsd/plugins/hadifix/hadifixproc.cpp b/kttsd/plugins/hadifix/hadifixproc.cpp
new file mode 100644
index 0000000..42730b3
--- /dev/null
+++ b/kttsd/plugins/hadifix/hadifixproc.cpp
@@ -0,0 +1,411 @@
+/***************************************************************************
+ hadifixproc.cpp - description
+ -------------------
+ begin : Mon Okt 14 2002
+ copyright : (C) 2002 by Gunnar Schmi Dt
+ email : gunnar@schmi-dt.de
+ current mainainer: : 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. *
+ * *
+ ***************************************************************************/
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qtextcodec.h>
+
+#include <kdebug.h>
+#include <kconfig.h>
+#include <kprocess.h>
+#include <kstandarddirs.h>
+
+#include "hadifixproc.h"
+#include "hadifixproc.moc"
+
+class HadifixProcPrivate {
+ friend class HadifixProc;
+ private:
+ HadifixProcPrivate () {
+ hadifixProc = 0;
+ waitingStop = false;
+ state = psIdle;
+ synthFilename = QString::null;
+ gender = false;
+ volume = 100;
+ time = 100;
+ pitch = 100;
+ codec = 0;
+ };
+
+ ~HadifixProcPrivate() {
+ delete hadifixProc;
+ };
+
+ void load(KConfig *config, const QString &configGroup) {
+ config->setGroup(configGroup);
+ hadifix = config->readEntry ("hadifixExec", QString::null);
+ mbrola = config->readEntry ("mbrolaExec", QString::null);
+ voice = config->readEntry ("voice", QString::null);
+ gender = config->readBoolEntry("gender", false);
+ volume = config->readNumEntry ("volume", 100);
+ time = config->readNumEntry ("time", 100);
+ pitch = config->readNumEntry ("pitch", 100);
+ codec = PlugInProc::codecNameToCodec(config->readEntry ("codec", "Local"));
+ };
+
+ QString hadifix;
+ QString mbrola;
+ QString voice;
+ bool gender;
+ int volume;
+ int time;
+ int pitch;
+
+ bool waitingStop;
+ KShellProcess* hadifixProc;
+ volatile pluginState state;
+ QTextCodec* codec;
+ QString synthFilename;
+};
+
+/** Constructor */
+HadifixProc::HadifixProc( QObject* parent, const char* name, const QStringList &) :
+ PlugInProc( parent, name ){
+ // kdDebug() << "HadifixProc::HadifixProc: Running" << endl;
+ d = 0;
+}
+
+/** Destructor */
+HadifixProc::~HadifixProc(){
+ // kdDebug() << "HadifixProc::~HadifixProc: Running" << endl;
+
+ if (d != 0) {
+ delete d;
+ d = 0;
+ }
+}
+
+/** Initializate the speech */
+bool HadifixProc::init(KConfig *config, const QString &configGroup){
+ // kdDebug() << "HadifixProc::init: Initializing plug in: Hadifix" << endl;
+
+ if (d == 0)
+ d = new HadifixProcPrivate();
+ d->load(config, configGroup);
+ return true;
+}
+
+/**
+* Say a text. Synthesize and audibilize it.
+* @param text The text to be spoken.
+*
+* If the plugin supports asynchronous operation, it should return immediately
+* 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.
+*/
+void HadifixProc::sayText(const QString& /*text*/)
+{
+ kdDebug() << "HadifixProc::sayText: Warning, sayText not implemented." << endl;
+ return;
+}
+
+/**
+* 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.
+*/
+void HadifixProc::synthText(const QString &text, const QString &suggestedFilename)
+{
+ if (d == 0) return; // Caller should have called init.
+ synth(text, d->hadifix, d->gender, d->mbrola, d->voice, d->volume,
+ d->time, d->pitch, d->codec, suggestedFilename);
+}
+
+/**
+* Synthesize text using a specified configuration.
+* @param text The text to synthesize.
+* @param hadifix Command to run hadifix (txt2pho).
+* @param isMale True to use male voice.
+* @param mbrola Command to run mbrola.
+* @param voice Voice file for mbrola to use.
+* @param volume Volume percent. 100 = normal
+* @param time Speed percent. 100 = normal
+* @param pitch Frequency. 100 = normal
+* @param waveFilename Name of file to synthesize to.
+*/
+void HadifixProc::synth(QString text,
+ QString hadifix, bool isMale,
+ QString mbrola, QString voice,
+ int volume, int time, int pitch,
+ QTextCodec *codec,
+ const QString waveFilename)
+{
+ // kdDebug() << "HadifixProc::synth: Saying text: '" << text << "' using Hadifix plug in" << endl;
+ if (d == 0)
+ {
+ d = new HadifixProcPrivate();
+ }
+ if (hadifix.isNull() || hadifix.isEmpty())
+ return;
+ if (mbrola.isNull() || mbrola.isEmpty())
+ return;
+ if (voice.isNull() || voice.isEmpty())
+ return;
+
+ // If process exists, delete it so we can create a new one.
+ // kdDebug() << "HadifixProc::synth: creating process" << endl;
+ if (d->hadifixProc) delete d->hadifixProc;
+
+ // Create process.
+ d->hadifixProc = new KShellProcess;
+
+ // Set up txt2pho and mbrola commands.
+ // kdDebug() << "HadifixProc::synth: setting up commands" << endl;
+ QString hadifixCommand = d->hadifixProc->quote(hadifix);
+ if (isMale)
+ hadifixCommand += " -m";
+ else
+ hadifixCommand += " -f";
+
+ QString mbrolaCommand = d->hadifixProc->quote(mbrola);
+ mbrolaCommand += " -e"; //Ignore fatal errors on unkown diphone
+ mbrolaCommand += QString(" -v %1").arg(volume/100.0); // volume ratio
+ mbrolaCommand += QString(" -f %1").arg(pitch/100.0); // freqency ratio
+ mbrolaCommand += QString(" -t %1").arg(1/(time/100.0)); // time ratio
+ mbrolaCommand += " " + d->hadifixProc->quote(voice);
+ mbrolaCommand += " - " + d->hadifixProc->quote(waveFilename);
+
+ // kdDebug() << "HadifixProc::synth: Hadifix command: " << hadifixCommand << endl;
+ // kdDebug() << "HadifixProc::synth: Mbrola command: " << mbrolaCommand << endl;
+
+ QString command = hadifixCommand + "|" + mbrolaCommand;
+ *(d->hadifixProc) << command;
+
+ // Connect signals from process.
+ connect(d->hadifixProc, SIGNAL(processExited(KProcess *)),
+ this, SLOT(slotProcessExited(KProcess *)));
+ connect(d->hadifixProc, SIGNAL(wroteStdin(KProcess *)),
+ this, SLOT(slotWroteStdin(KProcess *)));
+
+ // Store off name of wave file to be generated.
+ d->synthFilename = waveFilename;
+ // Set state, busy synthing.
+ d->state = psSynthing;
+ if (!d->hadifixProc->start(KProcess::NotifyOnExit, KProcess::Stdin))
+ {
+ kdDebug() << "HadifixProc::synth: start process failed." << endl;
+ d->state = psIdle;
+ } else {
+ QCString encodedText;
+ if (codec) {
+ encodedText = codec->fromUnicode(text);
+ // kdDebug() << "HadifixProc::synth: encoding using " << codec->name() << endl;
+ } else
+ encodedText = text.latin1(); // Should not happen, but just in case.
+ // Send the text to be synthesized to process.
+ d->hadifixProc->writeStdin(encodedText, encodedText.length());
+ }
+}
+
+/**
+* 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.
+*/
+QString HadifixProc::getFilename() { return d->synthFilename; }
+
+/**
+* Stop current operation (saying or synthesizing text).
+* Important: This function may be called from a thread different from the
+* one that called sayText or synthText.
+* If the plugin cannot stop an in-progress @ref sayText or
+* @ref synthText operation, it must not block waiting for it to complete.
+* Instead, return immediately.
+*
+* If a plugin returns before the operation has actually been stopped,
+* the plugin must emit the @ref stopped signal when the operation has
+* actually stopped.
+*
+* The plugin should change to the psIdle state after stopping the
+* operation.
+*/
+void HadifixProc::stopText(){
+ // kdDebug() << "Running: HadifixProc::stopText()" << endl;
+ if (d->hadifixProc)
+ {
+ if (d->hadifixProc->isRunning())
+ {
+ // kdDebug() << "HadifixProc::stopText: killing Hadifix shell." << endl;
+ d->waitingStop = true;
+ d->hadifixProc->kill();
+ } else d->state = psIdle;
+ } else d->state = psIdle;
+ // d->state = psIdle;
+ // kdDebug() << "HadifixProc::stopText: Hadifix stopped." << endl;
+}
+
+/**
+* Return the current state of the plugin.
+* This function only makes sense in asynchronous mode.
+* @return The pluginState of the plugin.
+*
+* @see pluginState
+*/
+pluginState HadifixProc::getState() { return d->state; }
+
+/**
+* Acknowledges a finished state and resets the plugin state to psIdle.
+*
+* If the plugin is not in state psFinished, nothing happens.
+* The plugin may use this call to do any post-processing cleanup,
+* for example, blanking the stored filename (but do not delete the file).
+* Calling program should call getFilename prior to ackFinished.
+*/
+void HadifixProc::ackFinished()
+{
+ if (d->state == psFinished)
+ {
+ d->state = psIdle;
+ d->synthFilename = QString::null;
+ }
+}
+
+/**
+* Returns True if the plugin supports asynchronous processing,
+* i.e., returns immediately from sayText or synthText.
+* @return True if this plugin supports asynchronous processing.
+*
+* If the plugin returns True, it must also implement @ref getState .
+* It must also emit @ref sayFinished or @ref synthFinished signals when
+* saying or synthesis is completed.
+*/
+bool HadifixProc::supportsAsync() { return true; }
+
+/**
+* Returns True if the plugin supports synthText method,
+* i.e., is able to synthesize text to a sound file without
+* audibilizing the text.
+* @return True if this plugin supports synthText method.
+*
+* 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 .
+*/
+bool HadifixProc::supportsSynth() { return true; }
+
+
+void HadifixProc::slotProcessExited(KProcess*)
+{
+ // kdDebug() << "HadifixProc:hadifixProcExited: Hadifix process has exited." << endl;
+ pluginState prevState = d->state;
+ if (d->waitingStop)
+ {
+ d->waitingStop = false;
+ d->state = psIdle;
+ emit stopped();
+ } else {
+ d->state = psFinished;
+ if (prevState == psSynthing)
+ emit synthFinished();
+ }
+}
+
+void HadifixProc::slotWroteStdin(KProcess*)
+{
+ // kdDebug() << "HadifixProc::slotWroteStdin: closing Stdin" << endl;
+ d->hadifixProc->closeStdin();
+}
+
+
+/***************************************************************************/
+
+/**
+* Static function to determine whether the voice file is male or female.
+* @param mbrola the mbrola executable
+* @param voice the voice file
+* @param output the output of mbrola will be written into this QString*
+* @return HadifixSpeech::MaleGender if the voice is male,
+* HadifixSpeech::FemaleGender if the voice is female,
+* HadifixSpeech::NoGender if the gender cannot be determined,
+* HadifixSpeech::NoVoice if there is an error in the voice file
+*/
+HadifixProc::VoiceGender HadifixProc::determineGender(QString mbrola, QString voice, QString *output)
+{
+ QString command = mbrola + " -i " + voice + " - -";
+
+ // create a new process
+ HadifixProc speech;
+ KShellProcess proc;
+ proc << command;
+ connect(&proc, SIGNAL(receivedStdout(KProcess *, char *, int)),
+ &speech, SLOT(receivedStdout(KProcess *, char *, int)));
+ connect(&proc, SIGNAL(receivedStderr(KProcess *, char *, int)),
+ &speech, SLOT(receivedStderr(KProcess *, char *, int)));
+
+ speech.stdOut = QString::null;
+ speech.stdErr = QString::null;
+ proc.start (KProcess::Block, KProcess::AllOutput);
+
+ VoiceGender result;
+ if (!speech.stdErr.isNull() && !speech.stdErr.isEmpty()) {
+ if (output != 0)
+ *output = speech.stdErr;
+ result = NoVoice;
+ }
+ else {
+ if (output != 0)
+ *output = speech.stdOut;
+ if (speech.stdOut.contains("female", false))
+ result = FemaleGender;
+ else if (speech.stdOut.contains("male", false))
+ result = MaleGender;
+ else
+ result = NoGender;
+ }
+
+ return result;
+}
+
+void HadifixProc::receivedStdout (KProcess *, char *buffer, int buflen) {
+ stdOut += QString::fromLatin1(buffer, buflen);
+}
+
+void HadifixProc::receivedStderr (KProcess *, char *buffer, int buflen) {
+ stdErr += QString::fromLatin1(buffer, buflen);
+}
+
+/**
+ * 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 HadifixProc::getSsmlXsltFilename()
+{
+ return KGlobal::dirs()->resourceDirs("data").last() + "kttsd/hadifix/xslt/SSMLtoTxt2pho.xsl";
+}
diff --git a/kttsd/plugins/hadifix/hadifixproc.h b/kttsd/plugins/hadifix/hadifixproc.h
new file mode 100644
index 0000000..225c7ed
--- /dev/null
+++ b/kttsd/plugins/hadifix/hadifixproc.h
@@ -0,0 +1,203 @@
+/***************************************************************************
+ hadifixproc.h - description
+ -------------------
+ begin : Mon Okt 14 2002
+ copyright : (C) 2002 by Gunnar Schmi Dt
+ email : gunnar@schmi-dt.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 _HADIFIXPROC_H_
+#define _HADIFIXPROC_H_
+
+#include <qstringlist.h>
+
+#include <pluginproc.h>
+
+class KProcess;
+
+class HadifixProcPrivate;
+class HadifixProc : public PlugInProc{
+ Q_OBJECT
+
+ public:
+ enum VoiceGender {
+ MaleGender = 2,
+ FemaleGender = 1,
+ NoGender = 0,
+ NoVoice = -1
+ };
+
+ /** Constructor */
+ HadifixProc( QObject* parent = 0, const char* name = 0, const QStringList &args = QStringList());
+
+ /** Destructor */
+ ~HadifixProc();
+
+ /** Initializate the speech */
+ 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();
+
+ /**
+ * Synthesize text using a specified configuration.
+ * @param text The text to synthesize.
+ * @param hadifix Command to run hadifix (txt2pho).
+ * @param isMale True to use male voice.
+ * @param mbrola Command to run mbrola.
+ * @param voice Voice file for mbrola to use.
+ * @param volume Volume percent. 100 = normal
+ * @param time Speed percent. 100 = normal
+ * @param pitch Frequency. 100 = normal
+ * @param waveFilename Name of file to synthesize to.
+ */
+ void synth(QString text,
+ QString hadifix, bool isMale,
+ QString mbrola, QString voice,
+ int volume, int time, int pitch,
+ QTextCodec* codec,
+ const QString waveFilename);
+
+ /**
+ * Static function to determine whether the voice file is male or female.
+ * @param mbrola the mbrola executable
+ * @param voice the voice file
+ * @param output the output of mbrola will be written into this QString*
+ * @return HadifixSpeech::MaleGender if the voice is male,
+ * HadifixSpeech::FemaleGender if the voice is female,
+ * HadifixSpeech::NoGender if the gender cannot be determined,
+ * HadifixSpeech::NoVoice if there is an error in the voice file
+ */
+ static VoiceGender determineGender(QString mbrola, QString voice, QString *output = 0);
+
+ /**
+ * 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();
+
+ private slots:
+ void slotProcessExited(KProcess*);
+ void slotWroteStdin(KProcess*);
+
+ void receivedStdout (KProcess *, char *buffer, int buflen);
+ void receivedStderr (KProcess *, char *buffer, int buflen);
+
+ private:
+ HadifixProcPrivate *d;
+
+ QString stdOut;
+ QString stdErr;
+};
+
+#endif
diff --git a/kttsd/plugins/hadifix/initialconfig.h b/kttsd/plugins/hadifix/initialconfig.h
new file mode 100644
index 0000000..4fac632
--- /dev/null
+++ b/kttsd/plugins/hadifix/initialconfig.h
@@ -0,0 +1,164 @@
+
+/**
+ * Tries to find hadifix and mbrola by looking onto the hard disk. This is
+ * neccessary because both hadifix and mbrola do not have standard
+ * installation directories.
+ */
+void findInitialConfig() {
+ QString hadifixDataPath = findHadifixDataPath();
+
+ defaultHadifixExec = findExecutable("txt2pho", hadifixDataPath+"/../");
+
+ QStringList list; list += "mbrola"; list += "mbrola-linux-i386";
+ defaultMbrolaExec = findExecutable(list, hadifixDataPath+"/../../mbrola/");
+
+ defaultVoices = findVoices (defaultMbrolaExec, hadifixDataPath);
+}
+
+/** Tries to find the hadifix data path by looking into a number of files. */
+QString findHadifixDataPath () {
+ QStringList files;
+ files += "/etc/txt2pho";
+ files += QDir::homeDirPath()+"/.txt2phorc";
+
+ QStringList::iterator it;
+ for (it = files.begin(); it != files.end(); ++it) {
+
+ QFile file(*it);
+ if ( file.open(IO_ReadOnly) ) {
+ QTextStream stream(&file);
+
+ while (!stream.atEnd()) {
+ QString s = stream.readLine().stripWhiteSpace();
+ // look for a line "DATAPATH=..."
+
+ if (s.startsWith("DATAPATH")) {
+ s = s.mid(8, s.length()-8).stripWhiteSpace();
+ if (s.startsWith("=")) {
+ s = s.mid(1, s.length()-1).stripWhiteSpace();
+ if (s.startsWith("/"))
+ return s;
+ else {
+ QFileInfo info (QFileInfo(*it).dirPath() + "/" + s);
+ return info.absFilePath();
+ }
+ }
+ }
+ }
+ file.close();
+ }
+ }
+ return "/usr/local/txt2pho/";
+}
+
+/** Tries to find the an executable by looking onto the hard disk. */
+QString findExecutable (const QStringList &names, const QString &possiblePath) {
+ // a) Try to find it directly
+ QStringList::ConstIterator it;
+ QStringList::ConstIterator itEnd = names.constEnd();
+ for (it = names.constBegin(); it != itEnd; ++it) {
+ QString executable = KStandardDirs::findExe (*it);
+ if (!executable.isNull() && !executable.isEmpty())
+ return executable;
+ }
+
+ // b) Try to find it in the path specified by the second parameter
+ for (it = names.constBegin(); it != itEnd; ++it) {
+ QFileInfo info (possiblePath+*it);
+ if (info.exists() && info.isExecutable() && info.isFile()) {
+ return info.absFilePath();
+ }
+ }
+
+ // Both tries failed, so the user has to locate the executable.
+ return QString::null;
+}
+
+/** Tries to find the voice files by looking onto the hard disk. */
+QStringList findVoices(QString mbrolaExec, const QString &hadifixDataPath) {
+
+ // First of all:
+ // dereference links to the mbrola executable (if mbrolaExec is a link).
+ for (int i = 0; i < 10; ++i) {
+ // If we have a chain of more than ten links something is surely wrong.
+ QFileInfo info (mbrolaExec);
+ if (info.exists() && info.isSymLink())
+ mbrolaExec = info.readLink();
+ }
+
+ // Second:
+ // create a list of directories that possibly contain voice files
+ QStringList list;
+
+ // 2a) search near the mbrola executable
+ QFileInfo info (mbrolaExec);
+ if (info.exists() && info.isFile() && info.isExecutable()) {
+ QString mbrolaPath = info.dirPath (true);
+ list += mbrolaPath;
+ }
+
+ // 2b) search near the hadifix data path
+ info.setFile(hadifixDataPath + "../../mbrola");
+ QString mbrolaPath = info.dirPath (true) + "/mbrola";
+ if (!list.contains(mbrolaPath))
+ list += mbrolaPath;
+
+ // 2c) broaden the search by adding subdirs (with a depth of 2)
+ QStringList subDirs = findSubdirs (list);
+ QStringList subSubDirs = findSubdirs (subDirs);
+ list += subDirs;
+ list += subSubDirs;
+
+ // Third:
+ // look into each of these directories and search for voice files.
+ QStringList result;
+ QStringList::iterator it;
+ for (it = list.begin(); it != list.end(); ++it) {
+ QDir baseDir (*it, QString::null,
+ QDir::Name|QDir::IgnoreCase, QDir::Files);
+ QStringList files = baseDir.entryList();
+
+ QStringList::iterator iter;
+ for (iter = files.begin(); iter != files.end(); ++iter) {
+ // Voice files start with "MBROLA", but are afterwards binary files
+ QString filename = *it + "/" + *iter;
+ QFile file (filename);
+ if (file.open(IO_ReadOnly)) {
+ QTextStream stream(&file);
+ if (!stream.atEnd()) {
+ QString s = stream.readLine();
+ if (s.startsWith("MBROLA"))
+ if (HadifixProc::determineGender(mbrolaExec, filename)
+ != HadifixProc::NoVoice
+ )
+ result += filename;
+ file.close();
+ }
+ }
+ }
+ }
+ return result;
+}
+
+/** Returns a list of subdirs (with absolute paths) */
+QStringList findSubdirs (const QStringList &baseDirs) {
+ QStringList result;
+
+ QStringList::ConstIterator it;
+ QStringList::ConstIterator itEnd = baseDirs.constEnd();
+ for (it = baseDirs.constBegin(); it != itEnd; ++it) {
+ // a) get a list of directory names
+ QDir baseDir (*it, QString::null,
+ QDir::Name|QDir::IgnoreCase, QDir::Dirs);
+ QStringList list = baseDir.entryList();
+
+ // b) produce absolute paths
+ QStringList::ConstIterator iter;
+ QStringList::ConstIterator iterEnd = list.constEnd();
+ for (iter = list.constBegin(); iter != iterEnd; ++iter) {
+ if ((*iter != ".") && (*iter != ".."))
+ result += *it + "/" + *iter;
+ }
+ }
+ return result;
+}
diff --git a/kttsd/plugins/hadifix/kttsd_hadifixplugin.desktop b/kttsd/plugins/hadifix/kttsd_hadifixplugin.desktop
new file mode 100644
index 0000000..2db3e1c
--- /dev/null
+++ b/kttsd/plugins/hadifix/kttsd_hadifixplugin.desktop
@@ -0,0 +1,51 @@
+[Desktop Entry]
+Name=Hadifix
+Name[ne]=ह्याडिफिक्स
+Comment=German hadifix text-to-speech system
+Comment[bg]=Синтезатор на глас за немски език Hadifix
+Comment[ca]=Sistema de text a veu alemany hadifix
+Comment[cs]=Německý systém hlasové syntézy hadifix
+Comment[da]=Tysk hadifix tekst-til-tale system
+Comment[de]=Deutsches hadifix-Sprachausgabesystem
+Comment[el]=Σύστημα κειμένου-σε-ομιλία γερμανικού hadifix
+Comment[es]=Sintetizador de texto a voz en alemán hadifix
+Comment[et]=Saksa teksti kõneks muutmise süsteem Hadifix
+Comment[eu]=Alemanierazko hadifix testutik hizketarako sistema
+Comment[fa]=سیستم متن به گفتار hadifix آلمانی
+Comment[fi]=Saksalainen hadifix teksti puheeksi -systeemi
+Comment[fr]=Système allemand de synthèse vocale hadifix
+Comment[ga]=Córas téacs-go-caint Gearmáinise hadifix
+Comment[gl]=Sistema alemao de texto-para-fala hadifix
+Comment[hu]=Hadifix szövegfelolvasó (Mbrola-alapú)
+Comment[is]=Þýska hadifix texti-í-tal kerfið
+Comment[it]=Sistema di pronuncia tedesca Hadifix
+Comment[ja]=ドイツ語 hadifix テキスト読み上げシステム
+Comment[ka]=გერმანული hadifix ტექსტის გახმოვანების სისტემა
+Comment[km]=ប្រព័ន្ធ​អត្ថបទ​ដែល​ត្រូវ​និយាយ​ hadifix អាល្លឺម៉ង់
+Comment[mk]=Германски hadifix систем за текст-во-говор
+Comment[ms]=Sistem teks-ke-tutur hadifiks Jerman
+Comment[mt]=Sistema test-għal-vuċi hadifix Ġermaniż
+Comment[nb]=Tysk hadifix system for tekst-til-tale
+Comment[nds]=Düütsch Blicksnuut Hadifix
+Comment[ne]=जर्मनी ह्याडिफिक्स पाठ वाचक प्रणाली
+Comment[nl]=Duits hadifix tekst-tot-spraak-systeem
+Comment[pa]=ਜਰਮਨ hadifix ਪਾਠ ਤੋਂ ਬੋਲੀ ਸਿਸਟਮ
+Comment[pl]=Niemiecki system syntezy mowy hadifix
+Comment[pt]=Sistema alemão de texto-para-voz hadifix
+Comment[pt_BR]=Sistema de conversão de texto para áudio almeão hadifix
+Comment[ru]=Немецкая система синтеза речи Hadifix
+Comment[sk]=Nemecký systém text-na-reč hadifix
+Comment[sl]=Nemški sistem besedila v govor hadifix
+Comment[sr]=Hadifix, немачки систем за текст-у-говор
+Comment[sr@Latn]=Hadifix, nemački sistem za tekst-u-govor
+Comment[sv]=Tyska Hadifix text-till-tal system
+Comment[ta]=ஜெர்மன் ஹாடிஃபிக்ஸ் உரையில் இருந்து பேச்சு அமைப்பு
+Comment[tg]=Системаи олмонии таҳлили овози hadifix
+Comment[tr]=Alman hadifix metinden konuşmaya sistemi
+Comment[uk]=Німецька система синтезу мовлення hadifix
+Comment[vi]=Hệ thống tổng hợp tiếng nói Đức hadifix
+Comment[zh_TW]=德語 hadifix 文字轉語音系統
+Type=Service
+ServiceTypes=KTTSD/SynthPlugin
+X-KDE-Library=libkttsd_hadifixplugin
+X-KDE-Languages=de,hu
diff --git a/kttsd/plugins/hadifix/voicefileui.ui b/kttsd/plugins/hadifix/voicefileui.ui
new file mode 100644
index 0000000..6c038f7
--- /dev/null
+++ b/kttsd/plugins/hadifix/voicefileui.ui
@@ -0,0 +1,119 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>VoiceFileWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>VoiceFileWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>452</width>
+ <height>117</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Selecting Voice File</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>voiceFileLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Path of the voice file:</string>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="0" column="1">
+ <property name="name">
+ <cstring>voiceFileURL</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ </widget>
+ <widget class="QButtonGroup" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>genderOption</cstring>
+ </property>
+ <property name="title">
+ <string>Gender</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QRadioButton" row="0" column="0">
+ <property name="name">
+ <cstring>femaleOption</cstring>
+ </property>
+ <property name="text">
+ <string>Female</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="0" column="1">
+ <property name="name">
+ <cstring>maleOption</cstring>
+ </property>
+ <property name="text">
+ <string>Male</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="0" column="2">
+ <property name="name">
+ <cstring>genderButton</cstring>
+ </property>
+ <property name="text">
+ <string>Try to Determine From Voice File</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>genderButton</sender>
+ <signal>clicked()</signal>
+ <receiver>VoiceFileWidget</receiver>
+ <slot>genderButton_clicked()</slot>
+ </connection>
+</connections>
+<includes>
+ <include location="global" impldecl="in implementation">kurlrequesterdlg.h</include>
+ <include location="global" impldecl="in implementation">kmessagebox.h</include>
+ <include location="local" impldecl="in implementation">hadifixproc.h</include>
+ <include location="local" impldecl="in implementation">voicefileui.ui.h</include>
+</includes>
+<variables>
+ <variable access="public">QString mbrola;</variable>
+</variables>
+<slots>
+ <slot>genderButton_clicked()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kttsd/plugins/hadifix/voicefileui.ui.h b/kttsd/plugins/hadifix/voicefileui.ui.h
new file mode 100644
index 0000000..9d37375
--- /dev/null
+++ b/kttsd/plugins/hadifix/voicefileui.ui.h
@@ -0,0 +1,35 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename functions or slots use
+** Qt Designer which will update this file, preserving your code. Create an
+** init() function in place of a constructor, and a destroy() function in
+** place of a destructor.
+*****************************************************************************/
+
+
+void VoiceFileWidget::genderButton_clicked()
+{
+ HadifixProc::VoiceGender gender;
+ QString details;
+ gender = HadifixProc::determineGender(mbrola, voiceFileURL->url(), &details);
+
+ if (gender == HadifixProc::MaleGender) {
+ maleOption->setChecked (true);
+ femaleOption->setChecked (false);
+ }
+ else if (gender == HadifixProc::FemaleGender) {
+ maleOption->setChecked (false);
+ femaleOption->setChecked (true);
+ }
+ else if (gender == HadifixProc::NoGender) {
+ KMessageBox::sorry (this,
+ i18n("The gender of the voice file %1 could not be detected.").arg(voiceFileURL->url()),
+ i18n("Trying to Determine the Gender - Hadifix Plug In"));
+ }
+ else {
+ KMessageBox::detailedSorry (this,
+ i18n("The file %1 does not seem to be a voice file.").arg(voiceFileURL->url()),
+ details, i18n("Trying to Determine the Gender - Hadifix Plug In"));
+ }
+}