summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrederik Gladhorn <frederik.gladhorn@theqtcompany.com>2016-04-06 14:49:17 +0200
committerFrederik Gladhorn <frederik.gladhorn@theqtcompany.com>2016-04-06 14:49:17 +0200
commit892add20dffb5c6e492566c9a089021de0694891 (patch)
tree1cb63335ec4e36108e0921ca13274c2364537958
parenta7f2726dd55105aeb368f7ef96d6f45ccdb662bd (diff)
parenta75c3c174782f4c8e269d5d5854e780148a63b9c (diff)
Merge remote-tracking branch 'origin/dev' into wip/speech-recognition
-rw-r--r--.qmake.conf2
-rw-r--r--config.tests/flite/flite.cpp55
-rw-r--r--config.tests/flite/flite.pro3
-rw-r--r--config.tests/vocalizer/vocalizer.cpp48
-rw-r--r--config.tests/vocalizer/vocalizer.pro3
-rw-r--r--examples/speech/hello_speak/mainwindow.cpp85
-rw-r--r--examples/speech/hello_speak/mainwindow.h3
-rw-r--r--examples/speech/hello_speak/mainwindow.ui62
-rw-r--r--qtspeech.pro2
-rw-r--r--[-rwxr-xr-x]src/plugins/plugins.pro0
-rw-r--r--src/plugins/tts/common/qtexttospeechprocessor.cpp310
-rw-r--r--src/plugins/tts/common/qtexttospeechprocessor_p.h132
-rw-r--r--src/plugins/tts/flite/flite.pro23
-rw-r--r--src/plugins/tts/flite/flite_legal.qdoc217
-rw-r--r--src/plugins/tts/flite/flite_plugin.json6
-rwxr-xr-xsrc/plugins/tts/flite/qtexttospeech_flite.cpp208
-rwxr-xr-xsrc/plugins/tts/flite/qtexttospeech_flite.h98
-rwxr-xr-xsrc/plugins/tts/flite/qtexttospeech_flite_plugin.cpp55
-rwxr-xr-xsrc/plugins/tts/flite/qtexttospeech_flite_plugin.h66
-rw-r--r--src/plugins/tts/flite/qtexttospeech_flite_processor.cpp203
-rw-r--r--src/plugins/tts/flite/qtexttospeech_flite_processor.h95
-rw-r--r--src/plugins/tts/osx/osx.pro25
-rw-r--r--src/plugins/tts/osx/osx_plugin.json6
-rw-r--r--src/plugins/tts/osx/qtexttospeech_osx.h99
-rw-r--r--src/plugins/tts/osx/qtexttospeech_osx.mm (renamed from src/tts/qtexttospeech_mac.mm)177
-rw-r--r--src/plugins/tts/osx/qtexttospeech_osx_plugin.cpp44
-rw-r--r--src/plugins/tts/osx/qtexttospeech_osx_plugin.h63
-rw-r--r--src/plugins/tts/sapi/qtexttospeech_sapi.cpp (renamed from src/tts/qtexttospeech_win.cpp)169
-rw-r--r--src/plugins/tts/sapi/qtexttospeech_sapi.h104
-rw-r--r--src/plugins/tts/sapi/qtexttospeech_sapi_plugin.cpp44
-rw-r--r--src/plugins/tts/sapi/qtexttospeech_sapi_plugin.h63
-rw-r--r--src/plugins/tts/sapi/sapi.pro26
-rw-r--r--src/plugins/tts/sapi/sapi_plugin.json6
-rw-r--r--src/plugins/tts/speechdispatcher/speechdispatcher.pro2
-rw-r--r--[-rwxr-xr-x]src/plugins/tts/tts.pro15
-rw-r--r--src/plugins/tts/winrt/qtexttospeech_winrt.cpp448
-rw-r--r--src/plugins/tts/winrt/qtexttospeech_winrt.h103
-rw-r--r--src/plugins/tts/winrt/qtexttospeech_winrt_plugin.cpp44
-rw-r--r--src/plugins/tts/winrt/qtexttospeech_winrt_plugin.h63
-rw-r--r--src/plugins/tts/winrt/winrt.pro18
-rw-r--r--src/plugins/tts/winrt/winrt_plugin.json6
-rw-r--r--src/src.pro2
-rw-r--r--src/tts/qtexttospeech.cpp36
-rw-r--r--src/tts/qtexttospeech.h3
-rw-r--r--src/tts/qtexttospeech_p.h13
-rw-r--r--src/tts/qtexttospeechengine.h2
-rw-r--r--src/tts/qvoice.cpp15
-rw-r--r--src/tts/qvoice.h3
-rw-r--r--src/tts/qvoice_p.h13
-rw-r--r--sync.profile2
-rw-r--r--tests/auto/texttospeech/tst_qtexttospeech.cpp2
51 files changed, 3001 insertions, 291 deletions
diff --git a/.qmake.conf b/.qmake.conf
index 7fcbf65..0957f58 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -2,4 +2,4 @@ load(qt_build_config)
CONFIG += qt_example_installs
CONFIG += warning_clean
-MODULE_VERSION = 5.5.0
+MODULE_VERSION = 5.8.0
diff --git a/config.tests/flite/flite.cpp b/config.tests/flite/flite.cpp
new file mode 100644
index 0000000..d040c0b
--- /dev/null
+++ b/config.tests/flite/flite.cpp
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Speech module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <flite/flite.h>
+
+static int fliteAudioCb(const cst_wave *w, int start, int size,
+ int last, cst_audio_streaming_info *asi)
+{
+ (void)w;
+ (void)start;
+ (void)size;
+ (void)last;
+ (void)asi;
+ return CST_AUDIO_STREAM_STOP;
+}
+
+int main()
+{
+ cst_audio_streaming_info *asi = new_audio_streaming_info();
+ asi->asc = fliteAudioCb; // This fails for old Flite
+ return 0;
+}
diff --git a/config.tests/flite/flite.pro b/config.tests/flite/flite.pro
new file mode 100644
index 0000000..81e94eb
--- /dev/null
+++ b/config.tests/flite/flite.pro
@@ -0,0 +1,3 @@
+SOURCES = flite.cpp
+
+LIBS += -lflite_cmu_us_kal16 -lflite_usenglish -lflite_cmulex -lflite
diff --git a/config.tests/vocalizer/vocalizer.cpp b/config.tests/vocalizer/vocalizer.cpp
new file mode 100644
index 0000000..5ef65cd
--- /dev/null
+++ b/config.tests/vocalizer/vocalizer.cpp
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Speech module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <ve_ttsapi.h>
+#include <string.h>
+
+int main()
+{
+ VE_HSPEECH vocalizerClass;
+ VE_INSTALL vocalizerInstall;
+ memset(&vocalizerInstall, 0, sizeof(VE_INSTALL));
+ ve_ttsInitialize(&vocalizerInstall, &vocalizerClass);
+ ve_ttsUnInitialize(vocalizerClass);
+ return 0;
+}
diff --git a/config.tests/vocalizer/vocalizer.pro b/config.tests/vocalizer/vocalizer.pro
new file mode 100644
index 0000000..b15ad7a
--- /dev/null
+++ b/config.tests/vocalizer/vocalizer.pro
@@ -0,0 +1,3 @@
+SOURCES = vocalizer.cpp
+
+LIBS += -lve
diff --git a/examples/speech/hello_speak/mainwindow.cpp b/examples/speech/hello_speak/mainwindow.cpp
index 9daba96..d22d931 100644
--- a/examples/speech/hello_speak/mainwindow.cpp
+++ b/examples/speech/hello_speak/mainwindow.cpp
@@ -41,55 +41,45 @@
#include "mainwindow.h"
+#include <QLoggingCategory>
MainWindow::MainWindow(QWidget *parent)
- : QMainWindow(parent)
+ : QMainWindow(parent),
+ m_speech(0)
{
ui.setupUi(this);
+ QLoggingCategory::setFilterRules(QStringLiteral("qt.speech.tts=true \n qt.speech.tts.*=true"));
- // Populate the languages combobox before connecting its signal.
- QVector<QLocale> locales = m_speech.availableLocales();
- QLocale current = m_speech.locale();
- foreach (const QLocale &locale, locales) {
- QVariant localeVariant(locale);
- ui.language->addItem(QLocale::languageToString(locale.language()), localeVariant);
- if (locale.name() == current.name())
- ui.language->setCurrentIndex(ui.language->count() - 1);
- }
- localeChanged(current);
+ // Populate engine selection list
+ ui.engine->addItem("Default", QString("default"));
+ foreach (QString engine, QTextToSpeech::availableEngines())
+ ui.engine->addItem(engine, engine);
+ ui.engine->setCurrentIndex(0);
+ engineSelected(0);
connect(ui.speakButton, &QPushButton::clicked, this, &MainWindow::speak);
- connect(ui.stopButton, &QPushButton::clicked, &m_speech, &QTextToSpeech::stop);
- connect(ui.pauseButton, &QPushButton::clicked, &m_speech, &QTextToSpeech::pause);
- connect(ui.resumeButton, &QPushButton::clicked, &m_speech, &QTextToSpeech::resume);
-
connect(ui.pitch, &QSlider::valueChanged, this, &MainWindow::setPitch);
connect(ui.rate, &QSlider::valueChanged, this, &MainWindow::setRate);
- connect(ui.volume, &QSlider::valueChanged, &m_speech, &QTextToSpeech::setVolume);
-
- connect(&m_speech, &QTextToSpeech::stateChanged, this, &MainWindow::stateChanged);
- connect(&m_speech, &QTextToSpeech::localeChanged, this, &MainWindow::localeChanged);
- connect(ui.language, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &MainWindow::languageSelected);
- connect(ui.voice, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &MainWindow::voiceSelected);
+ connect(ui.engine, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &MainWindow::engineSelected);
}
void MainWindow::speak()
{
- m_speech.say(ui.plainTextEdit->toPlainText());
+ m_speech->say(ui.plainTextEdit->toPlainText());
}
void MainWindow::stop()
{
- m_speech.stop();
+ m_speech->stop();
}
void MainWindow::setRate(int rate)
{
- m_speech.setRate(rate / 10.0);
+ m_speech->setRate(rate / 10.0);
}
void MainWindow::setPitch(int pitch)
{
- m_speech.setPitch(pitch / 10.0);
+ m_speech->setPitch(pitch / 10.0);
}
void MainWindow::stateChanged(QTextToSpeech::State state)
@@ -108,15 +98,52 @@ void MainWindow::stateChanged(QTextToSpeech::State state)
ui.stopButton->setEnabled(state == QTextToSpeech::Speaking || QTextToSpeech::Paused);
}
+void MainWindow::engineSelected(int index)
+{
+ QString engineName = ui.engine->itemData(index).toString();
+ delete m_speech;
+ if (engineName == "default")
+ m_speech = new QTextToSpeech(this);
+ else
+ m_speech = new QTextToSpeech(engineName, QVariantMap(), this);
+ disconnect(ui.language, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &MainWindow::languageSelected);
+ ui.language->clear();
+ // Populate the languages combobox before connecting its signal.
+ QVector<QLocale> locales = m_speech->availableLocales();
+ QLocale current = m_speech->locale();
+ foreach (const QLocale &locale, locales) {
+ QString name(QString("%1 (%2)")
+ .arg(QLocale::languageToString(locale.language()))
+ .arg(QLocale::countryToString(locale.country())));
+ QVariant localeVariant(locale);
+ ui.language->addItem(name, localeVariant);
+ if (locale.name() == current.name())
+ current = locale;
+ }
+ setRate(ui.volume->value());
+ setPitch(ui.pitch->value());
+ m_speech->setVolume(ui.volume->value());
+ connect(ui.stopButton, &QPushButton::clicked, m_speech, &QTextToSpeech::stop);
+ connect(ui.pauseButton, &QPushButton::clicked, m_speech, &QTextToSpeech::pause);
+ connect(ui.resumeButton, &QPushButton::clicked, m_speech, &QTextToSpeech::resume);
+ connect(ui.volume, &QSlider::valueChanged, m_speech, &QTextToSpeech::setVolume);
+
+ connect(m_speech, &QTextToSpeech::stateChanged, this, &MainWindow::stateChanged);
+ connect(m_speech, &QTextToSpeech::localeChanged, this, &MainWindow::localeChanged);
+
+ connect(ui.language, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &MainWindow::languageSelected);
+ localeChanged(current);
+}
+
void MainWindow::languageSelected(int language)
{
QLocale locale = ui.language->itemData(language).toLocale();
- m_speech.setLocale(locale);
+ m_speech->setLocale(locale);
}
void MainWindow::voiceSelected(int index)
{
- m_speech.setVoice(m_voices.at(index));
+ m_speech->setVoice(m_voices.at(index));
}
void MainWindow::localeChanged(const QLocale &locale)
@@ -127,8 +154,8 @@ void MainWindow::localeChanged(const QLocale &locale)
disconnect(ui.voice, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &MainWindow::voiceSelected);
ui.voice->clear();
- m_voices = m_speech.availableVoices();
- QVoice currentVoice = m_speech.voice();
+ m_voices = m_speech->availableVoices();
+ QVoice currentVoice = m_speech->voice();
foreach (const QVoice &voice, m_voices) {
ui.voice->addItem(QString("%1 - %2 - %3").arg(voice.name())
.arg(QVoice::genderName(voice.gender()))
diff --git a/examples/speech/hello_speak/mainwindow.h b/examples/speech/hello_speak/mainwindow.h
index 7b89efc..154ea97 100644
--- a/examples/speech/hello_speak/mainwindow.h
+++ b/examples/speech/hello_speak/mainwindow.h
@@ -63,6 +63,7 @@ public slots:
void setPitch(int);
void stateChanged(QTextToSpeech::State state);
+ void engineSelected(int index);
void languageSelected(int language);
void voiceSelected(int index);
@@ -70,7 +71,7 @@ public slots:
private:
Ui::MainWindow ui;
- QTextToSpeech m_speech;
+ QTextToSpeech *m_speech;
QVector<QVoice> m_voices;
};
diff --git a/examples/speech/hello_speak/mainwindow.ui b/examples/speech/hello_speak/mainwindow.ui
index 966dd28..6f3accf 100644
--- a/examples/speech/hello_speak/mainwindow.ui
+++ b/examples/speech/hello_speak/mainwindow.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>552</width>
- <height>449</height>
+ <width>551</width>
+ <height>448</height>
</rect>
</property>
<property name="windowTitle">
@@ -34,6 +34,22 @@ Done, over and out.</string>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Engine</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
@@ -50,7 +66,7 @@ Done, over and out.</string>
</property>
</widget>
</item>
- <item row="4" column="0">
+ <item row="5" column="0">
<widget class="QLabel" name="label_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
@@ -69,7 +85,7 @@ Done, over and out.</string>
</property>
</widget>
</item>
- <item row="3" column="1">
+ <item row="3" column="2">
<widget class="QSlider" name="pitch">
<property name="minimum">
<number>-10</number>
@@ -85,7 +101,7 @@ Done, over and out.</string>
</property>
</widget>
</item>
- <item row="5" column="0">
+ <item row="6" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Voice name:</string>
@@ -95,7 +111,7 @@ Done, over and out.</string>
</property>
</widget>
</item>
- <item row="1" column="1">
+ <item row="1" column="2">
<widget class="QSlider" name="volume">
<property name="maximum">
<number>100</number>
@@ -114,7 +130,17 @@ Done, over and out.</string>
</property>
</widget>
</item>
- <item row="5" column="1">
+ <item row="5" column="2">
+ <widget class="QComboBox" name="language">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="2">
<widget class="QComboBox" name="voice"/>
</item>
<item row="2" column="0">
@@ -133,16 +159,6 @@ Done, over and out.</string>
</property>
</widget>
</item>
- <item row="4" column="1">
- <widget class="QComboBox" name="language">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- </widget>
- </item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
@@ -159,7 +175,7 @@ Done, over and out.</string>
</property>
</widget>
</item>
- <item row="2" column="1">
+ <item row="2" column="2">
<widget class="QSlider" name="rate">
<property name="minimum">
<number>-10</number>
@@ -172,6 +188,16 @@ Done, over and out.</string>
</property>
</widget>
</item>
+ <item row="4" column="2">
+ <widget class="QComboBox" name="engine">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
</layout>
</item>
<item>
diff --git a/qtspeech.pro b/qtspeech.pro
index c330123..842a546 100644
--- a/qtspeech.pro
+++ b/qtspeech.pro
@@ -1,4 +1,6 @@
lessThan(QT_MAJOR_VERSION, 5): error("The QtSpeech library only supports Qt 5.")
load(configure)
qtCompileTest(voconhybrid)
+qtCompileTest(flite)
+qtCompileTest(vocalizer)
load(qt_parts)
diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro
index 94ad477..94ad477 100755..100644
--- a/src/plugins/plugins.pro
+++ b/src/plugins/plugins.pro
diff --git a/src/plugins/tts/common/qtexttospeechprocessor.cpp b/src/plugins/tts/common/qtexttospeechprocessor.cpp
new file mode 100644
index 0000000..cbb7819
--- /dev/null
+++ b/src/plugins/tts/common/qtexttospeechprocessor.cpp
@@ -0,0 +1,310 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Speech module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtexttospeechprocessor_p.h"
+
+#include <QtCore/QDateTime>
+
+QT_BEGIN_NAMESPACE
+
+QTextToSpeechProcessor::QTextToSpeechProcessor():
+ m_stop(true),
+ m_idle(true),
+ m_paused(false),
+ m_rate(0),
+ m_pitch(0),
+ m_volume(100),
+ m_audio(0),
+ m_audioBuffer(0)
+{
+}
+
+QTextToSpeechProcessor::~QTextToSpeechProcessor()
+{
+}
+
+void QTextToSpeechProcessor::say(const QString &text, int voiceId)
+{
+ if (isInterruptionRequested())
+ return;
+ QMutexLocker lock(&m_lock);
+ bool wasPaused = m_paused;
+ m_stop = true; // Cancel any previous utterance
+ m_idle = false;
+ m_paused = false;
+ m_nextText = text;
+ m_nextVoice = voiceId;
+ // If the speech was paused, one signal is needed to release the pause
+ // and another to start processing the new text.
+ m_speakSem.release(wasPaused ? 2 : 1);
+}
+
+void QTextToSpeechProcessor::stop()
+{
+ QMutexLocker lock(&m_lock);
+ m_stop = true;
+ m_paused = false;
+ m_nextText.clear();
+ m_speakSem.release();
+}
+
+void QTextToSpeechProcessor::pause()
+{
+ QMutexLocker lock(&m_lock);
+ m_paused = true;
+ m_speakSem.release();
+}
+
+void QTextToSpeechProcessor::resume()
+{
+ QMutexLocker lock(&m_lock);
+ m_paused = false;
+ m_speakSem.release();
+}
+
+bool QTextToSpeechProcessor::setRate(float rate)
+{
+ QMutexLocker lock(&m_lock);
+ if (rate >= -1.0 && rate <= 1.0) {
+ if (updateRate(rate)) {
+ m_rate = rate;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool QTextToSpeechProcessor::setPitch(float pitch)
+{
+ QMutexLocker lock(&m_lock);
+ if (pitch >= -1.0 && pitch <= 1.0) {
+ if (updatePitch(pitch)) {
+ m_pitch = pitch;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool QTextToSpeechProcessor::setVolume(int volume)
+{
+ QMutexLocker lock(&m_lock);
+ if (volume >= 0 && volume <= 100) {
+ if (updateVolume(volume)) {
+ m_volume = volume;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool QTextToSpeechProcessor::isIdle() const
+{
+ QMutexLocker lock(&m_lock);
+ return m_idle;
+}
+
+float QTextToSpeechProcessor::rate() const
+{
+ QMutexLocker lock(&m_lock);
+ return m_rate;
+}
+
+float QTextToSpeechProcessor::pitch() const
+{
+ QMutexLocker lock(&m_lock);
+ return m_pitch;
+}
+
+int QTextToSpeechProcessor::volume() const
+{
+ QMutexLocker lock(&m_lock);
+ return m_volume;
+}
+
+void QTextToSpeechProcessor::start(QThread::Priority priority)
+{
+ QThread::start(priority);
+}
+
+void QTextToSpeechProcessor::exit(int retcode)
+{
+ QThread::exit(retcode);
+ QThread::requestInterruption();
+ stop();
+ if (!QThread::wait(5000)) {
+ QThread::terminate();
+ QThread::wait();
+ }
+}
+
+void QTextToSpeechProcessor::run()
+{
+ int statusCode = 0;
+ forever {
+ m_lock.lock();
+ if (!m_speakSem.tryAcquire()) {
+ m_idle = true;
+ m_lock.unlock();
+ emit notSpeaking(statusCode); // Going idle
+ m_speakSem.acquire();
+ m_lock.lock();
+ }
+ if (isInterruptionRequested()) {
+ if (m_audio) {
+ delete m_audio;
+ m_audio = 0;
+ m_audioBuffer = 0;
+ }
+ m_lock.unlock();
+ break;
+ }
+ m_stop = false;
+ if (!m_nextText.isEmpty()) {
+ QString text = m_nextText;
+ int voice = m_nextVoice;
+ m_nextText.clear();
+ m_lock.unlock();
+ statusCode = processText(text, voice);
+ } else {
+ m_lock.unlock();
+ }
+ }
+}
+
+bool QTextToSpeechProcessor::audioStart(int sampleRate, int channelCount, QString *errorString)
+{
+ QMutexLocker lock(&m_lock);
+ QAudioFormat format;
+ format.setSampleRate(sampleRate);
+ format.setChannelCount(channelCount);
+ format.setSampleSize(16);
+ format.setSampleType(QAudioFormat::SignedInt);
+ format.setCodec("audio/pcm");
+ if (errorString)
+ *errorString = QString();
+ if (m_audio)
+ delete m_audio;
+ m_audio = new QAudioOutput(format);
+ m_audioBuffer = m_audio->start();
+ updateVolume(m_volume);
+ if (m_audioBuffer && m_audio->state() == QAudio::IdleState)
+ return true;
+ if (errorString)
+ *errorString = QLatin1String("Failed to start audio output (error ")
+ + QString::number(m_audio->error()) + QLatin1Char(')');
+ delete m_audio;
+ m_audio = 0;
+ m_audioBuffer = 0;
+ return false;
+}
+
+bool QTextToSpeechProcessor::audioOutput(const char *data, qint64 dataSize, QString *errorString)
+{
+ bool ret = true;
+ int bytesWritten = 0;
+ QString error;
+ forever {
+ m_lock.lock();
+ if (m_paused) {
+ m_audio->suspend();
+ do {
+ m_lock.unlock();
+ m_speakSem.acquire(); // Wait for any command
+ m_lock.lock();
+ } while (m_paused);
+ m_audio->resume();
+ }
+ if (m_stop || !m_audioBuffer
+ || m_audio->state() == QAudio::StoppedState || isInterruptionRequested()) {
+ if (m_audio->error() != QAudio::NoError) {
+ error = QLatin1String("Audio error (")
+ + QString::number(m_audio->error()) + QLatin1Char(')');
+ }
+ m_lock.unlock();
+ ret = false;
+ break;
+ }
+ bytesWritten += m_audioBuffer->write(data + bytesWritten, dataSize - bytesWritten);
+ m_lock.unlock();
+ if (bytesWritten >= dataSize)
+ break;
+ QThread::msleep(50);
+ }
+ if (errorString)
+ *errorString = error;
+ return ret;
+}
+
+void QTextToSpeechProcessor::audioStop(bool abort)
+{
+ QMutexLocker lock(&m_lock);
+ if (m_audio) {
+ if (abort) {
+ m_audio->reset(); // Discard buffered audio
+ } else {
+ // TODO: Find a way to reliably check if all the audio has been written out before stopping
+ m_audioBuffer->write(QByteArray(1024, 0));
+ QThread::msleep(200);
+ m_audio->stop();
+ }
+ delete m_audio;
+ m_audio = 0;
+ m_audioBuffer = 0;
+ }
+}
+
+bool QTextToSpeechProcessor::updateRate(float rate)
+{
+ Q_UNUSED(rate);
+ return true;
+}
+
+bool QTextToSpeechProcessor::updatePitch(float pitch)
+{
+ Q_UNUSED(pitch);
+ return true;
+}
+
+bool QTextToSpeechProcessor::updateVolume(int volume)
+{
+ if (m_audio)
+ m_audio->setVolume(((qreal)volume) / 100.0);
+ return true;
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/plugins/tts/common/qtexttospeechprocessor_p.h b/src/plugins/tts/common/qtexttospeechprocessor_p.h
new file mode 100644
index 0000000..094fe73
--- /dev/null
+++ b/src/plugins/tts/common/qtexttospeechprocessor_p.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Speech module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTTOSPEECHPROCESSOR_P_H
+#define QTEXTTOSPEECHPROCESSOR_P_H
+
+#include "qvoice.h"
+
+#include <QtCore/QString>
+#include <QtCore/QThread>
+#include <QtCore/QMutex>
+#include <QtCore/QSemaphore>
+#include <QtCore/QIODevice>
+#include <QtMultimedia/QAudioOutput>
+
+QT_BEGIN_NAMESPACE
+
+// A common base class for text-to-speech engine integrations
+// that require audio output implementation and thread handling.
+//
+// QAudioOutput is used for audio, and each call to say() cancels
+// any previous processing. The public interface is thread-safe.
+class QTextToSpeechProcessor : public QThread {
+ Q_OBJECT
+
+public:
+ struct VoiceInfo
+ {
+ int id;
+ QString name;
+ QString locale;
+ QVoice::Gender gender;
+ QVoice::Age age;
+ };
+ QTextToSpeechProcessor();
+ ~QTextToSpeechProcessor();
+ void say(const QString &text, int voiceId);
+ void stop();
+ void pause();
+ void resume();
+ bool isIdle() const;
+ bool setRate(float rate);
+ bool setPitch(float pitch);
+ bool setVolume(int volume);
+ float rate() const;
+ float pitch() const;
+ int volume() const;
+ virtual const QVector<VoiceInfo> &voices() const = 0;
+
+protected:
+ // These are re-implemented QThread methods.
+ // exit() waits until the processor thread finishes or the wait times out.
+ void start(QThread::Priority = QThread::InheritPriority);
+ void exit(int retcode = 0);
+
+ // These methods can be used for audio output.
+ // audioOutput() blocks until all the audio has been written or processing
+ // is interrupted.
+ bool audioStart(int sampleRate, int channelCount, QString *errorString = 0);
+ bool audioOutput(const char* data, qint64 dataSize, QString *errorString = 0);
+ void audioStop(bool abort = false);
+
+ // These methods should be re-implemented if the parameters need
+ // to be changed while TTS is speaking. By default, updateVolume() just
+ // changes the QAudioOutput volume. The other methods do nothing by default.
+ virtual bool updateRate(float rate);
+ virtual bool updatePitch(float pitch);
+ virtual bool updateVolume(int volume);
+
+ // This method is called from the internal processor thread, and should block
+ // until the given text has been processed or processing is interrupted.
+ virtual int processText(const QString &text, int voiceId) = 0;
+
+signals:
+ // This signal is emitted when the processor goes to idle state, i.e. when no
+ // new text is set to be spoken. The parameter is the latest return value of
+ // processText(). As the signal is emitted from the internal thread, the recipient
+ // should call isIdle() to get updated state.
+ void notSpeaking(int statusCode);
+
+private:
+ void run() Q_DECL_OVERRIDE;
+ mutable QMutex m_lock;
+ volatile bool m_stop;
+ volatile bool m_idle;
+ volatile bool m_paused;
+ float m_rate;
+ float m_pitch;
+ int m_volume;
+ QSemaphore m_speakSem;
+ QString m_nextText;
+ int m_nextVoice;
+ QAudioOutput *m_audio;
+ QIODevice *m_audioBuffer;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/tts/flite/flite.pro b/src/plugins/tts/flite/flite.pro
new file mode 100644
index 0000000..f80bea0
--- /dev/null
+++ b/src/plugins/tts/flite/flite.pro
@@ -0,0 +1,23 @@
+TARGET = qttexttospeech_flite
+QT = core multimedia texttospeech
+
+PLUGIN_TYPE = texttospeech
+PLUGIN_CLASS_NAME = QTextToSpeechEngineFlite
+load(qt_plugin)
+
+HEADERS += \
+ qtexttospeech_flite.h \
+ qtexttospeech_flite_plugin.h \
+ qtexttospeech_flite_processor.h \
+ ../common/qtexttospeechprocessor_p.h
+
+SOURCES += \
+ qtexttospeech_flite.cpp \
+ qtexttospeech_flite_plugin.cpp \
+ qtexttospeech_flite_processor.cpp \
+ ../common/qtexttospeechprocessor.cpp
+
+OTHER_FILES += \
+ flite_plugin.json
+
+LIBS += -lflite_cmu_us_kal16 -lflite_usenglish -lflite_cmulex -lflite
diff --git a/src/plugins/tts/flite/flite_legal.qdoc b/src/plugins/tts/flite/flite_legal.qdoc
new file mode 100644
index 0000000..f8f4d96
--- /dev/null
+++ b/src/plugins/tts/flite/flite_legal.qdoc
@@ -0,0 +1,217 @@
+/*!
+\page legal-flite.html
+\title Flite Library
+\ingroup licensing
+
+\legalese
+\code
+
+Flite is free software.
+
+We have kept the core code to BSD-like copyright, thus the system is
+free to use in commercial products, with commercial extensions. GPL
+code is only included as part of the build process and does not
+taint any of the run-time code.
+
+As a collection it is distributed under the following license. Note
+a few files in this distribution have a different but equally free
+non-conflicting license, see below.
+
+ Language Technologies Institute
+ Carnegie Mellon University
+ Copyright (c) 1999-2014
+ All Rights Reserved.
+
+ Permission is hereby granted, free of charge, to use and distribute
+ this software and its documentation without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of this work, and to
+ permit persons to whom this work is furnished to do so, subject to
+ the following conditions:
+ 1. The code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+ 2. Any modifications must be clearly marked as such.
+ 3. Original authors' names are not deleted.
+ 4. The authors' names are not used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+ CARNEGIE MELLON UNIVERSITY AND THE CONTRIBUTORS TO THIS WORK
+ DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT
+ SHALL CARNEGIE MELLON UNIVERSITY NOR THE CONTRIBUTORS BE LIABLE
+ FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ THIS SOFTWARE.
+
+All files within this distribution have the above license except
+the following
+
+src/cg/cst_mlpg.h
+src/cg/cst_mlpg.c
+src/cg/cst_mlsa.h
+src/cg/cst_mlsa.c
+src/cg/cst_vc.h
+src/cg/cst_vc.c
+*********************************************************************
+* *
+* Nagoya Institute of Technology, Aichi, Japan, *
+* Nara Institute of Science and Technology, Nara, Japan *
+* and *
+* Carnegie Mellon University, Pittsburgh, PA *
+* Copyright (c) 2003-2004 *
+* All Rights Reserved. *
+* *
+* Permission is hereby granted, free of charge, to use and *
+* distribute this software and its documentation without *
+* restriction, including without limitation the rights to use, *
+* copy, modify, merge, publish, distribute, sublicense, and/or *
+* sell copies of this work, and to permit persons to whom this *
+* work is furnished to do so, subject to the following conditions: *
+* *
+* 1. The code must retain the above copyright notice, this list *
+* of conditions and the following disclaimer. *
+* 2. Any modifications must be clearly marked as such. *
+* 3. Original authors' names are not deleted. *
+* *
+* NAGOYA INSTITUTE OF TECHNOLOGY, NARA INSTITUTE OF SCIENCE AND *
+* TECHNOLOGY, CARNEGIE MELLON UNIVERSITY, AND THE CONTRIBUTORS TO *
+* THIS WORK DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, *
+* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, *
+* IN NO EVENT SHALL NAGOYA INSTITUTE OF TECHNOLOGY, NARA *
+* INSTITUTE OF SCIENCE AND TECHNOLOGY, CARNEGIE MELLON UNIVERSITY, *
+* NOR THE CONTRIBUTORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR *
+* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM *
+* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, *
+* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN *
+* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *
+* *
+*********************************************************************
+
+These functions are derived from the versions in festvox/src/vc/ as
+modified by Tomoki Toda which in turn contain code derived from
+NITECH's HTS system. Their copyright has the same freedoms as
+as Flite's but under NAIST, NITECH and/or CMU.
+
+src/audio/au_wince.c
+src/utils/cst_file_stdio.c
+src/utils/cst_mmap_posix.c
+src/utils/cst_mmap_win32.c
+src/utils/cst_mmap_none.c
+src/utils/cst_file_wince.c
+sapi/
+ are copyright Cepstral, LLC rather than CMU but fall under the same
+ free license as the above, except for the owner. (Note the SAPI stuff
+ probably doesn't work any more)
+
+doc/alice
+ Is the first two chapters of Alice in Wonderland as distributed by the
+ Gutenburg project and is now in the public domain
+
+src/regex/regexp.c
+src/regex/regsub.c
+
+ * Copyright (c) 1986 by University of Toronto.
+ * Written by Henry Spencer. Not derived from licensed software.
+ *
+ * Permission is granted to anyone to use this software for any
+ * purpose on any computer system, and to redistribute it freely,
+ * subject to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of
+ * this software, no matter how awful, even if they arise
+ * from defects in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either
+ * by explicit claim or by omission.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+
+src/speech/rateconv.c
+
+ * Copyright (c) 1992, 1995 by Markus Mummert
+ *
+ * Redistribution and use of this software, modifcation and inclusion
+ * into other forms of software are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of this software must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. If this software is redistributed in a modified condition
+ * it must reveal clearly that it has been modified.
+
+lang/usenglish/us_durz_cart.c
+lang/usenglish/us_durz_cart.h
+lang/usenglish/us_int_accent_cart.c
+lang/usenglish/us_int_accent_cart.h
+lang/usenglish/us_int_tone_cart.c
+lang/usenglish/us_int_tone_cart.h
+lang/usenglish/us_phoneset.c
+lang/usenglish/us_f0lr.c
+ These are directly (or indirectly) compiled/derived from files that are
+ part of the Festival Speech Synthesis System (1.4.1). Hence they have
+ a joint copyright CMU/Edinburgh but with the same free license
+
+configure
+ # Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+ #
+ # This configure script is free software; the Free Software Foundation
+ # gives unlimited permission to copy, distribute and modify it.
+
+configure.sub
+config.guess
+missing
+install-sh
+mkinstalldirs
+ Copyright FSF, and under the GPL, these files are only used for
+ convenient configuration and are not part of the generated binary,
+ and therefore do not impose any GPL restrctions on the rest of the
+ system. But as they are standard methods for configuration they
+ are included.
+
+src/speech/g72x.h
+src/speech/g721.c
+src/speech/g72x.c
+src/speech/g723_24.c
+src/speech/g723_40.c
+
+ *
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use. Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ *
+
+lang/cmu_grapheme_lex/grapheme_unitran_tables.c
+ * Copyright 2008-2012, University of Illinois at Urbana-Champaign *
+ * distributed under the Apache License, Version (2.0) *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * Original table developed by Richard Sproat and Kyoung-young Kim *
+ * Ported for Festvox by Gopala Anumachipalli gopalakr@cs.cmu.edu Sep 2012 *
+ * Then converted to C for CMU Flite (cmuflite.org) *
+
+\endcode
+\endlegalese
+*/
diff --git a/src/plugins/tts/flite/flite_plugin.json b/src/plugins/tts/flite/flite_plugin.json
new file mode 100644
index 0000000..dad4918
--- /dev/null
+++ b/src/plugins/tts/flite/flite_plugin.json
@@ -0,0 +1,6 @@
+{
+ "Keys": ["flite"],
+ "Provider": "flite",
+ "Version": 100,
+ "Features": []
+}
diff --git a/src/plugins/tts/flite/qtexttospeech_flite.cpp b/src/plugins/tts/flite/qtexttospeech_flite.cpp
new file mode 100755
index 0000000..364727f
--- /dev/null
+++ b/src/plugins/tts/flite/qtexttospeech_flite.cpp
@@ -0,0 +1,208 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Speech module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtexttospeech_flite.h"
+
+QT_BEGIN_NAMESPACE
+
+QTextToSpeechEngineFlite::QTextToSpeechEngineFlite(
+ const QVariantMap &parameters, QObject *parent) :
+ QTextToSpeechEngine(parent),
+ m_state(QTextToSpeech::Ready),
+ m_processor(QTextToSpeechProcessorFlite::instance())
+{
+ Q_UNUSED(parameters);
+}
+
+QTextToSpeechEngineFlite::~QTextToSpeechEngineFlite()
+{
+}
+
+QVector<QLocale> QTextToSpeechEngineFlite::availableLocales() const
+{
+ return m_locales;
+}
+
+QVector<QVoice> QTextToSpeechEngineFlite::availableVoices() const
+{
+ return m_voices.values(m_currentLocale.name()).toVector();
+}
+
+void QTextToSpeechEngineFlite::say(const QString &text)
+{
+ int id = QTextToSpeechEngine::voiceData(m_currentVoice).toInt();
+ m_state = QTextToSpeech::Speaking;
+ emit stateChanged(m_state);
+ m_processor->say(text, id);
+}
+
+void QTextToSpeechEngineFlite::stop()
+{
+ m_processor->stop();
+ m_state = QTextToSpeech::Ready;
+ emit stateChanged(m_state);
+}
+
+void QTextToSpeechEngineFlite::pause()
+{
+ if (m_state == QTextToSpeech::Speaking) {
+ m_processor->pause();
+ m_state = QTextToSpeech::Paused;
+ emit stateChanged(m_state);
+ }
+}
+
+void QTextToSpeechEngineFlite::resume()
+{
+ if (m_state == QTextToSpeech::Paused) {
+ m_processor->resume();
+ m_state = QTextToSpeech::Speaking;
+ emit stateChanged(m_state);
+ }
+}
+
+double QTextToSpeechEngineFlite::rate() const
+{
+ return m_processor->rate();
+}
+
+bool QTextToSpeechEngineFlite::setRate(double rate)
+{
+ return m_processor->setRate(rate);
+}
+
+double QTextToSpeechEngineFlite::pitch() const
+{
+ return m_processor->pitch();
+}
+
+bool QTextToSpeechEngineFlite::setPitch(double pitch)
+{
+ return m_processor->setPitch(pitch);
+}
+
+QLocale QTextToSpeechEngineFlite::locale() const
+{
+ return m_currentLocale;
+}
+
+bool QTextToSpeechEngineFlite::setLocale(const QLocale &locale)
+{
+ bool localeFound = false;
+ foreach (const QLocale &l, m_locales) {
+ if (l.name() == locale.name()) {
+ localeFound = true;
+ break;
+ }
+ }
+ if (!localeFound)
+ return false;
+ if (m_currentLocale.name() != locale.name()) {
+ m_currentLocale = locale;
+ m_currentVoice = availableVoices().at(0);
+ }
+ return true;
+}
+
+int QTextToSpeechEngineFlite::volume() const
+{
+ return m_processor->volume();
+}
+
+bool QTextToSpeechEngineFlite::setVolume(int volume)
+{
+ return m_processor->setVolume(volume);
+}
+
+QVoice QTextToSpeechEngineFlite::voice() const
+{
+ return m_currentVoice;
+}
+
+bool QTextToSpeechEngineFlite::setVoice(const QVoice &voice)
+{
+ foreach (const QVoice &availableVoice, availableVoices()) {
+ if (QTextToSpeechEngine::voiceData(availableVoice) == QTextToSpeechEngine::voiceData(voice)) {
+ m_currentVoice = voice;
+ return true;
+ }
+ }
+ return false;
+}
+
+QTextToSpeech::State QTextToSpeechEngineFlite::state() const
+{
+ return m_state;
+}
+
+bool QTextToSpeechEngineFlite::init(QString *errorString)
+{
+ int i = 0;
+ const QVector<QTextToSpeechProcessor::VoiceInfo> &voices = m_processor->voices();
+ foreach (const QTextToSpeechProcessor::VoiceInfo &voiceInfo, voices) {
+ QString name = voiceInfo.name;
+ QLocale locale(voiceInfo.locale);
+ QVoice voice = QTextToSpeechEngine::createVoice(name, voiceInfo.gender, voiceInfo.age,
+ QVariant(voiceInfo.id));
+ m_voices.insert(voiceInfo.locale, voice);
+ if (!m_locales.contains(locale))
+ m_locales.append(locale);
+ // Use the first available locale/voice as a fallback
+ if (i == 0) {
+ m_currentVoice = voice;
+ m_currentLocale = locale;
+ }
+ i++;
+ }
+ // Attempt to switch to the system locale
+ setLocale(QLocale::system());
+ connect(m_processor.data(), &QTextToSpeechProcessor::notSpeaking,
+ this, &QTextToSpeechEngineFlite::onNotSpeaking);
+ if (errorString)
+ *errorString = QString();
+ return true;
+}
+
+void QTextToSpeechEngineFlite::onNotSpeaking(int statusCode)
+{
+ Q_UNUSED(statusCode);
+ if (m_state != QTextToSpeech::Ready && m_processor->isIdle()) {
+ m_state = QTextToSpeech::Ready;
+ emit stateChanged(m_state);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/tts/flite/qtexttospeech_flite.h b/src/plugins/tts/flite/qtexttospeech_flite.h
new file mode 100755
index 0000000..311ebbd
--- /dev/null
+++ b/src/plugins/tts/flite/qtexttospeech_flite.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Speech module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTTOSPEECHENGINE_FLITE_H
+#define QTEXTTOSPEECHENGINE_FLITE_H
+
+#include "qtexttospeech_flite_processor.h"
+#include "qtexttospeechengine.h"
+#include "qvoice.h"
+
+#include <QtCore/QString>
+#include <QtCore/QLocale>
+#include <QtCore/QVector>
+#include <QtCore/QSharedPointer>
+
+#include <flite/flite.h>
+
+QT_BEGIN_NAMESPACE
+
+class QTextToSpeechEngineFlite : public QTextToSpeechEngine
+{
+ Q_OBJECT
+
+public:
+ QTextToSpeechEngineFlite(const QVariantMap &parameters, QObject *parent);
+ virtual ~QTextToSpeechEngineFlite();
+
+ // Plug-in API:
+ QVector<QLocale> availableLocales() const Q_DECL_OVERRIDE;
+ QVector<QVoice> availableVoices() const Q_DECL_OVERRIDE;
+ void say(const QString &text) Q_DECL_OVERRIDE;
+ void stop() Q_DECL_OVERRIDE;
+ void pause() Q_DECL_OVERRIDE;
+ void resume() Q_DECL_OVERRIDE;
+ double rate() const Q_DECL_OVERRIDE;
+ bool setRate(double rate) Q_DECL_OVERRIDE;
+ double pitch() const Q_DECL_OVERRIDE;
+ bool setPitch(double pitch) Q_DECL_OVERRIDE;
+ QLocale locale() const Q_DECL_OVERRIDE;
+ bool setLocale(const QLocale &locale) Q_DECL_OVERRIDE;
+ int volume() const Q_DECL_OVERRIDE;
+ bool setVolume(int volume) Q_DECL_OVERRIDE;
+ QVoice voice() const Q_DECL_OVERRIDE;
+ bool setVoice(const QVoice &voice) Q_DECL_OVERRIDE;
+ QTextToSpeech::State state() const Q_DECL_OVERRIDE;
+
+ // Internal API:
+ bool init(QString *errorString);
+
+public slots:
+ void onNotSpeaking(int statusCode);
+
+private:
+ QTextToSpeech::State m_state;
+ QSharedPointer<QTextToSpeechProcessorFlite> m_processor;
+ QLocale m_currentLocale;
+ QVector<QLocale> m_locales;
+ QVoice m_currentVoice;
+ // Voices mapped by their locale name.
+ QMultiMap<QString, QVoice> m_voices;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/tts/flite/qtexttospeech_flite_plugin.cpp b/src/plugins/tts/flite/qtexttospeech_flite_plugin.cpp
new file mode 100755
index 0000000..adf3f54
--- /dev/null
+++ b/src/plugins/tts/flite/qtexttospeech_flite_plugin.cpp
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Speech module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtexttospeech_flite_plugin.h"
+#include "qtexttospeech_flite.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcSpeechTtsFlite, "qt.speech.tts.flite")
+
+QTextToSpeechEngine *QTextToSpeechPluginFlite::createTextToSpeechEngine(
+ const QVariantMap &parameters, QObject *parent, QString *errorString) const
+{
+ QTextToSpeechEngineFlite *flite = new QTextToSpeechEngineFlite(parameters, parent);
+ if (flite && flite->init(errorString)) {
+ return flite;
+ }
+ delete flite;
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/tts/flite/qtexttospeech_flite_plugin.h b/src/plugins/tts/flite/qtexttospeech_flite_plugin.h
new file mode 100755
index 0000000..fe9ed64
--- /dev/null
+++ b/src/plugins/tts/flite/qtexttospeech_flite_plugin.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Speech module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTTOSPEECHPLUGIN_FLITE_H
+#define QTEXTTOSPEECHPLUGIN_FLITE_H
+
+#include "qtexttospeechplugin.h"
+#include "qtexttospeechengine.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QLoggingCategory>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcSpeechTtsFlite)
+
+class QTextToSpeechPluginFlite : public QObject, public QTextToSpeechPlugin
+{
+ Q_OBJECT
+ Q_INTERFACES(QTextToSpeechPlugin)
+ Q_PLUGIN_METADATA(IID "org.qt-project.qt.speech.tts.plugin/5.0"
+ FILE "flite_plugin.json")
+
+public:
+ QTextToSpeechEngine *createTextToSpeechEngine(
+ const QVariantMap &parameters,
+ QObject *parent,
+ QString *errorString) const;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/tts/flite/qtexttospeech_flite_processor.cpp b/src/plugins/tts/flite/qtexttospeech_flite_processor.cpp
new file mode 100644
index 0000000..5cb051d
--- /dev/null
+++ b/src/plugins/tts/flite/qtexttospeech_flite_processor.cpp
@@ -0,0 +1,203 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Speech module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtexttospeech_flite_processor.h"
+#include "qtexttospeech_flite_plugin.h"
+
+#include <QtCore/QString>
+#include <QtCore/QLocale>
+#include <QtCore/QMap>
+
+#include <flite/flite.h>
+
+// en_US voice:
+extern "C" cst_voice *register_cmu_us_kal16();
+extern "C" void unregister_cmu_us_kal16(cst_voice *vox);
+
+QT_BEGIN_NAMESPACE
+
+QWeakPointer<QTextToSpeechProcessorFlite> QTextToSpeechProcessorFlite::m_instance;
+QMutex QTextToSpeechProcessorFlite::m_instanceLock;
+
+QSharedPointer<QTextToSpeechProcessorFlite> QTextToSpeechProcessorFlite::instance()
+{
+ QSharedPointer<QTextToSpeechProcessorFlite> inst = m_instance.toStrongRef();
+ if (inst.isNull()) {
+ QMutexLocker lock(&m_instanceLock);
+ inst = m_instance.toStrongRef();
+ if (inst.isNull()) {
+ inst = QSharedPointer<QTextToSpeechProcessorFlite>(new QTextToSpeechProcessorFlite());
+ m_instance = inst;
+ }
+ }
+ Q_ASSERT(inst);
+ Q_ASSERT(inst == m_instance);
+ return inst;
+}
+
+QTextToSpeechProcessorFlite::QTextToSpeechProcessorFlite():
+ m_initialized(false),
+ m_currentVoice(-1)
+{
+ if (init()) {
+ m_initialized = true;
+ start();
+ } else {
+ deinit();
+ }
+}
+
+QTextToSpeechProcessorFlite::~QTextToSpeechProcessorFlite()
+{
+ if (m_initialized) {
+ exit();
+ deinit();
+ }
+}
+
+const QVector<QTextToSpeechProcessor::VoiceInfo> &QTextToSpeechProcessorFlite::voices() const
+{
+ return m_voices;
+}
+
+int QTextToSpeechProcessorFlite::fliteOutputCb(const cst_wave *w, int start, int size,
+ int last, cst_audio_streaming_info *asi)
+{
+ QTextToSpeechProcessorFlite *processor = static_cast<QTextToSpeechProcessorFlite *>(asi->userdata);
+ if (processor)
+ return processor->fliteOutput(w, start, size, last, asi);
+ return CST_AUDIO_STREAM_STOP;
+}
+
+int QTextToSpeechProcessorFlite::fliteOutput(const cst_wave *w, int start, int size,
+ int last, cst_audio_streaming_info *asi)
+{
+ Q_UNUSED(asi);
+ QString errorString;
+ if (start == 0) {
+ if (!audioStart(w->sample_rate, w->num_channels, &errorString)) {
+ if (!errorString.isEmpty())
+ qCCritical(lcSpeechTtsFlite) << errorString;
+ return CST_AUDIO_STREAM_STOP;
+ }
+ }
+ int bytesToWrite = size * sizeof(short);
+ if (!audioOutput((const char *)(&w->samples[start]), bytesToWrite, &errorString)) {
+ if (!errorString.isEmpty())
+ qCCritical(lcSpeechTtsFlite) << errorString;
+ audioStop(true); // Abort audio output
+ return CST_AUDIO_STREAM_STOP;
+ }
+ if (last == 1)
+ audioStop();
+ return CST_AUDIO_STREAM_CONT;
+}
+
+int QTextToSpeechProcessorFlite::processText(const QString &text, int voiceId)
+{
+ qCDebug(lcSpeechTtsFlite) << "processText() begin";
+ if (voiceId >= 0 && voiceId < m_fliteVoices.size()) {
+ const FliteVoice &voiceInfo = m_fliteVoices.at(voiceId);
+ cst_voice *voice = voiceInfo.vox;
+ cst_audio_streaming_info *asi = new_audio_streaming_info();
+ asi->asc = QTextToSpeechProcessorFlite::fliteOutputCb;
+ asi->userdata = (void *)this;
+ feat_set(voice->features, "streaming_info", audio_streaming_info_val(asi));
+ setRateForVoice(voice, rate());
+ setPitchForVoice(voice, pitch());
+ flite_text_to_speech(text.toUtf8().constData(), voice, "none");
+ }
+ qCDebug(lcSpeechTtsFlite) << "processText() end";
+ return 0;
+}
+
+void QTextToSpeechProcessorFlite::setRateForVoice(cst_voice *voice, float rate)
+{
+ float stretch = 1.0;
+ Q_ASSERT(rate >= -1.0 && rate <= 1.0);
+ // Stretch multipliers taken from Speech Dispatcher
+ if (rate < 0)
+ stretch -= rate * 2;
+ if (rate > 0)
+ stretch -= rate * (100.0 / 175.0);
+ feat_set_float(voice->features, "duration_stretch", stretch);
+}
+
+void QTextToSpeechProcessorFlite::setPitchForVoice(cst_voice *voice, float pitch)
+{
+ float f0;
+ Q_ASSERT(pitch >= -1.0 && pitch <= 1.0);
+ // Conversion taken from Speech Dispatcher
+ f0 = (pitch * 80) + 100;
+ feat_set_float(voice->features, "int_f0_target_mean", f0);
+}
+
+bool QTextToSpeechProcessorFlite::init()
+{
+ flite_init();
+ FliteVoice voice_enus = {
+ register_cmu_us_kal16(),
+ unregister_cmu_us_kal16,
+ "kal16",
+ QLocale(QLocale::English, QLocale::UnitedStates).name(),
+ QVoice::Male,
+ QVoice::Adult
+ };
+ m_fliteVoices.append(voice_enus);
+
+ int totalVoiceCount = 0;
+ foreach (const FliteVoice &voice, m_fliteVoices) {
+ QTextToSpeechProcessor::VoiceInfo voiceInfo;
+ voiceInfo.name = voice.name;
+ voiceInfo.locale = voice.locale;
+ voiceInfo.age = voice.age;
+ voiceInfo.gender = voice.gender;
+ voiceInfo.id = totalVoiceCount;
+ m_voices.append(voiceInfo);
+ totalVoiceCount++;
+ }
+ return true;
+}
+
+void QTextToSpeechProcessorFlite::deinit()
+{
+ foreach (const FliteVoice &voice, m_fliteVoices)
+ voice.unregister_func(voice.vox);
+ m_fliteVoices.clear();
+ m_voices.clear();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/tts/flite/qtexttospeech_flite_processor.h b/src/plugins/tts/flite/qtexttospeech_flite_processor.h
new file mode 100644
index 0000000..6eb1263
--- /dev/null
+++ b/src/plugins/tts/flite/qtexttospeech_flite_processor.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Speech module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTTOSPEECHPROCESSOR_FLITE_H
+#define QTEXTTOSPEECHPROCESSOR_FLITE_H
+
+#include "../common/qtexttospeechprocessor_p.h"
+
+#include "qtexttospeechengine.h"
+#include "qvoice.h"
+
+#include <QtCore/QString>
+#include <QtCore/QVector>
+#include <QtCore/QSharedPointer>
+#include <QtCore/QMutex>
+
+#include <flite/flite.h>
+
+QT_BEGIN_NAMESPACE
+
+// This is a reference counted singleton class.
+// The instance is automatically deleted when no users remain.
+class QTextToSpeechProcessorFlite : public QTextToSpeechProcessor {
+ Q_OBJECT
+
+public:
+ static QSharedPointer<QTextToSpeechProcessorFlite> instance();
+ ~QTextToSpeechProcessorFlite();
+ const QVector<VoiceInfo> &voices() const Q_DECL_OVERRIDE;
+
+private:
+ QTextToSpeechProcessorFlite();
+ static int fliteOutputCb(const cst_wave *w, int start, int size,
+ int last, cst_audio_streaming_info *asi);
+ int fliteOutput(const cst_wave *w, int start, int size,
+ int last, cst_audio_streaming_info *asi);
+ int processText(const QString &text, int voiceId) Q_DECL_OVERRIDE;
+ void setRateForVoice(cst_voice *voice, float rate);
+ void setPitchForVoice(cst_voice *voice, float pitch);
+ bool init();
+ void deinit();
+
+private:
+ struct FliteVoice {
+ cst_voice *vox;
+ void (*unregister_func)(cst_voice *vox);
+ QString name;
+ QString locale;
+ QVoice::Gender gender;
+ QVoice::Age age;
+ };
+ static QWeakPointer<QTextToSpeechProcessorFlite> m_instance;
+ static QMutex m_instanceLock;
+ bool m_initialized;
+ QVector<VoiceInfo> m_voices;
+ QVector<FliteVoice> m_fliteVoices;
+ int m_currentVoice;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/tts/osx/osx.pro b/src/plugins/tts/osx/osx.pro
new file mode 100644
index 0000000..4eb42e8
--- /dev/null
+++ b/src/plugins/tts/osx/osx.pro
@@ -0,0 +1,25 @@
+TARGET = qtexttospeech_speechosx
+PLUGIN_TYPE = texttospeech
+PLUGIN_CLASS_NAME = QTextToSpeechPluginOsx
+
+load(qt_plugin)
+
+QT += core texttospeech
+
+LIBS += -framework Cocoa
+
+HEADERS += \
+ qtexttospeech_osx_plugin.h \
+ qtexttospeech_osx.h \
+
+OBJECTIVE_HEADERS += \
+ qtexttospeech_osx.h \
+
+SOURCES += \
+ qtexttospeech_osx_plugin.cpp \
+
+OBJECTIVE_SOURCES += \
+ qtexttospeech_osx.mm \
+
+OTHER_FILES += \
+ osx_plugin.json
diff --git a/src/plugins/tts/osx/osx_plugin.json b/src/plugins/tts/osx/osx_plugin.json
new file mode 100644
index 0000000..5b039f3
--- /dev/null
+++ b/src/plugins/tts/osx/osx_plugin.json
@@ -0,0 +1,6 @@
+{
+ "Keys": ["osx"],
+ "Provider": "osx",
+ "Version": 100,
+ "Features": []
+}
diff --git a/src/plugins/tts/osx/qtexttospeech_osx.h b/src/plugins/tts/osx/qtexttospeech_osx.h
new file mode 100644
index 0000000..4739326
--- /dev/null
+++ b/src/plugins/tts/osx/qtexttospeech_osx.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Speech module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTTOSPEECHENGINE_OSX_H
+#define QTEXTTOSPEECHENGINE_OSX_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qlocale.h>
+#include <QtTextToSpeech/qtexttospeechengine.h>
+#include <QtTextToSpeech/qvoice.h>
+
+Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(StateDelegate));
+Q_FORWARD_DECLARE_OBJC_CLASS(NSSpeechSynthesizer);
+Q_FORWARD_DECLARE_OBJC_CLASS(NSString);
+
+QT_BEGIN_NAMESPACE
+
+class QTextToSpeechEngineOsx : public QTextToSpeechEngine
+{
+ Q_OBJECT
+
+public:
+ QTextToSpeechEngineOsx(const QVariantMap &parameters, QObject *parent);
+ ~QTextToSpeechEngineOsx();
+
+ // Plug-in API:
+ QVector<QLocale> availableLocales() const Q_DECL_OVERRIDE;
+ QVector<QVoice> availableVoices() const Q_DECL_OVERRIDE;
+ void say(const QString &text) Q_DECL_OVERRIDE;
+ void stop() Q_DECL_OVERRIDE;
+ void pause() Q_DECL_OVERRIDE;
+ void resume() Q_DECL_OVERRIDE;
+ double rate() const Q_DECL_OVERRIDE;
+ bool setRate(double rate) Q_DECL_OVERRIDE;
+ double pitch() const Q_DECL_OVERRIDE;
+ bool setPitch(double pitch) Q_DECL_OVERRIDE;
+ QLocale locale() const Q_DECL_OVERRIDE;
+ bool setLocale(const QLocale &locale) Q_DECL_OVERRIDE;
+ int volume() const Q_DECL_OVERRIDE;
+ bool setVolume(int volume) Q_DECL_OVERRIDE;
+ QVoice voice() const Q_DECL_OVERRIDE;
+ bool setVoice(const QVoice &voice) Q_DECL_OVERRIDE;
+ QTextToSpeech::State state() const Q_DECL_OVERRIDE;
+
+ void speechStopped(bool);
+
+private:
+ void updateVoices();
+ bool isSpeaking() const;
+ bool isPaused() const;
+
+ QTextToSpeech::State m_state;
+// QVoice m_currentVoice;
+
+ QVoice voiceForNSVoice(NSString *voiceString) const;
+ NSSpeechSynthesizer *speechSynthesizer;
+ QT_MANGLE_NAMESPACE(StateDelegate) *stateDelegate;
+ QVector<QLocale> m_locales;
+ QMultiMap<QString, QVoice> m_voices;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/tts/qtexttospeech_mac.mm b/src/plugins/tts/osx/qtexttospeech_osx.mm
index 886676a..9092144 100644
--- a/src/tts/qtexttospeech_mac.mm
+++ b/src/plugins/tts/osx/qtexttospeech_osx.mm
@@ -34,66 +34,18 @@
**
****************************************************************************/
-
-
-#include "qtexttospeech.h"
-#include "qtexttospeech_p.h"
-
-#import <Cocoa/Cocoa.h>
+#include <Cocoa/Cocoa.h>
+#include "qtexttospeech_osx.h"
#include <qdebug.h>
-
-QT_BEGIN_NAMESPACE
-
-class QTextToSpeechPrivateMac;
-
-@interface StateDelegate : NSObject <NSSpeechSynthesizerDelegate>
+@interface QT_MANGLE_NAMESPACE(StateDelegate) : NSObject <NSSpeechSynthesizerDelegate>
{
- QTextToSpeechPrivateMac *speechPrivate;
+ QT_PREPEND_NAMESPACE(QTextToSpeechEngineOsx) *speechPrivate;
}
@end
-class QTextToSpeechPrivateMac : public QTextToSpeechPrivate
-{
-public:
- QTextToSpeechPrivateMac(QTextToSpeech *speech);
- ~QTextToSpeechPrivateMac();
-
- QVector<QLocale> availableLocales() const Q_DECL_OVERRIDE;
- QVector<QVoice> availableVoices() const Q_DECL_OVERRIDE;
- void say(const QString &text) Q_DECL_OVERRIDE;
- void stop() Q_DECL_OVERRIDE;
- void pause() Q_DECL_OVERRIDE;
- void resume() Q_DECL_OVERRIDE;
-
- double rate() const Q_DECL_OVERRIDE;
- void setRate(double rate) Q_DECL_OVERRIDE;
- double pitch() const Q_DECL_OVERRIDE;
- void setPitch(double pitch) Q_DECL_OVERRIDE;
- int volume() const Q_DECL_OVERRIDE;
- void setVolume(int volume) Q_DECL_OVERRIDE;
- void setLocale(const QLocale &locale) Q_DECL_OVERRIDE;
- QLocale locale() const Q_DECL_OVERRIDE;
- void setVoice(const QVoice &voice) Q_DECL_OVERRIDE;
- QVoice voice() const Q_DECL_OVERRIDE;
- QTextToSpeech::State state() const Q_DECL_OVERRIDE;
-
- bool isPaused() const;
- bool isSpeaking() const;
-
- void speechStopped(bool success);
-
-private:
- void updateVoices();
- QVoice voiceForNSVoice(NSString *voiceString) const;
- NSSpeechSynthesizer *speechSynthesizer;
- StateDelegate *stateDelegate;
- QVector<QLocale> m_locales;
- QMultiMap<QString, QVoice> m_voices;
-};
-
-@implementation StateDelegate
-- (id)initWithSpeechPrivate:(QTextToSpeechPrivateMac *) priv {
+@implementation QT_MANGLE_NAMESPACE(StateDelegate)
+- (id)initWithSpeechPrivate:(QTextToSpeechEngineOsx *) priv {
self = [super init];
speechPrivate = priv;
return self;
@@ -102,52 +54,47 @@ private:
Q_UNUSED(sender);
speechPrivate->speechStopped(success);
}
-
@end
-QTextToSpeech::QTextToSpeech(QObject *parent)
- : QObject(*new QTextToSpeechPrivateMac(this), parent)
-{
- qRegisterMetaType<QTextToSpeech::State>();
-}
+QT_BEGIN_NAMESPACE
-QTextToSpeechPrivateMac::QTextToSpeechPrivateMac(QTextToSpeech *speech)
- : QTextToSpeechPrivate(speech)
+QTextToSpeechEngineOsx::QTextToSpeechEngineOsx(const QVariantMap &/*parameters*/, QObject *parent)
+ : QTextToSpeechEngine(parent), m_state(QTextToSpeech::Ready)
{
- stateDelegate = [[StateDelegate alloc] initWithSpeechPrivate:this];
+ stateDelegate = [[QT_MANGLE_NAMESPACE(StateDelegate) alloc] initWithSpeechPrivate:this];
speechSynthesizer = [[NSSpeechSynthesizer alloc] init];
[speechSynthesizer setDelegate: stateDelegate];
updateVoices();
}
-QTextToSpeechPrivateMac::~QTextToSpeechPrivateMac()
+QTextToSpeechEngineOsx::~QTextToSpeechEngineOsx()
{
[speechSynthesizer release];
[stateDelegate release];
}
-QTextToSpeech::State QTextToSpeechPrivate::state() const
+QTextToSpeech::State QTextToSpeechEngineOsx::state() const
{
return m_state;
}
-bool QTextToSpeechPrivateMac::isSpeaking() const
+bool QTextToSpeechEngineOsx::isSpeaking() const
{
return [speechSynthesizer isSpeaking];
}
-void QTextToSpeechPrivateMac::speechStopped(bool success)
+void QTextToSpeechEngineOsx::speechStopped(bool success)
{
Q_UNUSED(success);
if (m_state != QTextToSpeech::Ready) {
m_state = QTextToSpeech::Ready;
- emitStateChanged(m_state);
+ emit stateChanged(m_state);
}
}
-void QTextToSpeechPrivateMac::say(const QString &text)
+void QTextToSpeechEngineOsx::say(const QString &text)
{
if (text.isEmpty())
return;
@@ -164,70 +111,73 @@ void QTextToSpeechPrivateMac::say(const QString &text)
if (m_state != QTextToSpeech::Speaking) {
m_state = QTextToSpeech::Speaking;
- emitStateChanged(m_state);
+ emit stateChanged(m_state);
}
}
-void QTextToSpeechPrivateMac::stop()
+void QTextToSpeechEngineOsx::stop()
{
if([speechSynthesizer isSpeaking])
[speechSynthesizer stopSpeaking];
}
-void QTextToSpeechPrivateMac::pause()
+void QTextToSpeechEngineOsx::pause()
{
if ([speechSynthesizer isSpeaking]) {
[speechSynthesizer pauseSpeakingAtBoundary: NSSpeechWordBoundary];
m_state = QTextToSpeech::Paused;
- emitStateChanged(m_state);
+ emit stateChanged(m_state);
}
}
-bool QTextToSpeechPrivateMac::isPaused() const
+bool QTextToSpeechEngineOsx::isPaused() const
{
return m_state == QTextToSpeech::Paused;
}
-void QTextToSpeechPrivateMac::resume()
+void QTextToSpeechEngineOsx::resume()
{
m_state = QTextToSpeech::Speaking;
- emitStateChanged(m_state);
+ emit stateChanged(m_state);
[speechSynthesizer continueSpeaking];
}
-double QTextToSpeechPrivateMac::rate() const
+double QTextToSpeechEngineOsx::rate() const
{
return ([speechSynthesizer rate] - 200) / 200.0;
}
-void QTextToSpeechPrivateMac::setPitch(double pitch)
+bool QTextToSpeechEngineOsx::setPitch(double pitch)
{
// 30 to 65
double p = 30.0 + ((pitch + 1.0) / 2.0) * 35.0;
[speechSynthesizer setObject:[NSNumber numberWithFloat:p] forProperty:NSSpeechPitchBaseProperty error:nil];
+ return true;
}
-double QTextToSpeechPrivateMac::pitch() const
+double QTextToSpeechEngineOsx::pitch() const
{
double pitch = [[speechSynthesizer objectForProperty:NSSpeechPitchBaseProperty error:nil] floatValue];
return (pitch - 30.0) / 35.0 * 2.0 - 1.0;
}
-int QTextToSpeechPrivateMac::volume() const
+int QTextToSpeechEngineOsx::volume() const
{
return [speechSynthesizer volume] * 100;
}
-void QTextToSpeechPrivateMac::setRate(double rate)
+bool QTextToSpeechEngineOsx::setRate(double rate)
{
// NSSpeechSynthesizer supports words per minute,
// human speech is 180 to 220 - use 0 to 400 as range here
[speechSynthesizer setRate: 200 + (rate * 200)];
+ return true;
}
-void QTextToSpeechPrivateMac::setVolume(int volume)
+bool QTextToSpeechEngineOsx::setVolume(int volume)
{
[speechSynthesizer setVolume: volume / 100.0];
+ return true;
}
QLocale localeForVoice(NSString *voice)
@@ -236,66 +186,59 @@ QLocale localeForVoice(NSString *voice)
return QString::fromNSString(attrs[NSVoiceLocaleIdentifier]);
}
-QVoice QTextToSpeechPrivateMac::voiceForNSVoice(NSString *voiceString) const
+QVoice QTextToSpeechEngineOsx::voiceForNSVoice(NSString *voiceString) const
{
NSDictionary *attrs = [NSSpeechSynthesizer attributesForVoice:voiceString];
- QVoice voice;
QString voiceName = QString::fromNSString(attrs[NSVoiceName]);
- voice.setName(voiceName);
- NSString *gender = attrs[NSVoiceGender];
- voice.setGender([gender isEqualToString:NSVoiceGenderMale] ? QVoice::Male :
- [gender isEqualToString:NSVoiceGenderFemale] ? QVoice::Female :
- QVoice::Unknown);
- NSNumber *age = attrs[NSVoiceAge];
- int ageInt = age.intValue;
- voice.setAge(ageInt < 13 ? QVoice::Child :
- ageInt < 20 ? QVoice::Teenager :
- ageInt < 45 ? QVoice::Adult :
- ageInt < 90 ? QVoice::Senior : QVoice::Other);
- voice.setData(QVariant(QString::fromNSString(attrs[NSVoiceIdentifier])));
+
+ NSString *genderString = attrs[NSVoiceGender];
+ QVoice::Gender gender = [genderString isEqualToString:NSVoiceGenderMale] ? QVoice::Male :
+ [genderString isEqualToString:NSVoiceGenderFemale] ? QVoice::Female :
+ QVoice::Unknown;
+
+ NSNumber *ageNSNumber = attrs[NSVoiceAge];
+ int ageInt = ageNSNumber.intValue;
+ QVoice::Age age = (ageInt < 13 ? QVoice::Child :
+ ageInt < 20 ? QVoice::Teenager :
+ ageInt < 45 ? QVoice::Adult :
+ ageInt < 90 ? QVoice::Senior : QVoice::Other);
+ QVariant data = QString::fromNSString(attrs[NSVoiceIdentifier]);
+ QVoice voice = createVoice(voiceName, gender, age, data);
return voice;
}
-QVector<QLocale> QTextToSpeechPrivateMac::availableLocales() const
+QVector<QLocale> QTextToSpeechEngineOsx::availableLocales() const
{
return m_locales;
}
-void QTextToSpeechPrivateMac::setLocale(const QLocale &locale)
+bool QTextToSpeechEngineOsx::setLocale(const QLocale &locale)
{
NSArray *voices = [NSSpeechSynthesizer availableVoices];
NSString *voice = [NSSpeechSynthesizer defaultVoice];
// always prefer default
if (locale == localeForVoice(voice)) {
[speechSynthesizer setVoice:voice];
- emitLocaleChanged(locale);
- emitVoiceChanged(voiceForNSVoice(voice));
- return;
+ return true;
}
for (voice in voices) {
QLocale voiceLocale = localeForVoice(voice);
if (locale == voiceLocale) {
[speechSynthesizer setVoice:voice];
- emitLocaleChanged(locale);
- emitVoiceChanged(voiceForNSVoice(voice));
- return;
+ return true;
}
}
+ return false;
}
-QLocale QTextToSpeechPrivateMac::locale() const
+QLocale QTextToSpeechEngineOsx::locale() const
{
NSString *voice = [speechSynthesizer voice];
return localeForVoice(voice);
}
-QTextToSpeech::State QTextToSpeechPrivateMac::state() const
-{
- return m_state;
-}
-
-void QTextToSpeechPrivateMac::updateVoices()
+void QTextToSpeechEngineOsx::updateVoices()
{
NSArray *voices = [NSSpeechSynthesizer availableVoices];
for (NSString *voice in voices) {
@@ -307,21 +250,19 @@ void QTextToSpeechPrivateMac::updateVoices()
}
}
-QVector<QVoice> QTextToSpeechPrivateMac::availableVoices() const
+QVector<QVoice> QTextToSpeechEngineOsx::availableVoices() const
{
return m_voices.values(locale().name()).toVector();
}
-void QTextToSpeechPrivateMac::setVoice(const QVoice &voice)
+bool QTextToSpeechEngineOsx::setVoice(const QVoice &voice)
{
- NSString *identifier = voice.data().toString().toNSString();
+ NSString *identifier = voiceData(voice).toString().toNSString();
[speechSynthesizer setVoice:identifier];
- QLocale newLocale = localeForVoice(identifier);
- emitLocaleChanged(newLocale);
- emitVoiceChanged(voice);
+ return true;
}
-QVoice QTextToSpeechPrivateMac::voice() const
+QVoice QTextToSpeechEngineOsx::voice() const
{
NSString *voice = [speechSynthesizer voice];
return voiceForNSVoice(voice);
diff --git a/src/plugins/tts/osx/qtexttospeech_osx_plugin.cpp b/src/plugins/tts/osx/qtexttospeech_osx_plugin.cpp
new file mode 100644
index 0000000..3dbac52
--- /dev/null
+++ b/src/plugins/tts/osx/qtexttospeech_osx_plugin.cpp
@@ -0,0 +1,44 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Speech module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtexttospeech_osx_plugin.h"
+#include "qtexttospeech_osx.h"
+
+QTextToSpeechEngine *QTextToSpeechPluginOsx::createTextToSpeechEngine(const QVariantMap &parameters, QObject *parent, QString *errorString) const
+{
+ Q_UNUSED(errorString)
+ return new QTextToSpeechEngineOsx(parameters, parent);
+}
diff --git a/src/plugins/tts/osx/qtexttospeech_osx_plugin.h b/src/plugins/tts/osx/qtexttospeech_osx_plugin.h
new file mode 100644
index 0000000..36cdb3a
--- /dev/null
+++ b/src/plugins/tts/osx/qtexttospeech_osx_plugin.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Speech module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTTOSPEECHPLUGIN_OSX_H
+#define QTEXTTOSPEECHPLUGIN_OSX_H
+
+#include <QtCore/QObject>
+#include <QtCore/QLoggingCategory>
+#include <QtTextToSpeech/qtexttospeechplugin.h>
+#include <QtTextToSpeech/qtexttospeechengine.h>
+
+QT_BEGIN_NAMESPACE
+
+class QTextToSpeechPluginOsx : public QObject, public QTextToSpeechPlugin
+{
+ Q_OBJECT
+ Q_INTERFACES(QTextToSpeechPlugin)
+ Q_PLUGIN_METADATA(IID "org.qt-project.qt.speech.tts.plugin/5.0"
+ FILE "osx_plugin.json")
+
+public:
+ QTextToSpeechEngine *createTextToSpeechEngine(
+ const QVariantMap &parameters,
+ QObject *parent,
+ QString *errorString) const;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/tts/qtexttospeech_win.cpp b/src/plugins/tts/sapi/qtexttospeech_sapi.cpp
index 75caa59..7df3399 100644
--- a/src/tts/qtexttospeech_win.cpp
+++ b/src/plugins/tts/sapi/qtexttospeech_sapi.cpp
@@ -34,9 +34,7 @@
**
****************************************************************************/
-
-
-#include "qtexttospeech_p.h"
+#include "qtexttospeech_sapi.h"
#include <windows.h>
#include <sapi.h>
@@ -45,92 +43,43 @@
QT_BEGIN_NAMESPACE
-
-class QTextToSpeechPrivateWindows : public QTextToSpeechPrivate, public ISpNotifyCallback
+QTextToSpeechEngineSapi::QTextToSpeechEngineSapi(const QVariantMap &, QObject *)
+ : m_pitch(0.0), m_pauseCount(0), m_state(QTextToSpeech::BackendError)
{
-public:
- QTextToSpeechPrivateWindows(QTextToSpeech *speech);
- ~QTextToSpeechPrivateWindows();
-
- QVector<QLocale> availableLocales() const Q_DECL_OVERRIDE;
- QVector<QVoice> availableVoices() const Q_DECL_OVERRIDE;
-
- void say(const QString &text) Q_DECL_OVERRIDE;
- void stop() Q_DECL_OVERRIDE;
- void pause() Q_DECL_OVERRIDE;
- void resume() Q_DECL_OVERRIDE;
-
-
- double rate() const Q_DECL_OVERRIDE;
- void setRate(double rate) Q_DECL_OVERRIDE;
- double pitch() const Q_DECL_OVERRIDE;
- void setPitch(double pitch) Q_DECL_OVERRIDE;
- int volume() const Q_DECL_OVERRIDE;
- void setVolume(int volume) Q_DECL_OVERRIDE;
- void setLocale(const QLocale &locale) Q_DECL_OVERRIDE;
- QLocale locale() const Q_DECL_OVERRIDE;
- void setVoice(const QVoice &voiceName) Q_DECL_OVERRIDE;
- QVoice voice() const Q_DECL_OVERRIDE;
- QTextToSpeech::State state() const Q_DECL_OVERRIDE;
-
- bool isPaused() const { return m_pauseCount; }
- bool isSpeaking() const;
-
- HRESULT STDMETHODCALLTYPE NotifyCallback(WPARAM /*wParam*/, LPARAM /*lParam*/);
-
-private:
- QMap<QString, QString> voiceAttributes(ISpObjectToken *speechToken) const;
- QString voiceId(ISpObjectToken *speechToken) const;
- QLocale lcidToLocale(const QString &lcid) const;
- void updateVoices();
-
- ISpVoice *m_voice;
- double m_pitch;
- int m_pauseCount;
- QVector<QLocale> m_locales;
- QVoice m_currentVoice;
- QMultiMap<QString, QVoice> m_voices;
-};
-
-
-QTextToSpeech::QTextToSpeech(QObject *parent)
- : QObject(*new QTextToSpeechPrivateWindows(this), parent)
+ init();
+}
+
+QTextToSpeechEngineSapi::~QTextToSpeechEngineSapi()
{
- qRegisterMetaType<QTextToSpeech::State>();
}
-QTextToSpeechPrivateWindows::QTextToSpeechPrivateWindows(QTextToSpeech *speech)
- : QTextToSpeechPrivate(speech), m_pitch(0.0), m_pauseCount(0) //, m_voices(0)
+void QTextToSpeechEngineSapi::init()
{
- if (FAILED(::CoInitialize(NULL)))
+ if (FAILED(::CoInitialize(NULL))) {
qWarning() << "Init of COM failed";
+ return;
+ }
HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&m_voice);
- if (!SUCCEEDED(hr))
+ if (!SUCCEEDED(hr)) {
qWarning() << "Could not init voice";
+ return;
+ }
m_voice->SetInterest(SPFEI_ALL_TTS_EVENTS, SPFEI_ALL_TTS_EVENTS);
m_voice->SetNotifyCallbackInterface(this, 0, 0);
updateVoices();
+ m_state = QTextToSpeech::Ready;
}
-QTextToSpeechPrivateWindows::~QTextToSpeechPrivateWindows()
-{
-}
-
-QTextToSpeech::State QTextToSpeechPrivate::state() const
-{
- return m_state;
-}
-
-bool QTextToSpeechPrivateWindows::isSpeaking() const
+bool QTextToSpeechEngineSapi::isSpeaking() const
{
SPVOICESTATUS eventStatus;
m_voice->GetStatus(&eventStatus, NULL);
return eventStatus.dwRunningState == SPRS_IS_SPEAKING;
}
-void QTextToSpeechPrivateWindows::say(const QString &text)
+void QTextToSpeechEngineSapi::say(const QString &text)
{
if (text.isEmpty())
return;
@@ -140,20 +89,19 @@ void QTextToSpeechPrivateWindows::say(const QString &text)
stop();
textString.prepend(QString::fromLatin1("<pitch absmiddle=\"%1\"/>").arg(m_pitch * 10));
- qDebug() << "say: " << textString;
std::wstring wtext = textString.toStdWString();
m_voice->Speak(wtext.data(), SPF_ASYNC, NULL);
}
-void QTextToSpeechPrivateWindows::stop()
+void QTextToSpeechEngineSapi::stop()
{
- if (isPaused())
+ if (m_state == QTextToSpeech::Paused)
resume();
m_voice->Speak(NULL, SPF_PURGEBEFORESPEAK, 0);
}
-void QTextToSpeechPrivateWindows::pause()
+void QTextToSpeechEngineSapi::pause()
{
if (!isSpeaking())
return;
@@ -162,11 +110,11 @@ void QTextToSpeechPrivateWindows::pause()
++m_pauseCount;
m_voice->Pause();
m_state = QTextToSpeech::Paused;
- emitStateChanged(m_state);
+ emit stateChanged(m_state);
}
}
-void QTextToSpeechPrivateWindows::resume()
+void QTextToSpeechEngineSapi::resume()
{
if (m_pauseCount > 0) {
--m_pauseCount;
@@ -176,27 +124,29 @@ void QTextToSpeechPrivateWindows::resume()
} else {
m_state = QTextToSpeech::Ready;
}
- emitStateChanged(m_state);
+ emit stateChanged(m_state);
}
}
-void QTextToSpeechPrivateWindows::setPitch(double pitch)
+bool QTextToSpeechEngineSapi::setPitch(double pitch)
{
m_pitch = pitch;
+ return true;
}
-double QTextToSpeechPrivateWindows::pitch() const
+double QTextToSpeechEngineSapi::pitch() const
{
return m_pitch;
}
-void QTextToSpeechPrivateWindows::setRate(double rate)
+bool QTextToSpeechEngineSapi::setRate(double rate)
{
// -10 to 10
m_voice->SetRate(long(rate*10));
+ return true;
}
-double QTextToSpeechPrivateWindows::rate() const
+double QTextToSpeechEngineSapi::rate() const
{
long rateValue;
if (m_voice->GetRate(&rateValue) == S_OK)
@@ -204,13 +154,14 @@ double QTextToSpeechPrivateWindows::rate() const
return -1;
}
-void QTextToSpeechPrivateWindows::setVolume(int volume)
+bool QTextToSpeechEngineSapi::setVolume(int volume)
{
// 0 to 100
m_voice->SetVolume(volume);
+ return true;
}
-int QTextToSpeechPrivateWindows::volume() const
+int QTextToSpeechEngineSapi::volume() const
{
USHORT baseVolume;
if (m_voice->GetVolume(&baseVolume) == S_OK)
@@ -220,7 +171,7 @@ int QTextToSpeechPrivateWindows::volume() const
return -1;
}
-QString QTextToSpeechPrivateWindows::voiceId(ISpObjectToken *speechToken) const
+QString QTextToSpeechEngineSapi::voiceId(ISpObjectToken *speechToken) const
{
HRESULT hr = S_OK;
LPWSTR vId = nullptr;
@@ -232,7 +183,7 @@ QString QTextToSpeechPrivateWindows::voiceId(ISpObjectToken *speechToken) const
return QString::fromWCharArray(vId);
}
-QMap<QString, QString> QTextToSpeechPrivateWindows::voiceAttributes(ISpObjectToken *speechToken) const
+QMap<QString, QString> QTextToSpeechEngineSapi::voiceAttributes(ISpObjectToken *speechToken) const
{
HRESULT hr = S_OK;
QMap<QString, QString> result;
@@ -276,7 +227,7 @@ QMap<QString, QString> QTextToSpeechPrivateWindows::voiceAttributes(ISpObjectTok
return result;
}
-QLocale QTextToSpeechPrivateWindows::lcidToLocale(const QString &lcid) const
+QLocale QTextToSpeechEngineSapi::lcidToLocale(const QString &lcid) const
{
bool ok;
LCID locale = lcid.toInt(&ok, 16);
@@ -292,7 +243,7 @@ QLocale QTextToSpeechPrivateWindows::lcidToLocale(const QString &lcid) const
return QLocale(iso);
}
-void QTextToSpeechPrivateWindows::updateVoices()
+void QTextToSpeechEngineSapi::updateVoices()
{
HRESULT hr = S_OK;
CComPtr<ISpObjectToken> cpVoiceToken;
@@ -318,37 +269,37 @@ void QTextToSpeechPrivateWindows::updateVoices()
m_locales.append(vLocale);
// Create voice
- QVoice voice;
- voice.setName(vAttr["Name"]);
- voice.setAge(vAttr["Age"] == "Adult" ? QVoice::Adult : QVoice::Other);
- voice.setGender(vAttr["Gender"] == "Male" ? QVoice::Male :
- vAttr["Gender"] == "Female" ? QVoice::Female :
- QVoice::Unknown);
+
+ QString name = vAttr["Name"];
+ QVoice::Age age = vAttr["Age"] == "Adult" ? QVoice::Adult : QVoice::Other;
+ QVoice::Gender gender = vAttr["Gender"] == "Male" ? QVoice::Male :
+ vAttr["Gender"] == "Female" ? QVoice::Female :
+ QVoice::Unknown;
// Getting the ID of the voice to set the voice later
QString vId = voiceId(cpVoiceToken);
- voice.setData(vId);
+ QVoice voice = createVoice(name, gender, age, vId);
m_voices.insert(vLocale.name(), voice);
}
}
-QVector<QLocale> QTextToSpeechPrivateWindows::availableLocales() const
+QVector<QLocale> QTextToSpeechEngineSapi::availableLocales() const
{
return m_locales;
}
-void QTextToSpeechPrivateWindows::setLocale(const QLocale &locale)
+bool QTextToSpeechEngineSapi::setLocale(const QLocale &locale)
{
QList<QVoice> voicesForLocale = m_voices.values(locale.name());
if (voicesForLocale.length() > 0) {
setVoice(voicesForLocale[0]);
- emitLocaleChanged(locale);
- emitVoiceChanged(voicesForLocale[0]);
+ return true;
} else {
qWarning() << "No voice found for given locale";
}
+ return false;
}
-QLocale QTextToSpeechPrivateWindows::locale() const
+QLocale QTextToSpeechEngineSapi::locale() const
{
// Get current voice id
CComPtr<ISpObjectToken> cpVoiceToken;
@@ -358,15 +309,15 @@ QLocale QTextToSpeechPrivateWindows::locale() const
return lcidToLocale(vAttr["Language"]);
}
-QVector<QVoice> QTextToSpeechPrivateWindows::availableVoices() const
+QVector<QVoice> QTextToSpeechEngineSapi::availableVoices() const
{
return m_voices.values(locale().name()).toVector();
}
-void QTextToSpeechPrivateWindows::setVoice(const QVoice &voice)
+bool QTextToSpeechEngineSapi::setVoice(const QVoice &voice)
{
// Convert voice id to null-terminated wide char string
- QString vId = voice.data().toString();
+ QString vId = voiceData(voice).toString();
wchar_t* tokenId = new wchar_t[vId.size()+1];
vId.toWCharArray(tokenId);
tokenId[vId.size()] = 0;
@@ -378,39 +329,39 @@ void QTextToSpeechPrivateWindows::setVoice(const QVoice &voice)
if (FAILED(hr)) {
qWarning() << "Creating the voice token from ID failed";
m_state = QTextToSpeech::BackendError;
- emitStateChanged(m_state);
- return;
+ emit stateChanged(m_state);
+ return false;
}
if (m_state != QTextToSpeech::Ready) {
m_state = QTextToSpeech::Ready;
- emitStateChanged(m_state);
+ emit stateChanged(m_state);
}
delete[] tokenId;
m_voice->SetVoice(cpVoiceToken);
- emitVoiceChanged(voice);
+ return true;
}
-QVoice QTextToSpeechPrivateWindows::voice() const
+QVoice QTextToSpeechEngineSapi::voice() const
{
CComPtr<ISpObjectToken> cpVoiceToken;
m_voice->GetVoice(&cpVoiceToken);
QString vId = voiceId(cpVoiceToken);
foreach (const QVoice &voice, m_voices.values()) {
- if (voice.data().toString() == vId) {
+ if (voiceData(voice).toString() == vId) {
return voice;
}
}
return QVoice();
}
-QTextToSpeech::State QTextToSpeechPrivateWindows::state() const
+QTextToSpeech::State QTextToSpeechEngineSapi::state() const
{
return m_state;
}
-HRESULT QTextToSpeechPrivateWindows::NotifyCallback(WPARAM /*wParam*/, LPARAM /*lParam*/)
+HRESULT QTextToSpeechEngineSapi::NotifyCallback(WPARAM /*wParam*/, LPARAM /*lParam*/)
{
QTextToSpeech::State newState = QTextToSpeech::Ready;
if (isPaused()) {
@@ -423,7 +374,7 @@ HRESULT QTextToSpeechPrivateWindows::NotifyCallback(WPARAM /*wParam*/, LPARAM /*
if (m_state != newState) {
m_state = newState;
- emitStateChanged(newState);
+ emit stateChanged(newState);
}
return S_OK;
diff --git a/src/plugins/tts/sapi/qtexttospeech_sapi.h b/src/plugins/tts/sapi/qtexttospeech_sapi.h
new file mode 100644
index 0000000..341a75f
--- /dev/null
+++ b/src/plugins/tts/sapi/qtexttospeech_sapi.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Speech module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTTOSPEECHENGINE_SAPI_H
+#define QTEXTTOSPEECHENGINE_SAPI_H
+
+#include <windows.h>
+#include <sapi.h>
+#include <sphelper.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qlocale.h>
+#include <QtTextToSpeech/qtexttospeechengine.h>
+#include <QtTextToSpeech/qvoice.h>
+
+QT_BEGIN_NAMESPACE
+
+class QTextToSpeechEngineSapi : public QTextToSpeechEngine, public ISpNotifyCallback
+{
+ Q_OBJECT
+
+public:
+ QTextToSpeechEngineSapi(const QVariantMap &parameters, QObject *parent);
+ ~QTextToSpeechEngineSapi();
+
+ // Plug-in API:
+ QVector<QLocale> availableLocales() const Q_DECL_OVERRIDE;
+ QVector<QVoice> availableVoices() const Q_DECL_OVERRIDE;
+ void say(const QString &text) Q_DECL_OVERRIDE;
+ void stop() Q_DECL_OVERRIDE;
+ void pause() Q_DECL_OVERRIDE;
+ void resume() Q_DECL_OVERRIDE;
+ double rate() const Q_DECL_OVERRIDE;
+ bool setRate(double rate) Q_DECL_OVERRIDE;
+ double pitch() const Q_DECL_OVERRIDE;
+ bool setPitch(double pitch) Q_DECL_OVERRIDE;
+ QLocale locale() const Q_DECL_OVERRIDE;
+ bool setLocale(const QLocale &locale) Q_DECL_OVERRIDE;
+ int volume() const Q_DECL_OVERRIDE;
+ bool setVolume(int volume) Q_DECL_OVERRIDE;
+ QVoice voice() const Q_DECL_OVERRIDE;
+ bool setVoice(const QVoice &voice) Q_DECL_OVERRIDE;
+ QTextToSpeech::State state() const Q_DECL_OVERRIDE;
+
+ HRESULT STDMETHODCALLTYPE NotifyCallback(WPARAM /*wParam*/, LPARAM /*lParam*/);
+private:
+
+ void init();
+ bool isSpeaking() const;
+ bool isPaused() const { return m_pauseCount; }
+ QMap<QString, QString> voiceAttributes(ISpObjectToken *speechToken) const;
+ QString voiceId(ISpObjectToken *speechToken) const;
+ QLocale lcidToLocale(const QString &lcid) const;
+ void updateVoices();
+
+ QTextToSpeech::State m_state;
+ QVector<QLocale> m_locales;
+// QLocale m_currentLocale;
+ QVoice m_currentVoice;
+ // Voices mapped by their locale name.
+ QMultiMap<QString, QVoice> m_voices;
+
+ ISpVoice *m_voice;
+ double m_pitch;
+ int m_pauseCount;
+};
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/tts/sapi/qtexttospeech_sapi_plugin.cpp b/src/plugins/tts/sapi/qtexttospeech_sapi_plugin.cpp
new file mode 100644
index 0000000..b22126d
--- /dev/null
+++ b/src/plugins/tts/sapi/qtexttospeech_sapi_plugin.cpp
@@ -0,0 +1,44 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Speech module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtexttospeech_sapi_plugin.h"
+#include "qtexttospeech_sapi.h"
+
+QTextToSpeechEngine *QTextToSpeechPluginSapi::createTextToSpeechEngine(const QVariantMap &parameters, QObject *parent, QString *errorString) const
+{
+ Q_UNUSED(errorString)
+ return new QTextToSpeechEngineSapi(parameters, parent);
+}
diff --git a/src/plugins/tts/sapi/qtexttospeech_sapi_plugin.h b/src/plugins/tts/sapi/qtexttospeech_sapi_plugin.h
new file mode 100644
index 0000000..8d6b75b
--- /dev/null
+++ b/src/plugins/tts/sapi/qtexttospeech_sapi_plugin.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Speech module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTTOSPEECHPLUGIN_SAPI_H
+#define QTEXTTOSPEECHPLUGIN_SAPI_H
+
+#include <QtCore/QObject>
+#include <QtCore/QLoggingCategory>
+#include <QtTextToSpeech/qtexttospeechplugin.h>
+#include <QtTextToSpeech/qtexttospeechengine.h>
+
+QT_BEGIN_NAMESPACE
+
+class QTextToSpeechPluginSapi : public QObject, public QTextToSpeechPlugin
+{
+ Q_OBJECT
+ Q_INTERFACES(QTextToSpeechPlugin)
+ Q_PLUGIN_METADATA(IID "org.qt-project.qt.speech.tts.plugin/5.0"
+ FILE "sapi_plugin.json")
+
+public:
+ QTextToSpeechEngine *createTextToSpeechEngine(
+ const QVariantMap &parameters,
+ QObject *parent,
+ QString *errorString) const;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/tts/sapi/sapi.pro b/src/plugins/tts/sapi/sapi.pro
new file mode 100644
index 0000000..b78641d
--- /dev/null
+++ b/src/plugins/tts/sapi/sapi.pro
@@ -0,0 +1,26 @@
+TARGET = qtexttospeech_sapi
+PLUGIN_TYPE = texttospeech
+PLUGIN_CLASS_NAME = QTextToSpeechPluginSapi
+
+# sapi.h contains some parts that fail with "strictStrings"
+QMAKE_CFLAGS_RELEASE -= -Zc:strictStrings
+QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO -= -Zc:strictStrings
+QMAKE_CXXFLAGS_RELEASE -= -Zc:strictStrings
+QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO -= -Zc:strictStrings
+QMAKE_CFLAGS -= -Zc:strictStrings
+QMAKE_CXXFLAGS -= -Zc:strictStrings
+
+load(qt_plugin)
+
+QT += core texttospeech
+
+HEADERS += \
+ qtexttospeech_sapi.h \
+ qtexttospeech_sapi_plugin.h \
+
+SOURCES += \
+ qtexttospeech_sapi.cpp \
+ qtexttospeech_sapi_plugin.cpp \
+
+OTHER_FILES += \
+ sapi_plugin.json
diff --git a/src/plugins/tts/sapi/sapi_plugin.json b/src/plugins/tts/sapi/sapi_plugin.json
new file mode 100644
index 0000000..84e1e48
--- /dev/null
+++ b/src/plugins/tts/sapi/sapi_plugin.json
@@ -0,0 +1,6 @@
+{
+ "Keys": ["sapi"],
+ "Provider": "sapi",
+ "Version": 100,
+ "Features": []
+}
diff --git a/src/plugins/tts/speechdispatcher/speechdispatcher.pro b/src/plugins/tts/speechdispatcher/speechdispatcher.pro
index 6f4e5cc..5a5c0ab 100644
--- a/src/plugins/tts/speechdispatcher/speechdispatcher.pro
+++ b/src/plugins/tts/speechdispatcher/speechdispatcher.pro
@@ -6,8 +6,8 @@ load(qt_plugin)
QT += core texttospeech
+CONFIG += link_pkgconfig
PKGCONFIG = speech-dispatcher
-LIBS += -lspeechd
HEADERS += \
qtexttospeech_speechd.h \
diff --git a/src/plugins/tts/tts.pro b/src/plugins/tts/tts.pro
index ef07835..90b79a6 100755..100644
--- a/src/plugins/tts/tts.pro
+++ b/src/plugins/tts/tts.pro
@@ -6,3 +6,18 @@ unix {
SUBDIRS += speechdispatcher
}
}
+
+# mingw needs copies of the structures defined in sapi.h
+# until those are written, disable the sapi plugin for mingw
+windows:!winrt:!mingw: SUBDIRS += sapi
+winrt: SUBDIRS += winrt
+
+osx: SUBDIRS += osx
+
+config_flite {
+ SUBDIRS += flite
+}
+
+config_vocalizer: exists(vocalizer) {
+ SUBDIRS += vocalizer
+}
diff --git a/src/plugins/tts/winrt/qtexttospeech_winrt.cpp b/src/plugins/tts/winrt/qtexttospeech_winrt.cpp
new file mode 100644
index 0000000..583cb6f
--- /dev/null
+++ b/src/plugins/tts/winrt/qtexttospeech_winrt.cpp
@@ -0,0 +1,448 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Speech module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtexttospeech_winrt.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/qfunctions_winrt.h>
+#include <QtCore/QMap>
+#include <QtCore/QTimer>
+#include <private/qeventdispatcher_winrt_p.h>
+
+#include <windows.foundation.h>
+#include <windows.foundation.collections.h>
+#include <windows.media.speechsynthesis.h>
+#include <windows.storage.streams.h>
+#include <windows.ui.xaml.h>
+#include <windows.ui.xaml.controls.h>
+#include <windows.ui.xaml.markup.h>
+
+#include <functional>
+#include <wrl.h>
+
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::Foundation::Collections;
+using namespace ABI::Windows::Media::SpeechSynthesis;
+using namespace ABI::Windows::Storage::Streams;
+using namespace ABI::Windows::UI::Xaml;
+using namespace ABI::Windows::UI::Xaml::Controls;
+using namespace ABI::Windows::UI::Xaml::Markup;
+using namespace ABI::Windows::UI::Xaml::Media;
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+
+QT_BEGIN_NAMESPACE
+
+#define LSTRING(str) L#str
+static const wchar_t webviewXaml[] = LSTRING(
+<MediaElement xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" />
+);
+
+class QTextToSpeechEngineWinRTPrivate
+{
+public:
+ QTimer timer;
+ ComPtr<IXamlReaderStatics> xamlReader;
+ ComPtr<ISpeechSynthesizer> synth;
+ QVector<QLocale> locales;
+ QVector<QVoice> voices;
+ QVector<ComPtr<IVoiceInformation>> infos;
+ EventRegistrationToken tok;
+
+ ComPtr<IMediaElement> media;
+
+ double rate;
+ double volume;
+
+ QTextToSpeech::State state;
+};
+
+QTextToSpeechEngineWinRT::QTextToSpeechEngineWinRT(const QVariantMap &, QObject *parent)
+ : QTextToSpeechEngine(parent)
+ , d_ptr(new QTextToSpeechEngineWinRTPrivate)
+{
+ d_ptr->rate = 0;
+ d_ptr->volume = 100;
+ d_ptr->timer.setInterval(100);
+ connect(&d_ptr->timer, &QTimer::timeout, this, &QTextToSpeechEngineWinRT::checkElementState);
+
+ init();
+}
+
+QTextToSpeechEngineWinRT::~QTextToSpeechEngineWinRT()
+{
+}
+
+QVector<QLocale> QTextToSpeechEngineWinRT::availableLocales() const
+{
+ Q_D(const QTextToSpeechEngineWinRT);
+ return d->locales;
+}
+
+QVector<QVoice> QTextToSpeechEngineWinRT::availableVoices() const
+{
+ Q_D(const QTextToSpeechEngineWinRT);
+ return d->voices;
+}
+
+void QTextToSpeechEngineWinRT::say(const QString &text)
+{
+ Q_D(QTextToSpeechEngineWinRT);
+
+ HRESULT hr;
+
+ hr = QEventDispatcherWinRT::runOnXamlThread([text, d]() {
+ HRESULT hr;
+ HStringReference nativeText(reinterpret_cast<LPCWSTR>(text.utf16()), text.length());
+ ComPtr<IAsyncOperation<SpeechSynthesisStream*>> op;
+
+ hr = d->synth->SynthesizeTextToStreamAsync(nativeText.Get(), &op);
+ RETURN_HR_IF_FAILED("Could not synthesize text.");
+
+ ComPtr<ISpeechSynthesisStream> stream;
+ hr = QWinRTFunctions::await(op, stream.GetAddressOf());
+ RETURN_HR_IF_FAILED("Synthesizing failed.");
+
+ ComPtr<IRandomAccessStream> randomStream;
+ hr = stream.As(&randomStream);
+ RETURN_HR_IF_FAILED("Could not cast to RandomAccessStream.");
+
+ // Directly instantiating a MediaElement works, but it throws an exception
+ // when setting the source. Using a XamlReader appears to set it up properly.
+ ComPtr<IInspectable> element;
+ hr = d->xamlReader->Load(HString::MakeReference(webviewXaml).Get(), &element);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ if (d->media)
+ d->media.Reset();
+
+ hr = element.As(&d->media);
+ RETURN_HR_IF_FAILED("Could not create MediaElement for playback.");
+
+ // Volume and Playback Rate cannot be changed for synthesized audio once
+ // it has been created. Hence QTextToSpeechEngineWinRT::setVolume/Rate
+ // only cache the value until playback is started.
+ hr = d->media->put_DefaultPlaybackRate(d->rate + 1);
+ if (FAILED(hr))
+ qWarning("Could not set playback rate.");
+
+ const DOUBLE vol = DOUBLE(d->volume) / 100.;
+ hr = d->media->put_Volume(vol);
+ if (FAILED(hr))
+ qWarning("Could not set volume.");
+
+ static const HStringReference empty(L"");
+ hr = d->media->SetSource(randomStream.Get(), empty.Get());
+ RETURN_HR_IF_FAILED("Could not set media source.");
+
+ hr = d->media->Play();
+ RETURN_HR_IF_FAILED("Could not initiate playback.");
+
+ return S_OK;
+ });
+ if (SUCCEEDED(hr)) {
+ d->timer.start();
+ d->state = QTextToSpeech::Speaking;
+ } else {
+ d->state = QTextToSpeech::BackendError;
+ }
+ emit stateChanged(d->state);
+}
+
+void QTextToSpeechEngineWinRT::stop()
+{
+ Q_D(QTextToSpeechEngineWinRT);
+
+ if (!d->media)
+ return;
+
+ HRESULT hr;
+ hr = QEventDispatcherWinRT::runOnXamlThread([d]() {
+ HRESULT hr = d->media->Stop();
+ RETURN_HR_IF_FAILED("Could not stop playback.");
+
+ d->media.Reset();
+ return hr;
+ });
+ if (SUCCEEDED(hr)) {
+ d->timer.stop();
+ d->state = QTextToSpeech::Ready;
+ emit stateChanged(d->state);
+ }
+}
+
+void QTextToSpeechEngineWinRT::pause()
+{
+ Q_D(QTextToSpeechEngineWinRT);
+
+ if (!d->media)
+ return;
+
+ // Stop timer first to not have checkElementState being invoked
+ // while context switch to/from Xaml thread happens.
+ d->timer.stop();
+
+ HRESULT hr;
+ hr = QEventDispatcherWinRT::runOnXamlThread([d]() {
+ HRESULT hr = d->media->Pause();
+ RETURN_HR_IF_FAILED("Could not pause playback.");
+ return hr;
+ });
+ if (SUCCEEDED(hr)) {
+ d->state = QTextToSpeech::Paused;
+ emit stateChanged(d->state);
+ }
+}
+
+void QTextToSpeechEngineWinRT::resume()
+{
+ Q_D(QTextToSpeechEngineWinRT);
+
+ if (!d->media)
+ return;
+
+ HRESULT hr;
+ hr = QEventDispatcherWinRT::runOnXamlThread([d]() {
+ HRESULT hr = d->media->Play();
+ RETURN_HR_IF_FAILED("Could not resume playback.");
+ return hr;
+ });
+ if (SUCCEEDED(hr)) {
+ d->timer.start();
+ d->state = QTextToSpeech::Speaking;
+ emit stateChanged(d->state);
+ }
+}
+
+double QTextToSpeechEngineWinRT::rate() const
+{
+ Q_D(const QTextToSpeechEngineWinRT);
+
+ return d->rate;
+}
+
+bool QTextToSpeechEngineWinRT::setRate(double rate)
+{
+ Q_D(QTextToSpeechEngineWinRT);
+
+ d->rate = rate;
+ return true;
+}
+
+double QTextToSpeechEngineWinRT::pitch() const
+{
+ // Not supported for WinRT
+ Q_UNIMPLEMENTED();
+ return 1.;
+}
+
+bool QTextToSpeechEngineWinRT::setPitch(double pitch)
+{
+ // Not supported for WinRT
+ Q_UNUSED(pitch);
+ Q_UNIMPLEMENTED();
+ return false;
+}
+
+QLocale QTextToSpeechEngineWinRT::locale() const
+{
+ Q_D(const QTextToSpeechEngineWinRT);
+
+ HRESULT hr;
+ ComPtr<IVoiceInformation> info;
+ hr = d->synth->get_Voice(&info);
+
+ HString language;
+ hr = info->get_Language(language.GetAddressOf());
+
+ return QLocale(QString::fromWCharArray(language.GetRawBuffer(0)));
+}
+
+bool QTextToSpeechEngineWinRT::setLocale(const QLocale &locale)
+{
+ Q_D(QTextToSpeechEngineWinRT);
+
+ const int index = d->locales.indexOf(locale);
+ if (index == -1)
+ return false;
+
+ return setVoice(d->voices.at(index));
+}
+
+int QTextToSpeechEngineWinRT::volume() const
+{
+ Q_D(const QTextToSpeechEngineWinRT);
+
+ return d->volume;
+}
+
+bool QTextToSpeechEngineWinRT::setVolume(int volume)
+{
+ Q_D(QTextToSpeechEngineWinRT);
+
+ d->volume = volume;
+ return true;
+}
+
+QVoice QTextToSpeechEngineWinRT::voice() const
+{
+ Q_D(const QTextToSpeechEngineWinRT);
+
+ HRESULT hr;
+ ComPtr<IVoiceInformation> info;
+ hr = d->synth->get_Voice(&info);
+
+ return createVoiceForInformation(info);
+}
+
+bool QTextToSpeechEngineWinRT::setVoice(const QVoice &voice)
+{
+ Q_D(QTextToSpeechEngineWinRT);
+
+ const int index = d->voices.indexOf(voice);
+ if (index == -1)
+ return false;
+
+ HRESULT hr;
+ hr = d->synth->put_Voice(d->infos.at(index).Get());
+ return SUCCEEDED(hr);
+}
+
+QTextToSpeech::State QTextToSpeechEngineWinRT::state() const
+{
+ Q_D(const QTextToSpeechEngineWinRT);
+ return d->state;
+}
+
+void QTextToSpeechEngineWinRT::checkElementState()
+{
+ Q_D(QTextToSpeechEngineWinRT);
+
+ // MediaElement does not move into Stopped or Closed state when it finished
+ // playback of synthesised text. Instead it goes into Pause mode.
+ // Because of this MediaElement::add_MediaEnded() is not invoked and we
+ // cannot add an event listener to the Media Element to properly emit
+ // state changes.
+ // To still be able to capture when it is ready, use a periodic timer and
+ // check if the MediaElement went into Pause state.
+ bool finished = false;
+ HRESULT hr;
+ hr = QEventDispatcherWinRT::runOnXamlThread([d, &finished]() {
+ HRESULT hr;
+ ABI::Windows::UI::Xaml::Media::MediaElementState s;
+ hr = d->media.Get()->get_CurrentState(&s);
+ if (SUCCEEDED(hr) && s == MediaElementState_Paused)
+ finished = true;
+ return hr;
+ });
+
+ if (finished)
+ stop();
+}
+
+void QTextToSpeechEngineWinRT::init()
+{
+ Q_D(QTextToSpeechEngineWinRT);
+
+ d->state = QTextToSpeech::BackendError;
+
+ HRESULT hr;
+
+ hr = QEventDispatcherWinRT::runOnXamlThread([d]() {
+ HRESULT hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_Xaml_Markup_XamlReader).Get(),
+ IID_PPV_ARGS(&d->xamlReader));
+ Q_ASSERT_SUCCEEDED(hr);
+
+ return hr;
+ });
+
+ ComPtr<IInstalledVoicesStatic> stat;
+ hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Media_SpeechSynthesis_SpeechSynthesizer).Get(),
+ IID_PPV_ARGS(&stat));
+ Q_ASSERT_SUCCEEDED(hr);
+
+ hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Media_SpeechSynthesis_SpeechSynthesizer).Get(),
+ &d->synth);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ ComPtr<IVectorView<VoiceInformation*>> voices;
+ hr = stat->get_AllVoices(&voices);
+ RETURN_VOID_IF_FAILED("Could not get voice information.");
+
+ quint32 voiceSize;
+ hr = voices->get_Size(&voiceSize);
+ RETURN_VOID_IF_FAILED("Could not access size of voice information.");
+
+ for (quint32 i = 0; i < voiceSize; ++i) {
+ ComPtr<IVoiceInformation> info;
+ hr = voices->GetAt(i, &info);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ HString nativeLanguage;
+ hr = info->get_Language(nativeLanguage.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+
+ const QString languageString = QString::fromWCharArray(nativeLanguage.GetRawBuffer(0));
+ QLocale locale(languageString);
+ if (!d->locales.contains(locale))
+ d->locales.append(locale);
+
+ QVoice voice = createVoiceForInformation(info);
+ d->voices.append(voice);
+ d->infos.append(info);
+ }
+
+ d->state = QTextToSpeech::Ready;
+}
+
+QVoice QTextToSpeechEngineWinRT::createVoiceForInformation(ComPtr<IVoiceInformation> info) const
+{
+ HRESULT hr;
+ HString nativeName;
+ hr = info->get_DisplayName(nativeName.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+
+ const QString name = QString::fromWCharArray(nativeName.GetRawBuffer(0));
+
+ VoiceGender gender;
+ hr = info->get_Gender(&gender);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ return QTextToSpeechEngine::createVoice(name, gender == VoiceGender_Male ? QVoice::Male : QVoice::Female,
+ QVoice::Other, QVariant());
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/tts/winrt/qtexttospeech_winrt.h b/src/plugins/tts/winrt/qtexttospeech_winrt.h
new file mode 100644
index 0000000..a39fc08
--- /dev/null
+++ b/src/plugins/tts/winrt/qtexttospeech_winrt.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Speech module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTTOSPEECHENGINE_WINRT_H
+#define QTEXTTOSPEECHENGINE_WINRT_H
+
+#include <QtTextToSpeech/qtexttospeechengine.h>
+#include <QtTextToSpeech/qvoice.h>
+
+#include <QtCore/QObject>
+#include <QtCore/QVector>
+#include <QtCore/QString>
+#include <QtCore/QLocale>
+#include <QtCore/QScopedPointer>
+#include <QtCore/qt_windows.h>
+#include <wrl.h>
+
+namespace ABI {
+ namespace Windows {
+ namespace Media {
+ namespace SpeechSynthesis {
+ struct IVoiceInformation;
+ }
+ }
+ }
+}
+
+QT_BEGIN_NAMESPACE
+
+class QTextToSpeechEngineWinRTPrivate;
+
+class QTextToSpeechEngineWinRT : public QTextToSpeechEngine
+{
+ Q_OBJECT
+
+public:
+ QTextToSpeechEngineWinRT(const QVariantMap &parameters, QObject *parent);
+ ~QTextToSpeechEngineWinRT();
+
+ QVector<QLocale> availableLocales() const Q_DECL_OVERRIDE;
+ QVector<QVoice> availableVoices() const Q_DECL_OVERRIDE;
+ void say(const QString &text) Q_DECL_OVERRIDE;
+ void stop() Q_DECL_OVERRIDE;
+ void pause() Q_DECL_OVERRIDE;
+ void resume() Q_DECL_OVERRIDE;
+ double rate() const Q_DECL_OVERRIDE;
+ bool setRate(double rate) Q_DECL_OVERRIDE;
+ double pitch() const Q_DECL_OVERRIDE;
+ bool setPitch(double pitch) Q_DECL_OVERRIDE;
+ QLocale locale() const Q_DECL_OVERRIDE;
+ bool setLocale(const QLocale &locale) Q_DECL_OVERRIDE;
+ int volume() const Q_DECL_OVERRIDE;
+ bool setVolume(int volume) Q_DECL_OVERRIDE;
+ QVoice voice() const Q_DECL_OVERRIDE;
+ bool setVoice(const QVoice &voice) Q_DECL_OVERRIDE;
+ QTextToSpeech::State state() const Q_DECL_OVERRIDE;
+
+public slots:
+ void checkElementState();
+private:
+ void init();
+ QVoice createVoiceForInformation(Microsoft::WRL::ComPtr<ABI::Windows::Media::SpeechSynthesis::IVoiceInformation> info) const;
+
+ QScopedPointer<QTextToSpeechEngineWinRTPrivate> d_ptr;
+ Q_DECLARE_PRIVATE(QTextToSpeechEngineWinRT)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/tts/winrt/qtexttospeech_winrt_plugin.cpp b/src/plugins/tts/winrt/qtexttospeech_winrt_plugin.cpp
new file mode 100644
index 0000000..16e8050
--- /dev/null
+++ b/src/plugins/tts/winrt/qtexttospeech_winrt_plugin.cpp
@@ -0,0 +1,44 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Speech module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtexttospeech_winrt_plugin.h"
+#include "qtexttospeech_winrt.h"
+
+QTextToSpeechEngine *QTextToSpeechPluginWinRT::createTextToSpeechEngine(const QVariantMap &parameters, QObject *parent, QString *errorString) const
+{
+ Q_UNUSED(errorString)
+ return new QTextToSpeechEngineWinRT(parameters, parent);
+}
diff --git a/src/plugins/tts/winrt/qtexttospeech_winrt_plugin.h b/src/plugins/tts/winrt/qtexttospeech_winrt_plugin.h
new file mode 100644
index 0000000..4f92651
--- /dev/null
+++ b/src/plugins/tts/winrt/qtexttospeech_winrt_plugin.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Speech module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTTOSPEECHPLUGIN_WINRT_H
+#define QTEXTTOSPEECHPLUGIN_WINRT_H
+
+#include <QtCore/QObject>
+#include <QtCore/QLoggingCategory>
+#include <QtTextToSpeech/qtexttospeechplugin.h>
+#include <QtTextToSpeech/qtexttospeechengine.h>
+
+QT_BEGIN_NAMESPACE
+
+class QTextToSpeechPluginWinRT : public QObject, public QTextToSpeechPlugin
+{
+ Q_OBJECT
+ Q_INTERFACES(QTextToSpeechPlugin)
+ Q_PLUGIN_METADATA(IID "org.qt-project.qt.speech.tts.plugin/5.0"
+ FILE "winrt_plugin.json")
+
+public:
+ QTextToSpeechEngine *createTextToSpeechEngine(
+ const QVariantMap &parameters,
+ QObject *parent,
+ QString *errorString) const;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/tts/winrt/winrt.pro b/src/plugins/tts/winrt/winrt.pro
new file mode 100644
index 0000000..6896620
--- /dev/null
+++ b/src/plugins/tts/winrt/winrt.pro
@@ -0,0 +1,18 @@
+TARGET = qtexttospeech_winrt
+PLUGIN_TYPE = texttospeech
+PLUGIN_CLASS_NAME = QTextToSpeechPluginWinRT
+
+load(qt_plugin)
+
+QT += core core-private texttospeech
+
+HEADERS += \
+ qtexttospeech_winrt.h \
+ qtexttospeech_winrt_plugin.h \
+
+SOURCES += \
+ qtexttospeech_winrt.cpp \
+ qtexttospeech_winrt_plugin.cpp \
+
+OTHER_FILES += \
+ winrt_plugin.json
diff --git a/src/plugins/tts/winrt/winrt_plugin.json b/src/plugins/tts/winrt/winrt_plugin.json
new file mode 100644
index 0000000..1f54076
--- /dev/null
+++ b/src/plugins/tts/winrt/winrt_plugin.json
@@ -0,0 +1,6 @@
+{
+ "Keys": ["winrt"],
+ "Provider": "winrt",
+ "Version": 100,
+ "Features": []
+}
diff --git a/src/src.pro b/src/src.pro
index 1567f40..e84a89f 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -5,4 +5,4 @@ load(qt_docs)
SUBDIRS = tts asr plugins
android:SUBDIRS += tts/android
-plugins.depends += asr
+plugins.depends += tts asr
diff --git a/src/tts/qtexttospeech.cpp b/src/tts/qtexttospeech.cpp
index efd07a1..d6b2b10 100644
--- a/src/tts/qtexttospeech.cpp
+++ b/src/tts/qtexttospeech.cpp
@@ -53,12 +53,13 @@ Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
QMutex QTextToSpeechPrivate::m_mutex;
-QTextToSpeechPrivate::QTextToSpeechPrivate(QTextToSpeech *speech, const QString &engine)
+QTextToSpeechPrivate::QTextToSpeechPrivate(QTextToSpeech *speech, const QString &engine, const QVariantMap &parameters)
: m_engine(0),
m_speech(speech),
m_providerName(engine),
m_plugin(0)
{
+ qRegisterMetaType<QTextToSpeech::State>();
if (m_providerName.isEmpty()) {
m_providerName = QTextToSpeech::availableEngines().value(0);
if (m_providerName.isEmpty()) {
@@ -73,10 +74,9 @@ QTextToSpeechPrivate::QTextToSpeechPrivate(QTextToSpeech *speech, const QString
loadPlugin();
if (m_plugin) {
QString errorString;
- m_engine = m_plugin->createTextToSpeechEngine(QVariantMap(), 0, &errorString);
+ m_engine = m_plugin->createTextToSpeechEngine(parameters, 0, &errorString);
if (!m_engine) {
- qCritical() << "Error creating text-to-speech engine" << m_providerName
- << (errorString.isEmpty() ? QStringLiteral("") : (QStringLiteral(": ") + errorString));
+ qCritical() << "Error creating text-to-speech engine" << m_providerName << errorString;
}
} else {
qCritical() << "Error loading text-to-speech plug-in" << m_providerName;
@@ -186,21 +186,43 @@ void QTextToSpeechPrivate::loadPluginMetadata(QHash<QString, QJsonObject> &list)
*/
/*!
+ 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.
+
+ The default engine may be platform-specific.
+
+ If loading the plug-in fails, QTextToSpeech::state() will return
+ QTextToSpeech::BackendError.
+
+ \sa availableEngines()
+*/
+QTextToSpeech::QTextToSpeech(QObject *parent)
+ : QObject(*new QTextToSpeechPrivate(this, QString(), QVariantMap()), parent)
+{
+ Q_D(QTextToSpeech);
+ // Connect state change signal directly from the engine to the public API signal
+ if (d->m_engine)
+ connect(d->m_engine, &QTextToSpeechEngine::stateChanged, this, &QTextToSpeech::stateChanged);
+}
+
+/*!
Loads a text-to-speech engine from a plug-in that matches parameter \a engine and
constructs a QTextToSpeech object as the child of \a parent.
If \a engine is empty, the default engine plug-in is used. The default
engine may be platform-specific.
+ The method optionally accepts \a parameters that may be engine-specific.
+
If loading the plug-in fails, QTextToSpeech::state() will return QTextToSpeech::BackendError.
\sa availableEngines()
*/
-QTextToSpeech::QTextToSpeech(QObject *parent, const QString &engine)
- : QObject(*new QTextToSpeechPrivate(this, engine), parent)
+QTextToSpeech::QTextToSpeech(const QString &engine, const QVariantMap &parameters, QObject *parent)
+ : QObject(*new QTextToSpeechPrivate(this, engine, parameters), parent)
{
Q_D(QTextToSpeech);
- qRegisterMetaType<QTextToSpeech::State>();
// Connect state change signal directly from the engine to the public API signal
if (d->m_engine)
connect(d->m_engine, &QTextToSpeechEngine::stateChanged, this, &QTextToSpeech::stateChanged);
diff --git a/src/tts/qtexttospeech.h b/src/tts/qtexttospeech.h
index 8142e32..bd35c8f 100644
--- a/src/tts/qtexttospeech.h
+++ b/src/tts/qtexttospeech.h
@@ -70,7 +70,8 @@ public:
BackendError
};
- explicit QTextToSpeech(QObject *parent = 0, const QString &engine = QString());
+ explicit QTextToSpeech(QObject *parent = Q_NULLPTR);
+ explicit QTextToSpeech(const QString &engine, const QVariantMap &parameters = QVariantMap(), QObject *parent = Q_NULLPTR);
State state() const;
QVector<QLocale> availableLocales() const;
diff --git a/src/tts/qtexttospeech_p.h b/src/tts/qtexttospeech_p.h
index 446e920..45c0395 100644
--- a/src/tts/qtexttospeech_p.h
+++ b/src/tts/qtexttospeech_p.h
@@ -39,6 +39,17 @@
#ifndef QTEXTTOSPEECH_P_H
#define QTEXTTOSPEECH_P_H
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
#include <qtexttospeech.h>
#include <qtexttospeechplugin.h>
#include <QMutex>
@@ -50,7 +61,7 @@ class QTextToSpeech;
class QTextToSpeechPrivate : public QObjectPrivate
{
public:
- QTextToSpeechPrivate(QTextToSpeech *speech, const QString &engine);
+ QTextToSpeechPrivate(QTextToSpeech *speech, const QString &engine, const QVariantMap &parameters);
~QTextToSpeechPrivate();
static QHash<QString, QJsonObject> plugins(bool reload = false);
diff --git a/src/tts/qtexttospeechengine.h b/src/tts/qtexttospeechengine.h
index 44d45df..319f6c5 100644
--- a/src/tts/qtexttospeechengine.h
+++ b/src/tts/qtexttospeechengine.h
@@ -50,7 +50,7 @@ class QTEXTTOSPEECH_EXPORT QTextToSpeechEngine : public QObject
Q_OBJECT
public:
- explicit QTextToSpeechEngine(QObject *parent = 0);
+ explicit QTextToSpeechEngine(QObject *parent = Q_NULLPTR);
~QTextToSpeechEngine();
virtual QVector<QLocale> availableLocales() const = 0;
diff --git a/src/tts/qvoice.cpp b/src/tts/qvoice.cpp
index d138f94..8a578fd 100644
--- a/src/tts/qvoice.cpp
+++ b/src/tts/qvoice.cpp
@@ -69,6 +69,21 @@ void QVoice::operator=(const QVoice &other)
d->data = other.d->data;
}
+bool QVoice::operator==(const QVoice &other)
+{
+ if (d->name != other.d->name ||
+ d->gender != other.d->gender ||
+ d->age != other.d->age ||
+ d->data != other.d->data)
+ return false;
+ return true;
+}
+
+bool QVoice::operator!=(const QVoice &other)
+{
+ return !operator==(other);
+}
+
void QVoice::setName(const QString &name)
{
d->name = name;
diff --git a/src/tts/qvoice.h b/src/tts/qvoice.h
index ab4a122..e7f1021 100644
--- a/src/tts/qvoice.h
+++ b/src/tts/qvoice.h
@@ -71,6 +71,9 @@ public:
void operator=(const QVoice &other);
+ bool operator==(const QVoice &other);
+ bool operator!=(const QVoice &other);
+
QString name() const;
Gender gender() const;
Age age() const;
diff --git a/src/tts/qvoice_p.h b/src/tts/qvoice_p.h
index d9afe73..79dd220 100644
--- a/src/tts/qvoice_p.h
+++ b/src/tts/qvoice_p.h
@@ -39,6 +39,17 @@
#ifndef QVOICE_P_H
#define QVOICE_P_H
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
#include <qvoice.h>
#include <QString>
@@ -53,7 +64,7 @@ public:
QVoicePrivate();
QVoicePrivate(const QVoicePrivate &other);
QVoicePrivate(const QString &n, QVoice::Gender g, QVoice::Age a, const QVariant &d);
- ~QVoicePrivate() {};
+ ~QVoicePrivate() {}
QString name;
QVoice::Gender gender;
diff --git a/sync.profile b/sync.profile
index 74052f8..4609f32 100644
--- a/sync.profile
+++ b/sync.profile
@@ -11,6 +11,6 @@
# - any git symbolic ref resolvable from the module's repository (e.g. "refs/heads/master" to track master branch)
#
%dependencies = (
- "qtbase" => "refs/heads/stable",
+ "qtbase" => "",
);
diff --git a/tests/auto/texttospeech/tst_qtexttospeech.cpp b/tests/auto/texttospeech/tst_qtexttospeech.cpp
index afdecba..1109e01 100644
--- a/tests/auto/texttospeech/tst_qtexttospeech.cpp
+++ b/tests/auto/texttospeech/tst_qtexttospeech.cpp
@@ -40,7 +40,7 @@
#include <QSignalSpy>
#if defined(Q_OS_UNIX) && !(defined(Q_OS_MAC) || defined(Q_OS_ANDROID))
- #include <speech-dispatcher/libspeechd.h>
+ #include <libspeechd.h>
#if LIBSPEECHD_MAJOR_VERSION > 0 || LIBSPEECHD_MINOR_VERSION >= 9
#define HAVE_SPD_090
#endif