mirror of
https://github.com/xiaoyifang/goldendict-ng.git
synced 2024-12-04 16:14:05 +00:00
use QTextToSpeech module to play tts.
fix code smells
This commit is contained in:
parent
e0831bf36c
commit
2407cf2a88
|
@ -44,6 +44,7 @@ find_package(Qt6 REQUIRED COMPONENTS
|
||||||
Widgets
|
Widgets
|
||||||
Svg
|
Svg
|
||||||
Xml
|
Xml
|
||||||
|
TextToSpeech
|
||||||
)
|
)
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
|
@ -148,6 +149,7 @@ set(UI_SRC
|
||||||
preferences.ui
|
preferences.ui
|
||||||
scanpopup.ui
|
scanpopup.ui
|
||||||
sources.ui
|
sources.ui
|
||||||
|
texttospeechsource.ui
|
||||||
)
|
)
|
||||||
if(WITH_EPWING_SUPPORT)
|
if(WITH_EPWING_SUPPORT)
|
||||||
set(EPWING_SUPPORT_SRC
|
set(EPWING_SUPPORT_SRC
|
||||||
|
@ -167,28 +169,12 @@ if (APPLE)
|
||||||
machotkeywrapper.mm
|
machotkeywrapper.mm
|
||||||
macmouseover.hh
|
macmouseover.hh
|
||||||
macmouseover.mm
|
macmouseover.mm
|
||||||
speechclient.hh
|
|
||||||
speechclient_mac.mm
|
|
||||||
texttospeechsource.cc
|
|
||||||
texttospeechsource.hh
|
|
||||||
texttospeechsource.ui
|
|
||||||
src/platform/gd_clipboard.cpp
|
src/platform/gd_clipboard.cpp
|
||||||
src/platform/gd_clipboard.h
|
src/platform/gd_clipboard.h
|
||||||
)
|
)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if(MSVC)
|
|
||||||
set(WIN_SRC
|
|
||||||
guids.c # ONLY RELATED TO TTS
|
|
||||||
speechclient.hh
|
|
||||||
speechclient_win.cc
|
|
||||||
speechhlp.cc
|
|
||||||
speechhlp.hh
|
|
||||||
texttospeechsource.cc
|
|
||||||
texttospeechsource.hh
|
|
||||||
texttospeechsource.ui
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(WITH_FFMPEG_PLAYER)
|
if(WITH_FFMPEG_PLAYER)
|
||||||
set(FFMPEG_SUPPORT_SRC
|
set(FFMPEG_SUPPORT_SRC
|
||||||
|
@ -201,7 +187,6 @@ endif()
|
||||||
set(PROJECT_SOURCES
|
set(PROJECT_SOURCES
|
||||||
${UI_SRC}
|
${UI_SRC}
|
||||||
${MAC_SRC}
|
${MAC_SRC}
|
||||||
${WIN_SRC}
|
|
||||||
${QRC_RESOURCES}
|
${QRC_RESOURCES}
|
||||||
${EPWING_SUPPORT_SRC}
|
${EPWING_SUPPORT_SRC}
|
||||||
${FFMPEG_SUPPORT_SRC}
|
${FFMPEG_SUPPORT_SRC}
|
||||||
|
@ -404,7 +389,6 @@ set(PROJECT_SOURCES
|
||||||
sounddir.hh
|
sounddir.hh
|
||||||
sources.cc
|
sources.cc
|
||||||
sources.hh
|
sources.hh
|
||||||
sphelper.hh
|
|
||||||
splitfile.cc
|
splitfile.cc
|
||||||
splitfile.hh
|
splitfile.hh
|
||||||
sptr.hh
|
sptr.hh
|
||||||
|
@ -465,6 +449,10 @@ set(PROJECT_SOURCES
|
||||||
src/ui/articleview.cpp src/ui/articleview.h
|
src/ui/articleview.cpp src/ui/articleview.h
|
||||||
src/ui/ftssearchpanel.cpp src/ui/ftssearchpanel.h
|
src/ui/ftssearchpanel.cpp src/ui/ftssearchpanel.h
|
||||||
src/ui/searchpanel.cpp src/ui/searchpanel.h
|
src/ui/searchpanel.cpp src/ui/searchpanel.h
|
||||||
|
speechclient.hh
|
||||||
|
speechclient.cc
|
||||||
|
texttospeechsource.cc
|
||||||
|
texttospeechsource.hh
|
||||||
)
|
)
|
||||||
|
|
||||||
qt_add_executable(${CMAKE_PROJECT_NAME}
|
qt_add_executable(${CMAKE_PROJECT_NAME}
|
||||||
|
@ -494,6 +482,7 @@ target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
|
||||||
Qt6::WebEngineWidgets
|
Qt6::WebEngineWidgets
|
||||||
Qt6::Widgets
|
Qt6::Widgets
|
||||||
Qt6::Svg
|
Qt6::Svg
|
||||||
|
Qt6::TextToSpeech
|
||||||
)
|
)
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
|
|
48
config.cc
48
config.cc
|
@ -841,21 +841,24 @@ Class load()
|
||||||
{
|
{
|
||||||
QDomNodeList nl = ves.toElement().elementsByTagName( "voiceEngine" );
|
QDomNodeList nl = ves.toElement().elementsByTagName( "voiceEngine" );
|
||||||
|
|
||||||
for ( int x = 0; x < nl.length(); ++x )
|
for( int x = 0; x < nl.length(); ++x ) {
|
||||||
{
|
|
||||||
QDomElement ve = nl.item( x ).toElement();
|
QDomElement ve = nl.item( x ).toElement();
|
||||||
VoiceEngine v;
|
VoiceEngine v;
|
||||||
|
|
||||||
v.enabled = ve.attribute( "enabled" ) == "1";
|
v.enabled = ve.attribute( "enabled" ) == "1";
|
||||||
v.id = ve.attribute( "id" );
|
v.engine_name = ve.attribute( "engine_name" );
|
||||||
v.name = ve.attribute( "name" );
|
v.name = ve.attribute( "name" );
|
||||||
|
v.voice_name = ve.attribute( "voice_name" );
|
||||||
|
v.locale = QLocale( ve.attribute( "locale" ) );
|
||||||
v.iconFilename = ve.attribute( "icon" );
|
v.iconFilename = ve.attribute( "icon" );
|
||||||
v.volume = ve.attribute( "volume", "50" ).toInt();
|
v.volume = ve.attribute( "volume", "50" ).toInt();
|
||||||
if( v.volume < 0 || v.volume > 100 )
|
if ( ( v.volume < 0 ) || ( v.volume > 100 ) ) {
|
||||||
v.volume = 50;
|
v.volume = 50;
|
||||||
v.rate = ve.attribute( "rate", "50" ).toInt();
|
}
|
||||||
if( v.rate < 0 || v.rate > 100 )
|
v.rate = ve.attribute( "rate", "0" ).toInt();
|
||||||
v.rate = 50;
|
if ( ( v.rate < -10 ) || ( v.rate > 10 ) ) {
|
||||||
|
v.rate = 0;
|
||||||
|
}
|
||||||
c.voiceEngines.push_back( v );
|
c.voiceEngines.push_back( v );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1656,33 +1659,40 @@ void save( Class const & c )
|
||||||
QDomNode ves = dd.createElement( "voiceEngines" );
|
QDomNode ves = dd.createElement( "voiceEngines" );
|
||||||
root.appendChild( ves );
|
root.appendChild( ves );
|
||||||
|
|
||||||
for ( VoiceEngines::const_iterator i = c.voiceEngines.begin(); i != c.voiceEngines.end(); ++i )
|
for( const auto & voiceEngine : c.voiceEngines ) {
|
||||||
{
|
|
||||||
QDomElement v = dd.createElement( "voiceEngine" );
|
QDomElement v = dd.createElement( "voiceEngine" );
|
||||||
ves.appendChild( v );
|
ves.appendChild( v );
|
||||||
|
|
||||||
QDomAttr id = dd.createAttribute( "id" );
|
QDomAttr id = dd.createAttribute( "engine_name" );
|
||||||
id.setValue( i->id );
|
id.setValue( voiceEngine.engine_name );
|
||||||
v.setAttributeNode( id );
|
v.setAttributeNode( id );
|
||||||
|
|
||||||
|
QDomAttr locale = dd.createAttribute( "locale" );
|
||||||
|
locale.setValue( voiceEngine.locale.name() );
|
||||||
|
v.setAttributeNode( locale );
|
||||||
|
|
||||||
QDomAttr name = dd.createAttribute( "name" );
|
QDomAttr name = dd.createAttribute( "name" );
|
||||||
name.setValue( i->name );
|
name.setValue( voiceEngine.name );
|
||||||
v.setAttributeNode( name );
|
v.setAttributeNode( name );
|
||||||
|
|
||||||
|
QDomAttr voice_name = dd.createAttribute( "voice_name" );
|
||||||
|
voice_name.setValue( voiceEngine.voice_name );
|
||||||
|
v.setAttributeNode( voice_name );
|
||||||
|
|
||||||
QDomAttr enabled = dd.createAttribute( "enabled" );
|
QDomAttr enabled = dd.createAttribute( "enabled" );
|
||||||
enabled.setValue( i->enabled ? "1" : "0" );
|
enabled.setValue( voiceEngine.enabled ? "1" : "0" );
|
||||||
v.setAttributeNode( enabled );
|
v.setAttributeNode( enabled );
|
||||||
|
|
||||||
QDomAttr icon = dd.createAttribute( "icon" );
|
QDomAttr icon = dd.createAttribute( "icon" );
|
||||||
icon.setValue( i->iconFilename );
|
icon.setValue( voiceEngine.iconFilename );
|
||||||
v.setAttributeNode( icon );
|
v.setAttributeNode( icon );
|
||||||
|
|
||||||
QDomAttr volume = dd.createAttribute( "volume" );
|
QDomAttr volume = dd.createAttribute( "volume" );
|
||||||
volume.setValue( QString::number( i->volume ) );
|
volume.setValue( QString::number( voiceEngine.volume ) );
|
||||||
v.setAttributeNode( volume );
|
v.setAttributeNode( volume );
|
||||||
|
|
||||||
QDomAttr rate = dd.createAttribute( "rate" );
|
QDomAttr rate = dd.createAttribute( "rate" );
|
||||||
rate.setValue( QString::number( i->rate ) );
|
rate.setValue( QString::number( voiceEngine.rate ) );
|
||||||
v.setAttributeNode( rate );
|
v.setAttributeNode( rate );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
44
config.hh
44
config.hh
|
@ -13,6 +13,7 @@
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
#include "ex.hh"
|
#include "ex.hh"
|
||||||
|
#include <QLocale>
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
#include <QRect>
|
#include <QRect>
|
||||||
|
@ -639,32 +640,37 @@ typedef QVector< Program > Programs;
|
||||||
struct VoiceEngine
|
struct VoiceEngine
|
||||||
{
|
{
|
||||||
bool enabled;
|
bool enabled;
|
||||||
QString id;
|
//engine name.
|
||||||
|
QString engine_name;
|
||||||
QString name;
|
QString name;
|
||||||
|
//voice name.
|
||||||
|
QString voice_name;
|
||||||
QString iconFilename;
|
QString iconFilename;
|
||||||
int volume; // 0-100 allowed
|
QLocale locale;
|
||||||
int rate; // 0-100 allowed
|
int volume; // 0~1 allowed
|
||||||
|
int rate; // -1 ~ 1 allowed
|
||||||
|
|
||||||
VoiceEngine(): enabled( false )
|
VoiceEngine():
|
||||||
, volume( 50 )
|
enabled( false ),
|
||||||
, rate( 50 )
|
volume( 50 ),
|
||||||
{}
|
rate( 0 )
|
||||||
VoiceEngine( QString id_, QString name_, int volume_, int rate_ ):
|
{
|
||||||
enabled( false )
|
}
|
||||||
, id( id_ )
|
VoiceEngine( QString engine_nane_, QString name_, QString voice_name_, QLocale locale_, int volume_, int rate_ ):
|
||||||
, name( name_ )
|
enabled( false ),
|
||||||
, volume( volume_ )
|
engine_name( engine_nane_ ),
|
||||||
, rate( rate_ )
|
name( name_ ),
|
||||||
|
voice_name( voice_name_ ),
|
||||||
|
locale( locale_ ),
|
||||||
|
volume( volume_ ),
|
||||||
|
rate( rate_ )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool operator == ( VoiceEngine const & other ) const
|
bool operator == ( VoiceEngine const & other ) const
|
||||||
{
|
{
|
||||||
return enabled == other.enabled &&
|
return enabled == other.enabled && engine_name == other.engine_name && name == other.name
|
||||||
id == other.id &&
|
&& voice_name == other.voice_name && locale == other.locale && iconFilename == other.iconFilename
|
||||||
name == other.name &&
|
&& volume == other.volume && rate == other.rate;
|
||||||
iconFilename == other.iconFilename &&
|
|
||||||
volume == other.volume &&
|
|
||||||
rate == other.rate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator != ( VoiceEngine const & other ) const
|
bool operator != ( VoiceEngine const & other ) const
|
||||||
|
|
|
@ -45,7 +45,8 @@ QT += core \
|
||||||
webchannel\
|
webchannel\
|
||||||
printsupport \
|
printsupport \
|
||||||
help \
|
help \
|
||||||
concurrent
|
concurrent \
|
||||||
|
texttospeech
|
||||||
|
|
||||||
greaterThan(QT_MAJOR_VERSION, 5): QT += webenginecore core5compat
|
greaterThan(QT_MAJOR_VERSION, 5): QT += webenginecore core5compat
|
||||||
|
|
||||||
|
@ -246,8 +247,7 @@ mac {
|
||||||
}
|
}
|
||||||
|
|
||||||
OBJECTIVE_SOURCES += machotkeywrapper.mm \
|
OBJECTIVE_SOURCES += machotkeywrapper.mm \
|
||||||
macmouseover.mm \
|
macmouseover.mm
|
||||||
speechclient_mac.mm
|
|
||||||
ICON = icons/macicon.icns
|
ICON = icons/macicon.icns
|
||||||
QMAKE_INFO_PLIST = myInfo.plist
|
QMAKE_INFO_PLIST = myInfo.plist
|
||||||
|
|
||||||
|
@ -544,27 +544,17 @@ SOURCES += folding.cc \
|
||||||
src/ui/ftssearchpanel.cpp \
|
src/ui/ftssearchpanel.cpp \
|
||||||
src/ui/searchpanel.cpp
|
src/ui/searchpanel.cpp
|
||||||
|
|
||||||
win32 {
|
#speech to text
|
||||||
FORMS += texttospeechsource.ui
|
FORMS += texttospeechsource.ui
|
||||||
SOURCES += guids.c \
|
SOURCES += speechclient.cc \
|
||||||
speechclient_win.cc \
|
texttospeechsource.cc
|
||||||
texttospeechsource.cc \
|
HEADERS += texttospeechsource.hh \
|
||||||
speechhlp.cc
|
speechclient.hh
|
||||||
HEADERS += texttospeechsource.hh \
|
|
||||||
sapi.hh \
|
|
||||||
sphelper.hh \
|
|
||||||
speechclient.hh \
|
|
||||||
speechhlp.hh
|
|
||||||
}
|
|
||||||
|
|
||||||
mac {
|
mac {
|
||||||
HEADERS += macmouseover.hh \
|
HEADERS += macmouseover.hh \
|
||||||
texttospeechsource.hh \
|
|
||||||
speechclient.hh \
|
|
||||||
src/platform/gd_clipboard.h
|
src/platform/gd_clipboard.h
|
||||||
FORMS += texttospeechsource.ui
|
SOURCES += src/platform/gd_clipboard.cpp
|
||||||
SOURCES += texttospeechsource.cc \
|
|
||||||
src/platform/gd_clipboard.cpp
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unix:!mac {
|
unix:!mac {
|
||||||
|
@ -573,8 +563,8 @@ unix:!mac {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
HEADERS += wildcard.hh
|
HEADERS += wildcard.hh
|
||||||
SOURCES += wildcard.cc
|
SOURCES += wildcard.cc
|
||||||
|
|
||||||
|
|
||||||
CONFIG( zim_support ) {
|
CONFIG( zim_support ) {
|
||||||
|
|
11
sources.cc
11
sources.cc
|
@ -5,7 +5,6 @@
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QStandardItemModel>
|
#include <QStandardItemModel>
|
||||||
#include "gddebug.hh"
|
|
||||||
|
|
||||||
#ifdef MAKE_CHINESE_CONVERSION_SUPPORT
|
#ifdef MAKE_CHINESE_CONVERSION_SUPPORT
|
||||||
#include "chineseconversion.hh"
|
#include "chineseconversion.hh"
|
||||||
|
@ -17,9 +16,7 @@ Sources::Sources( QWidget * parent, Config::Class const & cfg):
|
||||||
#ifdef MAKE_CHINESE_CONVERSION_SUPPORT
|
#ifdef MAKE_CHINESE_CONVERSION_SUPPORT
|
||||||
chineseConversion( new ChineseConversion( this, cfg.transliteration.chinese ) ),
|
chineseConversion( new ChineseConversion( this, cfg.transliteration.chinese ) ),
|
||||||
#endif
|
#endif
|
||||||
#if defined( Q_OS_WIN32 ) || defined( Q_OS_MAC )
|
textToSpeechSource( nullptr ),
|
||||||
textToSpeechSource( NULL ),
|
|
||||||
#endif
|
|
||||||
itemDelegate( new QItemDelegate( this ) ),
|
itemDelegate( new QItemDelegate( this ) ),
|
||||||
itemEditorFactory( new QItemEditorFactory() ),
|
itemEditorFactory( new QItemEditorFactory() ),
|
||||||
mediawikisModel( this, cfg.mediawikis ),
|
mediawikisModel( this, cfg.mediawikis ),
|
||||||
|
@ -124,10 +121,8 @@ Sources::Sources( QWidget * parent, Config::Class const & cfg):
|
||||||
ui.forvoLanguageCodes->setText( forvo.languageCodes );
|
ui.forvoLanguageCodes->setText( forvo.languageCodes );
|
||||||
|
|
||||||
// Text to speech
|
// Text to speech
|
||||||
#if defined( Q_OS_WIN32 ) || defined( Q_OS_MAC )
|
|
||||||
textToSpeechSource = new TextToSpeechSource( this, cfg.voiceEngines );
|
textToSpeechSource = new TextToSpeechSource( this, cfg.voiceEngines );
|
||||||
ui.tabWidget->addTab( textToSpeechSource, QIcon(":/icons/text2speech.svg"), tr( "Text to Speech" ) );
|
ui.tabWidget->addTab( textToSpeechSource, QIcon(":/icons/text2speech.svg"), tr( "Text to Speech" ) );
|
||||||
#endif
|
|
||||||
|
|
||||||
if ( Config::isPortableVersion() )
|
if ( Config::isPortableVersion() )
|
||||||
{
|
{
|
||||||
|
@ -341,13 +336,9 @@ void Sources::on_removeProgram_clicked()
|
||||||
|
|
||||||
Config::VoiceEngines Sources::getVoiceEngines() const
|
Config::VoiceEngines Sources::getVoiceEngines() const
|
||||||
{
|
{
|
||||||
#if defined( Q_OS_WIN32 ) || defined( Q_OS_MAC )
|
|
||||||
if ( !textToSpeechSource )
|
if ( !textToSpeechSource )
|
||||||
return Config::VoiceEngines();
|
return Config::VoiceEngines();
|
||||||
return textToSpeechSource->getVoiceEnginesModel().getCurrentVoiceEngines();
|
return textToSpeechSource->getVoiceEnginesModel().getCurrentVoiceEngines();
|
||||||
#else
|
|
||||||
return Config::VoiceEngines();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Config::Hunspell Sources::getHunspell() const
|
Config::Hunspell Sources::getHunspell() const
|
||||||
|
|
|
@ -12,9 +12,7 @@
|
||||||
#include <QItemDelegate>
|
#include <QItemDelegate>
|
||||||
#include <QItemEditorFactory>
|
#include <QItemEditorFactory>
|
||||||
|
|
||||||
#if defined( Q_OS_WIN32 ) || defined( Q_OS_MAC )
|
|
||||||
#include "texttospeechsource.hh"
|
#include "texttospeechsource.hh"
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef MAKE_CHINESE_CONVERSION_SUPPORT
|
#ifdef MAKE_CHINESE_CONVERSION_SUPPORT
|
||||||
// Forward declaration
|
// Forward declaration
|
||||||
|
@ -295,9 +293,7 @@ private:
|
||||||
ChineseConversion *chineseConversion;
|
ChineseConversion *chineseConversion;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined( Q_OS_WIN32 ) || defined( Q_OS_MAC )
|
|
||||||
TextToSpeechSource *textToSpeechSource;
|
TextToSpeechSource *textToSpeechSource;
|
||||||
#endif
|
|
||||||
|
|
||||||
QItemDelegate * itemDelegate;
|
QItemDelegate * itemDelegate;
|
||||||
QItemEditorFactory * itemEditorFactory;
|
QItemEditorFactory * itemEditorFactory;
|
||||||
|
|
33
sources.ui
33
sources.ui
|
@ -33,7 +33,7 @@
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="filesTab">
|
<widget class="QWidget" name="filesTab">
|
||||||
<attribute name="icon">
|
<attribute name="icon">
|
||||||
<iconset resource="resources.qrc">
|
<iconset>
|
||||||
<normaloff>:/icons/folders.svg</normaloff>:/icons/folders.svg</iconset>
|
<normaloff>:/icons/folders.svg</normaloff>:/icons/folders.svg</iconset>
|
||||||
</attribute>
|
</attribute>
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
|
@ -96,7 +96,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tab">
|
<widget class="QWidget" name="tab">
|
||||||
<attribute name="icon">
|
<attribute name="icon">
|
||||||
<iconset resource="resources.qrc">
|
<iconset>
|
||||||
<normaloff>:/icons/folder-sound.svg</normaloff>:/icons/folder-sound.svg</iconset>
|
<normaloff>:/icons/folder-sound.svg</normaloff>:/icons/folder-sound.svg</iconset>
|
||||||
</attribute>
|
</attribute>
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
|
@ -152,7 +152,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tab_2">
|
<widget class="QWidget" name="tab_2">
|
||||||
<attribute name="icon">
|
<attribute name="icon">
|
||||||
<iconset resource="resources.qrc">
|
<iconset>
|
||||||
<normaloff>:/icons/icon32_hunspell.png</normaloff>:/icons/icon32_hunspell.png</iconset>
|
<normaloff>:/icons/icon32_hunspell.png</normaloff>:/icons/icon32_hunspell.png</iconset>
|
||||||
</attribute>
|
</attribute>
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
|
@ -220,7 +220,7 @@ of the appropriate groups to use them.</string>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="mediaWikisTab">
|
<widget class="QWidget" name="mediaWikisTab">
|
||||||
<attribute name="icon">
|
<attribute name="icon">
|
||||||
<iconset resource="resources.qrc">
|
<iconset>
|
||||||
<normaloff>:/icons/icon32_wiki.png</normaloff>:/icons/icon32_wiki.png</iconset>
|
<normaloff>:/icons/icon32_wiki.png</normaloff>:/icons/icon32_wiki.png</iconset>
|
||||||
</attribute>
|
</attribute>
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
|
@ -279,7 +279,7 @@ of the appropriate groups to use them.</string>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tab_4">
|
<widget class="QWidget" name="tab_4">
|
||||||
<attribute name="icon">
|
<attribute name="icon">
|
||||||
<iconset resource="resources.qrc">
|
<iconset>
|
||||||
<normaloff>:/icons/internet.svg</normaloff>:/icons/internet.svg</iconset>
|
<normaloff>:/icons/internet.svg</normaloff>:/icons/internet.svg</iconset>
|
||||||
</attribute>
|
</attribute>
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
|
@ -343,7 +343,7 @@ of the appropriate groups to use them.</string>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="dictdTab">
|
<widget class="QWidget" name="dictdTab">
|
||||||
<attribute name="icon">
|
<attribute name="icon">
|
||||||
<iconset resource="resources.qrc">
|
<iconset>
|
||||||
<normaloff>:/icons/network.svg</normaloff>:/icons/network.svg</iconset>
|
<normaloff>:/icons/network.svg</normaloff>:/icons/network.svg</iconset>
|
||||||
</attribute>
|
</attribute>
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
|
@ -399,7 +399,7 @@ of the appropriate groups to use them.</string>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tab_6">
|
<widget class="QWidget" name="tab_6">
|
||||||
<attribute name="icon">
|
<attribute name="icon">
|
||||||
<iconset resource="resources.qrc">
|
<iconset>
|
||||||
<normaloff>:/icons/programs.svg</normaloff>:/icons/programs.svg</iconset>
|
<normaloff>:/icons/programs.svg</normaloff>:/icons/programs.svg</iconset>
|
||||||
</attribute>
|
</attribute>
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
|
@ -458,7 +458,7 @@ of the appropriate groups to use them.</string>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tab_lingua">
|
<widget class="QWidget" name="tab_lingua">
|
||||||
<attribute name="icon">
|
<attribute name="icon">
|
||||||
<iconset resource="resources.qrc">
|
<iconset>
|
||||||
<normaloff>:/icons/lingualibre.svg</normaloff>:/icons/lingualibre.svg</iconset>
|
<normaloff>:/icons/lingualibre.svg</normaloff>:/icons/lingualibre.svg</iconset>
|
||||||
</attribute>
|
</attribute>
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
|
@ -526,7 +526,7 @@ Full list of availiable languages can be found <a href="https://linguali
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tab_5">
|
<widget class="QWidget" name="tab_5">
|
||||||
<attribute name="icon">
|
<attribute name="icon">
|
||||||
<iconset resource="resources.qrc">
|
<iconset>
|
||||||
<normaloff>:/icons/forvo.png</normaloff>:/icons/forvo.png</iconset>
|
<normaloff>:/icons/forvo.png</normaloff>:/icons/forvo.png</iconset>
|
||||||
</attribute>
|
</attribute>
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
|
@ -674,7 +674,7 @@ Full list of availiable languages can be found <a href="https://linguali
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tab_3">
|
<widget class="QWidget" name="tab_3">
|
||||||
<attribute name="icon">
|
<attribute name="icon">
|
||||||
<iconset resource="resources.qrc">
|
<iconset>
|
||||||
<normaloff>:/icons/transliteration.png</normaloff>:/icons/transliteration.png</iconset>
|
<normaloff>:/icons/transliteration.png</normaloff>:/icons/transliteration.png</iconset>
|
||||||
</attribute>
|
</attribute>
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
|
@ -692,7 +692,7 @@ Full list of availiable languages can be found <a href="https://linguali
|
||||||
<string>Greek transliteration</string>
|
<string>Greek transliteration</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="flags.qrc">
|
<iconset>
|
||||||
<normaloff>:/flags/gr.png</normaloff>:/flags/gr.png</iconset>
|
<normaloff>:/flags/gr.png</normaloff>:/flags/gr.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -703,7 +703,7 @@ Full list of availiable languages can be found <a href="https://linguali
|
||||||
<string>Russian transliteration</string>
|
<string>Russian transliteration</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="flags.qrc">
|
<iconset>
|
||||||
<normaloff>:/flags/ru.png</normaloff>:/flags/ru.png</iconset>
|
<normaloff>:/flags/ru.png</normaloff>:/flags/ru.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -714,7 +714,7 @@ Full list of availiable languages can be found <a href="https://linguali
|
||||||
<string>German transliteration</string>
|
<string>German transliteration</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="flags.qrc">
|
<iconset>
|
||||||
<normaloff>:/flags/de.png</normaloff>:/flags/de.png</iconset>
|
<normaloff>:/flags/de.png</normaloff>:/flags/de.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -725,7 +725,7 @@ Full list of availiable languages can be found <a href="https://linguali
|
||||||
<string>Belarusian transliteration</string>
|
<string>Belarusian transliteration</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="flags.qrc">
|
<iconset>
|
||||||
<normaloff>:/flags/by.png</normaloff>:/flags/by.png</iconset>
|
<normaloff>:/flags/by.png</normaloff>:/flags/by.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -857,9 +857,6 @@ Not implemented yet in GoldenDict.</string>
|
||||||
<tabstop>removeSoundDir</tabstop>
|
<tabstop>removeSoundDir</tabstop>
|
||||||
<tabstop>paths</tabstop>
|
<tabstop>paths</tabstop>
|
||||||
</tabstops>
|
</tabstops>
|
||||||
<resources>
|
<resources/>
|
||||||
<include location="flags.qrc"/>
|
|
||||||
<include location="resources.qrc"/>
|
|
||||||
</resources>
|
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
|
53
speechclient.cc
Normal file
53
speechclient.cc
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
#include "speechclient.hh"
|
||||||
|
|
||||||
|
#include <QtCore>
|
||||||
|
|
||||||
|
SpeechClient::SpeechClient( Config::VoiceEngine const & e, QObject * parent ):
|
||||||
|
QObject( parent ),
|
||||||
|
internalData( std::make_unique< InternalData >( e ) )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SpeechClient::Engines SpeechClient::availableEngines()
|
||||||
|
{
|
||||||
|
Engines engines;
|
||||||
|
const auto innerEngines = QTextToSpeech::availableEngines();
|
||||||
|
|
||||||
|
for( const auto & engine_name : innerEngines ) {
|
||||||
|
std::unique_ptr< QTextToSpeech > sp( std::make_unique< QTextToSpeech >( engine_name ) );
|
||||||
|
const QVector< QLocale > locales = sp->availableLocales();
|
||||||
|
for( const QLocale & locale : locales ) {
|
||||||
|
//on some platforms ,change the locale will change voices too.
|
||||||
|
sp->setLocale( locale );
|
||||||
|
for( const QVoice & voice : sp->availableVoices() ) {
|
||||||
|
QString name( QString( "%4 - %3 %1 (%2)" )
|
||||||
|
.arg( QLocale::languageToString( locale.language() ),
|
||||||
|
( QLocale::countryToString( locale.country() ) ),
|
||||||
|
voice.name(),
|
||||||
|
engine_name ) );
|
||||||
|
SpeechClient::Engine engine( Config::VoiceEngine( engine_name, name, voice.name(), locale, 50, 0 ) );
|
||||||
|
engines.push_back( engine );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return engines;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SpeechClient::tell( QString const & text, int volume, int rate )
|
||||||
|
{
|
||||||
|
if( internalData->sp->state() != QTextToSpeech::Ready )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
internalData->sp->setVolume( volume / 100.0 );
|
||||||
|
internalData->sp->setRate( rate / 10.0 );
|
||||||
|
|
||||||
|
internalData->sp->say( text );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SpeechClient::tell( QString const & text )
|
||||||
|
{
|
||||||
|
return tell(text, internalData->engine.volume, internalData->engine.rate);
|
||||||
|
}
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include "config.hh"
|
#include "config.hh"
|
||||||
|
#include <QTextToSpeech>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
class SpeechClient: public QObject
|
class SpeechClient: public QObject
|
||||||
{
|
{
|
||||||
|
@ -12,42 +14,65 @@ public:
|
||||||
|
|
||||||
struct Engine
|
struct Engine
|
||||||
{
|
{
|
||||||
QString id;
|
//engine name
|
||||||
|
QString engine_name;
|
||||||
QString name;
|
QString name;
|
||||||
// Volume and rate may vary from 0 to 100
|
//voice name
|
||||||
|
QString voice_name;
|
||||||
|
QString locale;
|
||||||
|
// Volume vary from 0~1 and rate vary from -1 to 1
|
||||||
int volume;
|
int volume;
|
||||||
int rate;
|
int rate;
|
||||||
Engine( Config::VoiceEngine const & e ) :
|
explicit Engine( Config::VoiceEngine const & e ):
|
||||||
id( e.id )
|
engine_name( e.engine_name ),
|
||||||
, name( e.name )
|
name( e.name ),
|
||||||
, volume( e.volume )
|
voice_name( e.voice_name ),
|
||||||
, rate( e.rate )
|
locale( e.locale.name() ),
|
||||||
{}
|
volume( e.volume ),
|
||||||
|
rate( e.rate )
|
||||||
|
{
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef QList<Engine> Engines;
|
struct InternalData
|
||||||
|
{
|
||||||
|
explicit InternalData( Config::VoiceEngine const & e ):
|
||||||
|
sp( std::make_unique< QTextToSpeech >( e.engine_name ) ),
|
||||||
|
engine( e )
|
||||||
|
{
|
||||||
|
sp->setLocale( e.locale );
|
||||||
|
auto voices = sp->availableVoices();
|
||||||
|
for( const auto & voice : voices ) {
|
||||||
|
if( voice.name() == e.voice_name ) {
|
||||||
|
sp->setVoice( voice );
|
||||||
|
|
||||||
SpeechClient( Config::VoiceEngine const & e, QObject * parent = 0L );
|
break;
|
||||||
virtual ~SpeechClient();
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sp->setVolume( e.volume / 100.0 );
|
||||||
|
sp->setRate( e.rate / 10.0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr< QTextToSpeech > sp;
|
||||||
|
Engine engine;
|
||||||
|
};
|
||||||
|
|
||||||
|
using Engines = QList< Engine >;
|
||||||
|
|
||||||
|
explicit SpeechClient( Config::VoiceEngine const & e, QObject * parent = nullptr );
|
||||||
|
|
||||||
static Engines availableEngines();
|
static Engines availableEngines();
|
||||||
|
|
||||||
const Engine & engine() const;
|
bool tell( QString const & text, int volume, int rate );
|
||||||
|
bool tell( QString const & text );
|
||||||
|
|
||||||
bool tell( QString const & text, int volume = -1, int rate = -1 );
|
signals:
|
||||||
bool say( QString const & text, int volume = -1, int rate = -1 );
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void started( bool ok );
|
void started( bool ok );
|
||||||
void finished();
|
void finished();
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
virtual void timerEvent( QTimerEvent * evt );
|
std::unique_ptr< InternalData > internalData;
|
||||||
|
|
||||||
private:
|
|
||||||
struct InternalData;
|
|
||||||
InternalData * internalData;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __SPEECHCLIENT_HH_INCLUDED__
|
#endif // __SPEECHCLIENT_HH_INCLUDED__
|
||||||
|
|
|
@ -1,191 +0,0 @@
|
||||||
#include "speechclient.hh"
|
|
||||||
|
|
||||||
#include <QtCore>
|
|
||||||
#include <AppKit/NSSpeechSynthesizer.h>
|
|
||||||
#include <Foundation/NSArray.h>
|
|
||||||
#include <Foundation/NSError.h>
|
|
||||||
#include <Foundation/NSString.h>
|
|
||||||
#include <Foundation/NSAutoreleasePool.h>
|
|
||||||
|
|
||||||
static QString NSStringToQString(const NSString * nsstr )
|
|
||||||
{
|
|
||||||
return QString::fromUtf8( [ nsstr UTF8String ] );
|
|
||||||
}
|
|
||||||
|
|
||||||
static NSString * QStringToNSString( QString const & qstr, bool needAlloc = false )
|
|
||||||
{
|
|
||||||
if( needAlloc )
|
|
||||||
return [ [ NSString alloc ] initWithUTF8String : qstr.toUtf8().data() ];
|
|
||||||
return [ NSString stringWithUTF8String : qstr.toUtf8().data() ];
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SpeechClient::InternalData
|
|
||||||
{
|
|
||||||
InternalData( Config::VoiceEngine const & e ):
|
|
||||||
waitingFinish( false )
|
|
||||||
, engine( e )
|
|
||||||
, oldVolume( -1 )
|
|
||||||
, oldRate( -1 )
|
|
||||||
, stringToPlay( nil )
|
|
||||||
{
|
|
||||||
NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
|
|
||||||
sp = [ [ NSSpeechSynthesizer alloc ] initWithVoice : QStringToNSString( e.id ) ];
|
|
||||||
[ pool drain ];
|
|
||||||
}
|
|
||||||
|
|
||||||
~InternalData()
|
|
||||||
{
|
|
||||||
[ sp release ];
|
|
||||||
if( stringToPlay != nil )
|
|
||||||
[ stringToPlay release ];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSSpeechSynthesizer * sp;
|
|
||||||
bool waitingFinish;
|
|
||||||
SpeechClient::Engine engine;
|
|
||||||
float oldVolume;
|
|
||||||
float oldRate;
|
|
||||||
QString oldMode;
|
|
||||||
NSString * stringToPlay;
|
|
||||||
};
|
|
||||||
|
|
||||||
SpeechClient::SpeechClient( Config::VoiceEngine const & e, QObject * parent ):
|
|
||||||
QObject( parent ),
|
|
||||||
internalData( new InternalData( e ) )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SpeechClient::~SpeechClient()
|
|
||||||
{
|
|
||||||
delete internalData;
|
|
||||||
}
|
|
||||||
|
|
||||||
SpeechClient::Engines SpeechClient::availableEngines()
|
|
||||||
{
|
|
||||||
Engines engines;
|
|
||||||
NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
|
|
||||||
|
|
||||||
NSArray * voices = [ NSSpeechSynthesizer availableVoices ];
|
|
||||||
int voicesNum = [ voices count ];
|
|
||||||
for( int i = 0; i < voicesNum; i++ )
|
|
||||||
{
|
|
||||||
QString id = NSStringToQString( [ voices objectAtIndex : i ] );
|
|
||||||
QString name;
|
|
||||||
int n = id.lastIndexOf( '.' );
|
|
||||||
if( n >= 0 )
|
|
||||||
name = id.right( id.size() - n - 1 );
|
|
||||||
else
|
|
||||||
name = id;
|
|
||||||
engines.push_back( SpeechClient::Engine( Config::VoiceEngine(
|
|
||||||
id, name, 50, 50 ) ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
[ pool drain ];
|
|
||||||
return engines;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SpeechClient::Engine & SpeechClient::engine() const
|
|
||||||
{
|
|
||||||
return internalData->engine;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SpeechClient::tell( QString const & text, int volume, int rate )
|
|
||||||
{
|
|
||||||
if( !internalData->sp || [ NSSpeechSynthesizer isAnyApplicationSpeaking ] )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ( internalData->waitingFinish )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if( volume < 0 )
|
|
||||||
volume = engine().volume;
|
|
||||||
if( rate < 0 )
|
|
||||||
rate = engine().rate;
|
|
||||||
|
|
||||||
NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
|
|
||||||
|
|
||||||
internalData->oldVolume = [ internalData->sp volume ];
|
|
||||||
[ internalData->sp setVolume : ( volume / 100.0 ) ];
|
|
||||||
|
|
||||||
internalData->oldRate = [ internalData->sp rate ];
|
|
||||||
[ internalData->sp setRate : ( rate * 2.0 + 100 ) ];
|
|
||||||
|
|
||||||
NSError * err = nil;
|
|
||||||
NSString * oldMode = [ internalData->sp objectForProperty : NSSpeechInputModeProperty error : &err ];
|
|
||||||
if( err == nil || [ err code ] == 0 )
|
|
||||||
{
|
|
||||||
internalData->oldMode = NSStringToQString( oldMode );
|
|
||||||
[ internalData->sp setObject : NSSpeechModeText forProperty : NSSpeechInputModeProperty error : &err ];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
internalData->oldMode.clear();
|
|
||||||
|
|
||||||
internalData->stringToPlay = QStringToNSString( text, true );
|
|
||||||
|
|
||||||
bool ok = [ internalData->sp startSpeakingString : internalData->stringToPlay ];
|
|
||||||
|
|
||||||
emit started( ok );
|
|
||||||
|
|
||||||
if ( ok )
|
|
||||||
{
|
|
||||||
internalData->waitingFinish = true;
|
|
||||||
startTimer( 50 );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if( internalData->stringToPlay != nil )
|
|
||||||
[ internalData->stringToPlay release ];
|
|
||||||
internalData->stringToPlay = nil;
|
|
||||||
emit finished();
|
|
||||||
}
|
|
||||||
|
|
||||||
[ pool drain ];
|
|
||||||
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SpeechClient::say( QString const & text, int volume, int rate )
|
|
||||||
{
|
|
||||||
(void) text;
|
|
||||||
(void) volume;
|
|
||||||
(void) rate;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpeechClient::timerEvent( QTimerEvent * evt )
|
|
||||||
{
|
|
||||||
QObject::timerEvent( evt );
|
|
||||||
|
|
||||||
if ( !internalData->waitingFinish )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( ![ internalData->sp isSpeaking ] )
|
|
||||||
{
|
|
||||||
killTimer( evt->timerId() ) ;
|
|
||||||
internalData->waitingFinish = false;
|
|
||||||
|
|
||||||
NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
|
|
||||||
|
|
||||||
if( internalData->oldVolume >=0 )
|
|
||||||
[ internalData->sp setVolume : internalData->oldVolume ];
|
|
||||||
if( internalData->oldRate >=0 )
|
|
||||||
[ internalData->sp setRate : internalData->oldRate ];
|
|
||||||
internalData->oldVolume = -1;
|
|
||||||
internalData->oldRate = -1;
|
|
||||||
|
|
||||||
NSError * err;
|
|
||||||
if( !internalData->oldMode.isEmpty() )
|
|
||||||
[ internalData->sp setObject : QStringToNSString( internalData->oldMode )
|
|
||||||
forProperty : NSSpeechInputModeProperty error : &err ];
|
|
||||||
|
|
||||||
internalData->oldMode.clear();
|
|
||||||
|
|
||||||
if( internalData->stringToPlay != nil )
|
|
||||||
[ internalData->stringToPlay release ];
|
|
||||||
internalData->stringToPlay = nil;
|
|
||||||
|
|
||||||
[ pool drain ];
|
|
||||||
|
|
||||||
emit finished();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,144 +0,0 @@
|
||||||
#include "speechclient.hh"
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include "speechhlp.hh"
|
|
||||||
|
|
||||||
#include <QtCore>
|
|
||||||
|
|
||||||
struct SpeechClient::InternalData
|
|
||||||
{
|
|
||||||
InternalData( Config::VoiceEngine const & e ):
|
|
||||||
waitingFinish( false )
|
|
||||||
, engine( e )
|
|
||||||
, oldVolume( -1 )
|
|
||||||
, oldRate( -1 )
|
|
||||||
{
|
|
||||||
sp = speechCreate( e.id.toStdWString().c_str() );
|
|
||||||
}
|
|
||||||
|
|
||||||
~InternalData()
|
|
||||||
{
|
|
||||||
speechDestroy( sp );
|
|
||||||
}
|
|
||||||
|
|
||||||
SpeechHelper sp;
|
|
||||||
bool waitingFinish;
|
|
||||||
Engine engine;
|
|
||||||
int oldVolume;
|
|
||||||
int oldRate;
|
|
||||||
};
|
|
||||||
|
|
||||||
SpeechClient::SpeechClient( Config::VoiceEngine const & e, QObject * parent ):
|
|
||||||
QObject( parent ),
|
|
||||||
internalData( new InternalData( e ) )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SpeechClient::~SpeechClient()
|
|
||||||
{
|
|
||||||
delete internalData;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool enumEngines( void * /* token */,
|
|
||||||
const wchar_t * id,
|
|
||||||
const wchar_t * name,
|
|
||||||
void * userData )
|
|
||||||
{
|
|
||||||
SpeechClient::Engines * pEngines = ( SpeechClient::Engines * )userData;
|
|
||||||
SpeechClient::Engine engine( Config::VoiceEngine(
|
|
||||||
QString::fromWCharArray( id ),
|
|
||||||
QString::fromWCharArray( name ),
|
|
||||||
50, 50 ) );
|
|
||||||
pEngines->push_back( engine );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
SpeechClient::Engines SpeechClient::availableEngines()
|
|
||||||
{
|
|
||||||
Engines engines;
|
|
||||||
speechEnumerateAvailableEngines( enumEngines, &engines );
|
|
||||||
return engines;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SpeechClient::Engine & SpeechClient::engine() const
|
|
||||||
{
|
|
||||||
return internalData->engine;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SpeechClient::tell( QString const & text, int volume, int rate )
|
|
||||||
{
|
|
||||||
if ( !speechAvailable( internalData->sp ) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ( internalData->waitingFinish )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if( volume < 0 )
|
|
||||||
volume = engine().volume;
|
|
||||||
if( rate < 0 )
|
|
||||||
rate = engine().rate;
|
|
||||||
|
|
||||||
internalData->oldVolume = setSpeechVolume( internalData->sp, volume );
|
|
||||||
internalData->oldRate = setSpeechRate( internalData->sp, rate );
|
|
||||||
|
|
||||||
bool ok = speechTell( internalData->sp, text.toStdWString().c_str() );
|
|
||||||
|
|
||||||
emit started( ok );
|
|
||||||
|
|
||||||
if ( ok )
|
|
||||||
{
|
|
||||||
internalData->waitingFinish = true;
|
|
||||||
startTimer( 50 );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
emit finished();
|
|
||||||
}
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SpeechClient::say( QString const & text, int volume, int rate )
|
|
||||||
{
|
|
||||||
if ( !speechAvailable( internalData->sp ) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if( volume < 0 )
|
|
||||||
volume = engine().volume;
|
|
||||||
if( rate < 0 )
|
|
||||||
rate = engine().rate;
|
|
||||||
|
|
||||||
int oldVolume = setSpeechVolume( internalData->sp, volume );
|
|
||||||
int oldRate = setSpeechRate( internalData->sp, rate );
|
|
||||||
|
|
||||||
bool ok = speechSay( internalData->sp, text.toStdWString().c_str() );
|
|
||||||
|
|
||||||
if( oldVolume >=0 )
|
|
||||||
setSpeechVolume( internalData->sp, oldVolume );
|
|
||||||
if( oldRate >=0 )
|
|
||||||
setSpeechRate( internalData->sp, oldRate );
|
|
||||||
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpeechClient::timerEvent( QTimerEvent * evt )
|
|
||||||
{
|
|
||||||
QObject::timerEvent( evt );
|
|
||||||
|
|
||||||
if ( !internalData->waitingFinish )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( speechTellFinished( internalData->sp ) )
|
|
||||||
{
|
|
||||||
killTimer( evt->timerId() ) ;
|
|
||||||
internalData->waitingFinish = false;
|
|
||||||
|
|
||||||
if( internalData->oldVolume >=0 )
|
|
||||||
setSpeechVolume( internalData->sp, internalData->oldVolume );
|
|
||||||
if( internalData->oldRate >=0 )
|
|
||||||
setSpeechRate( internalData->sp, internalData->oldRate );
|
|
||||||
internalData->oldVolume = -1;
|
|
||||||
internalData->oldRate = -1;
|
|
||||||
|
|
||||||
emit finished();
|
|
||||||
}
|
|
||||||
}
|
|
191
speechhlp.cc
191
speechhlp.cc
|
@ -1,191 +0,0 @@
|
||||||
#define WINVER 0x0500 // At least WinXP required
|
|
||||||
#include <windows.h>
|
|
||||||
#include <limits.h>
|
|
||||||
|
|
||||||
#include "speechhlp.hh"
|
|
||||||
#include <string>
|
|
||||||
#include "sapi.hh"
|
|
||||||
#include "sphelper.hh"
|
|
||||||
|
|
||||||
using std::wstring;
|
|
||||||
|
|
||||||
struct _SpeechHelper
|
|
||||||
{
|
|
||||||
ISpVoice * voice;
|
|
||||||
wstring engineId;
|
|
||||||
wstring engineName;
|
|
||||||
bool willInvokeCoUninitialize;
|
|
||||||
|
|
||||||
_SpeechHelper() :
|
|
||||||
willInvokeCoUninitialize(false)
|
|
||||||
{
|
|
||||||
HRESULT hr;
|
|
||||||
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
|
||||||
willInvokeCoUninitialize = (hr != RPC_E_CHANGED_MODE);
|
|
||||||
CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_INPROC_SERVER, IID_ISpVoice, (void**)&voice);
|
|
||||||
}
|
|
||||||
|
|
||||||
~_SpeechHelper()
|
|
||||||
{
|
|
||||||
if (voice)
|
|
||||||
voice->Release();
|
|
||||||
|
|
||||||
if (willInvokeCoUninitialize)
|
|
||||||
CoUninitialize();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
SpeechHelper speechCreate(const wchar_t *engineId)
|
|
||||||
{
|
|
||||||
SpeechHelper sp = new _SpeechHelper();
|
|
||||||
HRESULT hr;
|
|
||||||
ISpObjectToken * spToken;
|
|
||||||
|
|
||||||
hr = SpGetTokenFromId(engineId, &spToken);
|
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
if (SUCCEEDED(hr) && sp->voice)
|
|
||||||
{
|
|
||||||
sp->voice->SetVoice(spToken);
|
|
||||||
|
|
||||||
WCHAR * engineName = NULL;
|
|
||||||
SpGetDescription( spToken, &engineName );
|
|
||||||
sp->engineId = engineId;
|
|
||||||
if (engineName)
|
|
||||||
{
|
|
||||||
sp->engineName = engineName;
|
|
||||||
CoTaskMemFree(engineName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spToken->Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
return sp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void speechDestroy(SpeechHelper sp)
|
|
||||||
{
|
|
||||||
delete sp;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool speechAvailable(SpeechHelper sp)
|
|
||||||
{
|
|
||||||
if (!sp)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return !!(sp->voice);
|
|
||||||
}
|
|
||||||
|
|
||||||
void speechEnumerateAvailableEngines(EnumerateCallback callback, void *userData)
|
|
||||||
{
|
|
||||||
HRESULT hr;
|
|
||||||
IEnumSpObjectTokens * enumSpTokens = NULL;
|
|
||||||
ULONG count = 0;
|
|
||||||
bool next = true;
|
|
||||||
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
|
||||||
bool willInvokeCoUninitialize = (hr != RPC_E_CHANGED_MODE);
|
|
||||||
hr = SpEnumTokens(SPCAT_VOICES, NULL, NULL, &enumSpTokens);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
hr = enumSpTokens->GetCount(&count);
|
|
||||||
|
|
||||||
for (ULONG i = 0; i < count && next; i++)
|
|
||||||
{
|
|
||||||
ISpObjectToken * spToken = NULL;
|
|
||||||
WCHAR * engineName = NULL;
|
|
||||||
WCHAR * engineId = NULL;
|
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
hr = enumSpTokens->Next(1, &spToken, NULL);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
hr = SpGetDescription(spToken, &engineName);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
hr = spToken->GetId(&engineId);
|
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
next = callback(spToken, engineId, engineName, userData);
|
|
||||||
|
|
||||||
if( spToken )
|
|
||||||
spToken->Release();
|
|
||||||
|
|
||||||
if (engineName)
|
|
||||||
CoTaskMemFree(engineName);
|
|
||||||
if (engineId)
|
|
||||||
CoTaskMemFree(engineId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if( enumSpTokens )
|
|
||||||
enumSpTokens->Release();
|
|
||||||
|
|
||||||
if (willInvokeCoUninitialize)
|
|
||||||
CoUninitialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
const wchar_t * speechEngineId(SpeechHelper sp)
|
|
||||||
{
|
|
||||||
if (!sp)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return sp->engineId.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
const wchar_t * speechEngineName(SpeechHelper sp)
|
|
||||||
{
|
|
||||||
if (!sp)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return sp->engineName.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool speechTell(SpeechHelper sp, const wchar_t *text)
|
|
||||||
{
|
|
||||||
if (!sp || !sp->voice || !text)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
HRESULT hr = sp->voice->Speak(text, SPF_ASYNC | SPF_IS_NOT_XML, 0);
|
|
||||||
return !!SUCCEEDED(hr);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool speechTellFinished(SpeechHelper sp)
|
|
||||||
{
|
|
||||||
if (!sp || !sp->voice)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
SPVOICESTATUS es;
|
|
||||||
sp->voice->GetStatus(&es, NULL);
|
|
||||||
return es.dwRunningState == SPRS_DONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool speechSay(SpeechHelper sp, const wchar_t *text)
|
|
||||||
{
|
|
||||||
if (!sp || !sp->voice || !text)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
HRESULT hr = sp->voice->Speak(text, SPF_IS_NOT_XML, 0);
|
|
||||||
return !!SUCCEEDED(hr);
|
|
||||||
}
|
|
||||||
|
|
||||||
int setSpeechVolume( SpeechHelper sp, int newVolume )
|
|
||||||
{
|
|
||||||
if( !sp || !sp->voice || newVolume < 0 || newVolume > 100 )
|
|
||||||
return -1;
|
|
||||||
unsigned short oldVolume;
|
|
||||||
HRESULT hr = sp->voice->GetVolume( &oldVolume );
|
|
||||||
if( !SUCCEEDED( hr ) )
|
|
||||||
return -1;
|
|
||||||
sp->voice->SetVolume( (unsigned short) newVolume );
|
|
||||||
return oldVolume;
|
|
||||||
}
|
|
||||||
|
|
||||||
int setSpeechRate( SpeechHelper sp, int newRate )
|
|
||||||
{
|
|
||||||
if( !sp || !sp->voice || newRate < 0 || newRate > 100 )
|
|
||||||
return -1;
|
|
||||||
long oldRate;
|
|
||||||
HRESULT hr = sp->voice->GetRate( &oldRate );
|
|
||||||
if( !SUCCEEDED( hr ) )
|
|
||||||
return -1;
|
|
||||||
sp->voice->SetRate( ( newRate - 50 ) / 5 );
|
|
||||||
return oldRate * 5 + 50;
|
|
||||||
}
|
|
27
speechhlp.hh
27
speechhlp.hh
|
@ -1,27 +0,0 @@
|
||||||
#ifndef __SPEECHHLP_H__
|
|
||||||
#define __SPEECHHLP_H__
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct _SpeechHelper *SpeechHelper;
|
|
||||||
typedef bool (*EnumerateCallback)(void *token, const wchar_t *id, const wchar_t *name, void *userData);
|
|
||||||
|
|
||||||
SpeechHelper speechCreate(const wchar_t *engineId);
|
|
||||||
void speechDestroy(SpeechHelper sp);
|
|
||||||
bool speechAvailable(SpeechHelper sp);
|
|
||||||
void speechEnumerateAvailableEngines(EnumerateCallback callback, void *userData);
|
|
||||||
const wchar_t * speechEngineId(SpeechHelper sp);
|
|
||||||
const wchar_t * speechEngineName(SpeechHelper sp);
|
|
||||||
bool speechTell(SpeechHelper sp, const wchar_t *text);
|
|
||||||
bool speechTellFinished(SpeechHelper sp);
|
|
||||||
bool speechSay(SpeechHelper sp, const wchar_t *text);
|
|
||||||
int setSpeechVolume( SpeechHelper sp, int newVolume );
|
|
||||||
int setSpeechRate( SpeechHelper sp, int newRate );
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // __SPEECHHLP_H__
|
|
247
sphelper.hh
247
sphelper.hh
|
@ -1,247 +0,0 @@
|
||||||
#ifndef __SPHELPER_HH_INCLUDED__
|
|
||||||
#define __SPHELPER_HH_INCLUDED__
|
|
||||||
|
|
||||||
#ifndef SR_LOCALIZED_DESCRIPTION
|
|
||||||
#define SR_LOCALIZED_DESCRIPTION L"Description"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef REG_MUI_STRING_TRUNCATE
|
|
||||||
#define REG_MUI_STRING_TRUNCATE 0x00000001
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SPERR_NOT_FOUND
|
|
||||||
#define FACILITY_SAPI FACILITY_ITF
|
|
||||||
#define SAPI_ERROR_BASE 0x5000
|
|
||||||
#define MAKE_SAPI_HRESULT(sev, err) MAKE_HRESULT(sev, FACILITY_SAPI, err)
|
|
||||||
#define MAKE_SAPI_ERROR(err) MAKE_SAPI_HRESULT(SEVERITY_ERROR, err + SAPI_ERROR_BASE)
|
|
||||||
#define SPERR_NOT_FOUND MAKE_SAPI_ERROR(0x03a)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _SAPI_VER
|
|
||||||
#undef _SAPI_VER
|
|
||||||
#endif
|
|
||||||
#define _SAPI_VER 0x053
|
|
||||||
|
|
||||||
inline void SpHexFromUlong(WCHAR * psz, ULONG ul)
|
|
||||||
{
|
|
||||||
// If for some reason we cannot convert a number, set it to 0
|
|
||||||
|
|
||||||
if (_ultow(ul, psz, 16))
|
|
||||||
{
|
|
||||||
psz[0] = L'0';
|
|
||||||
psz[1] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline HRESULT SpGetTokenFromId(
|
|
||||||
const WCHAR * pszTokenId,
|
|
||||||
ISpObjectToken ** ppToken,
|
|
||||||
BOOL fCreateIfNotExist = FALSE)
|
|
||||||
{
|
|
||||||
HRESULT hr;
|
|
||||||
ISpObjectToken * cpToken;
|
|
||||||
hr = CoCreateInstance(CLSID_SpObjectToken, NULL, CLSCTX_INPROC_SERVER,
|
|
||||||
IID_ISpObjectToken, (void**)&cpToken);
|
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
hr = cpToken->SetId(NULL, pszTokenId, fCreateIfNotExist);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
*ppToken = cpToken;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
cpToken->Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline HRESULT SpGetCategoryFromId(
|
|
||||||
const WCHAR * pszCategoryId,
|
|
||||||
ISpObjectTokenCategory ** ppCategory,
|
|
||||||
BOOL fCreateIfNotExist = FALSE)
|
|
||||||
{
|
|
||||||
HRESULT hr;
|
|
||||||
|
|
||||||
ISpObjectTokenCategory * cpTokenCategory;
|
|
||||||
hr = CoCreateInstance(CLSID_SpObjectTokenCategory, NULL, CLSCTX_INPROC_SERVER,
|
|
||||||
IID_ISpObjectTokenCategory, (void**)&cpTokenCategory );
|
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
hr = cpTokenCategory->SetId(pszCategoryId, fCreateIfNotExist);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
*ppCategory = cpTokenCategory;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
cpTokenCategory->Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT SpEnumTokens(
|
|
||||||
const WCHAR * pszCategoryId,
|
|
||||||
const WCHAR * pszReqAttribs,
|
|
||||||
const WCHAR * pszOptAttribs,
|
|
||||||
IEnumSpObjectTokens ** ppEnum)
|
|
||||||
{
|
|
||||||
HRESULT hr = S_OK;
|
|
||||||
|
|
||||||
ISpObjectTokenCategory * cpCategory;
|
|
||||||
hr = SpGetCategoryFromId(pszCategoryId, &cpCategory);
|
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
hr = cpCategory->EnumTokens(
|
|
||||||
pszReqAttribs,
|
|
||||||
pszOptAttribs,
|
|
||||||
ppEnum);
|
|
||||||
cpCategory->Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline HRESULT SpGetDescription(ISpObjectToken * pObjToken, WCHAR ** ppszDescription, LANGID Language = GetUserDefaultUILanguage())
|
|
||||||
{
|
|
||||||
WCHAR szLangId[10];
|
|
||||||
HRESULT hr = S_OK;
|
|
||||||
|
|
||||||
if (ppszDescription == NULL)
|
|
||||||
{
|
|
||||||
return E_POINTER;
|
|
||||||
}
|
|
||||||
*ppszDescription = NULL;
|
|
||||||
|
|
||||||
#if _SAPI_VER >= 0x053
|
|
||||||
WCHAR* pRegKeyPath = 0;
|
|
||||||
WCHAR* pszTemp = 0;
|
|
||||||
HKEY Handle = NULL;
|
|
||||||
|
|
||||||
// Windows Vista does not encourage localized strings in the registry
|
|
||||||
// When running on Windows Vista query the localized engine name from a resource dll
|
|
||||||
OSVERSIONINFO ver;
|
|
||||||
ver.dwOSVersionInfoSize = sizeof( ver );
|
|
||||||
|
|
||||||
if( ( ::GetVersionEx( &ver ) == TRUE ) && ( ver.dwMajorVersion >= 6 ) )
|
|
||||||
{
|
|
||||||
// If we reach this code we are running under Windows Vista
|
|
||||||
HMODULE hmodAdvapi32Dll = NULL;
|
|
||||||
typedef HRESULT (WINAPI* LPFN_RegLoadMUIStringW)(HKEY, LPCWSTR, LPWSTR, DWORD, LPDWORD, DWORD, LPCWSTR);
|
|
||||||
LPFN_RegLoadMUIStringW pfnRegLoadMUIStringW = NULL;
|
|
||||||
|
|
||||||
// Delay bind with RegLoadMUIStringW since this function is not supported on previous versions of advapi32.dll
|
|
||||||
// RegLoadMUIStringW is supported only on advapi32.dll that ships with Windows Vista and above
|
|
||||||
// Calling RegLoadMUIStringW directly makes the loader try to resolve the function reference at load time which breaks,
|
|
||||||
// hence we manually load advapi32.dll, query for the function pointer and invoke it.
|
|
||||||
hmodAdvapi32Dll = ::LoadLibrary(TEXT("advapi32.dll"));
|
|
||||||
if(hmodAdvapi32Dll)
|
|
||||||
{
|
|
||||||
pfnRegLoadMUIStringW = (LPFN_RegLoadMUIStringW) ::GetProcAddress(hmodAdvapi32Dll, "RegLoadMUIStringW");
|
|
||||||
if (!pfnRegLoadMUIStringW)
|
|
||||||
{
|
|
||||||
// This should not happen in Vista
|
|
||||||
// _ASSERT (pfnRegLoadMUIStringW);
|
|
||||||
hr = TYPE_E_DLLFUNCTIONNOTFOUND;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hr = HRESULT_FROM_WIN32(ERROR_DLL_NOT_FOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
hr = pObjToken->GetId(&pszTemp);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
LONG lErrorCode = ERROR_SUCCESS;
|
|
||||||
|
|
||||||
pRegKeyPath = wcschr(pszTemp, L'\\'); // Find the first occurrence of '\\' in the absolute registry key path
|
|
||||||
if(pRegKeyPath)
|
|
||||||
{
|
|
||||||
*pRegKeyPath = L'\0';
|
|
||||||
pRegKeyPath++; // pRegKeyPath now points to the path to the recognizer token under the HKLM or HKCR hive
|
|
||||||
*ppszDescription = 0;
|
|
||||||
|
|
||||||
// Open the registry key for read and get the handle
|
|
||||||
if (wcsncmp(pszTemp, L"HKEY_LOCAL_MACHINE", MAX_PATH) == 0)
|
|
||||||
{
|
|
||||||
lErrorCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE, pRegKeyPath, 0, KEY_QUERY_VALUE, &Handle);
|
|
||||||
}
|
|
||||||
else if (wcsncmp(pszTemp, L"HKEY_CURRENT_USER", MAX_PATH) == 0)
|
|
||||||
{
|
|
||||||
lErrorCode = RegOpenKeyExW(HKEY_CURRENT_USER, pRegKeyPath, 0, KEY_QUERY_VALUE, &Handle);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lErrorCode = ERROR_BAD_ARGUMENTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use MUI RegLoadMUIStringW API to load the localized string
|
|
||||||
if(ERROR_SUCCESS == lErrorCode)
|
|
||||||
{
|
|
||||||
*ppszDescription = (WCHAR*) CoTaskMemAlloc(MAX_PATH * sizeof(WCHAR)); // This should be enough memory to allocate the localized Engine Name
|
|
||||||
lErrorCode = (*pfnRegLoadMUIStringW) (Handle, SR_LOCALIZED_DESCRIPTION, *ppszDescription, MAX_PATH * sizeof(WCHAR), NULL, REG_MUI_STRING_TRUNCATE, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// pRegKeyPath should never be 0 if we are querying for relative hkey path
|
|
||||||
lErrorCode = ERROR_BAD_ARGUMENTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = HRESULT_FROM_WIN32(lErrorCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close registry key handle
|
|
||||||
if(Handle)
|
|
||||||
{
|
|
||||||
RegCloseKey(Handle);
|
|
||||||
}
|
|
||||||
// Free memory allocated to locals
|
|
||||||
if(pszTemp)
|
|
||||||
{
|
|
||||||
CoTaskMemFree(pszTemp);
|
|
||||||
}
|
|
||||||
if (hmodAdvapi32Dll)
|
|
||||||
{
|
|
||||||
::FreeLibrary(hmodAdvapi32Dll);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If running on OSes released before Windows Vista query the localized string from the registry
|
|
||||||
// If RegLoadMUIStringW failed to retrieved the localized Engine name retrieve the localized string from the fallback (Default) attribute
|
|
||||||
#else
|
|
||||||
hr = E_FAIL;
|
|
||||||
#endif // _SAPI_VER >= 0x053
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
// Free memory allocated above if necessary
|
|
||||||
if (*ppszDescription != NULL)
|
|
||||||
{
|
|
||||||
CoTaskMemFree(*ppszDescription);
|
|
||||||
*ppszDescription = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
SpHexFromUlong(szLangId, Language);
|
|
||||||
hr = pObjToken->GetStringValue(szLangId, ppszDescription);
|
|
||||||
if (hr == SPERR_NOT_FOUND)
|
|
||||||
{
|
|
||||||
hr = pObjToken->GetStringValue(NULL, ppszDescription);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -43,9 +43,7 @@
|
||||||
|
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
|
|
||||||
#if defined( Q_OS_WIN32 ) || defined( Q_OS_MAC )
|
|
||||||
#include "speechclient.hh"
|
#include "speechclient.hh"
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "globalbroadcaster.h"
|
#include "globalbroadcaster.h"
|
||||||
using std::map;
|
using std::map;
|
||||||
|
@ -1402,30 +1400,22 @@ void ArticleView::openLink( QUrl const & url, QUrl const & ref, QString const &
|
||||||
tr( "The referenced audio program doesn't exist." ) );
|
tr( "The referenced audio program doesn't exist." ) );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
if ( url.scheme() == "gdtts" )
|
if ( url.scheme() == "gdtts" ) {
|
||||||
{
|
|
||||||
// TODO: Port TTS
|
|
||||||
#if defined( Q_OS_WIN32 ) || defined( Q_OS_MAC )
|
|
||||||
// Text to speech
|
// Text to speech
|
||||||
QString md5Id = Utils::Url::queryItemValue( url, "engine" );
|
QString md5Id = Utils::Url::queryItemValue( url, "engine" );
|
||||||
QString text( url.path().mid( 1 ) );
|
QString text( url.path().mid( 1 ) );
|
||||||
|
|
||||||
for ( Config::VoiceEngines::const_iterator i = cfg.voiceEngines.begin();
|
for( const auto & voiceEngine : cfg.voiceEngines ) {
|
||||||
i != cfg.voiceEngines.end(); ++i )
|
QString itemMd5Id =
|
||||||
{
|
QString( QCryptographicHash::hash( voiceEngine.name.toUtf8(), QCryptographicHash::Md5 ).toHex() );
|
||||||
QString itemMd5Id = QString( QCryptographicHash::hash(
|
|
||||||
i->id.toUtf8(),
|
|
||||||
QCryptographicHash::Md5 ).toHex() );
|
|
||||||
|
|
||||||
if ( itemMd5Id == md5Id )
|
if( itemMd5Id == md5Id ) {
|
||||||
{
|
SpeechClient * speechClient = new SpeechClient( voiceEngine, this );
|
||||||
SpeechClient * speechClient = new SpeechClient( *i, this );
|
|
||||||
connect( speechClient, SIGNAL( finished() ), speechClient, SLOT( deleteLater() ) );
|
connect( speechClient, SIGNAL( finished() ), speechClient, SLOT( deleteLater() ) );
|
||||||
speechClient->tell( text );
|
speechClient->tell( text );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
if ( isExternalLink( url ) )
|
if ( isExternalLink( url ) )
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||||
|
|
||||||
#include "texttospeechsource.hh"
|
#include "texttospeechsource.hh"
|
||||||
|
#include <QVariant>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
TextToSpeechSource::TextToSpeechSource( QWidget * parent,
|
TextToSpeechSource::TextToSpeechSource( QWidget * parent,
|
||||||
Config::VoiceEngines voiceEngines ):
|
Config::VoiceEngines voiceEngines ):
|
||||||
|
@ -16,14 +17,18 @@ TextToSpeechSource::TextToSpeechSource( QWidget * parent,
|
||||||
|
|
||||||
ui.selectedVoiceEngines->setTabKeyNavigation( true );
|
ui.selectedVoiceEngines->setTabKeyNavigation( true );
|
||||||
ui.selectedVoiceEngines->setModel( &voiceEnginesModel );
|
ui.selectedVoiceEngines->setModel( &voiceEnginesModel );
|
||||||
ui.selectedVoiceEngines->hideColumn( VoiceEnginesModel::kColumnEngineId );
|
ui.selectedVoiceEngines->hideColumn( VoiceEnginesModel::kColumnEngineName );
|
||||||
fitSelectedVoiceEnginesColumns();
|
fitSelectedVoiceEnginesColumns();
|
||||||
ui.selectedVoiceEngines->setItemDelegateForColumn( VoiceEnginesModel::kColumnEngineName,
|
ui.selectedVoiceEngines->setItemDelegateForColumn( VoiceEnginesModel::kColumnEngineDName,
|
||||||
new VoiceEngineItemDelegate( engines, this ) );
|
new VoiceEngineItemDelegate( engines, this ) );
|
||||||
|
|
||||||
foreach ( SpeechClient::Engine engine, engines )
|
foreach ( SpeechClient::Engine engine, engines )
|
||||||
{
|
{
|
||||||
ui.availableVoiceEngines->addItem( engine.name, engine.id );
|
QMap<QString,QVariant> map;
|
||||||
|
map[ "engine_name" ] = engine.engine_name;
|
||||||
|
map[ "locale" ] = engine.locale;
|
||||||
|
map["voice_name"] = engine.voice_name;
|
||||||
|
ui.availableVoiceEngines->addItem( engine.name, QVariant(map) );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( voiceEngines.count() > 0 )
|
if( voiceEngines.count() > 0 )
|
||||||
|
@ -65,8 +70,11 @@ void TextToSpeechSource::on_addVoiceEngine_clicked()
|
||||||
if( idx >= 0 )
|
if( idx >= 0 )
|
||||||
{
|
{
|
||||||
QString name = ui.availableVoiceEngines->itemText( idx );
|
QString name = ui.availableVoiceEngines->itemText( idx );
|
||||||
QString id = ui.availableVoiceEngines->itemData( idx ).toString();
|
auto map = ui.availableVoiceEngines->itemData( idx ).toMap();
|
||||||
voiceEnginesModel.addNewVoiceEngine( id, name, ui.volumeSlider->value(), ui.rateSlider->value() );
|
QString engine_name = map["engine_name"].toString();
|
||||||
|
QString locale = map["locale"].toString();
|
||||||
|
QString voice_name = map["voice_name"].toString();
|
||||||
|
voiceEnginesModel.addNewVoiceEngine( engine_name, QLocale(locale), name, voice_name, ui.volumeSlider->value(), ui.rateSlider->value() );
|
||||||
fitSelectedVoiceEnginesColumns();
|
fitSelectedVoiceEnginesColumns();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,17 +100,17 @@ void TextToSpeechSource::on_previewVoice_clicked()
|
||||||
if ( idx < 0 )
|
if ( idx < 0 )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QString engineId = ui.availableVoiceEngines->itemData( idx ).toString();
|
auto map = ui.availableVoiceEngines->itemData( idx ).toMap();
|
||||||
|
QString engineId = map["engine_name"].toString();
|
||||||
|
QString locale = map["locale"].toString();
|
||||||
QString name = ui.availableVoiceEngines->itemText( idx );
|
QString name = ui.availableVoiceEngines->itemText( idx );
|
||||||
QString text = ui.previewText->text();
|
QString text = ui.previewText->text();
|
||||||
int volume = ui.volumeSlider->value();
|
int volume = ui.volumeSlider->value();
|
||||||
int rate = ui.rateSlider->value();
|
int rate = ui.rateSlider->value();
|
||||||
|
|
||||||
SpeechClient * speechClient = new SpeechClient( Config::VoiceEngine( engineId, name, volume, rate ), this );
|
speechClient = std::make_unique< SpeechClient >(
|
||||||
|
Config::VoiceEngine( engineId, name, map[ "voice_name" ].toString(), QLocale( locale ), volume, rate ),
|
||||||
connect( speechClient, SIGNAL( started( bool ) ), ui.previewVoice, SLOT( setDisabled( bool ) ) );
|
this );
|
||||||
connect( speechClient, SIGNAL( finished() ), this, SLOT( previewVoiceFinished() ) );
|
|
||||||
connect( speechClient, SIGNAL( finished() ), speechClient, SLOT( deleteLater() ) );
|
|
||||||
speechClient->tell( text );
|
speechClient->tell( text );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +122,7 @@ void TextToSpeechSource::previewVoiceFinished()
|
||||||
void TextToSpeechSource::fitSelectedVoiceEnginesColumns()
|
void TextToSpeechSource::fitSelectedVoiceEnginesColumns()
|
||||||
{
|
{
|
||||||
ui.selectedVoiceEngines->resizeColumnToContents( VoiceEnginesModel::kColumnEnabled );
|
ui.selectedVoiceEngines->resizeColumnToContents( VoiceEnginesModel::kColumnEnabled );
|
||||||
ui.selectedVoiceEngines->resizeColumnToContents( VoiceEnginesModel::kColumnEngineName );
|
ui.selectedVoiceEngines->resizeColumnToContents( VoiceEnginesModel::kColumnEngineDName );
|
||||||
ui.selectedVoiceEngines->resizeColumnToContents( VoiceEnginesModel::kColumnIcon );
|
ui.selectedVoiceEngines->resizeColumnToContents( VoiceEnginesModel::kColumnIcon );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +137,7 @@ void TextToSpeechSource::adjustSliders()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ui.volumeSlider->setValue( 50 );
|
ui.volumeSlider->setValue( 50 );
|
||||||
ui.rateSlider->setValue( 50 );
|
ui.rateSlider->setValue( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextToSpeechSource::selectionChanged()
|
void TextToSpeechSource::selectionChanged()
|
||||||
|
@ -160,18 +168,20 @@ void VoiceEnginesModel::removeVoiceEngine( int index )
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoiceEnginesModel::addNewVoiceEngine( QString const & id, QString const & name,
|
void VoiceEnginesModel::addNewVoiceEngine(
|
||||||
int volume, int rate )
|
QString const & engine_name, QLocale locale, QString const & name, QString const & voice_name, int volume, int rate )
|
||||||
{
|
{
|
||||||
if ( id.isEmpty() || name.isEmpty() )
|
if( engine_name.isEmpty() || name.isEmpty() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Config::VoiceEngine v;
|
Config::VoiceEngine v;
|
||||||
v.enabled = true;
|
v.enabled = true;
|
||||||
v.id = id;
|
v.engine_name = engine_name;
|
||||||
v.name = name;
|
v.locale = locale;
|
||||||
|
v.name = name;
|
||||||
v.volume = volume;
|
v.volume = volume;
|
||||||
v.rate = rate;
|
v.rate = rate;
|
||||||
|
v.voice_name = voice_name;
|
||||||
|
|
||||||
beginInsertRows( QModelIndex(), voiceEngines.size(), voiceEngines.size() );
|
beginInsertRows( QModelIndex(), voiceEngines.size(), voiceEngines.size() );
|
||||||
voiceEngines.push_back( v );
|
voiceEngines.push_back( v );
|
||||||
|
@ -199,7 +209,7 @@ Qt::ItemFlags VoiceEnginesModel::flags( QModelIndex const & index ) const
|
||||||
case kColumnEnabled:
|
case kColumnEnabled:
|
||||||
result |= Qt::ItemIsUserCheckable;
|
result |= Qt::ItemIsUserCheckable;
|
||||||
break;
|
break;
|
||||||
case kColumnEngineName:
|
case kColumnEngineDName:
|
||||||
case kColumnIcon:
|
case kColumnIcon:
|
||||||
result |= Qt::ItemIsEditable;
|
result |= Qt::ItemIsEditable;
|
||||||
break;
|
break;
|
||||||
|
@ -231,9 +241,9 @@ QVariant VoiceEnginesModel::headerData( int section, Qt::Orientation /*orientati
|
||||||
{
|
{
|
||||||
case kColumnEnabled:
|
case kColumnEnabled:
|
||||||
return tr( "Enabled" );
|
return tr( "Enabled" );
|
||||||
case kColumnEngineName:
|
case kColumnEngineDName:
|
||||||
return tr( "Name" );
|
return tr( "Name" );
|
||||||
case kColumnEngineId:
|
case kColumnEngineName:
|
||||||
return tr( "Id" );
|
return tr( "Id" );
|
||||||
case kColumnIcon:
|
case kColumnIcon:
|
||||||
return tr( "Icon" );
|
return tr( "Icon" );
|
||||||
|
@ -250,11 +260,10 @@ QVariant VoiceEnginesModel::data( QModelIndex const & index, int role ) const
|
||||||
|
|
||||||
if ( role == Qt::DisplayRole || role == Qt::EditRole )
|
if ( role == Qt::DisplayRole || role == Qt::EditRole )
|
||||||
{
|
{
|
||||||
switch ( index.column() )
|
switch ( index.column() ) {
|
||||||
{
|
|
||||||
case kColumnEngineId:
|
|
||||||
return voiceEngines[ index.row() ].id;
|
|
||||||
case kColumnEngineName:
|
case kColumnEngineName:
|
||||||
|
return voiceEngines[ index.row() ].engine_name;
|
||||||
|
case kColumnEngineDName:
|
||||||
return voiceEngines[ index.row() ].name;
|
return voiceEngines[ index.row() ].name;
|
||||||
case kColumnIcon:
|
case kColumnIcon:
|
||||||
return voiceEngines[ index.row() ].iconFilename;
|
return voiceEngines[ index.row() ].iconFilename;
|
||||||
|
@ -284,13 +293,12 @@ bool VoiceEnginesModel::setData( QModelIndex const & index, const QVariant & val
|
||||||
|
|
||||||
if ( role == Qt::DisplayRole || role == Qt::EditRole )
|
if ( role == Qt::DisplayRole || role == Qt::EditRole )
|
||||||
{
|
{
|
||||||
switch ( index.column() )
|
switch ( index.column() ) {
|
||||||
{
|
case kColumnEngineName:
|
||||||
case kColumnEngineId:
|
voiceEngines[ index.row() ].engine_name = value.toString();
|
||||||
voiceEngines[ index.row() ].id = value.toString();
|
|
||||||
dataChanged( index, index );
|
dataChanged( index, index );
|
||||||
return true;
|
return true;
|
||||||
case kColumnEngineName:
|
case kColumnEngineDName:
|
||||||
voiceEngines[ index.row() ].name = value.toString();
|
voiceEngines[ index.row() ].name = value.toString();
|
||||||
dataChanged( index, index );
|
dataChanged( index, index );
|
||||||
return true;
|
return true;
|
||||||
|
@ -320,7 +328,7 @@ VoiceEngineEditor::VoiceEngineEditor( SpeechClient::Engines const & engines, QWi
|
||||||
{
|
{
|
||||||
foreach ( SpeechClient::Engine engine, engines )
|
foreach ( SpeechClient::Engine engine, engines )
|
||||||
{
|
{
|
||||||
addItem( engine.name, engine.id );
|
addItem( engine.name, engine.engine_name );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,7 +373,7 @@ QWidget * VoiceEngineItemDelegate::createEditor( QWidget * parent,
|
||||||
QStyleOptionViewItem const & option,
|
QStyleOptionViewItem const & option,
|
||||||
QModelIndex const & index ) const
|
QModelIndex const & index ) const
|
||||||
{
|
{
|
||||||
if ( index.column() != VoiceEnginesModel::kColumnEngineName )
|
if( index.column() != VoiceEnginesModel::kColumnEngineDName )
|
||||||
return QStyledItemDelegate::createEditor( parent, option, index );
|
return QStyledItemDelegate::createEditor( parent, option, index );
|
||||||
return new VoiceEngineEditor( engines, parent );
|
return new VoiceEngineEditor( engines, parent );
|
||||||
}
|
}
|
||||||
|
@ -376,9 +384,9 @@ void VoiceEngineItemDelegate::setEditorData( QWidget * uncastedEditor, const QMo
|
||||||
if ( !editor )
|
if ( !editor )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int currentRow = index.row();
|
int currentRow = index.row();
|
||||||
QModelIndex engineIdIndex = index.sibling( currentRow, VoiceEnginesModel::kColumnEngineId );
|
QModelIndex engineIdIndex = index.sibling( currentRow, VoiceEnginesModel::kColumnEngineName );
|
||||||
QString engineId = index.model()->data( engineIdIndex ).toString();
|
QString engineId = index.model()->data( engineIdIndex ).toString();
|
||||||
editor->setEngineId( engineId );
|
editor->setEngineId( engineId );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,12 +394,12 @@ void VoiceEngineItemDelegate::setModelData( QWidget * uncastedEditor, QAbstractI
|
||||||
const QModelIndex & index ) const
|
const QModelIndex & index ) const
|
||||||
{
|
{
|
||||||
VoiceEngineEditor * editor = qobject_cast< VoiceEngineEditor * >( uncastedEditor );
|
VoiceEngineEditor * editor = qobject_cast< VoiceEngineEditor * >( uncastedEditor );
|
||||||
if ( !editor )
|
if( !editor )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int currentRow = index.row();
|
int currentRow = index.row();
|
||||||
QModelIndex engineIdIndex = index.sibling( currentRow, VoiceEnginesModel::kColumnEngineId );
|
QModelIndex engineIdIndex = index.sibling( currentRow, VoiceEnginesModel::kColumnEngineName );
|
||||||
QModelIndex engineNameIndex = index.sibling( currentRow, VoiceEnginesModel::kColumnEngineName );
|
QModelIndex engineNameIndex = index.sibling( currentRow, VoiceEnginesModel::kColumnEngineDName );
|
||||||
model->setData( engineIdIndex, editor->engineId() );
|
model->setData( engineIdIndex, editor->engineId() );
|
||||||
model->setData( engineNameIndex, editor->engineName() );
|
model->setData( engineNameIndex, editor->engineName() );
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,37 +17,33 @@ class VoiceEnginesModel: public QAbstractItemModel
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum {
|
||||||
|
kColumnEnabled = 0,
|
||||||
|
kColumnEngineName,
|
||||||
|
kColumnEngineDName,
|
||||||
|
kColumnIcon,
|
||||||
|
kColumnCount
|
||||||
|
};
|
||||||
|
|
||||||
enum {
|
VoiceEnginesModel( QWidget * parent, Config::VoiceEngines const & voiceEngines );
|
||||||
kColumnEnabled = 0,
|
|
||||||
kColumnEngineId,
|
|
||||||
kColumnEngineName,
|
|
||||||
kColumnIcon,
|
|
||||||
kColumnCount
|
|
||||||
};
|
|
||||||
|
|
||||||
VoiceEnginesModel( QWidget * parent, Config::VoiceEngines const & voiceEngines );
|
void removeVoiceEngine( int index );
|
||||||
|
void addNewVoiceEngine( QString const & engine_name, QLocale locale, QString const & name, QString const & voice_name, int volume, int rate );
|
||||||
|
|
||||||
void removeVoiceEngine( int index );
|
Config::VoiceEngines const & getCurrentVoiceEngines() const { return voiceEngines; }
|
||||||
void addNewVoiceEngine( QString const & id, QString const & name,
|
void setEngineParams( QModelIndex idx, int volume, int rate );
|
||||||
int volume, int rate );
|
|
||||||
|
|
||||||
Config::VoiceEngines const & getCurrentVoiceEngines() const
|
QModelIndex index( int row, int column, QModelIndex const & parent ) const override;
|
||||||
{ return voiceEngines; }
|
QModelIndex parent( QModelIndex const & parent ) const override;
|
||||||
void setEngineParams( QModelIndex idx, int volume, int rate );
|
Qt::ItemFlags flags( QModelIndex const & index ) const override;
|
||||||
|
int rowCount( QModelIndex const & parent ) const override;
|
||||||
QModelIndex index( int row, int column, QModelIndex const & parent ) const;
|
int columnCount( QModelIndex const & parent ) const override;
|
||||||
QModelIndex parent( QModelIndex const & parent ) const;
|
QVariant headerData( int section, Qt::Orientation orientation, int role ) const override;
|
||||||
Qt::ItemFlags flags( QModelIndex const & index ) const;
|
QVariant data( QModelIndex const & index, int role ) const override;
|
||||||
int rowCount( QModelIndex const & parent ) const;
|
bool setData( QModelIndex const & index, const QVariant & value, int role ) override;
|
||||||
int columnCount( QModelIndex const & parent ) const;
|
|
||||||
QVariant headerData( int section, Qt::Orientation orientation, int role ) const;
|
|
||||||
QVariant data( QModelIndex const & index, int role ) const;
|
|
||||||
bool setData( QModelIndex const & index, const QVariant & value, int role );
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Config::VoiceEngines voiceEngines;
|
||||||
Config::VoiceEngines voiceEngines;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class VoiceEngineEditor: public QComboBox
|
class VoiceEngineEditor: public QComboBox
|
||||||
|
@ -55,10 +51,10 @@ class VoiceEngineEditor: public QComboBox
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VoiceEngineEditor( SpeechClient::Engines const & engines, QWidget * parent = 0 );
|
VoiceEngineEditor( SpeechClient::Engines const & engines, QWidget * parent = nullptr );
|
||||||
|
|
||||||
public:
|
|
||||||
QString engineName() const;
|
QString engineName() const;
|
||||||
QString engineId() const;
|
QString engineId() const;
|
||||||
void setEngineId( QString const & engineId );
|
void setEngineId( QString const & engineId );
|
||||||
};
|
};
|
||||||
|
@ -67,17 +63,16 @@ class VoiceEngineItemDelegate: public QStyledItemDelegate
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VoiceEngineItemDelegate( SpeechClient::Engines const & engines, QObject * parent = 0 );
|
VoiceEngineItemDelegate( SpeechClient::Engines const & engines, QObject * parent = nullptr );
|
||||||
|
|
||||||
virtual QWidget * createEditor( QWidget *parent,
|
QWidget *
|
||||||
QStyleOptionViewItem const & option,
|
createEditor( QWidget * parent, QStyleOptionViewItem const & option, QModelIndex const & index ) const override;
|
||||||
QModelIndex const & index ) const;
|
virtual void setEditorData( QWidget * uncastedEditor, const QModelIndex & index ) const override;
|
||||||
virtual void setEditorData( QWidget *uncastedEditor, const QModelIndex & index ) const;
|
virtual void
|
||||||
virtual void setModelData( QWidget *uncastedEditor, QAbstractItemModel * model,
|
setModelData( QWidget * uncastedEditor, QAbstractItemModel * model, const QModelIndex & index ) const override;
|
||||||
const QModelIndex & index ) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SpeechClient::Engines engines;
|
SpeechClient::Engines engines;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -103,6 +98,8 @@ private:
|
||||||
Ui::TextToSpeechSource ui;
|
Ui::TextToSpeechSource ui;
|
||||||
VoiceEnginesModel voiceEnginesModel;
|
VoiceEnginesModel voiceEnginesModel;
|
||||||
|
|
||||||
|
std::unique_ptr< SpeechClient > speechClient;
|
||||||
|
|
||||||
void fitSelectedVoiceEnginesColumns();
|
void fitSelectedVoiceEnginesColumns();
|
||||||
void adjustSliders();
|
void adjustSliders();
|
||||||
};
|
};
|
||||||
|
|
|
@ -109,11 +109,20 @@
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QSlider" name="rateSlider">
|
<widget class="QSlider" name="rateSlider">
|
||||||
|
<property name="minimum">
|
||||||
|
<number>-10</number>
|
||||||
|
</property>
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<number>100</number>
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
<property name="pageStep">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="sliderPosition">
|
<property name="sliderPosition">
|
||||||
<number>50</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="tracking">
|
<property name="tracking">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
|
|
|
@ -38,7 +38,7 @@ public:
|
||||||
|
|
||||||
VoiceEnginesDictionary( Config::VoiceEngine const & voiceEngine ):
|
VoiceEnginesDictionary( Config::VoiceEngine const & voiceEngine ):
|
||||||
Dictionary::Class(
|
Dictionary::Class(
|
||||||
toMd5( voiceEngine.id.toUtf8() ),
|
toMd5( voiceEngine.name.toUtf8() ),
|
||||||
vector< string >() ),
|
vector< string >() ),
|
||||||
voiceEngine( voiceEngine )
|
voiceEngine( voiceEngine )
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue