diff options
author | Volker Hilsheimer <volker.hilsheimer@qt.io> | 2023-05-24 16:20:12 +0200 |
---|---|---|
committer | Volker Hilsheimer <volker.hilsheimer@qt.io> | 2023-05-28 13:08:27 +0000 |
commit | 6bb360c464fa0a5fe571afab21b9edd3e863f630 (patch) | |
tree | a435deb8cd3105f3d0b719b5eadacdaea01f020a | |
parent | c1afe1600d37c5533904da7e2862e87fa05464e6 (diff) |
API review: Allow synthesize callbacks that take a QAudioBuffer
Formally, QAudioBuffer is the right type for carrying audio data, but
it's hardly used in Qt Multimedia itself, and not very practical to use
for writing the received PCM data to a file or to stream it out to a
QAudioSink (which operators on a QIODevice, e.g. with a byte array).
Nevertheless, allow a callback to take a QAudioBuffer instead of
QAudioFormat and QByteArray, as the QAudioBuffer facilities might be
useful for some use cases.
Change-Id: I260a4cf6cf91f57356373f4ef9cf248927159b40
Reviewed-by: Axel Spoerl <axel.spoerl@qt.io>
-rw-r--r-- | src/tts/qtexttospeech.cpp | 26 | ||||
-rw-r--r-- | src/tts/qtexttospeech.h | 29 | ||||
-rw-r--r-- | tests/auto/qtexttospeech/tst_qtexttospeech.cpp | 11 |
3 files changed, 55 insertions, 11 deletions
diff --git a/src/tts/qtexttospeech.cpp b/src/tts/qtexttospeech.cpp index 47cad9c..bcebc46 100644 --- a/src/tts/qtexttospeech.cpp +++ b/src/tts/qtexttospeech.cpp @@ -9,6 +9,8 @@ #include <QtCore/qdebug.h> #include <QtCore/private/qfactoryloader_p.h> +#include <QtMultimedia/qaudiobuffer.h> + QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; @@ -877,8 +879,9 @@ void QTextToSpeech::synthesize(const QString &text) This function synthesizes the speech asynchronously into raw audio data. When data is available, the \a functor will be called as - \c {functor(const QAudioFormat &format, const QByteArray &bytes)}, with - \c format describing the \l {QAudioFormat}{format} of the data in \c bytes. + \c {functor(QAudioFormat format, QByteArray bytes)}, with \c format + describing the \l {QAudioFormat}{format} of the data in \c bytes; + or as \c {functor(QAudioBuffer &buffer)}. The \l state property is set to \l Synthesizing when the synthesis starts, and to \l Ready once the synthesis is finished. While synthesizing, the @@ -923,19 +926,26 @@ void QTextToSpeech::synthesize(const QString &text) in updateState() when the state of the engine transitions back to Ready. */ void QTextToSpeech::synthesizeImpl(const QString &text, - QtPrivate::QSlotObjectBase *slotObj, const QObject *context) + QtPrivate::QSlotObjectBase *slotObj, const QObject *context, + SynthesizeOverload overload) { Q_D(QTextToSpeech); Q_ASSERT(slotObj); if (d->m_slotObject) d->m_slotObject->destroyIfLastRef(); d->m_slotObject = slotObj; - const auto receive = [d, context](const QAudioFormat &format, const QByteArray &bytes){ + const auto receive = [d, context, overload](const QAudioFormat &format, const QByteArray &bytes){ Q_ASSERT(d->m_slotObject); - void *args[] = {nullptr, - const_cast<QAudioFormat *>(&format), - const_cast<QByteArray *>(&bytes)}; - d->m_slotObject->call(const_cast<QObject *>(context), args); + if (overload == SynthesizeOverload::AudioBuffer) { + const QAudioBuffer buffer(bytes, format); + void *args[] = {nullptr, const_cast<QAudioBuffer *>(&buffer)}; + d->m_slotObject->call(const_cast<QObject *>(context), args); + } else { + void *args[] = {nullptr, + const_cast<QAudioFormat *>(&format), + const_cast<QByteArray *>(&bytes)}; + d->m_slotObject->call(const_cast<QObject *>(context), args); + } }; d->m_synthesizeConnection = connect(d->m_engine.get(), &QTextToSpeechEngine::synthesized, context ? context : this, receive); diff --git a/src/tts/qtexttospeech.h b/src/tts/qtexttospeech.h index 2b3951b..6792cf6 100644 --- a/src/tts/qtexttospeech.h +++ b/src/tts/qtexttospeech.h @@ -17,6 +17,7 @@ QT_BEGIN_NAMESPACE class QAudioFormat; +class QAudioBuffer; class QTextToSpeechPrivate; class Q_TEXTTOSPEECH_EXPORT QTextToSpeech : public QObject @@ -105,8 +106,19 @@ public: # endif // Q_QDOC Functor &&func) { - using Prototype = void(*)(QAudioFormat, QByteArray); - synthesizeImpl(text, QtPrivate::makeCallableObject<Prototype>(std::forward<Functor>(func)), receiver); + 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) @@ -167,8 +179,19 @@ 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); + 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 diff --git a/tests/auto/qtexttospeech/tst_qtexttospeech.cpp b/tests/auto/qtexttospeech/tst_qtexttospeech.cpp index 701effe..317c683 100644 --- a/tests/auto/qtexttospeech/tst_qtexttospeech.cpp +++ b/tests/auto/qtexttospeech/tst_qtexttospeech.cpp @@ -8,6 +8,7 @@ #include <QMediaDevices> #include <QAudioFormat> #include <QAudioDevice> +#include <QAudioBuffer> #include <QOperatingSystemVersion> #include <QRegularExpression> #include <qttexttospeech-config.h> @@ -1063,6 +1064,16 @@ void tst_QTextToSpeech::synthesizeCallback() QTRY_COMPARE(tts.state(), QTextToSpeech::Ready); QCOMPARE(processor.m_allBytes, QByteArray()); processor.reset(); + + // Taking QAudioBuffer + tts.synthesize(text, [&processor](const QAudioBuffer &buffer) { + processor.m_format = buffer.format(); + processor.m_allBytes += QByteArrayView(buffer.data<uchar>(), buffer.byteCount()); + }); + QTRY_COMPARE(processor.m_format, expectedFormat); + QTRY_COMPARE(tts.state(), QTextToSpeech::Ready); + QCOMPARE(processor.m_allBytes, expectedBytes); + processor.reset(); } QTEST_MAIN(tst_QTextToSpeech) |