diff options
author | Tim Blechmann <[email protected]> | 2025-05-27 09:44:03 +0800 |
---|---|---|
committer | Tim Blechmann <[email protected]> | 2025-07-03 14:44:20 +0800 |
commit | 0658aced75ec92ff4e798dee4b45b17493afc0c5 (patch) | |
tree | c1bb5c9976a853e5fd6a445b47209ef9af0f95a4 | |
parent | fa4e7b29a01a7cc9c6a058c5de56c4f8819f8b24 (diff) |
Pick-to: 6.8 6.9 6.10
Change-Id: Ib0e806f88e1266b91308f5b0fc510fbf9a1b6671
Reviewed-by: Timur Pocheptsov <[email protected]>
-rw-r--r-- | src/multimedia/audio/qaudio_platform_implementation_support_p.h | 2 | ||||
-rw-r--r-- | src/multimedia/audio/qaudioformat.cpp | 19 | ||||
-rw-r--r-- | src/multimedia/audio/qaudiosystem_p.h | 71 | ||||
-rw-r--r-- | src/multimedia/darwin/qdarwinaudiosink.mm | 15 | ||||
-rw-r--r-- | src/multimedia/pipewire/qpipewire_audiosink.cpp | 2 | ||||
-rw-r--r-- | src/multimedia/pulseaudio/qpulseaudiosink.cpp | 4 | ||||
-rw-r--r-- | src/multimedia/windows/qwindowsaudiosink.cpp | 10 | ||||
-rw-r--r-- | tests/auto/unit/multimedia/qaudiohelpers/tst_qaudiohelpers.cpp | 35 |
8 files changed, 124 insertions, 34 deletions
diff --git a/src/multimedia/audio/qaudio_platform_implementation_support_p.h b/src/multimedia/audio/qaudio_platform_implementation_support_p.h index 4f140ff6a..fde09c02e 100644 --- a/src/multimedia/audio/qaudio_platform_implementation_support_p.h +++ b/src/multimedia/audio/qaudio_platform_implementation_support_p.h @@ -154,7 +154,7 @@ template <STREAM_TYPE_ARG, typename DerivedType> void QPlatformAudioSinkImplementation<StreamType, DerivedType>::start(AudioCallback &&audioCallback) { using namespace QtMultimediaPrivate; - if (!validateAudioSinkCallback(audioCallback, m_format)) { + if (!validateAudioCallback(audioCallback, m_format)) { setError(QAudio::OpenError); return; } diff --git a/src/multimedia/audio/qaudioformat.cpp b/src/multimedia/audio/qaudioformat.cpp index 2483d490f..d4dd0d4e7 100644 --- a/src/multimedia/audio/qaudioformat.cpp +++ b/src/multimedia/audio/qaudioformat.cpp @@ -309,9 +309,24 @@ qint32 QAudioFormat::bytesForFrames(qint32 frameCount) const qint32 QAudioFormat::framesForBytes(qint32 byteCount) const { int size = bytesPerFrame(); - if (size > 0) + + // bitshifting to avoid integer division + switch (size) { + case 0: + return 0; + case 1: + return byteCount; + case 2: + return byteCount / 2; + case 4: + return byteCount / 4; + case 8: + return byteCount / 8; + case 16: + return byteCount / 16; + default: return byteCount / size; - return 0; + } } /*! diff --git a/src/multimedia/audio/qaudiosystem_p.h b/src/multimedia/audio/qaudiosystem_p.h index bdf436256..de338e786 100644 --- a/src/multimedia/audio/qaudiosystem_p.h +++ b/src/multimedia/audio/qaudiosystem_p.h @@ -20,6 +20,7 @@ #include <QtMultimedia/qaudio.h> #include <QtMultimedia/qaudiodevice.h> #include <QtMultimedia/qaudioformat.h> +#include <QtMultimedia/private/qmultimedia_assume_p.h> #include <QtCore/qelapsedtimer.h> #include <QtCore/qspan.h> @@ -50,6 +51,9 @@ enum class AudioEndpointRole : uint8_t template <typename SampleType> using AudioSinkCallbackType = std::function<void(QSpan<SampleType>)>; +template <typename SampleType> +using AudioSourceCallbackType = std::function<void(QSpan<const SampleType>)>; + template <typename> struct GetSampleTypeImpl; @@ -85,6 +89,11 @@ struct GetSampleTypeImpl<AudioSinkCallbackType<T>> : GetSampleTypeImpl<T> { }; +template <typename T> +struct GetSampleTypeImpl<AudioSourceCallbackType<T>> : GetSampleTypeImpl<T> +{ +}; + template <typename SampleTypeOrCallbackType> using GetSampleType = typename GetSampleTypeImpl<SampleTypeOrCallbackType>::type; @@ -97,8 +106,12 @@ static constexpr QAudioFormat::SampleFormat getSampleFormat() using AudioSinkCallback = std::variant<AudioSinkCallbackType<float>, AudioSinkCallbackType<uint8_t>, AudioSinkCallbackType<int16_t>, AudioSinkCallbackType<int32_t>>; +using AudioSourceCallback = + std::variant<AudioSourceCallbackType<float>, AudioSourceCallbackType<uint8_t>, + AudioSourceCallbackType<int16_t>, AudioSourceCallbackType<int32_t>>; -constexpr bool validateAudioSinkCallback(const AudioSinkCallback &audioCallback, +template <typename AnyAudioCallback> +constexpr bool validateAudioCallbackImpl(const AnyAudioCallback &audioCallback, const QAudioFormat &format) { bool isNonNullFunction = std::visit([](const auto &cb) { @@ -115,37 +128,61 @@ constexpr bool validateAudioSinkCallback(const AudioSinkCallback &audioCallback, return hasCorrectFormat; } -inline void runAudioSinkCallback(const AudioSinkCallback &audioCallback, std::byte *hostBuffer, - qsizetype numberOfSamples, const QAudioFormat &format) +constexpr bool validateAudioCallback(const AudioSinkCallback &audioCallback, + const QAudioFormat &format) +{ + return validateAudioCallbackImpl(audioCallback, format); +} + +constexpr bool validateAudioCallback(const AudioSourceCallback &audioCallback, + const QAudioFormat &format) +{ + return validateAudioCallbackImpl(audioCallback, format); +} + +template <bool IsSink> +inline void runAudioCallback( + const std::conditional_t<IsSink, AudioSinkCallback, AudioSourceCallback> &audioCallback, + QSpan<std::conditional_t<IsSink, std::byte, const std::byte>> hostBuffer, + const QAudioFormat &format) { - Q_ASSERT(numberOfSamples > 0); + Q_ASSERT(!hostBuffer.empty()); + + bool callbackIsValid = validateAudioCallback(audioCallback, format); + QT_MM_ASSUME(callbackIsValid); - bool callbackIsValid = validateAudioSinkCallback(audioCallback, format); - Q_ASSERT(callbackIsValid); -#if __has_cpp_attribute(assume) - [[assume(callbackIsValid)]]; -#endif + int numberOfSamples = format.framesForBytes(hostBuffer.size()) * format.channelCount(); std::visit([&](const auto &callback) { using FunctorType = std::decay_t<decltype(callback)>; - constexpr QAudioFormat::SampleFormat functorSampleFormat = getSampleFormat<FunctorType>(); - Q_ASSERT(functorSampleFormat == format.sampleFormat()); + Q_ASSERT(getSampleFormat<FunctorType>() == format.sampleFormat()); using SampleType = GetSampleType<FunctorType>; bool audioCallbackIsValid = bool(callback); - Q_ASSERT(audioCallbackIsValid); -#if __has_cpp_attribute(assume) - [[assume(audioCallbackIsValid)]]; -#endif - auto buffer = QSpan<SampleType>{ - reinterpret_cast<SampleType *>(hostBuffer), + QT_MM_ASSUME(audioCallbackIsValid); + using HostBufferType = std::conditional_t<IsSink, SampleType, const SampleType>; + + auto buffer = QSpan<HostBufferType>{ + reinterpret_cast<HostBufferType *>(hostBuffer.data()), numberOfSamples, }; callback(buffer); }, audioCallback); } +inline void runAudioCallback(const AudioSinkCallback &audioCallback, QSpan<std::byte> hostBuffer, + const QAudioFormat &format) +{ + return runAudioCallback<true>(audioCallback, hostBuffer, format); +} + +inline void runAudioCallback(const AudioSourceCallback &audioCallback, + QSpan<const std::byte> hostBuffer, const QAudioFormat &format) +{ + return runAudioCallback<false>(audioCallback, hostBuffer, format); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// } // namespace QtMultimediaPrivate diff --git a/src/multimedia/darwin/qdarwinaudiosink.mm b/src/multimedia/darwin/qdarwinaudiosink.mm index bdd638618..e1449aea6 100644 --- a/src/multimedia/darwin/qdarwinaudiosink.mm +++ b/src/multimedia/darwin/qdarwinaudiosink.mm @@ -241,7 +241,7 @@ void QCoreAudioSinkStream::resumeIfNecessary() OSStatus QCoreAudioSinkStream::processRingbuffer(uint32_t numberOfFrames, AudioBufferList *ioData) noexcept QT_MM_NONBLOCKING { - Q_ASSERT(ioData->mBuffers[0].mDataByteSize == numberOfFrames * m_format.bytesPerFrame()); + Q_ASSERT(int64_t(ioData->mBuffers[0].mDataByteSize) == m_format.bytesForFrames(numberOfFrames)); QSpan audioBufferSpan{ reinterpret_cast<std::byte *>(ioData->mBuffers[0].mData), @@ -257,12 +257,15 @@ OSStatus QCoreAudioSinkStream::processAudioCallback(uint32_t numberOfFrames, AudioBufferList *ioData) noexcept QT_MM_NONBLOCKING { using namespace QtMultimediaPrivate; - const uint32_t bytesPerFrame = m_format.bytesPerFrame(); - const uint32_t numberOfSamples = numberOfFrames * m_format.channelCount(); - Q_ASSERT(ioData->mBuffers[0].mDataByteSize == numberOfFrames * bytesPerFrame); - runAudioSinkCallback(m_audioCallback, reinterpret_cast<std::byte *>(ioData->mBuffers[0].mData), - numberOfSamples, m_format); + Q_ASSERT(int64_t(ioData->mBuffers[0].mDataByteSize) == m_format.bytesForFrames(numberOfFrames)); + + QSpan<std::byte> inputSpan{ + reinterpret_cast<std::byte *>(ioData->mBuffers[0].mData), + ioData->mBuffers[0].mDataByteSize, + }; + + runAudioCallback(m_audioCallback, inputSpan, m_format); return noErr; } diff --git a/src/multimedia/pipewire/qpipewire_audiosink.cpp b/src/multimedia/pipewire/qpipewire_audiosink.cpp index 02364d972..a7924728c 100644 --- a/src/multimedia/pipewire/qpipewire_audiosink.cpp +++ b/src/multimedia/pipewire/qpipewire_audiosink.cpp @@ -309,7 +309,7 @@ void QPipewireAudioSinkStream::processCallback() noexcept QT_MM_NONBLOCKING auto [writeBuffer, requestedSamples, totalNumberOfFrames] = resolveHostBuffer(b, m_format); - runAudioSinkCallback(*m_audioCallback, writeBuffer.data(), requestedSamples, m_format); + runAudioCallback(*m_audioCallback, writeBuffer, m_format); queueBuffer(b, requestedSamples); } diff --git a/src/multimedia/pulseaudio/qpulseaudiosink.cpp b/src/multimedia/pulseaudio/qpulseaudiosink.cpp index 83ce1ec7b..77c88bab0 100644 --- a/src/multimedia/pulseaudio/qpulseaudiosink.cpp +++ b/src/multimedia/pulseaudio/qpulseaudiosink.cpp @@ -335,9 +335,7 @@ void QPulseAudioSinkStream::writeCallbackAudioCallback(size_t requestedBytes) }); } QSpan<std::byte> hostBuffer{ reinterpret_cast<std::byte *>(dest), qsizetype(nbytes) }; - - runAudioSinkCallback(*m_audioCallback, hostBuffer.data(), - requestedFrames * m_format.channelCount(), m_format); + runAudioCallback(*m_audioCallback, hostBuffer, m_format); status = pa_stream_write(m_stream.get(), hostBuffer.data(), nbytes, /*free_cb= */ nullptr, /*offset=*/0, PA_SEEK_RELATIVE); diff --git a/src/multimedia/windows/qwindowsaudiosink.cpp b/src/multimedia/windows/qwindowsaudiosink.cpp index ca9530e52..076c24b1d 100644 --- a/src/multimedia/windows/qwindowsaudiosink.cpp +++ b/src/multimedia/windows/qwindowsaudiosink.cpp @@ -314,8 +314,6 @@ bool QWASAPIAudioSinkStream::processCallback() noexcept QT_MM_NONBLOCKING if (requiredFrames == 0) return true; - uint32_t requiredSamples = requiredFrames * m_format.channelCount(); - // Grab the next empty buffer from the audio device. unsigned char *hostBuffer{}; hr = m_renderClient->GetBuffer(requiredFrames, &hostBuffer); @@ -324,8 +322,12 @@ bool QWASAPIAudioSinkStream::processCallback() noexcept QT_MM_NONBLOCKING return false; } - runAudioSinkCallback(m_audioCallback, reinterpret_cast<std::byte *>(hostBuffer), - requiredSamples, m_format); + QSpan<std::byte> hostBufferSpan{ + reinterpret_cast<std::byte *>(hostBuffer), + m_format.bytesForFrames(requiredFrames), + }; + + runAudioCallback(m_audioCallback, hostBufferSpan, m_format); constexpr DWORD flags = 0; hr = m_renderClient->ReleaseBuffer(requiredFrames, flags); diff --git a/tests/auto/unit/multimedia/qaudiohelpers/tst_qaudiohelpers.cpp b/tests/auto/unit/multimedia/qaudiohelpers/tst_qaudiohelpers.cpp index 1d93528a8..d60643d21 100644 --- a/tests/auto/unit/multimedia/qaudiohelpers/tst_qaudiohelpers.cpp +++ b/tests/auto/unit/multimedia/qaudiohelpers/tst_qaudiohelpers.cpp @@ -5,6 +5,7 @@ #include <QtTest/QtTest> #include <QtMultimedia/private/qaudiohelpers_p.h> +#include <QtMultimedia/private/qaudiosystem_p.h> #include <QtMultimedia/private/qaudio_alignment_support_p.h> #include <QtMultimedia/private/qaudio_qspan_support_p.h> @@ -29,6 +30,8 @@ private slots: void wordConverter_checkLoopUnroll(); void findBestNativeSampleFormat(); + + void validateAudioCallbacks(); }; namespace WordConverter { @@ -387,6 +390,38 @@ void tst_QAudioHelpers::findBestNativeSampleFormat() QCOMPARE_EQ(bestNativeSampleFormat(fmtFloat, telephoneFormats), NativeSampleFormat::uint8_t); } +static constexpr QAudioFormat fmtFloat = [] { + QAudioFormat fmt; + fmt.setSampleFormat(QAudioFormat::Float); + fmt.setSampleRate(44100); + return fmt; +}(); + +void tst_QAudioHelpers::validateAudioCallbacks() +{ + using namespace QtMultimediaPrivate; + + std::function<void(QSpan<float>)> nullFunction; + QVERIFY(!validateAudioCallback(nullFunction, fmtFloat)); + + std::function<void(QSpan<float>)> trivialFunction = [](QSpan<float>) { + }; + QVERIFY(validateAudioCallback(trivialFunction, fmtFloat)); + + QVERIFY(validateAudioCallback([](QSpan<float>) { + }, fmtFloat)); + QVERIFY(!validateAudioCallback([](QSpan<int16_t>) { + }, fmtFloat)); + + static_assert(getSampleFormat<std::function<void(QSpan<float>)>>() + == QAudioFormat::SampleFormat::Float); + static_assert(getSampleFormat<std::function<void(QSpan<int16_t>)>>() + == QAudioFormat::SampleFormat::Int16); + + static_assert(getSampleFormat<float>() == QAudioFormat::SampleFormat::Float); + static_assert(getSampleFormat<int16_t>() == QAudioFormat::SampleFormat::Int16); +} + QTEST_APPLESS_MAIN(tst_QAudioHelpers); #include "tst_qaudiohelpers.moc" |