generic: drop text-to-speech support

Katana (or any interface designed around key and mouse events) is not
for blind people, trying to slap text-to-speech on top of it was the
wrong thing to do to begin with

side note: speech-dispatcher tends to hang if the output device is not
configured properly (i.e. editing /etc/speech-dispatcher/speechd.conf
manually)

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
Ivailo Monev 2022-12-18 15:49:55 +02:00
parent c728d46d16
commit 4dbb501d25
26 changed files with 4 additions and 845 deletions

View file

@ -299,14 +299,6 @@ set_package_properties(Libmicrohttpd PROPERTIES
PURPOSE "HTTP(S) server"
)
kde4_optional_find_package(Speechd)
set_package_properties(Speechd PROPERTIES
DESCRIPTION "Speech Dispatcher provides a high-level device independent layer for speech synthesis"
URL "https://freebsoft.org/speechd"
TYPE RECOMMENDED
PURPOSE "Text-To-Speech"
)
kde4_optional_find_package(LibArchive 3.0.3)
set_package_properties(LibArchive PROPERTIES
DESCRIPTION "Multi-format archive and compression library"

View file

@ -24,7 +24,6 @@ kde4_bool_to_01(EXIV2_FOUND HAVE_EXIV2) # kexiv2
kde4_bool_to_01(LIBMICROHTTPD_FOUND HAVE_LIBMICROHTTPD) # khttp
kde4_bool_to_01(MPV_FOUND HAVE_MPV) # kmediaplayer
kde4_bool_to_01(OPENSSL_FOUND HAVE_OPENSSL) # kpasswdstore
kde4_bool_to_01(SPEECHD_FOUND HAVE_SPEECHD) # kspeech
kde4_bool_to_01(ACL_FOUND HAVE_POSIX_ACL) # kio
check_include_files(fstab.h HAVE_FSTAB_H) # kio, kdecore

View file

@ -14,7 +14,6 @@ set(cmakeFilesDontInstall
FindLibCDIO.cmake
FindDevinfo.cmake
FindFFmpeg.cmake
FindSpeechd.cmake
FindLibRaw.cmake
FindLibJPEG.cmake
FindOpenJPEG.cmake

View file

@ -62,8 +62,7 @@
# KDE4_KPOWERMANAGER_LIBS - the kpowermanager library and all depending libraries
# KDE4_KDNSSD_LIBS - the kdnssd library and all depending libraries
# KDE4_KHTTP_LIBS - the khttp library and all depending libraries
# KDE4_KSPEECH_LIBS - the kspeech library and all depending libraries
# KDE4_KARCHIVE_LIBS - the kspeech library and all depending libraries
# KDE4_KARCHIVE_LIBS - the karchive library and all depending libraries
#
# The variable INSTALL_TARGETS_DEFAULT_ARGS can be used when installing libraries
# or executables into the default locations.
@ -243,7 +242,6 @@ set(_kde_libraries
kpowermanager
kdnssd
khttp
kspeech
karchive
kemail
kfile

View file

@ -1,38 +0,0 @@
# Try to find speech-dispatcher, once done this will define:
#
# SPEECHD_FOUND - system has speech-dispatcher
# SPEECHD_INCLUDE_DIR - the speech-dispatcher include directory
# SPEECHD_LIBRARIES - the libraries needed to use speech-dispatcher
#
# Copyright (c) 2020 Ivailo Monev <xakepa10@gmail.com>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
find_package(PkgConfig REQUIRED)
pkg_check_modules(PC_SPEECHD QUIET speech-dispatcher)
set(SPEECHD_INCLUDE_DIR ${PC_SPEECHD_INCLUDE_DIRS})
set(SPEECHD_LIBRARIES ${PC_SPEECHD_LIBRARIES})
set(SPEECHD_VERSION ${PC_SPEECHD_VERSION})
if(NOT SPEECHD_INCLUDE_DIR OR NOT SPEECHD_LIBRARIES)
find_path(SPEECHD_INCLUDE_DIR
NAMES libspeechd.h
PATH_SUFFIXES speech-dispatcher
HINTS $ENV{SPEECHDDIR}/include
)
find_library(SPEECHD_LIBRARIES
NAMES speechd
HINTS $ENV{SPEECHDDIR}/lib
)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Speechd
VERSION_VAR SPEECHD_VERSION
REQUIRED_VARS SPEECHD_INCLUDE_DIR SPEECHD_LIBRARIES
)
mark_as_advanced(SPEECHD_INCLUDE_DIR SPEECHD_LIBRARIES)

View file

@ -65,9 +65,6 @@
/* Define to 1 if you have OpenSSL */
#cmakedefine HAVE_OPENSSL 1
/* Define to 1 if you have Speech Dispatcher */
#cmakedefine HAVE_SPEECHD 1
/* Define to 1 if getmntinfo() uses statvfs struct */
#cmakedefine GETMNTINFO_USES_STATVFS 1

View file

@ -268,7 +268,6 @@ install(
KShortcutWidget
KSortableItem
KSortableList
KSpeech
KSplashScreen
KSqueezedTextLabel
KStandardDirs

View file

@ -1 +0,0 @@
#include "../kspeech.h"

View file

@ -70,7 +70,6 @@
51005 kpowermanager
51006 kdnssd
51007 khttp
51008 kspeech
51009 karchive
51010 kemail

View file

@ -5,8 +5,6 @@ include_directories(
${CMAKE_SOURCE_DIR}/interfaces/kregexpeditor
${CMAKE_SOURCE_DIR}/kdecore/sonnet
${CMAKE_SOURCE_DIR}/kdeui
${CMAKE_SOURCE_DIR}/kutils/kspeech
${CMAKE_BINARY_DIR}/kutils/kspeech
${KDE4_KDECORE_INCLUDES}
actions
colors
@ -335,7 +333,6 @@ target_link_libraries(kdeui PRIVATE
target_link_libraries(kdeui PUBLIC
${KDE4_KDECORE_LIBS}
${KDE4_KSPEECH_LIBS}
${QT_QTGUI_LIBRARY}
${QT_QTXML_LIBRARY}
${QT_QTNETWORK_LIBRARY}

View file

@ -126,7 +126,7 @@
* Action is the string representing the action. Actions can be added to
* the KNotify daemon as plugins, by deriving from KNotifyPlugin.
* At the time of writing, the following actions are available: Taskbar,
* Sound, Popup, Logfile, KTTS, Execute.
* Sound, Popup, Logfile, Execute.
* Actions can be combined by seperating them with '|'.
*
* Contexts is a comma separated list of possible context for this event.

View file

@ -34,7 +34,6 @@
#include <dialog.h>
#include "backgroundchecker.h"
#include <kdebug.h>
#include <kspeech.h>
#include <kaction.h>
#include <kcursor.h>
#include <kglobalsettings.h>
@ -573,27 +572,9 @@ QMenu *KTextEdit::mousePopupMenu()
popup->addAction(replaceAction);
}
}
if (KSpeech::isSupported()) {
popup->addSeparator();
QAction *speakAction = popup->addAction(i18n("Speak Text"));
speakAction->setIcon(KIcon("preferences-desktop-text-to-speech"));
speakAction->setEnabled(!emptyDocument );
connect( speakAction, SIGNAL(triggered(bool)), this, SLOT(slotSpeakText()) );
}
return popup;
}
void KTextEdit::slotSpeakText()
{
QString text;
if(textCursor().hasSelection())
text = textCursor().selectedText();
else
text = toPlainText();
KSpeech kspeech(this);
kspeech.say(text);
}
void KTextEdit::contextMenuEvent(QContextMenuEvent *event)
{
// Obtain the cursor at the mouse position and the current cursor

View file

@ -326,10 +326,6 @@ class KDEUI_EXPORT KTextEdit : public QTextEdit //krazy:exclude=qclasses
void slotFind();
void slotFindNext();
void slotReplace();
/**
* @since 4.3
*/
void slotSpeakText();
protected:
/**

View file

@ -4,8 +4,6 @@ include_directories(
${KDE4_KIO_INCLUDES}
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_SOURCE_DIR}/kutils/kspeech
${CMAKE_BINARY_DIR}/kutils/kspeech
)
if(ENABLE_TESTING)

View file

@ -22,7 +22,6 @@
#include <kstandarddirs.h>
#include <kiconloader.h>
#include <kspeech.h>
KNotifyConfigActionsWidget::KNotifyConfigActionsWidget( QWidget * parent )
: QWidget(parent)
@ -40,26 +39,16 @@ KNotifyConfigActionsWidget::KNotifyConfigActionsWidget( QWidget * parent )
m_ui.Logfile_check->setIcon(KIcon("text-x-generic"));
m_ui.Execute_check->setIcon(KIcon("system-run"));
m_ui.Taskbar_check->setIcon(KIcon("services"));
m_ui.KTTS_check->setIcon(KIcon("text-speak"));
connect(m_ui.Execute_check,SIGNAL(toggled(bool)), this, SIGNAL(changed()));
connect(m_ui.Sound_check,SIGNAL(toggled(bool)), this, SIGNAL(changed()));
connect(m_ui.Popup_check,SIGNAL(toggled(bool)), this, SIGNAL(changed()));
connect(m_ui.Logfile_check,SIGNAL(toggled(bool)), this, SIGNAL(changed()));
connect(m_ui.Taskbar_check,SIGNAL(toggled(bool)), this, SIGNAL(changed()));
connect(m_ui.KTTS_check,SIGNAL(toggled(bool)), this, SLOT(slotKTTSComboChanged()));
connect(m_ui.Execute_select,SIGNAL(textChanged(QString)), this, SIGNAL(changed()));
connect(m_ui.Sound_select,SIGNAL(textChanged(QString)), this, SIGNAL(changed()));
connect(m_ui.Logfile_select,SIGNAL(textChanged(QString)), this, SIGNAL(changed()));
connect(m_ui.Sound_play,SIGNAL(clicked()), this, SLOT(slotPlay()));
connect(m_ui.KTTS_combo,SIGNAL(currentIndexChanged(int)), this, SLOT(slotKTTSComboChanged()));
m_ui.KTTS_combo->setEnabled(false);
if(!KNotifyConfigElement::have_kttsd())
{
m_ui.KTTS_check->setVisible(false);
m_ui.KTTS_select->setVisible(false);
m_ui.KTTS_combo->setVisible(false);
}
}
@ -78,18 +67,10 @@ void KNotifyConfigActionsWidget::setConfigElement( KNotifyConfigElement * config
m_ui.Logfile_check->setChecked( actions.contains("Logfile") );
m_ui.Execute_check->setChecked( actions.contains("Execute") );
m_ui.Taskbar_check->setChecked( actions.contains("Taskbar") );
m_ui.KTTS_check->setChecked( actions.contains("KTTS") );
m_ui.Sound_select->setUrl( KUrl( config->readEntry( "Sound" , true ) ) );
m_ui.Logfile_select->setUrl( KUrl( config->readEntry( "Logfile" , true ) ) );
m_ui.Execute_select->setUrl( KUrl( config->readEntry( "Execute" ) ) );
m_ui.KTTS_select->setText( config->readEntry( "KTTS" ) );
if(m_ui.KTTS_select->text() == QLatin1String("%e"))
m_ui.KTTS_combo->setCurrentIndex(1);
else if(m_ui.KTTS_select->text() == QLatin1String("%m") || m_ui.KTTS_select->text() == QLatin1String("%s"))
m_ui.KTTS_combo->setCurrentIndex(0);
else
m_ui.KTTS_combo->setCurrentIndex(2);
blockSignals(blocked);
}
@ -106,26 +87,12 @@ void KNotifyConfigActionsWidget::save( KNotifyConfigElement * config )
actions << "Execute";
if(m_ui.Taskbar_check->isChecked())
actions << "Taskbar";
if(m_ui.KTTS_check->isChecked())
actions << "KTTS";
config->writeEntry( "Action" , actions.join("|") );
config->writeEntry( "Sound" , m_ui.Sound_select->url().url() );
config->writeEntry( "Logfile" , m_ui.Logfile_select->url().url() );
config->writeEntry( "Execute" , m_ui.Execute_select->url().path() );
switch(m_ui.KTTS_combo->currentIndex())
{
case 0:
config->writeEntry( "KTTS" , "%s" );
break;
case 1:
config->writeEntry( "KTTS" , "%e" );
break;
case 2:
default:
config->writeEntry( "KTTS" , m_ui.KTTS_select->text() );
}
}
void KNotifyConfigActionsWidget::slotPlay( )
@ -145,10 +112,4 @@ void KNotifyConfigActionsWidget::slotPlay( )
kaudioplayer.call("play", soundURL.prettyUrl(), QString::fromLatin1("knotify"));
}
void KNotifyConfigActionsWidget::slotKTTSComboChanged()
{
m_ui.KTTS_select->setEnabled(m_ui.KTTS_check->isChecked() && m_ui.KTTS_combo->currentIndex() == 2);
emit changed();
}
#include "moc_knotifyconfigactionswidget.cpp"

View file

@ -44,7 +44,6 @@ class KNotifyConfigActionsWidget : public QWidget
void changed();
private Q_SLOTS:
void slotPlay();
void slotKTTSComboChanged();
private:
Ui::KNotifyConfigActionsWidgetBase m_ui;
};

View file

@ -100,48 +100,6 @@
</property>
</widget>
</item>
<item row="5" column="0" >
<widget class="QCheckBox" name="KTTS_check" >
<property name="text" >
<string>Sp&amp;eech</string>
</property>
</widget>
</item>
<item row="5" column="1" colspan="2" >
<widget class="KComboBox" name="KTTS_combo" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="whatsThis" >
<string>&lt;qt>Specifies how Text-To-Speech should speak the event when received. If you select "Speak custom text", enter the text in the box. You may use the following substitution strings in the text:&lt;dl>&lt;dt>%e&lt;/dt>&lt;dd>Name of the event&lt;/dd>&lt;dt>%a&lt;/dt>&lt;dd>Application that sent the event&lt;/dd>&lt;dt>%m&lt;/dt>&lt;dd>The message sent by the application&lt;/dd>&lt;/dl>&lt;/qt></string>
</property>
<item>
<property name="text" >
<string>Speak Event Message</string>
</property>
</item>
<item>
<property name="text" >
<string>Speak Event Name</string>
</property>
</item>
<item>
<property name="text" >
<string>Speak Custom Text</string>
</property>
</item>
</widget>
</item>
<item row="5" column="3" >
<widget class="KLineEdit" name="KTTS_select" >
<property name="whatsThis" >
<string>&lt;qt>Specifies how Text-To-Speech should speak the event when received. If you select "Speak custom text", enter the text in the box. You may use the following substitution strings in the text:&lt;dl>&lt;dt>%e&lt;/dt>&lt;dd>Name of the event&lt;/dd>&lt;dt>%a&lt;/dt>&lt;dd>Application that sent the event&lt;/dd>&lt;dt>%m&lt;/dt>&lt;dd>The message sent by the application&lt;/dd>&lt;/dl>&lt;/qt></string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
@ -222,21 +180,5 @@
</hint>
</hints>
</connection>
<connection>
<sender>KTTS_check</sender>
<signal>toggled(bool)</signal>
<receiver>KTTS_combo</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel" >
<x>48</x>
<y>169</y>
</hint>
<hint type="destinationlabel" >
<x>131</x>
<y>169</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -25,7 +25,6 @@
#include <kdebug.h>
#include <kstandarddirs.h>
#include <kservice.h>
#include <kspeech.h>
KNotifyConfigElement::KNotifyConfigElement(const QString &eventid, KConfig *config)
: m_config( new KConfigGroup(config , "Event/" + eventid) )
@ -60,9 +59,3 @@ void KNotifyConfigElement::save( )
}
m_config->sync();
}
bool KNotifyConfigElement::have_kttsd() //[static]
{
return KSpeech::isSupported();
}

View file

@ -40,12 +40,7 @@ class KNotifyConfigElement
void writeEntry(const QString& entry, const QString & data);
void save();
/**
* return wither kttsd is installed or not.
*/
static bool have_kttsd();
private:
QMap<QString,QString> m_cache;
KConfigGroup* m_config;

View file

@ -69,8 +69,6 @@ void KNotifyEventList::KNotifyEventListDelegate::paint( QPainter* painter,
iconList << ( optionsList.contains("Logfile") ? KIcon("text-x-generic") : KIcon() );
iconList << ( optionsList.contains("Taskbar") ? KIcon("services") : KIcon() );
iconList << ( optionsList.contains("Execute") ? KIcon("system-run") : KIcon() );
if( KNotifyConfigElement::have_kttsd() )
iconList << ( optionsList.contains("KTTS") ? KIcon("text-speak") : KIcon() );
int mc_x=0;
@ -103,7 +101,7 @@ KNotifyEventList::KNotifyEventList(QWidget *parent)
setIconSize( QSize(iconWidth, iconWidth) );
header()->setResizeMode( 0, QHeaderView::Fixed );
header()->resizeSection( 0, KNotifyConfigElement::have_kttsd() ? (iconWidth+4)*6: (iconWidth+4)*5 );
header()->resizeSection( 0, (iconWidth + 4) * 5 );
header()->setResizeMode( 1, QHeaderView::ResizeToContents );
connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)) , this , SLOT(slotSelectionChanged(QTreeWidgetItem*,QTreeWidgetItem*)));

View file

@ -13,7 +13,6 @@ add_subdirectory(kpasswdstore)
add_subdirectory(kpowermanager)
add_subdirectory(kdnssd)
add_subdirectory(khttp)
add_subdirectory(kspeech)
add_subdirectory(karchive)
add_subdirectory(kemail)

View file

@ -1,44 +0,0 @@
if(SPEECHD_FOUND)
include_directories(${SPEECHD_INCLUDE_DIR})
endif()
add_definitions(-DKDE_DEFAULT_DEBUG_AREA=51008)
set(kspeech_LIB_SRCS
kspeech.cpp
)
add_library(kspeech ${LIBRARY_TYPE} ${kspeech_LIB_SRCS})
target_link_libraries(kspeech PUBLIC
${KDE4_KDECORE_LIBS}
)
if(SPEECHD_FOUND)
target_link_libraries(kspeech PRIVATE ${SPEECHD_LIBRARIES})
endif()
set_target_properties(kspeech PROPERTIES
VERSION ${GENERIC_LIB_VERSION}
SOVERSION ${GENERIC_LIB_SOVERSION}
)
generate_export_header(kspeech)
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/kspeech_export.h
kspeech.h
DESTINATION ${KDE4_INCLUDE_INSTALL_DIR}
COMPONENT Devel
)
install(
TARGETS kspeech
EXPORT kdelibsLibraryTargets
${INSTALL_TARGETS_DEFAULT_ARGS}
)
if(ENABLE_TESTING)
add_subdirectory(tests)
endif()

View file

@ -1,411 +0,0 @@
/* This file is part of the KDE libraries
Copyright (C) 2022 Ivailo Monev <xakepa10@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2, as published by the Free Software Foundation.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include "kspeech.h"
#include <QMutex>
#include <QCoreApplication>
#include <kdebug.h>
#include <kconfig.h>
#include <kconfiggroup.h>
#if defined(HAVE_SPEECHD)
# include <speech-dispatcher/libspeechd.h>
#endif
typedef QMap<int, KSpeechPrivate*> KSpeechMap;
Q_GLOBAL_STATIC(QMutex, globalKSpeechInstancesMutex)
Q_GLOBAL_STATIC(KSpeechMap, globalKSpeechInstances)
// NOTE: the callback function is not called in the same thread as the object that opens the
// connection
class KSpeechPrivate : public QObject
{
Q_OBJECT
public:
KSpeechPrivate(QObject *parent);
~KSpeechPrivate();
#if defined(HAVE_SPEECHD)
void openSpeechD();
void closeSpeechD();
static void speechCallback(size_t msg_id, size_t client_id, SPDNotificationType state);
#endif
Q_SIGNALS:
void signalJobStateChanged(int jobNum, int state);
private:
friend KSpeech;
#if defined(HAVE_SPEECHD)
SPDConnection* m_speechd;
int m_speechdid;
QByteArray m_voice;
#endif
QByteArray m_speechid;
};
KSpeechPrivate::KSpeechPrivate(QObject *parent)
: QObject(parent)
#if defined(HAVE_SPEECHD)
, m_speechd(nullptr),
m_speechdid(0)
#endif
{
m_speechid = QCoreApplication::applicationName().toLocal8Bit();
}
KSpeechPrivate::~KSpeechPrivate()
{
#if defined(HAVE_SPEECHD)
Q_ASSERT(!m_speechd);
Q_ASSERT(m_speechdid == 0);
#endif
}
#if defined(HAVE_SPEECHD)
void KSpeechPrivate::openSpeechD()
{
Q_ASSERT(!m_speechd);
m_speechd = spd_open(m_speechid.constData(), "main", NULL, SPD_MODE_THREADED);
if (Q_UNLIKELY(!m_speechd)) {
kWarning() << "Could not open speech-dispatcher connection";
return;
}
m_speechdid = spd_get_client_id(m_speechd);
{
QMutexLocker locker(globalKSpeechInstancesMutex());
globalKSpeechInstances()->insert(m_speechdid, this);
}
m_speechd->callback_begin = KSpeechPrivate::speechCallback;
m_speechd->callback_end = KSpeechPrivate::speechCallback;
m_speechd->callback_cancel = KSpeechPrivate::speechCallback;
spd_set_notification_on(m_speechd, SPD_BEGIN);
spd_set_notification_on(m_speechd, SPD_END);
spd_set_notification_on(m_speechd, SPD_CANCEL);
KConfig kconfig("kspeechrc", KConfig::SimpleConfig);
KConfigGroup kconfiggroup = kconfig.group(m_speechid);
const int volume = kconfiggroup.readEntry("volume", 100);
const int pitch = kconfiggroup.readEntry("pitch", 0);
const QByteArray voice = kconfiggroup.readEntry("voice", QByteArray());
KSpeech* kspeech = qobject_cast<KSpeech*>(parent());
Q_ASSERT(kspeech);
kspeech->setVolume(volume);
kspeech->setPitch(pitch);
if (!voice.isEmpty()) {
kspeech->setVoice(voice);
}
}
void KSpeechPrivate::closeSpeechD()
{
if (m_speechd) {
spd_close(m_speechd);
m_speechd = nullptr;
QMutexLocker locker(globalKSpeechInstancesMutex());
globalKSpeechInstances()->remove(m_speechdid);
m_speechdid = 0;
}
}
void KSpeechPrivate::speechCallback(size_t msg_id, size_t client_id, SPDNotificationType state)
{
QMutexLocker locker(globalKSpeechInstancesMutex());
KSpeechPrivate* kspeechprivate = globalKSpeechInstances()->value(static_cast<int>(client_id), 0);
if (Q_UNLIKELY(!kspeechprivate)) {
kWarning() << "Null kspeech private pointer";
return;
}
const int jobNum = msg_id;
switch (state) {
case SPD_EVENT_BEGIN: {
emit kspeechprivate->signalJobStateChanged(jobNum, KSpeech::JobStarted);
break;
}
case SPD_EVENT_END: {
emit kspeechprivate->signalJobStateChanged(jobNum, KSpeech::JobFinished);
break;
}
case SPD_EVENT_CANCEL: {
emit kspeechprivate->signalJobStateChanged(jobNum, KSpeech::JobCanceled);
break;
}
default: {
break;
}
}
}
#endif // HAVE_SPEECHD
KSpeech::KSpeech(QObject* parent)
: QObject(parent),
d(new KSpeechPrivate(this))
{
connect(
d, SIGNAL(signalJobStateChanged(int,int)),
this, SLOT(_jobStateChanged(int,int))
);
#if defined(HAVE_SPEECHD)
d->openSpeechD();
#endif
}
KSpeech::~KSpeech()
{
#if defined(HAVE_SPEECHD)
d->closeSpeechD();
#endif
delete d;
}
bool KSpeech::isSupported()
{
#if defined(HAVE_SPEECHD)
return true;
#else
return false;
#endif
}
void KSpeech::setSpeechID(const QString &id)
{
#if defined(HAVE_SPEECHD)
d->closeSpeechD();
#endif
d->m_speechid = id.toUtf8();
#if defined(HAVE_SPEECHD)
d->openSpeechD();
#endif
}
int KSpeech::volume() const
{
#if defined(HAVE_SPEECHD)
if (Q_UNLIKELY(!d->m_speechd)) {
kDebug() << "Null speech-dispatcher pointer";
return 0;
}
return spd_get_volume(d->m_speechd);
#else
return 0;
#endif
}
bool KSpeech::setVolume(const int volume)
{
#if defined(HAVE_SPEECHD)
if (Q_UNLIKELY(!d->m_speechd)) {
kDebug() << "Null speech-dispatcher pointer";
return false;
}
if (Q_UNLIKELY(volume < -100 || volume > 100)) {
kWarning() << "Invalid volume value" << volume;
return false;
}
const int speechdresult = spd_set_volume(d->m_speechd, volume);
if (Q_UNLIKELY(speechdresult < 0)) {
kWarning() << "Speech-dispatcher set volume failed";
return false;
}
return true;
#else
return false;
#endif
}
int KSpeech::pitch() const
{
#if defined(HAVE_SPEECHD)
if (Q_UNLIKELY(!d->m_speechd)) {
kDebug() << "Null speech-dispatcher pointer";
return 0;
}
return spd_get_voice_pitch(d->m_speechd);
#else
return 0;
#endif
}
bool KSpeech::setPitch(const int pitch)
{
#if defined(HAVE_SPEECHD)
if (Q_UNLIKELY(!d->m_speechd)) {
kDebug() << "Null speech-dispatcher pointer";
return false;
}
if (Q_UNLIKELY(pitch < -100 || pitch > 100)) {
kWarning() << "Invalid pitch value" << pitch;
return false;
}
const int speechdresult = spd_set_voice_pitch(d->m_speechd, pitch);
if (Q_UNLIKELY(speechdresult < 0)) {
kWarning() << "Speech-dispatcher set pitch failed";
return false;
}
return true;
#else
return false;
#endif
}
QByteArray KSpeech::voice() const
{
#if defined(HAVE_SPEECHD)
return d->m_voice;
#else
return QByteArray();
#endif
}
QList<QByteArray> KSpeech::voices() const
{
QList<QByteArray> result;
#if defined(HAVE_SPEECHD)
if (Q_UNLIKELY(!d->m_speechd)) {
kDebug() << "Null speech-dispatcher pointer";
return result;
}
SPDVoice** speechdvoices = spd_list_synthesis_voices(d->m_speechd);
if (Q_UNLIKELY(!speechdvoices)) {
kWarning() << "Null speech-dispatcher voices pointer";
return result;
}
int i = 0;
while (speechdvoices[i]) {
result.append(speechdvoices[i]->name);
i++;
}
free_spd_voices(speechdvoices);
#endif
return result;
}
bool KSpeech::setVoice(const QByteArray &voice)
{
#if defined(HAVE_SPEECHD)
if (Q_UNLIKELY(!d->m_speechd)) {
kDebug() << "Null speech-dispatcher pointer";
return false;
}
// could be empty if not set in kspeechrc
if (voice.isEmpty()) {
kWarning() << "Empty voice" << voice;
return false;
} else if (Q_UNLIKELY(!KSpeech::voices().contains(voice))) {
// NOTE: if voice is invalid speech-dispatcher will not dispatch anything
kWarning() << "Invalid voice value" << voice;
return false;
}
const int speechdresult = spd_set_synthesis_voice(d->m_speechd, voice.constData());
if (Q_UNLIKELY(speechdresult < 0)) {
kWarning() << "Speech-dispatcher set voice failed";
return false;
}
d->m_voice = voice;
return true;
#else
return false;
#endif
}
int KSpeech::say(const QString &text)
{
#if defined(HAVE_SPEECHD)
if (Q_UNLIKELY(!d->m_speechd)) {
kDebug() << "Null speech-dispatcher pointer";
return 0;
}
const QByteArray textbytes = text.toUtf8();
const int jobNum = spd_say(d->m_speechd, SPD_TEXT, textbytes.constData());
if (Q_UNLIKELY(jobNum < 0)) {
kWarning() << "Speech-dispatcher say failed";
return 0;
}
return jobNum;
#else
kWarning() << "KSpeech is a stub";
return 0;
#endif // HAVE_SPEECHD
}
bool KSpeech::removeAllJobs()
{
#if defined(HAVE_SPEECHD)
if (Q_UNLIKELY(!d->m_speechd)) {
kDebug() << "Null speech-dispatcher pointer";
return false;
}
const int speechdresult = spd_stop(d->m_speechd);
if (Q_UNLIKELY(speechdresult < 0)) {
kWarning() << "Speech-dispatcher stop failed";
return false;
}
return true;
#else
kWarning() << "KSpeech is a stub";
return false;
#endif
}
bool KSpeech::removeJob(int jobNum)
{
#if defined(HAVE_SPEECHD)
if (Q_UNLIKELY(!d->m_speechd)) {
kWarning() << "Null speech-dispatcher pointer";
return false;
}
const int speechdresult = spd_stop_uid(d->m_speechd, jobNum);
if (Q_UNLIKELY(speechdresult < 0)) {
kWarning() << "Speech-dispatcher stop uid failed";
return false;
}
return true;
#else
kWarning() << "KSpeech is a stub";
return false;
#endif
}
void KSpeech::_jobStateChanged(int jobNum, int state)
{
emit jobStateChanged(jobNum, state);
}
#include "kspeech.moc"

View file

@ -1,77 +0,0 @@
/* This file is part of the KDE libraries
Copyright (C) 2022 Ivailo Monev <xakepa10@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2, as published by the Free Software Foundation.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KSPEECH_H
#define KSPEECH_H
#include "kspeech_export.h"
#include <QObject>
class KSpeechPrivate;
/*!
Class for Text-To-Speech.
@since 4.22
*/
class KSPEECH_EXPORT KSpeech : public QObject
{
Q_OBJECT
public:
enum KSpeechState {
JobStarted = 0,
JobFinished = 1,
JobCanceled = 2
};
KSpeech(QObject* parent = nullptr);
~KSpeech();
static bool isSupported();
void setSpeechID(const QString &id);
int volume() const;
bool setVolume(const int volume);
int pitch() const;
bool setPitch(const int pitch);
QByteArray voice() const;
QList<QByteArray> voices() const;
bool setVoice(const QByteArray &voice);
public Q_SLOTS:
int say(const QString &text);
bool removeAllJobs();
bool removeJob(int jobNum);
Q_SIGNALS:
void jobStateChanged(int jobNum, int state);
private Q_SLOTS:
void _jobStateChanged(int jobNum, int state);
private:
Q_DISABLE_COPY(KSpeech);
KSpeechPrivate* const d;
};
#endif // KSPEECH_H

View file

@ -1,9 +0,0 @@
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/..
${CMAKE_CURRENT_BINARY_DIR}/..
)
kde4_add_test(kspeech-kspeechtest
kspeechtest.cpp
)
target_link_libraries(kspeech-kspeechtest ${KDE4_KSPEECH_LIBS} ${QT_QTTEST_LIBRARY})

View file

@ -1,103 +0,0 @@
/* This file is part of the KDE libraries
Copyright (C) 2022 Ivailo Monev <xakepa10@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2, as published by the Free Software Foundation.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "qtest_kde.h"
#include "kspeech.h"
#include "kdebug.h"
#include <QSignalSpy>
class KSpeechTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void cleanupTestCase();
void say_data();
void say();
void setters_and_getters_data();
void setters_and_getters();
};
QTEST_KDEMAIN_CORE(KSpeechTest)
void KSpeechTest::initTestCase()
{
if (!KSpeech::isSupported()) {
QSKIP("Built without speech-dispatcher", SkipAll);
}
}
void KSpeechTest::cleanupTestCase()
{
}
void KSpeechTest::say_data()
{
QTest::addColumn<QString>("saystr");
QTest::newRow("US-ASCII") << QString::fromAscii("foobar");
QTest::newRow("UTF-8") << QString::fromUtf8("лорем ипсум");
}
void KSpeechTest::say()
{
QFETCH(QString, saystr);
KSpeech kspeech(this);
QSignalSpy spyJobStateChanged(&kspeech, SIGNAL(jobStateChanged(int,int)));
QVERIFY(kspeech.say(saystr) != 0);
QTest::qWait(3000);
QVERIFY(spyJobStateChanged.count() == 2);
}
void KSpeechTest::setters_and_getters_data()
{
QTest::addColumn<int>("volume");
QTest::addColumn<int>("pitch");
QTest::addColumn<QByteArray>("voice");
QTest::addColumn<bool>("valid");
// check `spd-say -L` for valid voice
QTest::newRow("valid") << int(10) << int(2) << QByteArray("Slovak") << bool(true);
QTest::newRow("invalid") << int(-123) << int(321) << QByteArray("foobar") << bool(false);
}
void KSpeechTest::setters_and_getters()
{
QFETCH(int, volume);
QFETCH(int, pitch);
QFETCH(QByteArray, voice);
QFETCH(bool, valid);
KSpeech kspeech(this);
QCOMPARE(kspeech.setVolume(volume), valid);
QCOMPARE(kspeech.setPitch(pitch), valid);
QCOMPARE(kspeech.setVoice(voice), valid);
if (valid) {
QCOMPARE(kspeech.volume(), volume);
QCOMPARE(kspeech.pitch(), pitch);
QCOMPARE(kspeech.voice(), voice);
} else {
QCOMPARE(kspeech.volume(), 100);
QCOMPARE(kspeech.pitch(), 0);
QCOMPARE(kspeech.voice(), QByteArray());
}
}
#include "kspeechtest.moc"