Revision control
++ // Copyright (C) 2015 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #ifndef QTEXTTOSPEECH_H
++ #define QTEXTTOSPEECH_H
++
++ #include <QtCore/qlocale.h>
++ #include <QtCore/qobject.h>
++ #include <QtCore/qmap.h>
++ #include <QtCore/qshareddata.h>
++ #include <QtTextToSpeech/qtexttospeech_global.h>
++ #include <QtTextToSpeech/qvoice.h>
++
++ #include <QtCore/q20type_traits.h>
++
++ QT_BEGIN_NAMESPACE
++
++ class QAudioFormat;
++ class QAudioBuffer;
++
++ class QTextToSpeechPrivate;
++ class Q_TEXTTOSPEECH_EXPORT QTextToSpeech : public QObject
++ {
++ Q_OBJECT
++ Q_PROPERTY(QString engine READ engine WRITE setEngine NOTIFY engineChanged)
++ Q_PROPERTY(State state READ state NOTIFY stateChanged FINAL)
++ Q_PROPERTY(double volume READ volume WRITE setVolume NOTIFY volumeChanged FINAL)
++ Q_PROPERTY(double rate READ rate WRITE setRate NOTIFY rateChanged FINAL)
++ Q_PROPERTY(double pitch READ pitch WRITE setPitch NOTIFY pitchChanged FINAL)
++ Q_PROPERTY(QLocale locale READ locale WRITE setLocale NOTIFY localeChanged FINAL)
++ Q_PROPERTY(QVoice voice READ voice WRITE setVoice NOTIFY voiceChanged FINAL)
++ Q_PROPERTY(Capabilities engineCapabilities READ engineCapabilities NOTIFY engineChanged REVISION(6, 6) FINAL)
++ Q_DECLARE_PRIVATE(QTextToSpeech)
++
++ public:
++ enum State {
++ Ready,
++ Speaking,
++ Paused,
++ Error,
++ Synthesizing,
++ };
++ Q_ENUM(State)
++
++ enum class ErrorReason {
++ NoError,
++ Initialization,
++ Configuration,
++ Input,
++ Playback,
++ };
++ Q_ENUM(ErrorReason)
++
++ enum class BoundaryHint {
++ Default,
++ Immediate,
++ Word,
++ Sentence,
++ Utterance,
++ };
++ Q_ENUM(BoundaryHint)
++
++ enum class Capability {
++ None = 0,
++ Speak = 1 << 0,
++ PauseResume = 1 << 1,
++ WordByWordProgress = 1 << 2,
++ Synthesize = 1 << 3,
++ };
++ Q_DECLARE_FLAGS(Capabilities, Capability)
++ Q_FLAG(Capabilities)
++
++ explicit QTextToSpeech(QObject *parent = nullptr);
++ explicit QTextToSpeech(const QString &engine, QObject *parent = nullptr);
++ explicit QTextToSpeech(const QString &engine, const QVariantMap ¶ms,
++ QObject *parent = nullptr);
++ ~QTextToSpeech() override;
++
++ Q_INVOKABLE bool setEngine(const QString &engine, const QVariantMap ¶ms = QVariantMap());
++ QString engine() const;
++ QTextToSpeech::Capabilities engineCapabilities() const;
++
++ QTextToSpeech::State state() const;
++ Q_INVOKABLE QTextToSpeech::ErrorReason errorReason() const;
++ Q_INVOKABLE QString errorString() const;
++
++ Q_INVOKABLE QList<QLocale> availableLocales() const;
++ QLocale locale() const;
++
++ QVoice voice() const;
++ Q_INVOKABLE QList<QVoice> availableVoices() const;
++
++ double rate() const;
++ double pitch() const;
++ double volume() const;
++
++ Q_INVOKABLE static QStringList availableEngines();
++
++ template <typename Functor>
++ void synthesize(const QString &text,
++ #ifdef Q_QDOC
++ const QObject *receiver,
++ #else
++ const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *receiver,
++ # endif // Q_QDOC
++ Functor &&func)
++ {
++ using Prototype2 = void(*)(QAudioFormat, QByteArray);
++ using Prototype1 = void(*)(QAudioBuffer);
++ if constexpr (qxp::is_detected_v<CompatibleCallbackTest2, Functor>) {
++ synthesizeImpl(text, QtPrivate::makeCallableObject<Prototype2>(std::forward<Functor>(func)),
++ receiver, SynthesizeOverload::AudioFormatByteArray);
++ } else if constexpr (qxp::is_detected_v<CompatibleCallbackTest1, Functor>) {
++ synthesizeImpl(text, QtPrivate::makeCallableObject<Prototype1>(std::forward<Functor>(func)),
++ receiver, SynthesizeOverload::AudioBuffer);
++ } else {
++ static_assert(QtPrivate::type_dependent_false<Functor>(),
++ "Incompatible functor signature, must be either "
++ "(QAudioFormat, QByteArray) or (QAudioBuffer)!");
++ }
++ }
++
++ // synthesize to a functor or function pointer (without context)
++ template <typename Functor>
++ void synthesize(const QString &text, Functor &&func)
++ {
++ synthesize(text, nullptr, std::forward<Functor>(func));
++ }
++
++ template <typename ...Args>
++ QList<QVoice> findVoices(Args &&...args) const
++ {
++ // if any of the arguments is a locale, then we can avoid iterating through all
++ // and only have to search through the voices for that locale.
++ QLocale locale;
++ QLocale *plocale = nullptr;
++ if constexpr (std::disjunction_v<std::is_same<q20::remove_cvref_t<Args>, QLocale>...>) {
++ locale = std::get<QLocale>(std::make_tuple(args...));
++ plocale = &locale;
++ }
++
++ auto voices = allVoices(plocale);
++ if constexpr (sizeof...(args) > 0)
++ voices.removeIf([&](const QVoice &voice) -> bool { return !voiceMatches(voice, args...); });
++ return voices;
++ }
++
++ public Q_SLOTS:
++ void say(const QString &text);
++ qsizetype enqueue(const QString &text);
++ void stop(QTextToSpeech::BoundaryHint boundaryHint = QTextToSpeech::BoundaryHint::Default);
++ void pause(QTextToSpeech::BoundaryHint boundaryHint = QTextToSpeech::BoundaryHint::Default);
++ void resume();
++
++ void setLocale(const QLocale &locale);
++
++ void setRate(double rate);
++ void setPitch(double pitch);
++ void setVolume(double volume);
++ void setVoice(const QVoice &voice);
++
++ Q_SIGNALS:
++ void engineChanged(const QString &engine);
++ void stateChanged(QTextToSpeech::State state);
++ void errorOccurred(QTextToSpeech::ErrorReason error, const QString &errorString);
++ void localeChanged(const QLocale &locale);
++ void rateChanged(double rate);
++ void pitchChanged(double pitch);
++ void volumeChanged(double volume);
++ void voiceChanged(const QVoice &voice);
++
++ void sayingWord(const QString &word, qsizetype id, qsizetype start, qsizetype length);
++ void aboutToSynthesize(qsizetype id);
++
++ protected:
++ QList<QVoice> allVoices(const QLocale *locale) const;
++
++ private:
++ template <typename Functor>
++ using CompatibleCallbackTest2 = decltype(QtPrivate::makeCallableObject<void(*)(QAudioFormat, QByteArray)>(std::declval<Functor>()));
++ template <typename Functor>
++ using CompatibleCallbackTest1 = decltype(QtPrivate::makeCallableObject<void(*)(QAudioBuffer)>(std::declval<Functor>()));
++
++ enum class SynthesizeOverload {
++ AudioFormatByteArray,
++ AudioBuffer
++ };
++
++ void synthesizeImpl(const QString &text,
++ QtPrivate::QSlotObjectBase *slotObj, const QObject *context,
++ SynthesizeOverload overload);
++
++ // Helper type to find the index of a type in a tuple, which allows
++ // us to generate a compile-time error if there are multiple criteria
++ // of the same type.
++ template <typename T, typename Tuple> struct LastIndexOf;
++ template <typename T, typename ...Ts>
++ struct LastIndexOf<T, std::tuple<Ts...>> {
++ template <qsizetype... Is>
++ static constexpr qsizetype lastIndexOf(std::integer_sequence<qsizetype, Is...>) {
++ return std::max({(std::is_same_v<T, Ts> ? Is : -1)...});
++ }
++ static constexpr qsizetype value =
++ lastIndexOf(std::make_integer_sequence<qsizetype, sizeof...(Ts)>{});
++ };
++
++ template <typename Arg0, typename ...Args>
++ bool voiceMatches(const QVoice &voice, Arg0 &&arg0, Args &&...args) const {
++ using ArgType = q20::remove_cvref_t<Arg0>;
++ bool matches = [&]{
++ if constexpr (std::is_same_v<ArgType, QLocale>) {
++ return voice.locale() == arg0;
++ } else if constexpr (std::is_same_v<ArgType, QLocale::Language>) {
++ return voice.locale().language() == arg0;
++ } else if constexpr (std::is_same_v<ArgType, QLocale::Territory>) {
++ return voice.locale().territory() == arg0;
++ } else if constexpr (std::is_same_v<ArgType, QVoice::Gender>) {
++ return voice.gender() == arg0;
++ } else if constexpr (std::is_same_v<ArgType, QVoice::Age>) {
++ return voice.age() == arg0;
++ } else if constexpr (std::disjunction_v<std::is_convertible<ArgType, QString>,
++ std::is_convertible<ArgType, QStringView>>) {
++ return voice.name() == arg0;
++ } else if constexpr (std::is_same_v<ArgType, QRegularExpression>) {
++ return arg0.match(voice.name()).hasMatch();
++ } else {
++ static_assert(QtPrivate::type_dependent_false<Arg0>(),
++ "Type cannot be matched to a QVoice property!");
++ return false;
++ }
++ }();
++ if constexpr (sizeof...(args) > 0) {
++ static_assert(LastIndexOf<ArgType, std::tuple<q20::remove_cvref_t<Args>...>>::value == -1,
++ "Using multiple criteria of the same type is not supported");
++ matches = matches && voiceMatches(voice, args...);
++ }
++ return matches;
++ }
++
++ Q_DISABLE_COPY(QTextToSpeech)
++ };
++ Q_DECLARE_OPERATORS_FOR_FLAGS(QTextToSpeech::Capabilities)
++
++ QT_END_NAMESPACE
++
++ #endif