mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-23 18:32:49 +00:00
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:
parent
c728d46d16
commit
4dbb501d25
26 changed files with 4 additions and 845 deletions
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -14,7 +14,6 @@ set(cmakeFilesDontInstall
|
|||
FindLibCDIO.cmake
|
||||
FindDevinfo.cmake
|
||||
FindFFmpeg.cmake
|
||||
FindSpeechd.cmake
|
||||
FindLibRaw.cmake
|
||||
FindLibJPEG.cmake
|
||||
FindOpenJPEG.cmake
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
|
||||
|
|
|
@ -268,7 +268,6 @@ install(
|
|||
KShortcutWidget
|
||||
KSortableItem
|
||||
KSortableList
|
||||
KSpeech
|
||||
KSplashScreen
|
||||
KSqueezedTextLabel
|
||||
KStandardDirs
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
#include "../kspeech.h"
|
|
@ -70,7 +70,6 @@
|
|||
51005 kpowermanager
|
||||
51006 kdnssd
|
||||
51007 khttp
|
||||
51008 kspeech
|
||||
51009 karchive
|
||||
51010 kemail
|
||||
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
/**
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -44,7 +44,6 @@ class KNotifyConfigActionsWidget : public QWidget
|
|||
void changed();
|
||||
private Q_SLOTS:
|
||||
void slotPlay();
|
||||
void slotKTTSComboChanged();
|
||||
private:
|
||||
Ui::KNotifyConfigActionsWidgetBase m_ui;
|
||||
};
|
||||
|
|
|
@ -100,48 +100,6 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" >
|
||||
<widget class="QCheckBox" name="KTTS_check" >
|
||||
<property name="text" >
|
||||
<string>Sp&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><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:<dl><dt>%e</dt><dd>Name of the event</dd><dt>%a</dt><dd>Application that sent the event</dd><dt>%m</dt><dd>The message sent by the application</dd></dl></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><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:<dl><dt>%e</dt><dd>Name of the event</dd><dt>%a</dt><dd>Application that sent the event</dd><dt>%m</dt><dd>The message sent by the application</dd></dl></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>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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*)));
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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()
|
|
@ -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"
|
|
@ -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
|
|
@ -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})
|
|
@ -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"
|
Loading…
Add table
Reference in a new issue