Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qaudiosystem_p.h
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QAUDIOSYSTEM_H
5#define QAUDIOSYSTEM_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QtMultimedia/qtmultimediaglobal.h>
19
20#include <QtMultimedia/qaudio.h>
21#include <QtMultimedia/qaudiodevice.h>
22#include <QtMultimedia/qaudioformat.h>
23#include <QtMultimedia/private/qaudiohelpers_p.h>
24#include <QtMultimedia/private/qaudio_rtsan_support_p.h>
25#include <QtMultimedia/private/qmultimedia_assume_p.h>
26
27#include <QtCore/qelapsedtimer.h>
28#include <QtCore/qspan.h>
29#include <QtCore/private/qglobal_p.h>
30
31#include <array>
32#include <functional>
33#include <variant>
34
35QT_BEGIN_NAMESPACE
36
37class QIODevice;
38class QAudioSink;
39class QAudioSource;
40
42
43///////////////////////////////////////////////////////////////////////////////////////////////////
44
52
53///////////////////////////////////////////////////////////////////////////////////////////////////
54
55template <typename SampleType>
57
58template <typename SampleType>
60
61#if __cpp_lib_move_only_function
62template <typename SampleType>
64
65template <typename SampleType>
67#endif
68
69template <typename>
71
72template <>
73struct GetSampleTypeImpl<float>
74{
75 using type = float;
77};
78
79template <>
85template <>
91
92template <>
98
99template <typename T>
103
104template <typename T>
108
109#if __cpp_lib_move_only_function
110
111template <typename T>
113{
114};
115
116template <typename T>
118{
119};
120
121#endif
122
123template <typename SampleTypeOrCallbackType>
124using GetSampleType = typename GetSampleTypeImpl<SampleTypeOrCallbackType>::type;
125
126template <typename SampleTypeOrCallbackType>
128{
129 return GetSampleTypeImpl<SampleTypeOrCallbackType>::sample_format;
130}
131
132#if __cpp_lib_move_only_function
133using AudioSinkCallback =
140#else
147
148#endif
149
150template <typename AnyAudioCallback>
151constexpr bool validateAudioCallbackImpl(const AnyAudioCallback &audioCallback,
152 const QAudioFormat &format)
153{
154 bool isNonNullFunction = std::visit([](const auto &cb) {
155 return bool(cb);
156 }, audioCallback);
157
158 if (!isNonNullFunction)
159 return false;
160
161 bool hasCorrectFormat = std::visit([&](const auto &cb) {
162 return getSampleFormat<std::decay_t<decltype(cb)>>() == format.sampleFormat();
163 }, audioCallback);
164
165 return hasCorrectFormat;
166}
167
168constexpr bool validateAudioCallback(const AudioSinkCallback &audioCallback,
169 const QAudioFormat &format)
170{
171 return validateAudioCallbackImpl(audioCallback, format);
172}
173
174constexpr bool validateAudioCallback(const AudioSourceCallback &audioCallback,
175 const QAudioFormat &format)
176{
177 return validateAudioCallbackImpl(audioCallback, format);
178}
179
180template <bool IsSink>
181inline void
182runAudioCallback(std::conditional_t<IsSink, AudioSinkCallback, AudioSourceCallback> &audioCallback,
183 QSpan<std::conditional_t<IsSink, std::byte, const std::byte>> hostBuffer,
184 const QAudioFormat &format)
185{
186 Q_ASSERT(!hostBuffer.empty());
187
188 bool callbackIsValid = validateAudioCallback(audioCallback, format);
189 QT_MM_ASSUME(callbackIsValid);
190
191 int numberOfSamples = format.framesForBytes(hostBuffer.size()) * format.channelCount();
192
193 std::visit([&](auto &callback) {
194 using FunctorType = std::decay_t<decltype(callback)>;
195 Q_ASSERT(getSampleFormat<FunctorType>() == format.sampleFormat());
196
197 using SampleType = GetSampleType<FunctorType>;
198
199 bool audioCallbackIsValid = bool(callback);
200 QT_MM_ASSUME(audioCallbackIsValid);
201 using HostBufferType = std::conditional_t<IsSink, SampleType, const SampleType>;
202
203 auto buffer = QSpan<HostBufferType>{
204 reinterpret_cast<HostBufferType *>(hostBuffer.data()),
205 numberOfSamples,
206 };
207 callback(buffer);
208 }, audioCallback);
209}
210
211inline void runAudioCallback(AudioSinkCallback &audioCallback, QSpan<std::byte> hostBuffer,
212 const QAudioFormat &format, float volume)
213{
214 runAudioCallback<true>(audioCallback, hostBuffer, format);
215 QAudioHelperInternal::applyVolume(volume, format, hostBuffer, hostBuffer);
216}
217
218// NB: we we provide two overloads for running audio callbacks based on the host buffer:
219// * if the host buffer is immutable, we need to apply the volume on a temporary buffer
220// * if the host buffer is mutable, we can apply the volume in-place (currently unused)
221inline void runAudioCallback(AudioSourceCallback &audioCallback, QSpan<const std::byte> hostBuffer,
222 const QAudioFormat &format, float volume)
223{
224 if (volume == 1.0f) {
225 runAudioCallback<false>(audioCallback, hostBuffer, format);
226 } else {
227 // if the host buffer is reasonably small (64kb, big enougth for 16 channels, 1024 frames,
228 // float32) we can use a stack-allocated temporary buffer.
229 // otherwise we allocate a heap buffer.
230
231 constexpr qsizetype sizeEstimate = 1024 * 16 * sizeof(float);
232 if (hostBuffer.size() <= sizeEstimate) {
233 std::array<std::byte, sizeEstimate> stackBuffer;
234 QSpan<std::byte> stackBufferSpan{
235 stackBuffer.data(),
236 hostBuffer.size(),
237 };
238
239 QAudioHelperInternal::applyVolume(volume, format, hostBuffer, stackBufferSpan);
240 runAudioCallback<false>(audioCallback, stackBufferSpan, format);
241 } else {
242 QtPrivate::ScopedRTSanDisabler allowAllocations;
243
244 auto buffer = q20::make_unique_for_overwrite<std::byte[]>(hostBuffer.size());
245 auto heapBufferSpan = QSpan{
246 buffer.get(),
247 hostBuffer.size(),
248 };
249 QAudioHelperInternal::applyVolume(volume, format, hostBuffer, heapBufferSpan);
250 runAudioCallback<false>(audioCallback, heapBufferSpan, format);
251 }
252 }
253}
254
255inline void runAudioCallback(AudioSourceCallback &audioCallback, QSpan<std::byte> hostBuffer,
256 const QAudioFormat &format, float volume)
257{
258 QAudioHelperInternal::applyVolume(volume, format, hostBuffer, hostBuffer);
259 runAudioCallback<false>(audioCallback, hostBuffer, format);
260}
261
262///////////////////////////////////////////////////////////////////////////////////////////////////
263
264} // namespace QtMultimediaPrivate
265
266class Q_MULTIMEDIA_EXPORT QPlatformAudioEndpointBase : public QObject
267{
268 Q_OBJECT
269
270public:
271 explicit QPlatformAudioEndpointBase(QAudioDevice, const QAudioFormat &, QObject *parent);
272
273 // LATER: can we devirtualize these functions
274 QAudio::Error error() const { return m_error; }
275 virtual QAudio::State state() const { return m_inferredState; }
276 virtual void setError(QAudio::Error);
277
278 virtual bool isFormatSupported(const QAudioFormat &format) const;
279 QAudioFormat format() const { return m_format; }
280
281 virtual void setVolume(float volume) { m_volume = volume; }
282 float volume() const { return m_volume; }
283
284Q_SIGNALS:
285 void stateChanged(QtAudio::State);
286
287protected:
288 enum class EmitStateSignal : uint8_t
289 {
290 True,
291 False,
292 };
293
294 void updateStreamState(QAudio::State);
295 void updateStreamIdle(bool idle, EmitStateSignal = EmitStateSignal::True);
296
297 const QAudioDevice m_audioDevice;
298 const QAudioFormat m_format;
299
300private:
301 void inferState();
302
303 QAudio::State m_streamState = QAudio::StoppedState;
304 QAudio::State m_inferredState = QAudio::StoppedState;
305 QAudio::Error m_error{};
306 bool m_streamIsIdle = false;
307
308 float m_volume{ 1.f };
309};
310
311class Q_MULTIMEDIA_EXPORT QPlatformAudioSink : public QPlatformAudioEndpointBase
312{
313public:
314 explicit QPlatformAudioSink(QAudioDevice, const QAudioFormat &, QObject *parent);
315 virtual void start(QIODevice *device) = 0;
316 virtual QIODevice* start() = 0;
317 virtual void stop() = 0;
318 virtual void reset() = 0;
319 virtual void suspend() = 0;
320 virtual void resume() = 0;
321 virtual qsizetype bytesFree() const = 0;
322 virtual void setBufferSize(qsizetype value) = 0;
323 virtual qsizetype bufferSize() const = 0;
324 virtual void setHardwareBufferFrames(int32_t) { }
325 virtual int32_t hardwareBufferFrames() { return -1; }
326 virtual qint64 processedUSecs() const = 0;
327
328 using AudioCallback = QtMultimediaPrivate::AudioSinkCallback;
329
330 virtual void start(AudioCallback &&) { }
331 virtual bool hasCallbackAPI() { return false; }
332
333 QElapsedTimer elapsedTime;
334
335 static QPlatformAudioSink *get(const QAudioSink &);
336
337 using AudioEndpointRole = QtMultimediaPrivate::AudioEndpointRole;
338 virtual void setRole(AudioEndpointRole) { }
339};
340
341class Q_MULTIMEDIA_EXPORT QPlatformAudioSource : public QPlatformAudioEndpointBase
342{
343public:
344 explicit QPlatformAudioSource(QAudioDevice, const QAudioFormat &, QObject *parent);
345 virtual void start(QIODevice *device) = 0;
346 virtual QIODevice* start() = 0;
347 virtual void stop() = 0;
348 virtual void reset() = 0;
349 virtual void suspend() = 0;
350 virtual void resume() = 0;
351 virtual qsizetype bytesReady() const = 0;
352 virtual void setBufferSize(qsizetype value) = 0;
353 virtual void setHardwareBufferFrames(int32_t) { }
354 virtual int32_t hardwareBufferFrames() { return -1; }
355 virtual qsizetype bufferSize() const = 0;
356 virtual qint64 processedUSecs() const = 0;
357
358 using AudioCallback = QtMultimediaPrivate::AudioSourceCallback;
359
360 virtual void start(AudioCallback &&) { }
361 virtual bool hasCallbackAPI() { return false; }
362
363 QElapsedTimer elapsedTime;
364
365 static QPlatformAudioSource *get(const QAudioSource &);
366};
367
368// forward declarations
369namespace QtMultimediaPrivate {
372} // namespace QtMultimediaPrivate
373
374QT_END_NAMESPACE
375
376#endif // QAUDIOSYSTEM_H
void runAudioCallback(std::conditional_t< IsSink, AudioSinkCallback, AudioSourceCallback > &audioCallback, QSpan< std::conditional_t< IsSink, std::byte, const std::byte > > hostBuffer, const QAudioFormat &format)
constexpr bool validateAudioCallback(const AudioSinkCallback &audioCallback, const QAudioFormat &format)
typename GetSampleTypeImpl< SampleTypeOrCallbackType >::type GetSampleType
static constexpr QAudioFormat::SampleFormat getSampleFormat()
void runAudioCallback(AudioSinkCallback &audioCallback, QSpan< std::byte > hostBuffer, const QAudioFormat &format, float volume)
constexpr bool validateAudioCallbackImpl(const AnyAudioCallback &audioCallback, const QAudioFormat &format)
QDebug operator<<(QDebug debug, QIODevice::OpenMode modes)
static constexpr QAudioFormat::SampleFormat sample_format