diff options
21 files changed, 130 insertions, 65 deletions
diff --git a/examples/speech/hello_speak/mainwindow.cpp b/examples/speech/hello_speak/mainwindow.cpp index 04c7a7f..5c15b1a 100644 --- a/examples/speech/hello_speak/mainwindow.cpp +++ b/examples/speech/hello_speak/mainwindow.cpp @@ -140,8 +140,8 @@ void MainWindow::engineSelected(int index) setRate(ui.rate->value()); setPitch(ui.pitch->value()); setVolume(ui.volume->value()); - connect(ui.stopButton, &QPushButton::clicked, m_speech, &QTextToSpeech::stop); - connect(ui.pauseButton, &QPushButton::clicked, m_speech, &QTextToSpeech::pause); + connect(ui.stopButton, &QPushButton::clicked, m_speech, [this]{ m_speech->stop(); }); + connect(ui.pauseButton, &QPushButton::clicked, m_speech, [this]{ m_speech->pause(); }); connect(ui.resumeButton, &QPushButton::clicked, m_speech, &QTextToSpeech::resume); connect(m_speech, &QTextToSpeech::stateChanged, this, &MainWindow::stateChanged); diff --git a/src/plugins/tts/android/src/qtexttospeech_android.cpp b/src/plugins/tts/android/src/qtexttospeech_android.cpp index 42f798a..c5702a2 100644 --- a/src/plugins/tts/android/src/qtexttospeech_android.cpp +++ b/src/plugins/tts/android/src/qtexttospeech_android.cpp @@ -146,7 +146,7 @@ void QTextToSpeechEngineAndroid::say(const QString &text) return; if (m_state == QTextToSpeech::Speaking) - stop(); + stop(QTextToSpeech::BoundaryHint::Default); m_text = text; m_speech.callMethod<void>("say", "(Ljava/lang/String;)V", QJniObject::fromString(m_text).object()); @@ -200,8 +200,9 @@ void QTextToSpeechEngineAndroid::processNotifySpeaking() setState(QTextToSpeech::Speaking); } -void QTextToSpeechEngineAndroid::stop() +void QTextToSpeechEngineAndroid::stop(QTextToSpeech::BoundaryHint boundaryHint) { + Q_UNUSED(boundaryHint); if (m_state == QTextToSpeech::Ready) return; @@ -209,8 +210,9 @@ void QTextToSpeechEngineAndroid::stop() setState(QTextToSpeech::Ready); } -void QTextToSpeechEngineAndroid::pause() +void QTextToSpeechEngineAndroid::pause(QTextToSpeech::BoundaryHint boundaryHint) { + Q_UNUSED(boundaryHint); if (m_state == QTextToSpeech::Paused) return; diff --git a/src/plugins/tts/android/src/qtexttospeech_android.h b/src/plugins/tts/android/src/qtexttospeech_android.h index a21f71d..85e2675 100644 --- a/src/plugins/tts/android/src/qtexttospeech_android.h +++ b/src/plugins/tts/android/src/qtexttospeech_android.h @@ -59,8 +59,8 @@ public: QList<QLocale> availableLocales() const override; QList<QVoice> availableVoices() const override; void say(const QString &text) override; - void stop() override; - void pause() override; + void stop(QTextToSpeech::BoundaryHint boundaryHint) override; + void pause(QTextToSpeech::BoundaryHint boundaryHint) override; void resume() override; double rate() const override; bool setRate(double rate) override; diff --git a/src/plugins/tts/flite/qtexttospeech_flite.cpp b/src/plugins/tts/flite/qtexttospeech_flite.cpp index 1862584..b8e6d94 100644 --- a/src/plugins/tts/flite/qtexttospeech_flite.cpp +++ b/src/plugins/tts/flite/qtexttospeech_flite.cpp @@ -112,13 +112,15 @@ void QTextToSpeechEngineFlite::say(const QString &text) Q_ARG(double, rate()), Q_ARG(double, volume())); } -void QTextToSpeechEngineFlite::stop() +void QTextToSpeechEngineFlite::stop(QTextToSpeech::BoundaryHint boundaryHint) { + Q_UNUSED(boundaryHint); QMetaObject::invokeMethod(m_processor.get(), &QTextToSpeechProcessorFlite::stop, Qt::QueuedConnection); } -void QTextToSpeechEngineFlite::pause() +void QTextToSpeechEngineFlite::pause(QTextToSpeech::BoundaryHint boundaryHint) { + Q_UNUSED(boundaryHint); QMetaObject::invokeMethod(m_processor.get(), &QTextToSpeechProcessorFlite::pause, Qt::QueuedConnection); } diff --git a/src/plugins/tts/flite/qtexttospeech_flite.h b/src/plugins/tts/flite/qtexttospeech_flite.h index c60b3ae..dbae3ec 100644 --- a/src/plugins/tts/flite/qtexttospeech_flite.h +++ b/src/plugins/tts/flite/qtexttospeech_flite.h @@ -61,8 +61,8 @@ public: QList<QLocale> availableLocales() const override; QList<QVoice> availableVoices() const override; void say(const QString &text) override; - void stop() override; - void pause() override; + void stop(QTextToSpeech::BoundaryHint boundaryHint) override; + void pause(QTextToSpeech::BoundaryHint boundaryHint) override; void resume() override; double rate() const override; bool setRate(double rate) override; diff --git a/src/plugins/tts/ios/qtexttospeech_ios.h b/src/plugins/tts/ios/qtexttospeech_ios.h index 6d54fbc..457d4f1 100644 --- a/src/plugins/tts/ios/qtexttospeech_ios.h +++ b/src/plugins/tts/ios/qtexttospeech_ios.h @@ -57,8 +57,8 @@ public: QList<QLocale> availableLocales() const override; QList<QVoice> availableVoices() const override; void say(const QString &text) override; - void stop() override; - void pause() override; + void stop(QTextToSpeech::BoundaryHint boundaryHint) override; + void pause(QTextToSpeech::BoundaryHint boundaryHint) override; void resume() override; double rate() const override; bool setRate(double rate) override; diff --git a/src/plugins/tts/ios/qtexttospeech_ios.mm b/src/plugins/tts/ios/qtexttospeech_ios.mm index fe34aeb..04cc5bf 100644 --- a/src/plugins/tts/ios/qtexttospeech_ios.mm +++ b/src/plugins/tts/ios/qtexttospeech_ios.mm @@ -129,7 +129,7 @@ QTextToSpeechEngineIos::~QTextToSpeechEngineIos() void QTextToSpeechEngineIos::say(const QString &text) { - stop(); + stop(QTextToSpeech::BoundaryHint::Default); // Qt pitch: [-1.0, 1.0], 0 is normal // AVF range: [0.5, 2.0], 1.0 is normal @@ -176,14 +176,22 @@ void QTextToSpeechEngineIos::say(const QString &text) [m_speechSynthesizer speakUtterance:utterance]; } -void QTextToSpeechEngineIos::stop() +void QTextToSpeechEngineIos::stop(QTextToSpeech::BoundaryHint boundaryHint) { - [m_speechSynthesizer stopSpeakingAtBoundary:AVSpeechBoundaryImmediate]; + Q_UNUSED(boundaryHint); + const AVSpeechBoundary atBoundary = (boundaryHint == QTextToSpeech::BoundaryHint::Immediate + || boundaryHint == QTextToSpeech::BoundaryHint::Default) + ? AVSpeechBoundaryImmediate + : AVSpeechBoundaryWord; + [m_speechSynthesizer stopSpeakingAtBoundary:atBoundary]; } -void QTextToSpeechEngineIos::pause() +void QTextToSpeechEngineIos::pause(QTextToSpeech::BoundaryHint boundaryHint) { - [m_speechSynthesizer pauseSpeakingAtBoundary:AVSpeechBoundaryWord]; + const AVSpeechBoundary atBoundary = boundaryHint == QTextToSpeech::BoundaryHint::Immediate + ? AVSpeechBoundaryImmediate + : AVSpeechBoundaryWord; + [m_speechSynthesizer pauseSpeakingAtBoundary:atBoundary]; } void QTextToSpeechEngineIos::resume() diff --git a/src/plugins/tts/macos/qtexttospeech_macos.h b/src/plugins/tts/macos/qtexttospeech_macos.h index 37a7fac..814c8fc 100644 --- a/src/plugins/tts/macos/qtexttospeech_macos.h +++ b/src/plugins/tts/macos/qtexttospeech_macos.h @@ -63,8 +63,8 @@ public: QList<QLocale> availableLocales() const override; QList<QVoice> availableVoices() const override; void say(const QString &text) override; - void stop() override; - void pause() override; + void stop(QTextToSpeech::BoundaryHint boundaryHint) override; + void pause(QTextToSpeech::BoundaryHint boundaryHint) override; void resume() override; double rate() const override; bool setRate(double rate) override; diff --git a/src/plugins/tts/macos/qtexttospeech_macos.mm b/src/plugins/tts/macos/qtexttospeech_macos.mm index 727f972..f2c869e 100644 --- a/src/plugins/tts/macos/qtexttospeech_macos.mm +++ b/src/plugins/tts/macos/qtexttospeech_macos.mm @@ -139,24 +139,50 @@ void QTextToSpeechEngineMacOS::say(const QString &text) pauseRequested = false; if (m_state != QTextToSpeech::Ready) - stop(); + stop(QTextToSpeech::BoundaryHint::Default); NSString *ntext = text.toNSString(); [speechSynthesizer startSpeakingString:ntext]; speaking(); } -void QTextToSpeechEngineMacOS::stop() +void QTextToSpeechEngineMacOS::stop(QTextToSpeech::BoundaryHint boundaryHint) { - if (speechSynthesizer.isSpeaking || m_state == QTextToSpeech::Paused) - [speechSynthesizer stopSpeakingAtBoundary:NSSpeechImmediateBoundary]; + if (speechSynthesizer.isSpeaking || m_state == QTextToSpeech::Paused) { + const NSSpeechBoundary atBoundary = [boundaryHint]{ + switch (boundaryHint) { + case QTextToSpeech::BoundaryHint::Default: + case QTextToSpeech::BoundaryHint::Immediate: + return NSSpeechImmediateBoundary; + case QTextToSpeech::BoundaryHint::Word: + return NSSpeechWordBoundary; + case QTextToSpeech::BoundaryHint::Sentence: + return NSSpeechSentenceBoundary; + } + Q_UNREACHABLE(); + }(); + + [speechSynthesizer stopSpeakingAtBoundary:atBoundary]; + } } -void QTextToSpeechEngineMacOS::pause() +void QTextToSpeechEngineMacOS::pause(QTextToSpeech::BoundaryHint boundaryHint) { if (speechSynthesizer.isSpeaking) { pauseRequested = true; - [speechSynthesizer pauseSpeakingAtBoundary: NSSpeechWordBoundary]; + const NSSpeechBoundary atBoundary = [boundaryHint]{ + switch (boundaryHint) { + case QTextToSpeech::BoundaryHint::Immediate: + return NSSpeechImmediateBoundary; + case QTextToSpeech::BoundaryHint::Default: + case QTextToSpeech::BoundaryHint::Word: + return NSSpeechWordBoundary; + case QTextToSpeech::BoundaryHint::Sentence: + return NSSpeechSentenceBoundary; + } + Q_UNREACHABLE(); + }(); + [speechSynthesizer pauseSpeakingAtBoundary:atBoundary]; } } diff --git a/src/plugins/tts/mock/qtexttospeech_mock.cpp b/src/plugins/tts/mock/qtexttospeech_mock.cpp index 3da4ed2..f624170 100644 --- a/src/plugins/tts/mock/qtexttospeech_mock.cpp +++ b/src/plugins/tts/mock/qtexttospeech_mock.cpp @@ -98,8 +98,9 @@ void QTextToSpeechEngineMock::say(const QString &text) emit stateChanged(m_state); } -void QTextToSpeechEngineMock::stop() +void QTextToSpeechEngineMock::stop(QTextToSpeech::BoundaryHint boundaryHint) { + Q_UNUSED(boundaryHint); if (m_state == QTextToSpeech::Ready || m_state == QTextToSpeech::Error) return; @@ -112,8 +113,9 @@ void QTextToSpeechEngineMock::stop() emit stateChanged(m_state); } -void QTextToSpeechEngineMock::pause() +void QTextToSpeechEngineMock::pause(QTextToSpeech::BoundaryHint boundaryHint) { + Q_UNUSED(boundaryHint); if (m_state != QTextToSpeech::Speaking) return; diff --git a/src/plugins/tts/mock/qtexttospeech_mock.h b/src/plugins/tts/mock/qtexttospeech_mock.h index 7d70124..126eb54 100644 --- a/src/plugins/tts/mock/qtexttospeech_mock.h +++ b/src/plugins/tts/mock/qtexttospeech_mock.h @@ -54,8 +54,8 @@ public: QList<QVoice> availableVoices() const override; void say(const QString &text) override; - void stop() override; - void pause() override; + void stop(QTextToSpeech::BoundaryHint boundaryHint) override; + void pause(QTextToSpeech::BoundaryHint boundaryHint) override; void resume() override; double rate() const override; diff --git a/src/plugins/tts/sapi/qtexttospeech_sapi.cpp b/src/plugins/tts/sapi/qtexttospeech_sapi.cpp index d6aef7c..7b87e3f 100644 --- a/src/plugins/tts/sapi/qtexttospeech_sapi.cpp +++ b/src/plugins/tts/sapi/qtexttospeech_sapi.cpp @@ -118,7 +118,7 @@ void QTextToSpeechEngineSapi::say(const QString &text) QString textString = text; if (m_state != QTextToSpeech::Ready) - stop(); + stop(QTextToSpeech::BoundaryHint::Default); textString.prepend(QString::fromLatin1("<pitch absmiddle=\"%1\"/>").arg(m_pitch * 10)); @@ -126,15 +126,17 @@ void QTextToSpeechEngineSapi::say(const QString &text) m_voice->Speak(wtext.data(), SPF_ASYNC, NULL); } -void QTextToSpeechEngineSapi::stop() +void QTextToSpeechEngineSapi::stop(QTextToSpeech::BoundaryHint boundaryHint) { + Q_UNUSED(boundaryHint); if (m_state == QTextToSpeech::Paused) resume(); m_voice->Speak(NULL, SPF_PURGEBEFORESPEAK, 0); } -void QTextToSpeechEngineSapi::pause() +void QTextToSpeechEngineSapi::pause(QTextToSpeech::BoundaryHint boundaryHint) { + Q_UNUSED(boundaryHint); if (!isSpeaking()) return; diff --git a/src/plugins/tts/sapi/qtexttospeech_sapi.h b/src/plugins/tts/sapi/qtexttospeech_sapi.h index ff7ac9d..67a3018 100644 --- a/src/plugins/tts/sapi/qtexttospeech_sapi.h +++ b/src/plugins/tts/sapi/qtexttospeech_sapi.h @@ -62,8 +62,8 @@ public: QList<QLocale> availableLocales() const override; QList<QVoice> availableVoices() const override; void say(const QString &text) override; - void stop() override; - void pause() override; + void stop(QTextToSpeech::BoundaryHint boundaryHint) override; + void pause(QTextToSpeech::BoundaryHint boundaryHint) override; void resume() override; double rate() const override; bool setRate(double rate) override; diff --git a/src/plugins/tts/speechdispatcher/qtexttospeech_speechd.cpp b/src/plugins/tts/speechdispatcher/qtexttospeech_speechd.cpp index 172db4d..8b0abe4 100644 --- a/src/plugins/tts/speechdispatcher/qtexttospeech_speechd.cpp +++ b/src/plugins/tts/speechdispatcher/qtexttospeech_speechd.cpp @@ -153,12 +153,14 @@ void QTextToSpeechEngineSpeechd::say(const QString &text) return; if (m_state != QTextToSpeech::Ready) - stop(); + stop(QTextToSpeech::BoundaryHint::Default); + spd_say(speechDispatcher, SPD_MESSAGE, text.toUtf8().constData()); } -void QTextToSpeechEngineSpeechd::stop() +void QTextToSpeechEngineSpeechd::stop(QTextToSpeech::BoundaryHint boundaryHint) { + Q_UNUSED(boundaryHint); if (!connectToSpeechDispatcher()) return; @@ -167,8 +169,9 @@ void QTextToSpeechEngineSpeechd::stop() spd_cancel_all(speechDispatcher); } -void QTextToSpeechEngineSpeechd::pause() +void QTextToSpeechEngineSpeechd::pause(QTextToSpeech::BoundaryHint boundaryHint) { + Q_UNUSED(boundaryHint); if (!connectToSpeechDispatcher()) return; diff --git a/src/plugins/tts/speechdispatcher/qtexttospeech_speechd.h b/src/plugins/tts/speechdispatcher/qtexttospeech_speechd.h index 9f887af..b5adbd6 100644 --- a/src/plugins/tts/speechdispatcher/qtexttospeech_speechd.h +++ b/src/plugins/tts/speechdispatcher/qtexttospeech_speechd.h @@ -61,8 +61,8 @@ public: QList<QLocale> availableLocales() const override; QList<QVoice> availableVoices() const override; void say(const QString &text) override; - void stop() override; - void pause() override; + void stop(QTextToSpeech::BoundaryHint boundaryHint) override; + void pause(QTextToSpeech::BoundaryHint boundaryHint) override; void resume() override; double rate() const override; bool setRate(double rate) override; diff --git a/src/plugins/tts/winrt/qtexttospeech_winrt.cpp b/src/plugins/tts/winrt/qtexttospeech_winrt.cpp index 5f2b85d..eac802c 100644 --- a/src/plugins/tts/winrt/qtexttospeech_winrt.cpp +++ b/src/plugins/tts/winrt/qtexttospeech_winrt.cpp @@ -415,7 +415,7 @@ void QTextToSpeechEngineWinRT::say(const QString &text) Q_D(QTextToSpeechEngineWinRT); // stop ongoing speech - stop(); + stop(QTextToSpeech::BoundaryHint::Default); HRESULT hr = S_OK; @@ -449,8 +449,9 @@ void QTextToSpeechEngineWinRT::say(const QString &text) }); } -void QTextToSpeechEngineWinRT::stop() +void QTextToSpeechEngineWinRT::stop(QTextToSpeech::BoundaryHint boundaryHint) { + Q_UNUSED(boundaryHint); Q_D(QTextToSpeechEngineWinRT); if (d->audioSource) { @@ -459,8 +460,9 @@ void QTextToSpeechEngineWinRT::stop() } } -void QTextToSpeechEngineWinRT::pause() +void QTextToSpeechEngineWinRT::pause(QTextToSpeech::BoundaryHint boundaryHint) { + Q_UNUSED(boundaryHint); Q_D(QTextToSpeechEngineWinRT); if (d->audioSource) diff --git a/src/plugins/tts/winrt/qtexttospeech_winrt.h b/src/plugins/tts/winrt/qtexttospeech_winrt.h index dcb619b..565c2d5 100644 --- a/src/plugins/tts/winrt/qtexttospeech_winrt.h +++ b/src/plugins/tts/winrt/qtexttospeech_winrt.h @@ -61,8 +61,8 @@ public: QList<QLocale> availableLocales() const override; QList<QVoice> availableVoices() const override; void say(const QString &text) override; - void stop() override; - void pause() override; + void stop(QTextToSpeech::BoundaryHint boundaryHint) override; + void pause(QTextToSpeech::BoundaryHint boundaryHint) override; void resume() override; double rate() const override; bool setRate(double rate) override; diff --git a/src/tts/qtexttospeech.cpp b/src/tts/qtexttospeech.cpp index 88576d9..d2be850 100644 --- a/src/tts/qtexttospeech.cpp +++ b/src/tts/qtexttospeech.cpp @@ -67,7 +67,7 @@ void QTextToSpeechPrivate::setEngineProvider(const QString &engine, const QVaria { Q_Q(QTextToSpeech); - q->stop(); + q->stop(QTextToSpeech::BoundaryHint::Immediate); delete m_engine; m_providerName = engine; @@ -231,6 +231,20 @@ void QTextToSpeechPrivate::loadPluginMetadata(QMultiHash<QString, QCborMap> &lis */ /*! + \enum QTextToSpeech::BoundaryHint + + \brief describes when speech should be stopped and paused. + + \value Default Uses the engine specific default behavior. + \value Immediate The engine should stop playback immediately. + \value Word Stop speech when the current word is finished. + \value Sentence Stop speech when the current sentence is finished. + + \note These are hints to the engine. The current engine might not support + all options. +*/ + +/*! Loads a text-to-speech engine from a plug-in that uses the default engine plug-in and constructs a QTextToSpeech object as the child of \a parent. @@ -295,7 +309,7 @@ QTextToSpeech::QTextToSpeech(const QString &engine, const QVariantMap ¶ms, Q */ QTextToSpeech::~QTextToSpeech() { - stop(); + stop(QTextToSpeech::BoundaryHint::Immediate); } /*! @@ -424,35 +438,32 @@ void QTextToSpeech::say(const QString &text) } /*! - Stops the current reading. + Stops the current reading at \a boundaryHint. - The reading cannot be resumed. + The reading cannot be resumed. Whether the \a boundaryHint is + respected depends on the engine. \sa say() pause() */ -void QTextToSpeech::stop() +void QTextToSpeech::stop(BoundaryHint boundaryHint) { Q_D(QTextToSpeech); if (d->m_engine) - d->m_engine->stop(); + d->m_engine->stop(boundaryHint); } /*! - Pauses the current speech. + Pauses the current speech at \a boundaryHint. - \note The behavior of this function depends on the platform and the \l engine. - Some synthesizers will look for a break that they can later resume from, - such as a sentence end, while others may pause instantly. Due to - Android platform limitations, pause() stops what is presently being said, - while resume() starts the previously queued sentence from the beginning. + Whether the \a boundaryHint is respected depends on the \l engine. \sa resume() */ -void QTextToSpeech::pause() +void QTextToSpeech::pause(BoundaryHint boundaryHint) { Q_D(QTextToSpeech); if (d->m_engine) - d->m_engine->pause(); + d->m_engine->pause(boundaryHint); } /*! diff --git a/src/tts/qtexttospeech.h b/src/tts/qtexttospeech.h index c304bd4..efae715 100644 --- a/src/tts/qtexttospeech.h +++ b/src/tts/qtexttospeech.h @@ -79,6 +79,13 @@ public: }; Q_ENUM(ErrorReason) + enum class BoundaryHint { + Default, + Immediate, + Word, + Sentence + }; + explicit QTextToSpeech(QObject *parent = nullptr); explicit QTextToSpeech(const QString &engine, QObject *parent = nullptr); explicit QTextToSpeech(const QString &engine, const QVariantMap ¶ms, @@ -106,8 +113,8 @@ public: public Q_SLOTS: void say(const QString &text); - void stop(); - void pause(); + void stop(QTextToSpeech::BoundaryHint boundaryHint = QTextToSpeech::BoundaryHint::Default); + void pause(QTextToSpeech::BoundaryHint boundaryHint = QTextToSpeech::BoundaryHint::Default); void resume(); void setLocale(const QLocale &locale); diff --git a/src/tts/qtexttospeechengine.cpp b/src/tts/qtexttospeechengine.cpp index f38ca27..5ca9d48 100644 --- a/src/tts/qtexttospeechengine.cpp +++ b/src/tts/qtexttospeechengine.cpp @@ -70,13 +70,13 @@ QT_BEGIN_NAMESPACE */ /*! - \fn void QTextToSpeechEngine::stop() + \fn void QTextToSpeechEngine::stop(QTextToSpeech::BoundaryHint hint) Implementation of \l QTextToSpeech::stop(). */ /*! - \fn void QTextToSpeechEngine::pause() + \fn void QTextToSpeechEngine::pause(QTextToSpeech::BoundaryHint hint) Implementation of \l QTextToSpeech::pause(). */ diff --git a/src/tts/qtexttospeechengine.h b/src/tts/qtexttospeechengine.h index 32ae20c..7a3be53 100644 --- a/src/tts/qtexttospeechengine.h +++ b/src/tts/qtexttospeechengine.h @@ -57,8 +57,8 @@ public: virtual QList<QVoice> availableVoices() const = 0; virtual void say(const QString &text) = 0; - virtual void stop() = 0; - virtual void pause() = 0; + virtual void stop(QTextToSpeech::BoundaryHint boundaryHint) = 0; + virtual void pause(QTextToSpeech::BoundaryHint boundaryHint) = 0; virtual void resume() = 0; virtual double rate() const = 0; |