Allow user to select tts voice in addition to engine.

To make tts more user friendly allow user to select which
voice is used for tts playback.
This commit is contained in:
Jeremy Whiting 2022-02-07 14:24:39 -07:00
parent 3102761c51
commit 1be2290358
4 changed files with 56 additions and 6 deletions

View file

@ -207,6 +207,9 @@
<entry key="ttsEngine" type="String"> <entry key="ttsEngine" type="String">
<default>speechd</default> <default>speechd</default>
</entry> </entry>
<entry key="ttsVoice" type="String">
<default></default>
</entry>
<entry key="WatchFile" type="Bool" > <entry key="WatchFile" type="Bool" >
<default>true</default> <default>true</default>
</entry> </entry>

View file

@ -147,19 +147,42 @@ DlgAccessibility::DlgAccessibility(QWidget *parent)
layout->addRow(new QLabel(this)); layout->addRow(new QLabel(this));
// BEGIN Text-to-speech section // BEGIN Text-to-speech section
QComboBox *ttsEngine = new QComboBox(this); m_ttsEngineBox = new QComboBox(this);
// Populate tts engines and use their names directly as key and item text: // Populate tts engines and use their names directly as key and item text:
const QStringList engines = QTextToSpeech::availableEngines(); const QStringList engines = QTextToSpeech::availableEngines();
for (const QString &engine : engines) { for (const QString &engine : engines) {
ttsEngine->addItem(engine); m_ttsEngineBox->addItem(engine);
} }
ttsEngine->setProperty("kcfg_property", QByteArray("currentText")); m_ttsEngineBox->setProperty("kcfg_property", QByteArray("currentText"));
ttsEngine->setObjectName(QStringLiteral("kcfg_ttsEngine")); m_ttsEngineBox->setObjectName(QStringLiteral("kcfg_ttsEngine"));
layout->addRow(i18nc("@label:listbox Config dialog, accessibility page", "Text-to-speech engine:"), ttsEngine); layout->addRow(i18nc("@label:listbox Config dialog, accessibility page", "Text-to-speech engine:"), m_ttsEngineBox);
connect(m_ttsEngineBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &DlgAccessibility::slotTTSEngineChanged);
m_ttsVoiceBox = new QComboBox(this);
m_ttsVoiceBox->setProperty("kcfg_property", QByteArray("currentText"));
m_ttsVoiceBox->setObjectName(QStringLiteral("kcfg_ttsVoice"));
layout->addRow(i18nc("&label:listbox Config dialog, accessibility page", "Text-to-speech voice:"), m_ttsVoiceBox);
slotTTSEngineChanged();
// END Text-to-speech section // END Text-to-speech section
#endif #endif
} }
#ifdef HAVE_SPEECH
void DlgAccessibility::slotTTSEngineChanged()
{
QString engine = m_ttsEngineBox->currentText();
QTextToSpeech *ttsEngine = new QTextToSpeech(engine);
const QVector<QVoice> voices = ttsEngine->availableVoices();
m_ttsVoiceBox->clear();
for (const QVoice &voice : voices) {
m_ttsVoiceBox->addItem(voice.name());
}
delete ttsEngine;
}
#endif
void DlgAccessibility::slotColorModeSelected(int mode) void DlgAccessibility::slotColorModeSelected(int mode)
{ {
if (mode == Okular::Settings::EnumRenderMode::Paper) { if (mode == Okular::Settings::EnumRenderMode::Paper) {

View file

@ -9,6 +9,7 @@
#include <QWidget> #include <QWidget>
class QComboBox;
class QStackedWidget; class QStackedWidget;
class DlgAccessibility : public QWidget class DlgAccessibility : public QWidget
@ -20,9 +21,16 @@ public:
protected Q_SLOTS: protected Q_SLOTS:
void slotColorModeSelected(int mode); void slotColorModeSelected(int mode);
#ifdef HAVE_SPEECH
void slotTTSEngineChanged();
#endif
protected: protected:
QStackedWidget *m_colorModeConfigStack; QStackedWidget *m_colorModeConfigStack;
#ifdef HAVE_SPEECH
QComboBox *m_ttsEngineBox;
QComboBox *m_ttsVoiceBox;
#endif
}; };
#endif #endif

View file

@ -20,6 +20,13 @@ public:
: q(qq) : q(qq)
, speech(new QTextToSpeech(Okular::Settings::ttsEngine())) , speech(new QTextToSpeech(Okular::Settings::ttsEngine()))
{ {
const QVector<QVoice> voices = speech->availableVoices();
QString voiceName = Okular::Settings::ttsVoice();
for (const QVoice &voice : voices) {
if (voice.name() == voiceName) {
speech->setVoice(voice);
}
}
} }
~Private() ~Private()
@ -42,7 +49,7 @@ OkularTTS::OkularTTS(QObject *parent)
// Initialize speechEngine so we can reinitialize if it changes. // Initialize speechEngine so we can reinitialize if it changes.
d->speechEngine = Okular::Settings::ttsEngine(); d->speechEngine = Okular::Settings::ttsEngine();
connect(d->speech, &QTextToSpeech::stateChanged, this, &OkularTTS::slotSpeechStateChanged); connect(d->speech, &QTextToSpeech::stateChanged, this, &OkularTTS::slotSpeechStateChanged);
connect(Okular::Settings::self(), &KConfigSkeleton::configChanged, this, &OkularTTS::slotConfigChanged); connect(Okular::Settings::self(), &KCoreConfigSkeleton::configChanged, this, &OkularTTS::slotConfigChanged);
} }
OkularTTS::~OkularTTS() OkularTTS::~OkularTTS()
@ -94,6 +101,7 @@ void OkularTTS::slotSpeechStateChanged(QTextToSpeech::State state)
void OkularTTS::slotConfigChanged() void OkularTTS::slotConfigChanged()
{ {
const QString engine = Okular::Settings::ttsEngine(); const QString engine = Okular::Settings::ttsEngine();
const QString voiceName = Okular::Settings::ttsVoice();
if (engine != d->speechEngine) { if (engine != d->speechEngine) {
d->speech->stop(); d->speech->stop();
delete d->speech; delete d->speech;
@ -101,4 +109,12 @@ void OkularTTS::slotConfigChanged()
connect(d->speech, &QTextToSpeech::stateChanged, this, &OkularTTS::slotSpeechStateChanged); connect(d->speech, &QTextToSpeech::stateChanged, this, &OkularTTS::slotSpeechStateChanged);
d->speechEngine = engine; d->speechEngine = engine;
} }
const QVector<QVoice> voices = d->speech->availableVoices();
for (const QVoice &voice : voices) {
if (voice.name() == voiceName) {
d->speech->setVoice(voice);
break;
}
}
} }