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
41namespace QtMultimediaPrivate {
42
43///////////////////////////////////////////////////////////////////////////////////////////////////
44
45enum class AudioEndpointRole : uint8_t
46{
47 MediaPlayback,
48 SoundEffect,
49 Accessibility,
50 Other,
51};
52
53///////////////////////////////////////////////////////////////////////////////////////////////////
54
55template <typename SampleType>
56using AudioSinkCallbackType = std::function<void(QSpan<SampleType>)>;
57
58template <typename SampleType>
59using AudioSourceCallbackType = std::function<void(QSpan<const SampleType>)>;
60
61#if __cpp_lib_move_only_function
62template <typename SampleType>
63using AudioSinkMoveOnlyCallbackType = std::move_only_function<void(QSpan<SampleType>)>;
64
65template <typename SampleType>
66using AudioSourceMoveOnlyCallbackType = std::move_only_function<void(QSpan<const SampleType>)>;
67#endif
68
69template <typename>
70struct GetSampleTypeImpl;
71
72template <>
73struct GetSampleTypeImpl<float>
74{
75 using type = float;
76 static constexpr QAudioFormat::SampleFormat sample_format = QAudioFormat::SampleFormat::Float;
77};
78
79template <>
80struct GetSampleTypeImpl<int32_t>
81{
82 using type = int32_t;
83 static constexpr QAudioFormat::SampleFormat sample_format = QAudioFormat::SampleFormat::Int32;
84};
85template <>
86struct GetSampleTypeImpl<int16_t>
87{
88 using type = int16_t;
89 static constexpr QAudioFormat::SampleFormat sample_format = QAudioFormat::SampleFormat::Int16;
90};
91
92template <>
93struct GetSampleTypeImpl<uint8_t>
94{
95 using type = uint8_t;
96 static constexpr QAudioFormat::SampleFormat sample_format = QAudioFormat::SampleFormat::UInt8;
97};
98
99template <typename T>
100struct GetSampleTypeImpl<AudioSinkCallbackType<T>> : GetSampleTypeImpl<T>
101{
102};
103
104template <typename T>
105struct GetSampleTypeImpl<AudioSourceCallbackType<T>> : GetSampleTypeImpl<T>
106{
107};
108
109#if __cpp_lib_move_only_function
110
111template <typename T>
112struct GetSampleTypeImpl<AudioSinkMoveOnlyCallbackType<T>> : GetSampleTypeImpl<T>
113{
114};
115
116template <typename T>
117struct GetSampleTypeImpl<AudioSourceMoveOnlyCallbackType<T>> : GetSampleTypeImpl<T>
118{
119};
120
121#endif
122
123template <typename SampleTypeOrCallbackType>
124using GetSampleType = typename GetSampleTypeImpl<SampleTypeOrCallbackType>::type;
125
126template <typename SampleTypeOrCallbackType>
127static constexpr QAudioFormat::SampleFormat getSampleFormat()
128{
129 return GetSampleTypeImpl<SampleTypeOrCallbackType>::sample_format;
130}
131
132#if __cpp_lib_move_only_function
133using AudioSinkCallback =
134 std::variant<AudioSinkMoveOnlyCallbackType<float>, AudioSinkMoveOnlyCallbackType<uint8_t>,
135 AudioSinkMoveOnlyCallbackType<int16_t>,
136 AudioSinkMoveOnlyCallbackType<int32_t>>;
137using AudioSourceCallback = std::variant<
138 AudioSourceMoveOnlyCallbackType<float>, AudioSourceMoveOnlyCallbackType<uint8_t>,
139 AudioSourceMoveOnlyCallbackType<int16_t>, AudioSourceMoveOnlyCallbackType<int32_t>>;
140#else
141using AudioSinkCallback =
142 std::variant<AudioSinkCallbackType<float>, AudioSinkCallbackType<uint8_t>,
143 AudioSinkCallbackType<int16_t>, AudioSinkCallbackType<int32_t>>;
144using AudioSourceCallback =
145 std::variant<AudioSourceCallbackType<float>, AudioSourceCallbackType<uint8_t>,
146 AudioSourceCallbackType<int16_t>, AudioSourceCallbackType<int32_t>>;
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(byteCount: 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, source: hostBuffer, destination: 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, source: hostBuffer, destination: stackBufferSpan);
240 runAudioCallback<false>(audioCallback, hostBuffer: stackBufferSpan, format);
241 } else {
242 QtPrivate::ScopedRTSanDisabler allowAllocations;
243
244 auto buffer = q20::make_unique_for_overwrite<std::byte[]>(n: hostBuffer.size());
245 auto heapBufferSpan = QSpan{
246 buffer.get(),
247 hostBuffer.size(),
248 };
249 QAudioHelperInternal::applyVolume(volume, format, source: hostBuffer, destination: heapBufferSpan);
250 runAudioCallback<false>(audioCallback, hostBuffer: 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, source: hostBuffer, destination: 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 {
370class QPlatformAudioSinkStream;
371class QPlatformAudioSourceStream;
372} // namespace QtMultimediaPrivate
373
374QT_END_NAMESPACE
375
376#endif // QAUDIOSYSTEM_H
377

source code of qtmultimedia/src/multimedia/audio/qaudiosystem_p.h