Revision control

Copy as Markdown

// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtTextToSpeech
ApplicationWindow {
id: root
visible: true
title: qsTr("Text to Speech")
minimumWidth: inputForm.implicitWidth
minimumHeight: inputForm.implicitHeight + footer.implicitHeight
//! [initialize]
TextToSpeech {
id: tts
volume: volumeSlider.value
pitch: pitchSlider.value
rate: rateSlider.value
//! [initialize]
//! [stateChanged]
onStateChanged: updateStateLabel(state)
function updateStateLabel(state)
{
switch (state) {
case TextToSpeech.Ready:
statusLabel.text = qsTr("Ready")
break
case TextToSpeech.Speaking:
statusLabel.text = qsTr("Speaking")
break
case TextToSpeech.Paused:
statusLabel.text = qsTr("Paused...")
break
case TextToSpeech.Error:
statusLabel.text = qsTr("Error!")
break
}
}
//! [stateChanged]
//! [sayingWord]
onSayingWord: (word, id, start, length)=> {
input.select(start, start + length)
}
//! [sayingWord]
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 8
id: inputForm
TextArea {
id: input
wrapMode: TextEdit.WordWrap
text: qsTr("Hello, world!")
Layout.fillWidth: true
Layout.minimumHeight: implicitHeight
font.pointSize: 24
}
//! [say0]
RowLayout {
Button {
text: qsTr("Speak")
enabled: [TextToSpeech.Paused, TextToSpeech.Ready].includes(tts.state)
onClicked: {
let voices = tts.availableVoices()
tts.voice = voices[voicesComboBox.currentIndex]
tts.say(input.text)
}
}
//! [say0]
//! [pause]
Button {
text: qsTr("Pause")
enabled: tts.state == TextToSpeech.Speaking
onClicked: tts.pause()
visible: tts.engineCapabilities & TextToSpeech.Capabilities.PauseResume
}
//! [pause]
//! [resume]
Button {
text: qsTr("Resume")
enabled: tts.state == TextToSpeech.Paused
onClicked: tts.resume()
visible: tts.engineCapabilities & TextToSpeech.Capabilities.PauseResume
}
//! [resume]
//! [stop]
Button {
text: qsTr("Stop")
enabled: [TextToSpeech.Speaking, TextToSpeech.Paused].includes(tts.state)
onClicked: tts.stop()
}
//! [stop]
}
GridLayout {
columns: 2
Label {
text: qsTr("Engine:")
}
ComboBox {
id: enginesComboBox
Layout.fillWidth: true
model: tts.availableEngines()
onActivated: {
tts.engine = textAt(currentIndex)
updateLocales()
updateVoices()
}
}
Label {
text: qsTr("Locale:")
}
ComboBox {
id: localesComboBox
Layout.fillWidth: true
onActivated: {
let locales = tts.availableLocales()
tts.locale = locales[currentIndex]
updateVoices()
}
}
Label {
text: qsTr("Voice:")
}
ComboBox {
id: voicesComboBox
Layout.fillWidth: true
}
Label {
text: qsTr("Volume:")
}
Slider {
id: volumeSlider
from: 0
to: 1.0
stepSize: 0.2
value: 0.8
Layout.fillWidth: true
}
Label {
text: qsTr("Pitch:")
}
Slider {
id: pitchSlider
from: -1.0
to: 1.0
stepSize: 0.5
value: 0
Layout.fillWidth: true
}
Label {
text: qsTr("Rate:")
}
Slider {
id: rateSlider
from: -1.0
to: 1.0
stepSize: 0.5
value: 0
Layout.fillWidth: true
}
}
}
footer: Label {
id: statusLabel
}
Component.onCompleted: {
enginesComboBox.currentIndex = tts.availableEngines().indexOf(tts.engine)
// some engines initialize asynchronously
if (tts.state == TextToSpeech.Ready) {
engineReady()
} else {
tts.stateChanged.connect(root.engineReady)
}
tts.updateStateLabel(tts.state)
}
function engineReady() {
tts.stateChanged.disconnect(root.engineReady)
if (tts.state != TextToSpeech.Ready) {
tts.updateStateLabel(tts.state)
return;
}
updateLocales()
updateVoices()
}
function updateLocales() {
let allLocales = tts.availableLocales().map((locale) => locale.nativeLanguageName)
let currentLocaleIndex = allLocales.indexOf(tts.locale.nativeLanguageName)
localesComboBox.model = allLocales
localesComboBox.currentIndex = currentLocaleIndex
}
function updateVoices() {
voicesComboBox.model = tts.availableVoices().map((voice) => voice.name)
let indexOfVoice = tts.availableVoices().indexOf(tts.voice)
voicesComboBox.currentIndex = indexOfVoice
}
}