summaryrefslogtreecommitdiffstats
path: root/kttsd/filters/sbd
diff options
context:
space:
mode:
Diffstat (limited to 'kttsd/filters/sbd')
-rw-r--r--kttsd/filters/sbd/Makefile.am27
-rw-r--r--kttsd/filters/sbd/kttsd_sbdplugin.desktop91
-rw-r--r--kttsd/filters/sbd/polish_festival_sbdrc6
-rw-r--r--kttsd/filters/sbd/sbdconf.cpp341
-rw-r--r--kttsd/filters/sbd/sbdconf.h139
-rw-r--r--kttsd/filters/sbd/sbdconfwidget.ui363
-rw-r--r--kttsd/filters/sbd/sbdplugin.cpp33
-rw-r--r--kttsd/filters/sbd/sbdproc.cpp784
-rw-r--r--kttsd/filters/sbd/sbdproc.h366
-rw-r--r--kttsd/filters/sbd/standard_sbdrc6
10 files changed, 2156 insertions, 0 deletions
diff --git a/kttsd/filters/sbd/Makefile.am b/kttsd/filters/sbd/Makefile.am
new file mode 100644
index 0000000..4faa8a9
--- /dev/null
+++ b/kttsd/filters/sbd/Makefile.am
@@ -0,0 +1,27 @@
+INCLUDES = \
+ -I$(top_srcdir)/kttsd/libkttsd -I$(top_builddir)/kttsd/libkttsd \
+ $(all_includes)
+
+METASOURCES = AUTO
+
+kde_module_LTLIBRARIES = libkttsd_sbdplugin.la
+
+libkttsd_sbdplugin_la_SOURCES = \
+ sbdproc.cpp \
+ sbdconf.cpp \
+ sbdconfwidget.ui \
+ sbdplugin.cpp
+
+libkttsd_sbdplugin_la_LDFLAGS = $(KDE_PLUGIN) $(all_libraries)
+libkttsd_sbdplugin_la_LIBADD = $(top_builddir)/kttsd/libkttsd/libkttsd.la
+
+services_DATA = kttsd_sbdplugin.desktop
+servicesdir = $(kde_servicesdir)
+
+# Install data files.
+sbddatadir = $(kde_datadir)/kttsd/sbd/
+sbddata_DATA = \
+ standard_sbdrc \
+ polish_festival_sbdrc
+
+noinst_HEADERS = sbdproc.h
diff --git a/kttsd/filters/sbd/kttsd_sbdplugin.desktop b/kttsd/filters/sbd/kttsd_sbdplugin.desktop
new file mode 100644
index 0000000..0e8a483
--- /dev/null
+++ b/kttsd/filters/sbd/kttsd_sbdplugin.desktop
@@ -0,0 +1,91 @@
+[Desktop Entry]
+Name=Sentence Boundary Detector
+Name[bg]=Детектор на изречения
+Name[ca]=Detector de límit de frase
+Name[cs]=Zjištění vět
+Name[da]=Grænsedetektor for sætninger
+Name[de]=Satzgrenzenerkennung
+Name[el]=Ανιχνευτής ορίων πρότασης
+Name[es]=Detector de final de frase
+Name[et]=Lausepiiri tuvastaja
+Name[eu]=Esaldien mugen detektorea
+Name[fa]=آشکارساز کرانۀ جمله
+Name[fi]=Lauserajojen tunnistin
+Name[fr]=Détecteur de fins de phrases
+Name[ga]=Brathadóir Teorainneacha Abairtí
+Name[gl]=Detector de Límite de Frases
+Name[hu]=Mondathatár-detektáló
+Name[is]=Setningamarka greinir
+Name[it]=Rilevatore del limite delle frasi
+Name[ja]=センテンスの境界検出
+Name[ka]=წინადადების საზღვრის დამდგენი
+Name[km]= ឧបករណ៍​ចាប់​ព្រំដែន​ប្រយោគ
+Name[lt]=Sakinio ribų aptikiklis
+Name[mk]=Детектор на граница на реченица
+Name[ms]=Pengesan Sempadan Ayat
+Name[nb]=Finner setningsgrenser
+Name[nds]=Satzgrenz-Faststellen
+Name[ne]=वाक्य सीमा संसूचक
+Name[nl]=Zingrensdetectie
+Name[pl]=Wykrywanie granicy zdania
+Name[pt]=Detector de Limite de Frase
+Name[pt_BR]=Detector de Fim de Sentenças
+Name[ru]=Определитель границ предложения
+Name[sk]=Detekcia okraja vety
+Name[sl]=Detektor meje stavkov
+Name[sr]=Детектор граница реченице
+Name[sr@Latn]=Detektor granica rečenice
+Name[sv]=Meningsgränsdetektering
+Name[ta]=வாக்கிய பவுண்டரி கண்டுபிடிப்பான்
+Name[tg]=Муайянсозии ҳудуди ҷумлаҳо
+Name[tr]=Cümle Sınırlama Algılayıcı
+Name[uk]=Виявлення меж речень
+Name[vi]=Trình nhận dạng Ranh giới Câu văn
+Name[zh_CN]=语句分界检测器
+Name[zh_TW]=句子界限偵測器
+Comment=Sentence Boundary Detection Filter Plugin for KTTS
+Comment[bg]=Приставка филтър за определяне границите на изречението
+Comment[ca]=Connector del filtre detector de límit de frase pel KTTS
+Comment[cs]=Modul filtru zjišťování vět KTTS
+Comment[da]=Grænsedetektor for sætninger, filter-plugin for KTTS
+Comment[de]=Generisches KTTS-Modul zur Erkennung von Satzgrenzen
+Comment[el]=Φίλτρο πρόσθετο ανίχνευσης ορίων πρότασης για το KTTS
+Comment[es]=Complemento de filtrado que detecta el final de las frases para KTTS
+Comment[et]=KTTS-i lausepiiri tuvastamise filtri plugin
+Comment[eu]=KTTS-ren esaldien mugen detektorearen iragazkien plugina
+Comment[fa]=وصلۀ پالایۀ آشکارسازی کرانۀ جمله برای KTTS
+Comment[fi]=Lauserajojen tunnistussuodatin liitännäinen KTTS-ohjelmalle
+Comment[fr]=Module de filtrage de fins de phrases pour KTTS
+Comment[gl]=Plugin de Filtro Detector de Límite de Frases para KTTS
+Comment[hu]=Mondatvég-detektáló szűrőmodul a KTTS-hez
+Comment[is]=Setningamarka greinasía fyrir KTTS
+Comment[it]=Plugin per il filtro di rilevazione del limite delle frasi per KTTS
+Comment[ja]=KTTS 用センテンスの境界検出フィルタプラグイン
+Comment[ka]=წინადადების საზღვრის დამდგენის ფილტრის მოდული KTTS-სთვის
+Comment[km]=កម្មវិធី​ជំនួយ​តម្រង​ឧបករណ៍​ចាប់​ព្រំដែន​ប្រយោគ​សម្រាប់ KTTS
+Comment[lt]=Sakinio ribų aptikiklio filtro priedas, skirtas KTTS
+Comment[mk]=Филтер за KTTS за детекција на границата на реченица
+Comment[ms]=Plugin Penapis Pengesan Sempadan Ayat bagi KTTS
+Comment[nb]=Programtillegg for KTTS som finner setningsgrenser
+Comment[nds]=KTTS-Filtermoduul för dat Faststellen vun Satzgrenzen
+Comment[ne]=KTTS का लागि वाक्य सीमा पत्ता लगाउने फिल्टर
+Comment[nl]=Zingrensdetectieplugin voor KTTS
+Comment[pl]=Wtyczka filtra wykrywania granicy zdania dla KTTS
+Comment[pt]='Plugin' de Filtro de Detecção de Limite de Frase para o KTTS
+Comment[pt_BR]=Plug-in de Detector de Fim de Sentenças para o KTTSD
+Comment[ru]=Фильтр границ предложения для KTTS
+Comment[sk]=Modul filtra na detekciu okraja vety v KTTS
+Comment[sl]=Filtrni vstavek KTTS za zanavanje mej stavka
+Comment[sr]=Филтерски прикључак KTTS-а за откривање граница реченице
+Comment[sr@Latn]=Filterski priključak KTTS-a za otkrivanje granica rečenice
+Comment[sv]=Insticksprogram för KTTS med filter för meningsgränsdetektering
+Comment[ta]=KTTSக்கான வாக்கிய கண்டறி அலங்கார சொருகுப்பொருள்
+Comment[tg]=Филтри ҳудуди ҷумлаҳо барои KTTS
+Comment[tr]=KTTS için Cümle Sınırlama Algılayıcı Süzgeci Eklentisi
+Comment[uk]=Втулок фільтра виявлення меж для KTTS
+Comment[vi]=Trình bổ sung Lọc Nhận dạng Ranh giới Câu văn cho KTTS
+Comment[zh_TW]=KTTS 使用的偵測句子是否已結束的外掛程式
+Type=Service
+ServiceTypes=KTTSD/FilterPlugin
+X-KDE-Library=libkttsd_sbdplugin
+X-KDE-Languages=en,en_US,en_GB,en_CA,es,es_mx,cy,de,fi,cs,pl
diff --git a/kttsd/filters/sbd/polish_festival_sbdrc b/kttsd/filters/sbd/polish_festival_sbdrc
new file mode 100644
index 0000000..ad64153
--- /dev/null
+++ b/kttsd/filters/sbd/polish_festival_sbdrc
@@ -0,0 +1,6 @@
+[Filter]
+AppID=
+LanguageCodes=pl
+SentenceBoundary=\\t
+SentenceDelimiterRegExp=([\\.\\?\\!\\:\\;])(\\s|$|(\\n *\\n))
+UserFilterName=Polish Sentence Boundary Detector
diff --git a/kttsd/filters/sbd/sbdconf.cpp b/kttsd/filters/sbd/sbdconf.cpp
new file mode 100644
index 0000000..2d4cf5d
--- /dev/null
+++ b/kttsd/filters/sbd/sbdconf.cpp
@@ -0,0 +1,341 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Sentence Boundary Detection Filter Configuration class.
+ -------------------
+ 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 <qfile.h>
+#include <qfileinfo.h>
+#include <qstring.h>
+#include <qhbox.h>
+#include <qlayout.h>
+#include <qdom.h>
+#include <qfile.h>
+#include <qradiobutton.h>
+
+// KDE includes.
+#include <kglobal.h>
+#include <klocale.h>
+#include <klistview.h>
+#include <klineedit.h>
+#include <kdialog.h>
+#include <kdialogbase.h>
+#include <kpushbutton.h>
+#include <kconfig.h>
+#include <kstandarddirs.h>
+#include <kregexpeditorinterface.h>
+#include <ktrader.h>
+#include <kparts/componentfactory.h>
+#include <kfiledialog.h>
+
+// KTTS includes.
+#include "filterconf.h"
+
+// SBD includes.
+#include "sbdconf.h"
+#include "sbdconf.moc"
+
+/**
+* Constructor
+*/
+SbdConf::SbdConf( QWidget *parent, const char *name, const QStringList& /*args*/) :
+ KttsFilterConf(parent, name)
+{
+ // kdDebug() << "SbdConf::SbdConf: Running" << endl;
+
+ // Create configuration widget.
+ QVBoxLayout *layout = new QVBoxLayout(this, KDialog::marginHint(),
+ KDialog::spacingHint(), "SbdConfigWidgetLayout");
+ layout->setAlignment (Qt::AlignTop);
+ m_widget = new SbdConfWidget(this, "SbdConfigWidget");
+ layout->addWidget(m_widget);
+
+ // Determine if kdeutils Regular Expression Editor is installed.
+ m_reEditorInstalled = !KTrader::self()->query("KRegExpEditor/KRegExpEditor").isEmpty();
+
+ m_widget->reButton->setEnabled( m_reEditorInstalled );
+ if ( m_reEditorInstalled )
+ connect( m_widget->reButton, SIGNAL(clicked()), this, SLOT(slotReButton_clicked()) );
+
+ connect( m_widget->reLineEdit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(configChanged()) );
+ connect( m_widget->sbLineEdit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(configChanged()) );
+ connect( m_widget->nameLineEdit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(configChanged()) );
+ connect( m_widget->appIdLineEdit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(configChanged()) );
+ connect(m_widget->languageBrowseButton, SIGNAL(clicked()),
+ this, SLOT(slotLanguageBrowseButton_clicked()));
+ connect(m_widget->loadButton, SIGNAL(clicked()),
+ this, SLOT(slotLoadButton_clicked()));
+ connect(m_widget->saveButton, SIGNAL(clicked()),
+ this, SLOT(slotSaveButton_clicked()));
+ connect(m_widget->clearButton, SIGNAL(clicked()),
+ this, SLOT(slotClearButton_clicked()));
+
+ // Set up defaults.
+ defaults();
+}
+
+/**
+* Destructor.
+*/
+SbdConf::~SbdConf(){
+ // kdDebug() << "SbdConf::~SbdConf: 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 SbdConf::load(KConfig* config, const QString& configGroup){
+ // kdDebug() << "SbdConf::load: Running" << endl;
+ config->setGroup( configGroup );
+ m_widget->nameLineEdit->setText(
+ config->readEntry("UserFilterName", m_widget->nameLineEdit->text()) );
+ m_widget->reLineEdit->setText(
+ config->readEntry("SentenceDelimiterRegExp", m_widget->reLineEdit->text()) );
+ m_widget->sbLineEdit->setText(
+ config->readEntry("SentenceBoundary", m_widget->sbLineEdit->text()) );
+ QStringList langCodeList = config->readListEntry("LanguageCodes");
+ if (!langCodeList.isEmpty())
+ m_languageCodeList = langCodeList;
+ QString language = "";
+ for ( uint ndx=0; ndx < m_languageCodeList.count(); ++ndx)
+ {
+ if (!language.isEmpty()) language += ",";
+ language += KGlobal::locale()->twoAlphaToLanguageName(m_languageCodeList[ndx]);
+ }
+ m_widget->languageLineEdit->setText(language);
+ m_widget->appIdLineEdit->setText(
+ config->readEntry("AppID", m_widget->appIdLineEdit->text()) );
+}
+
+/**
+* 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 SbdConf::save(KConfig* config, const QString& configGroup){
+ // kdDebug() << "SbdConf::save: Running" << endl;
+ config->setGroup( configGroup );
+ config->writeEntry("UserFilterName", m_widget->nameLineEdit->text() );
+ config->writeEntry("SentenceDelimiterRegExp", m_widget->reLineEdit->text() );
+ config->writeEntry("SentenceBoundary", m_widget->sbLineEdit->text() );
+ config->writeEntry("LanguageCodes", m_languageCodeList );
+ config->writeEntry("AppID", m_widget->appIdLineEdit->text().replace(" ", "") );
+}
+
+/**
+* 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 SbdConf::defaults(){
+ // kdDebug() << "SbdConf::defaults: Running" << endl;
+ m_widget->nameLineEdit->setText( i18n("Standard Sentence Boundary Detector") );
+ m_widget->reLineEdit->setText( "([\\.\\?\\!\\:\\;])(\\s|$|(\\n *\\n))" );
+ m_widget->sbLineEdit->setText( "\\1\\t" );
+ m_languageCodeList.clear();
+ m_widget->languageLineEdit->setText( "" );
+ m_widget->appIdLineEdit->setText( "" );
+ // kdDebug() << "SbdConf::defaults: Exiting" << 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 SbdConf::supportsMultiInstance() { return true; }
+
+/**
+ * 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 SbdConf::userPlugInName()
+{
+ if ( m_widget->reLineEdit->text().isEmpty() )
+ return QString::null;
+ else
+ return m_widget->nameLineEdit->text();
+}
+
+/**
+ * Returns True if this filter is a Sentence Boundary Detector.
+ * @return True if this filter is a SBD.
+ */
+bool SbdConf::isSBD() { return true; }
+
+void SbdConf::slotReButton_clicked()
+{
+ // Show Regular Expression Editor dialog if it is installed.
+ if ( !m_reEditorInstalled ) return;
+ QDialog *editorDialog =
+ KParts::ComponentFactory::createInstanceFromQuery<QDialog>( "KRegExpEditor/KRegExpEditor" );
+ if ( editorDialog )
+ {
+ // kdeutils was installed, so the dialog was found. Fetch the editor interface.
+ KRegExpEditorInterface *reEditor =
+ static_cast<KRegExpEditorInterface *>(editorDialog->qt_cast( "KRegExpEditorInterface" ) );
+ Q_ASSERT( reEditor ); // This should not fail!// now use the editor.
+ reEditor->setRegExp( m_widget->reLineEdit->text() );
+ int dlgResult = editorDialog->exec();
+ if ( dlgResult == QDialog::Accepted )
+ {
+ QString re = reEditor->regExp();
+ m_widget->reLineEdit->setText( re );
+ configChanged();
+ }
+ delete editorDialog;
+ } else return;
+}
+
+void SbdConf::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::Extended);
+ QStringList allLocales = KGlobal::locale()->allLanguagesTwoAlpha();
+ QString locale;
+ QString languageCode;
+ QString countryCode;
+ QString charSet;
+ QString language;
+ // Blank line so user can select no language.
+ QListViewItem* item = new KListViewItem(langLView, "", "");
+ if (m_languageCodeList.isEmpty()) item->setSelected(true);
+ const int allLocalesCount = allLocales.count();
+ for (int ndx=0; ndx < allLocalesCount; ++ndx)
+ {
+ locale = allLocales[ndx];
+ KGlobal::locale()->splitLocale(locale, languageCode, countryCode, charSet);
+ language = KGlobal::locale()->twoAlphaToLanguageName(languageCode);
+ if (!countryCode.isEmpty()) language +=
+ " (" + KGlobal::locale()->twoAlphaToCountryName(countryCode)+")";
+ QListViewItem* item = new KListViewItem(langLView, language, locale);
+ if (m_languageCodeList.contains(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);
+ int dlgResult = dlg->exec();
+ languageCode = QString::null;
+ if (dlgResult == QDialog::Accepted)
+ {
+ m_languageCodeList.clear();
+ QListViewItem* item = langLView->firstChild();
+ while (item)
+ {
+ if (item->isSelected()) m_languageCodeList += item->text(1);
+ item = item->nextSibling();
+ }
+ }
+ delete dlg;
+ // TODO: Also delete KListView and QHBox?
+ if (dlgResult != QDialog::Accepted) return;
+ language = "";
+ for ( uint ndx=0; ndx < m_languageCodeList.count(); ++ndx)
+ {
+ if (!language.isEmpty()) language += ",";
+ language += KGlobal::locale()->twoAlphaToLanguageName(m_languageCodeList[ndx]);
+ }
+ m_widget->languageLineEdit->setText(language);
+ configChanged();
+}
+
+void SbdConf::slotLoadButton_clicked()
+{
+ // QString dataDir = KGlobal::dirs()->resourceDirs("data").last() + "/kttsd/stringreplacer/";
+ QString dataDir = KGlobal::dirs()->findAllResources("data", "kttsd/sbd/").last();
+ QString filename = KFileDialog::getOpenFileName(
+ dataDir,
+ "*rc|SBD Config (*rc)",
+ m_widget,
+ "sbd_loadfile");
+ if ( filename.isEmpty() ) return;
+ KConfig* cfg = new KConfig( filename, true, false, 0 );
+ load( cfg, "Filter" );
+ delete cfg;
+ configChanged();
+}
+
+void SbdConf::slotSaveButton_clicked()
+{
+ QString filename = KFileDialog::getSaveFileName(
+ KGlobal::dirs()->saveLocation( "data" ,"kttsd/sbd/", false ),
+ "*rc|SBD Config (*rc)",
+ m_widget,
+ "sbd_savefile");
+ if ( filename.isEmpty() ) return;
+ KConfig* cfg = new KConfig( filename, false, false, 0 );
+ save( cfg, "Filter" );
+ delete cfg;
+}
+
+void SbdConf::slotClearButton_clicked()
+{
+ m_widget->nameLineEdit->setText( QString::null );
+ m_widget->reLineEdit->setText( QString::null );
+ m_widget->sbLineEdit->setText( QString::null );
+ m_languageCodeList.clear();
+ m_widget->languageLineEdit->setText( QString::null );
+ m_widget->appIdLineEdit->setText( QString::null );
+ configChanged();
+}
diff --git a/kttsd/filters/sbd/sbdconf.h b/kttsd/filters/sbd/sbdconf.h
new file mode 100644
index 0000000..131a759
--- /dev/null
+++ b/kttsd/filters/sbd/sbdconf.h
@@ -0,0 +1,139 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Standard Sentence Boundary Detection Filter Configuration class.
+ -------------------
+ 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 _SBDCONF_H_
+#define _SBDCONF_H_
+
+// Qt includes.
+#include <qwidget.h>
+
+// KDE includes.
+#include <kconfig.h>
+#include <kdebug.h>
+
+// KTTS includes.
+#include "filterconf.h"
+
+// SBD includes.
+#include "sbdconfwidget.h"
+
+class KDialogBase;
+class EditReplacementWidget;
+
+class SbdConf : public KttsFilterConf
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Constructor
+ */
+ SbdConf( QWidget *parent, const char *name, const QStringList &args = QStringList() );
+
+ /**
+ * Destructor
+ */
+ virtual ~SbdConf();
+
+ /**
+ * 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();
+
+ private slots:
+ void slotReButton_clicked();
+ void slotLanguageBrowseButton_clicked();
+ void slotLoadButton_clicked();
+ void slotSaveButton_clicked();
+ void slotClearButton_clicked();
+
+ private:
+
+ // Configuration Widget.
+ SbdConfWidget* m_widget;
+ // True if kdeutils Regular Expression Editor is installed.
+ bool m_reEditorInstalled;
+ // Language Code.
+ QStringList m_languageCodeList;
+};
+
+#endif //_SBDCONF_H_
diff --git a/kttsd/filters/sbd/sbdconfwidget.ui b/kttsd/filters/sbd/sbdconfwidget.ui
new file mode 100644
index 0000000..17cd525
--- /dev/null
+++ b/kttsd/filters/sbd/sbdconfwidget.ui
@@ -0,0 +1,363 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>SbdConfWidget</class>
+<author>Gary Cramblitt &lt;garycramblitt@comcast.net&gt;</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>SbdConfWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>544</width>
+ <height>315</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;b&gt;WARNING: This filter is a key component of the KTTS system. Please read the KTTS Handbook before modifying these settings.&lt;/b&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>nameLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Name:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>nameLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The name of this filter. Enter any descriptive name you like.</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>nameLineEdit</cstring>
+ </property>
+ <property name="text">
+ <string>Standard Sentence Boundary Detector</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The name of this filter. Enter any descriptive name you like.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="0">
+ <property name="name">
+ <cstring>layout17</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>reLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Sentence boundary regular expression:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>nameLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The regular expression that detects boundaries between sentences in text jobs.</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>reLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The regular expression that detects boundaries between sentences in text jobs.</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>reButton</cstring>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="3" column="0">
+ <property name="name">
+ <cstring>layout17_2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>sbLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Replacement sentence boundary:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>nameLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This string replaces the matched regular expression. &lt;b&gt;Important&lt;/b&gt;: must end with tab (\t).</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>sbLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This string replaces the matched regular expression. &lt;b&gt;Important&lt;/b&gt;: must end with tab (\t).</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox" row="4" column="0">
+ <property name="name">
+ <cstring>applyGroupBox</cstring>
+ </property>
+ <property name="title">
+ <string>Apply This &amp;Filter When</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>These settings determines when the filter is applied to text.</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>languageLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Language is:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>languageLineEdit</cstring>
+ </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>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>appIdLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Application &amp;ID contains:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>appIdLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enter a DCOP Application ID. This filter will only apply to text jobs queued by that application. You may enter more than one ID separated by commas. If blank, this filter applies to text jobs queued by all applications. Tip: Use kdcop from the command line to get the Application IDs of running applications. Example: "konversation, kvirc,ksirc,kopete"</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="1">
+ <property name="name">
+ <cstring>layout13</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout12</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>languageLineEdit</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </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>
+ </hbox>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>appIdLineEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;Enter a DCOP Application ID. This filter will only apply to text jobs queued by that application. You may enter more than one ID separated by commas. Use &lt;b&gt;knotify&lt;/b&gt; to match all messages sent as KDE notifications. If blank, this filter applies to text jobs queued by all applications. Tip: Use kdcop from the command line to get the Application IDs of running applications. Example: "konversation, kvirc,ksirc,kopete"&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget" row="5" column="0">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>loadButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Load...</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to load a Sentence Boundary Detection configuration from a file.</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>saveButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Sa&amp;ve...</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to save this Sentence Boundary Detection configuration to a file.</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>clearButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Clea&amp;r</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to clear everything.</string>
+ <comment>What's this text</comment>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>nameLineEdit</tabstop>
+ <tabstop>reLineEdit</tabstop>
+ <tabstop>reButton</tabstop>
+ <tabstop>sbLineEdit</tabstop>
+ <tabstop>languageLineEdit</tabstop>
+ <tabstop>languageBrowseButton</tabstop>
+ <tabstop>appIdLineEdit</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kttsd/filters/sbd/sbdplugin.cpp b/kttsd/filters/sbd/sbdplugin.cpp
new file mode 100644
index 0000000..ab14080
--- /dev/null
+++ b/kttsd/filters/sbd/sbdplugin.cpp
@@ -0,0 +1,33 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Generating the factories so Sentence Boundary Detection Filter can be used
+ as plug in.
+ -------------------
+ Copyright:
+ (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
+ -------------------
+ Original author: Gary Cramblitt <garycramblitt@comcast.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ ******************************************************************************/
+
+#include <kgenericfactory.h>
+
+#include "sbdconf.h"
+#include "sbdproc.h"
+
+typedef K_TYPELIST_2( SbdProc, SbdConf ) SbdPlugin;
+K_EXPORT_COMPONENT_FACTORY( libkttsd_sbdplugin,
+ KGenericFactory<SbdPlugin>("kttsd_sbd") )
+
diff --git a/kttsd/filters/sbd/sbdproc.cpp b/kttsd/filters/sbd/sbdproc.cpp
new file mode 100644
index 0000000..cdc80d9
--- /dev/null
+++ b/kttsd/filters/sbd/sbdproc.cpp
@@ -0,0 +1,784 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Sentence Boundary Detection Filter class.
+ -------------------
+ 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 <qregexp.h>
+#include <qdom.h>
+#include <qapplication.h>
+
+// KDE includes.
+#include <kdebug.h>
+#include <klocale.h>
+#include <kconfig.h>
+
+// KTTS includes.
+#include "utils.h"
+#include "talkercode.h"
+
+// SdbProc includes.
+#include "sbdproc.h"
+
+/**
+ * Constructor.
+ */
+SbdThread::SbdThread( QObject *parent, const char *name ) :
+ QObject( parent, name ),
+ QThread()
+{
+}
+
+/**
+ * Destructor.
+ */
+/*virtual*/ SbdThread::~SbdThread()
+{
+}
+
+/**
+ * Get/Set text being processed.
+ */
+void SbdThread::setText( const QString& text ) { m_text = text; }
+QString SbdThread::text() { return m_text; }
+
+/**
+ * Set/Get TalkerCode.
+ */
+void SbdThread::setTalkerCode( TalkerCode* talkerCode ) { m_talkerCode = talkerCode; }
+TalkerCode* SbdThread::talkerCode() { return m_talkerCode; }
+
+/**
+ * Set Sentence Boundary Regular Expression.
+ * This method will only be called if the application overrode the default.
+ *
+ * @param re The sentence delimiter regular expression.
+ */
+void SbdThread::setSbRegExp( const QString& re ) { m_re = re; }
+
+/**
+ * The configured Sentence Boundary Regular Expression.
+ *
+ * @param re The sentence delimiter regular expression.
+ */
+void SbdThread::setConfiguredSbRegExp( const QString& re ) { m_configuredRe = re; }
+
+/**
+ * The configured Sentence Boundary that replaces SB regular expression.
+ *
+ * @param sb The sentence boundary replacement.
+ *
+ */
+void SbdThread::setConfiguredSentenceBoundary( const QString& sb ) { m_configuredSentenceBoundary = sb; }
+
+/**
+ * Did this filter do anything? If the filter returns the input as output
+ * unmolested, it should return False when this method is called.
+ */
+void SbdThread::setWasModified(bool wasModified) { m_wasModified = wasModified; }
+bool SbdThread::wasModified() { return m_wasModified; }
+
+// Given a tag name, returns SsmlElemType.
+SbdThread::SsmlElemType SbdThread::tagToSsmlElemType( const QString tagName )
+{
+ if ( tagName == "speak" ) return etSpeak;
+ if ( tagName == "voice" ) return etVoice;
+ if ( tagName == "prosody" ) return etProsody;
+ if ( tagName == "emphasis" ) return etEmphasis;
+ if ( tagName == "break" ) return etBreak;
+ if ( tagName == "s" ) return etPS;
+ if ( tagName == "p" ) return etPS;
+ return etNotSsml;
+}
+
+// Parses an SSML element, pushing current settings onto the context stack.
+void SbdThread::pushSsmlElem( SsmlElemType et, const QDomElement& elem )
+{
+ // TODO: Need to convert relative values into absolute values and also convert
+ // only to values recognized by SSML2SABLE stylesheet. Either that or enhance all
+ // the synth stylesheets.
+ QDomNamedNodeMap attrList = elem.attributes();
+ int attrCount = attrList.count();
+ switch ( et )
+ {
+ case etSpeak: {
+ SpeakElem e = m_speakStack.top();
+ for ( int ndx=0; ndx < attrCount; ++ndx )
+ {
+ QDomAttr a = attrList.item( ndx ).toAttr();
+ if ( a.name() == "lang" ) e.lang = a.value();
+ }
+ m_speakStack.push( e );
+ break; }
+ case etVoice: {
+ VoiceElem e = m_voiceStack.top();
+ // TODO: Since Festival chokes on <voice> tags, don't output them at all.
+ // This means we can't support voice changes, and probably more irritatingly,
+ // gender changes either.
+ m_voiceStack.push( e );
+ break; }
+ case etProsody: {
+ ProsodyElem e = m_prosodyStack.top();
+ for ( int ndx=0; ndx < attrCount; ++ndx )
+ {
+ QDomAttr a = attrList.item( ndx ).toAttr();
+ if ( a.name() == "pitch" ) e.pitch = a.value();
+ if ( a.name() == "contour" ) e.contour = a.value();
+ if ( a.name() == "range" ) e.range = a.value();
+ if ( a.name() == "rate" ) e.rate = a.value();
+ if ( a.name() == "duration" ) e.duration = a.value();
+ if ( a.name() == "volume" ) e.volume = a.value();
+ }
+ m_prosodyStack.push( e );
+ break; }
+ case etEmphasis: {
+ EmphasisElem e = m_emphasisStack.top();
+ for ( int ndx=0; ndx < attrCount; ++ndx )
+ {
+ QDomAttr a = attrList.item( ndx ).toAttr();
+ if ( a.name() == "level" ) e.level = a.value();
+ }
+ m_emphasisStack.push( e );
+ break; }
+ case etPS: {
+ PSElem e = m_psStack.top();
+ for ( int ndx=0; ndx < attrCount; ++ndx )
+ {
+ QDomAttr a = attrList.item( ndx ).toAttr();
+ if ( a.name() == "lang" ) e.lang = a.value();
+ }
+ m_psStack.push( e );
+ break; }
+ default: break;
+ }
+}
+
+// Given an attribute name and value, constructs an XML representation of the attribute,
+// i.e., name="value".
+QString SbdThread::makeAttr( const QString& name, const QString& value )
+{
+ if ( value.isEmpty() ) return QString::null;
+ return " " + name + "=\"" + value + "\"";
+}
+
+// Returns an XML representation of an SSML tag from the top of the context stack.
+QString SbdThread::makeSsmlElem( SsmlElemType et )
+{
+ QString s;
+ QString a;
+ switch ( et )
+ {
+ // Must always output speak tag, otherwise kttsd won't think each sentence is SSML.
+ // For all other tags, only output the tag if it contains at least one attribute.
+ case etSpeak: {
+ SpeakElem e = m_speakStack.top();
+ s = "<speak";
+ if ( !e.lang.isEmpty() ) s += makeAttr( "lang", e.lang );
+ s += ">";
+ break; }
+ case etVoice: {
+ VoiceElem e = m_voiceStack.top();
+ a += makeAttr( "lang", e.lang );
+ a += makeAttr( "gender", e.gender );
+ a += makeAttr( "age", QString::number(e.age) );
+ a += makeAttr( "name", e.name );
+ a += makeAttr( "variant", e.variant );
+ if ( !a.isEmpty() ) s = "<voice" + a + ">";
+ break; }
+ case etProsody: {
+ ProsodyElem e = m_prosodyStack.top();
+ a += makeAttr( "pitch", e.pitch );
+ a += makeAttr( "contour", e.contour );
+ a += makeAttr( "range", e.range );
+ a += makeAttr( "rate", e.rate );
+ a += makeAttr( "duration", e.duration );
+ a += makeAttr( "volume", e.volume );
+ if ( !a.isEmpty() ) s = "<prosody" + a + ">";
+ break; }
+ case etEmphasis: {
+ EmphasisElem e = m_emphasisStack.top();
+ a += makeAttr( "level", e.level );
+ if ( !a.isEmpty() ) s = "<emphasis" + a + ">";
+ break; }
+ case etPS: {
+ break; }
+ default: break;
+ }
+ return s;
+}
+
+// Pops element from the indicated context stack.
+void SbdThread::popSsmlElem( SsmlElemType et )
+{
+ switch ( et )
+ {
+ case etSpeak: m_speakStack.pop(); break;
+ case etVoice: m_voiceStack.pop(); break;
+ case etProsody: m_prosodyStack.pop(); break;
+ case etEmphasis: m_emphasisStack.pop(); break;
+ case etPS: m_psStack.pop(); break;
+ default: break;
+ }
+}
+
+// Returns an XML representation of a break element.
+QString SbdThread::makeBreakElem( const QDomElement& e )
+{
+ QString s = "<break";
+ QDomNamedNodeMap attrList = e.attributes();
+ int attrCount = attrList.count();
+ for ( int ndx=0; ndx < attrCount; ++ndx )
+ {
+ QDomAttr a = attrList.item( ndx ).toAttr();
+ s += makeAttr( a.name(), a.value() );
+ }
+ s += ">";
+ return s;
+}
+
+// Converts a text fragment into a CDATA section.
+QString SbdThread::makeCDATA( const QString& text )
+{
+ QString s = "<![CDATA[";
+ s += text;
+ s += "]]>";
+ return s;
+}
+
+// Returns an XML representation of an utterance node consisting of voice,
+// prosody, and emphasis elements.
+QString SbdThread::makeSentence( const QString& text )
+{
+ QString s;
+ QString v = makeSsmlElem( etVoice );
+ QString p = makeSsmlElem( etProsody );
+ QString e = makeSsmlElem( etEmphasis );
+ // TODO: Lang settings from psStack.
+ if ( !v.isEmpty() ) s += v;
+ if ( !p.isEmpty() ) s += p;
+ if ( !e.isEmpty() ) s += e;
+ // Escape ampersands and less thans.
+ QString newText = text;
+ newText.replace(QRegExp("&(?!amp;)"), "&amp;");
+ newText.replace(QRegExp("<(?!lt;)"), "&lt;");
+ s += newText;
+ if ( !e.isEmpty() ) s += "</emphasis>";
+ if ( !p.isEmpty() ) s += "</prosody>";
+ if ( !v.isEmpty() ) s += "</voice>";
+ return s;
+}
+
+// Starts a sentence by returning a speak tag.
+QString SbdThread::startSentence()
+{
+ if ( m_sentenceStarted ) return QString::null;
+ QString s;
+ s += makeSsmlElem( etSpeak );
+ m_sentenceStarted = true;
+ return s;
+}
+
+// Ends a sentence and appends a Tab.
+QString SbdThread::endSentence()
+{
+ if ( !m_sentenceStarted ) return QString::null;
+ QString s = "</speak>";
+ s += "\t";
+ m_sentenceStarted = false;
+ return s;
+}
+
+// Parses a node of the SSML tree and recursively parses its children.
+// Returns the filtered text with each sentence a complete ssml tree.
+QString SbdThread::parseSsmlNode( QDomNode& n, const QString& re )
+{
+ QString result;
+ switch ( n.nodeType() )
+ {
+ case QDomNode::ElementNode: { // = 1
+ QDomElement e = n.toElement();
+ QString tagName = e.tagName();
+ SsmlElemType et = tagToSsmlElemType( tagName );
+ switch ( et )
+ {
+ case etSpeak:
+ case etVoice:
+ case etProsody:
+ case etEmphasis:
+ case etPS:
+ {
+ pushSsmlElem( et, e );
+ QDomNode t = n.firstChild();
+ while ( !t.isNull() )
+ {
+ result += parseSsmlNode( t, re );
+ t = t.nextSibling();
+ }
+ popSsmlElem( et );
+ if ( et == etPS )
+ result += endSentence();
+ break;
+ }
+ case etBreak:
+ {
+ // Break elements are empty.
+ result += makeBreakElem( e );
+ }
+ // Ignore any elements we don't recognize.
+ default: break;
+ }
+ break; }
+ case QDomNode::AttributeNode: { // = 2
+ break; }
+ case QDomNode::TextNode: { // = 3
+ QString s = parsePlainText( n.toText().data(), re );
+ // QString d = s;
+ // d.replace("\t", "\\t");
+ // kdDebug() << "SbdThread::parseSsmlNode: parsedPlainText = [" << d << "]" << endl;
+ QStringList sentenceList = QStringList::split( '\t', s, false );
+ int lastNdx = sentenceList.count() - 1;
+ for ( int ndx=0; ndx < lastNdx; ++ndx )
+ {
+ result += startSentence();
+ result += makeSentence( sentenceList[ndx] );
+ result += endSentence();
+ }
+ // Only output sentence boundary if last text fragment ended a sentence.
+ if ( lastNdx >= 0 )
+ {
+ result += startSentence();
+ result += makeSentence( sentenceList[lastNdx] );
+ if ( s.endsWith( "\t" ) ) result += endSentence();
+ }
+ break; }
+ case QDomNode::CDATASectionNode: { // = 4
+ QString s = parsePlainText( n.toCDATASection().data(), re );
+ QStringList sentenceList = QStringList::split( '\t', s, false );
+ int lastNdx = sentenceList.count() - 1;
+ for ( int ndx=0; ndx < lastNdx; ++ndx )
+ {
+ result += startSentence();
+ result += makeSentence( makeCDATA( sentenceList[ndx] ) );
+ result += endSentence();
+ }
+ // Only output sentence boundary if last text fragment ended a sentence.
+ if ( lastNdx >= 0 )
+ {
+ result += startSentence();
+ result += makeSentence( makeCDATA( sentenceList[lastNdx] ) );
+ if ( s.endsWith( "\t" ) ) result += endSentence();
+ }
+ break; }
+ case QDomNode::EntityReferenceNode: { // = 5
+ break; }
+ case QDomNode::EntityNode: { // = 6
+ break; }
+ case QDomNode::ProcessingInstructionNode: { // = 7
+ break; }
+ case QDomNode::CommentNode: { // = 8
+ break; }
+ case QDomNode::DocumentNode: { // = 9
+ break; }
+ case QDomNode::DocumentTypeNode: { // = 10
+ break; }
+ case QDomNode::DocumentFragmentNode: { // = 11
+ break; }
+ case QDomNode::NotationNode: { // = 12
+ break; }
+ case QDomNode::BaseNode: { // = 21
+ break; }
+ case QDomNode::CharacterDataNode: { // = 22
+ break; }
+ }
+ return result;
+}
+
+// Parses Ssml.
+QString SbdThread::parseSsml( const QString& inputText, const QString& re )
+{
+ QRegExp sentenceDelimiter = QRegExp( re );
+
+ // Read the text into xml dom tree.
+ QDomDocument doc( "" );
+ // If an error occurs parsing the SSML, return "invalid S S M L".
+ if ( !doc.setContent( inputText ) ) return i18n("Invalid S S M L.");
+
+ // Set up context stacks and set defaults for all element attributes.
+ m_speakStack.clear();
+ m_voiceStack.clear();
+ m_prosodyStack.clear();
+ m_emphasisStack.clear();
+ m_psStack.clear();
+ SpeakElem se = { "" };
+ m_speakStack.push ( se );
+ VoiceElem ve = {"", "neutral", 40, "", ""};
+ m_voiceStack.push( ve );
+ ProsodyElem pe = { "medium", "", "medium", "medium", "", "medium" };
+ m_prosodyStack.push( pe );
+ EmphasisElem em = { "" };
+ m_emphasisStack.push( em );
+ PSElem pse = { "" };
+ m_psStack.push ( pse );
+
+ // This flag is used to close out a previous sentence.
+ m_sentenceStarted = false;
+
+ // Get the root element (speak) and recursively process its children.
+ QDomElement docElem = doc.documentElement();
+ QDomNode n = docElem.firstChild();
+ QString ssml = parseSsmlNode( docElem, re );
+
+ // Close out last sentence.
+ if ( m_sentenceStarted ) ssml += "</speak>";
+
+ return ssml;
+}
+
+// Parses code. Each newline is converted into a tab character (\t).
+QString SbdThread::parseCode( const QString& inputText )
+{
+ QString temp = inputText;
+ // Replace newlines with tabs.
+ temp.replace("\n","\t");
+ // Remove leading spaces.
+ temp.replace(QRegExp("\\t +"), "\t");
+ // Remove trailing spaces.
+ temp.replace(QRegExp(" +\\t"), "\t");
+ // Remove blank lines.
+ temp.replace(QRegExp("\t\t+"),"\t");
+ return temp;
+}
+
+// Parses plain text.
+QString SbdThread::parsePlainText( const QString& inputText, const QString& re )
+{
+ // kdDebug() << "SbdThread::parsePlainText: parsing " << inputText << " with re " << re << endl;
+ QRegExp sentenceDelimiter = QRegExp( re );
+ QString temp = inputText;
+ // Replace sentence delimiters with tab.
+ temp.replace(sentenceDelimiter, m_configuredSentenceBoundary);
+ // Replace remaining newlines with spaces.
+ temp.replace("\n"," ");
+ temp.replace("\r"," ");
+ // Remove leading spaces.
+ temp.replace(QRegExp("\\t +"), "\t");
+ // Remove trailing spaces.
+ temp.replace(QRegExp(" +\\t"), "\t");
+ // Remove blank lines.
+ temp.replace(QRegExp("\t\t+"),"\t");
+ return temp;
+}
+
+// This is where the real work takes place.
+/*virtual*/ void SbdThread::run()
+{
+ // kdDebug() << "SbdThread::run: processing text = " << m_text << endl;
+
+ // TODO: Determine if we should do anything or not.
+ m_wasModified = true;
+
+ // Determine what kind of input text we are dealing with.
+ int textType;
+ if ( KttsUtils::hasRootElement( m_text, "speak" ) )
+ textType = ttSsml;
+ else
+ {
+ // Examine just the first 500 chars to see if it is code.
+ QString p = m_text.left( 500 );
+ if ( p.contains( QRegExp( "(/\\*)|(if\\b\\()|(^#include\\b)" ) ) )
+ textType = ttCode;
+ else
+ textType = ttPlain;
+ }
+
+ // If application specified a sentence delimiter regular expression, use that,
+ // otherwise use configured default.
+ QString re = m_re;
+ if ( re.isEmpty() ) re = m_configuredRe;
+
+ // Replace spaces, tabs, and formfeeds with a single space.
+ m_text.replace(QRegExp("[ \\t\\f]+"), " ");
+
+ // Perform the filtering based on type of text.
+ switch ( textType )
+ {
+ case ttSsml:
+ m_text = parseSsml( m_text, re );
+ break;
+
+ case ttCode:
+ m_text = parseCode( m_text );
+ break;
+
+ case ttPlain:
+ m_text = parsePlainText( m_text, re);
+ break;
+ }
+
+ // Clear app-specified sentence delimiter. App must call setSbRegExp for each conversion.
+ m_re = QString::null;
+
+ // kdDebug() << "SbdThread::run: filtered text = " << m_text << endl;
+
+ // Result is in m_text;
+
+ // Post an event. We need to emit filterFinished signal, but not from the
+ // separate thread.
+ QCustomEvent* ev = new QCustomEvent(QEvent::User + 301);
+ QApplication::postEvent(this, ev);
+}
+
+bool SbdThread::event ( QEvent * e )
+{
+ if ( e->type() == (QEvent::User + 301) )
+ {
+ // kdDebug() << "SbdThread::event: emitting filteringFinished signal." << endl;
+ emit filteringFinished();
+ return true;
+ }
+ else return false;
+}
+
+// ----------------------------------------------------------------------------
+
+/**
+ * Constructor.
+ */
+SbdProc::SbdProc( QObject *parent, const char *name, const QStringList& /*args*/) :
+ KttsFilterProc(parent, name)
+{
+ // kdDebug() << "SbdProc::SbdProc: Running" << endl;
+ m_sbdThread = new SbdThread( parent, *name + "_thread" );
+ connect( m_sbdThread, SIGNAL(filteringFinished()), this, SLOT(slotSbdThreadFilteringFinished()) );
+}
+
+/**
+ * Destructor.
+ */
+SbdProc::~SbdProc()
+{
+ // kdDebug() << "SbdProc::~SbdProc: Running" << endl;
+ if ( m_sbdThread )
+ {
+ if ( m_sbdThread->running() )
+ {
+ m_sbdThread->terminate();
+ m_sbdThread->wait();
+ }
+ delete m_sbdThread;
+ }
+}
+
+/**
+ * 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 SbdProc::init(KConfig* config, const QString& configGroup){
+ // kdDebug() << "PlugInProc::init: Running" << endl;
+ config->setGroup( configGroup );
+// m_configuredRe = config->readEntry( "SentenceDelimiterRegExp", "([\\.\\?\\!\\:\\;])\\s|(\\n *\\n)" );
+ m_configuredRe = config->readEntry( "SentenceDelimiterRegExp", "([\\.\\?\\!\\:\\;])(\\s|$|(\\n *\\n))" );
+ m_sbdThread->setConfiguredSbRegExp( m_configuredRe );
+ QString sb = config->readEntry( "SentenceBoundary", "\\1\t" );
+ sb.replace( "\\t", "\t" );
+ m_sbdThread->setConfiguredSentenceBoundary( sb );
+ m_appIdList = config->readListEntry( "AppID" );
+ m_languageCodeList = config->readListEntry( "LanguageCodes" );
+ return true;
+}
+
+/**
+ * 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 SbdProc::isSBD() { return true; }
+
+/**
+ * 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 SbdProc::supportsAsync() { return true; }
+
+/**
+ * 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 SbdProc::convert(const QString& inputText, TalkerCode* talkerCode, const QCString& appId)
+{
+ if ( asyncConvert( inputText, talkerCode, appId) )
+ {
+ waitForFinished();
+ // kdDebug() << "SbdProc::convert: returning " << getOutput() << endl;
+ return getOutput();
+ } else 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 SbdProc::asyncConvert(const QString& inputText, TalkerCode* talkerCode,
+ const QCString& appId)
+{
+ m_sbdThread->setWasModified( false );
+ // If language doesn't match, return input unmolested.
+ if ( !m_languageCodeList.isEmpty() )
+ {
+ QString languageCode = talkerCode->languageCode();
+ // kdDebug() << "StringReplacerProc::convert: converting " << inputText <<
+ // " if language code " << languageCode << " matches " << m_languageCodeList << endl;
+ if ( !m_languageCodeList.contains( languageCode ) )
+ {
+ if ( !talkerCode->countryCode().isEmpty() )
+ {
+ languageCode += '_' + talkerCode->countryCode();
+ // kdDebug() << "StringReplacerProc::convert: converting " << inputText <<
+ // " if language code " << languageCode << " matches " << m_languageCodeList << endl;
+ if ( !m_languageCodeList.contains( languageCode ) ) return false;
+ } else return false;
+ }
+ }
+ // If appId doesn't match, return input unmolested.
+ if ( !m_appIdList.isEmpty() )
+ {
+ // kdDebug() << "SbdProc::convert: converting " << inputText << " if appId "
+ // << appId << " matches " << m_appIdList << endl;
+ bool found = false;
+ QString appIdStr = appId;
+ for ( uint ndx=0; ndx < m_appIdList.count(); ++ndx )
+ {
+ if ( appIdStr.contains(m_appIdList[ndx]) )
+ {
+ found = true;
+ break;
+ }
+ }
+ if ( !found ) return false;
+ }
+ m_sbdThread->setText( inputText );
+ m_sbdThread->setTalkerCode( talkerCode );
+ m_state = fsFiltering;
+ m_sbdThread->start();
+ return true;
+}
+
+/**
+ * Waits for a previous call to asyncConvert to finish.
+ */
+/*virtual*/ void SbdProc::waitForFinished()
+{
+ if ( m_sbdThread->running() )
+ {
+ // kdDebug() << "SbdProc::waitForFinished: waiting" << endl;
+ m_sbdThread->wait();
+ // kdDebug() << "SbdProc::waitForFinished: finished waiting" << endl;
+ m_state = fsFinished;
+ }
+}
+
+/**
+ * Returns the state of the Filter.
+ */
+/*virtual*/ int SbdProc::getState() { return m_state; }
+
+/**
+ * Returns the filtered output.
+ */
+/*virtual*/ QString SbdProc::getOutput() { return m_sbdThread->text(); }
+
+/**
+ * Acknowledges the finished filtering.
+ */
+/*virtual*/ void SbdProc::ackFinished()
+{
+ m_state = fsIdle;
+ m_sbdThread->setText( QString::null );
+}
+
+/**
+ * Stops filtering. The filteringStopped signal will emit when filtering
+ * has in fact stopped and state returns to fsIdle;
+ */
+/*virtual*/ void SbdProc::stopFiltering()
+{
+ if ( m_sbdThread->running() )
+ {
+ m_sbdThread->terminate();
+ m_sbdThread->wait();
+ delete m_sbdThread;
+ m_sbdThread = new SbdThread();
+ m_sbdThread->setConfiguredSbRegExp( m_configuredRe );
+ connect( m_sbdThread, SIGNAL(filteringFinished()), this, SLOT(slotSbdThreadFilteringFinished()) );
+ m_state = fsIdle;
+ emit filteringStopped();
+ }
+}
+
+/**
+ * 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 SbdProc::wasModified() { return m_sbdThread->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 SbdProc::setSbRegExp(const QString& re) { m_sbdThread->setSbRegExp( re ); }
+
+// Received when SBD Thread finishes.
+void SbdProc::slotSbdThreadFilteringFinished()
+{
+ m_state = fsFinished;
+ // kdDebug() << "SbdProc::slotSbdThreadFilteringFinished: emitting filterFinished signal." << endl;
+ emit filteringFinished();
+}
+
+#include "sbdproc.moc"
diff --git a/kttsd/filters/sbd/sbdproc.h b/kttsd/filters/sbd/sbdproc.h
new file mode 100644
index 0000000..6c181d5
--- /dev/null
+++ b/kttsd/filters/sbd/sbdproc.h
@@ -0,0 +1,366 @@
+/***************************************************** vim:set ts=4 sw=4 sts=4:
+ Sentence Boundary Detection (SBD) Filter class.
+ -------------------
+ 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.
+ ******************************************************************************/
+
+/******************************************************************************
+
+ This class performs three kinds of SBD:
+ 1. If the text is SSML, generates new SSML where the prosody and voice
+ tags are fully specified for each sentence. This allows user to
+ advance or rewind by sentence without losing SSML context.
+ Input is considered to be SSML if the top-level element is a
+ <speak> tag.
+ 2. If the text is code, each line is considered to be a sentence.
+ Input is considered to be code if any of the following strings are
+ detected:
+ slash asterisk
+ if left-paren
+ pound include
+ 3. If the text is plain text, performs SBD using specified Regular
+ Expression.
+
+ Text is output with tab characters (\t) separating sentences.
+
+ ******************************************************************************/
+
+#ifndef _SBDPROC_H_
+#define _SBDPROC_H_
+
+// Qt includes.
+#include <qobject.h>
+#include <qstringlist.h>
+#include <qthread.h>
+#include <qvaluestack.h>
+#include <qevent.h>
+
+// KTTS includes.
+#include "filterproc.h"
+
+class TalkerCode;
+class KConfig;
+class QDomElement;
+class QDomNode;
+
+class SbdThread: public QObject, public QThread
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Constructor.
+ */
+ SbdThread( QObject *parent = 0, const char *name = 0);
+
+ /**
+ * Destructor.
+ */
+ virtual ~SbdThread();
+
+ /**
+ * Get/Set text being processed.
+ */
+ void setText( const QString& text );
+ QString text();
+
+ /**
+ * Set/Get TalkerCode.
+ */
+ void setTalkerCode( TalkerCode* talkerCode );
+ TalkerCode* talkerCode();
+
+ /**
+ * Set Sentence Boundary Regular Expression.
+ * This method will only be called if the application overrode the default.
+ *
+ * @param re The sentence delimiter regular expression.
+ */
+ void setSbRegExp( const QString& re );
+
+ /**
+ * The configured Sentence Boundary Regular Expression.
+ *
+ * @param re The sentence delimiter regular expression.
+ */
+ void setConfiguredSbRegExp( const QString& re );
+
+ /**
+ * The configured Sentence Boundary that replaces SB regular expression.
+ *
+ * @param sb The sentence boundary replacement.
+ *
+ */
+ void setConfiguredSentenceBoundary( const QString& sb );
+
+ /**
+ * Did this filter do anything? If the filter returns the input as output
+ * unmolested, it should return False when this method is called.
+ */
+ void setWasModified(bool wasModified);
+ bool wasModified();
+
+ signals:
+ void filteringFinished();
+
+ protected:
+ virtual void run();
+ virtual bool event ( QEvent * e );
+
+ private:
+ enum TextType {
+ ttSsml, // SSML
+ ttCode, // Code
+ ttPlain // Plain text
+ };
+
+ enum SsmlElemType {
+ etSpeak,
+ etVoice,
+ etProsody,
+ etEmphasis,
+ etPS, // Paragraph or sentence (we don't care).
+ etBreak,
+ etNotSsml
+ };
+
+ // Speak Element.
+ struct SpeakElem {
+ QString lang; // xml:lang="en".
+ };
+
+ // Voice Element.
+ struct VoiceElem {
+ QString lang; // xml:lang="en".
+ QString gender; // "male", "female", or "neutral".
+ uint age; // Age in years.
+ QString name; // Synth-specific voice name.
+ QString variant; // Ignored.
+ };
+
+ // Prosody Element.
+ struct ProsodyElem {
+ QString pitch; // "x-low", "low", "medium", "high", "x-high", "default".
+ QString contour; // Pitch contour (ignored).
+ QString range; // "x-low", "low", "medium", "high", "x-high", "default".
+ QString rate; // "x-slow", "slow", "medium", "fast", "x-fast", "default".
+ QString duration; // Ignored.
+ QString volume; // "silent", "x-soft", "soft", "medium", "load", "x-load", "default".
+ };
+
+ // Emphasis Element.
+ struct EmphasisElem {
+ QString level; // "strong", "moderate", "none" and "reduced"
+ };
+
+ // Break Element.
+ struct BreakElem {
+ QString strength; // "x-weak", "weak", "medium" (default value), "strong",
+ // or "x-strong", "none"
+ QString time; // Ignored.
+ };
+
+ // Paragraph and Sentence Elements.
+ struct PSElem {
+ QString lang; // xml:lang="en".
+ };
+
+ // Given a tag name, returns SsmlElemType.
+ SsmlElemType tagToSsmlElemType(const QString tagName);
+ // Parses an SSML element, pushing current settings onto the context stack.
+ void pushSsmlElem( SsmlElemType et, const QDomElement& elem );
+ // Given an attribute name and value, constructs an XML representation of the attribute,
+ // i.e., name="value".
+ QString makeAttr( const QString& name, const QString& value );
+ // Returns an XML representation of an SSML tag from the top of the context stack.
+ QString makeSsmlElem( SsmlElemType et );
+ // Pops element from the indicated context stack.
+ void popSsmlElem( SsmlElemType et );
+ QString makeBreakElem( const QDomElement& e );
+ // Converts a text fragment into a CDATA section.
+ QString makeCDATA( const QString& text );
+ // Returns an XML representation of an utterance node consisting of voice,
+ // prosody, and emphasis elements.
+ QString makeSentence( const QString& text );
+ // Starts a sentence by returning a speak tag.
+ QString startSentence();
+ // Ends a sentence and appends a Tab.
+ QString endSentence();
+ // Parses a node of the SSML tree and recursively parses its children.
+ // Returns the filtered text with each sentence a complete ssml tree.
+ QString parseSsmlNode( QDomNode& n, const QString& re );
+
+ // Parses Ssml.
+ QString parseSsml( const QString& inputText, const QString& re );
+ // Parses code. Each newline is converted into a tab character (\t).
+ QString parseCode( const QString& inputText );
+ // Parses plain text.
+ QString parsePlainText( const QString& inputText, const QString& re );
+
+ // Context stacks.
+ QValueStack<SpeakElem> m_speakStack;
+ QValueStack<VoiceElem> m_voiceStack;
+ QValueStack<ProsodyElem> m_prosodyStack;
+ QValueStack<EmphasisElem> m_emphasisStack;
+ QValueStack<PSElem> m_psStack;
+
+ // The text being processed.
+ QString m_text;
+ // Talker Code.
+ TalkerCode* m_talkerCode;
+ // Configured default Sentence Delimiter regular expression.
+ QString m_configuredRe;
+ // Configured Sentence Boundary replacement expression.
+ QString m_configuredSentenceBoundary;
+ // Application-specified Sentence Delimiter regular expression (if any).
+ QString m_re;
+ // False if input was not modified.
+ bool m_wasModified;
+ // True when a sentence has been started.
+ bool m_sentenceStarted;
+};
+
+class SbdProc : virtual public KttsFilterProc
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Constructor.
+ */
+ SbdProc( QObject *parent, const char *name, const QStringList &args = QStringList() );
+
+ /**
+ * Destructor.
+ */
+ virtual ~SbdProc();
+
+ /**
+ * 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.
+ */
+ virtual void setSbRegExp( const QString& re );
+
+ private slots:
+ // Received when SBD Thread finishes.
+ void slotSbdThreadFilteringFinished();
+
+ private:
+ // If not empty, apply filters only to apps using talkers speaking these language codes.
+ QStringList m_languageCodeList;
+ // If not empty, apply filter only to apps containing this string.
+ QStringList m_appIdList;
+ // SBD Thread Object.
+ SbdThread* m_sbdThread;
+ // State.
+ int m_state;
+ // Configured default Sentence Delimiter regular expression.
+ QString m_configuredRe;
+};
+
+#endif // _SBDPROC_H_
diff --git a/kttsd/filters/sbd/standard_sbdrc b/kttsd/filters/sbd/standard_sbdrc
new file mode 100644
index 0000000..76037a5
--- /dev/null
+++ b/kttsd/filters/sbd/standard_sbdrc
@@ -0,0 +1,6 @@
+[Filter]
+AppID=
+LanguageCodes=
+SentenceBoundary=\\1\\t
+SentenceDelimiterRegExp=([\\.\\?\\!\\:\\;])(\\s|$|(\\n *\\n))
+UserFilterName=Standard Sentence Boundary Detector