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
qrtaudioengine_p.h
Go to the documentation of this file.
1// Copyright (C) 2025 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 QRTAUDIOENGINE_P_H
5#define QRTAUDIOENGINE_P_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 <QtCore/qtclasshelpermacros.h>
19#include <QtCore/qtimer.h>
20#include <QtCore/qmutex.h>
21
22#include <QtMultimedia/qaudiosink.h>
23#include <QtMultimedia/qtmultimediaglobal.h>
24#include <QtMultimedia/private/qaudio_rtsan_support_p.h>
25#include <QtMultimedia/private/qaudioringbuffer_p.h>
26#include <QtMultimedia/private/qautoresetevent_p.h>
27#include <QtMultimedia/private/q_pmr_emulation_p.h>
28
29#include <cstdint>
30#include <deque>
31#include <set>
32#include <variant>
33#include <vector>
34
35QT_BEGIN_NAMESPACE
36
37namespace QtMultimediaPrivate {
38
39///////////////////////////////////////////////////////////////////////////////////////////////////
40
41// ID to uniquely identify a voice
42enum class VoiceId : uint64_t
43{
44};
45
51
52///////////////////////////////////////////////////////////////////////////////////////////////////
53
54class Q_MULTIMEDIA_EXPORT QRtAudioEngineVoice
55{
56public:
59
62 virtual ~QRtAudioEngineVoice() = default;
63
64 // once play() returns finished or isActive is false, the QAudioPlaybackEngine will stop the
65 // voice
66 [[nodiscard]] virtual VoicePlayResult play(QSpan<float>) noexcept QT_MM_NONBLOCKING = 0;
67 virtual bool isActive() noexcept QT_MM_NONBLOCKING = 0;
68
69 virtual const QAudioFormat &format() noexcept = 0;
70
71 VoiceId voiceId() const { return m_voiceId; }
72
73private:
74 const VoiceId m_voiceId;
75};
76
78{
79 using std::less<uint64_t>::operator();
80 template <typename Lhs, typename Rhs>
81 bool operator()(const Lhs &lhs, const Rhs &rhs) const
82 {
83 return operator()(getId(lhs), getId(rhs));
84 }
85
86 static uint64_t getId(VoiceId arg) { return qToUnderlying(arg); }
87 static uint64_t getId(const QRtAudioEngineVoice &arg) { return getId(arg.voiceId()); }
88 static uint64_t getId(const std::shared_ptr<QRtAudioEngineVoice> &arg) { return getId(*arg); }
89
91};
92
93///////////////////////////////////////////////////////////////////////////////////////////////////
94
95namespace Impl {
96template <typename T>
97struct visitor_arg;
98
99template <typename R, typename Arg>
100struct visitor_arg<R(Arg)>
101{
102 using type = Arg;
103};
104
105template <typename R, typename Arg>
106struct visitor_arg<R (*)(Arg)>
107{
108 using type = Arg;
109};
110
111template <typename F>
112struct visitor_arg : visitor_arg<decltype(&F::operator())>
113{
114};
115
116template <typename C, typename R, typename Arg>
117struct visitor_arg<R (C::*)(Arg) const>
118{
119 using type = Arg;
120};
121
122template <typename C, typename R, typename Arg>
123struct visitor_arg<R (C::*)(Arg)>
124{
125 using type = Arg;
126};
127
128} // namespace Impl
129
130template <typename F>
131using visitor_arg_t = typename Impl::visitor_arg<F>::type;
132
133///////////////////////////////////////////////////////////////////////////////////////////////////
134
135// playback engine for QRtAudioEngineVoice instances
136// keeps a QAudioSink alive, but in a suspended state if no voices are playing
137class Q_MULTIMEDIA_EXPORT QRtAudioEngine final : public QObject
138{
139public:
142
144
145 // commands (app->rt)
147 {
149 };
150
152 {
154 };
155
156 // visitors are sent back to the non-rt thread, so that they are destroyed in a safe context
162
163 // "trivial" visitors are not sent back to the non-rt thread
169
171
172 // notifications (rt->app)
177
182
184
185public:
186 // we keep a pool of engines with one engine per device/format
188
189 QRtAudioEngine(const QAudioDevice &, const QAudioFormat &);
192
193 // play/stop/visitVoiceRT are thread-safe
194 void play(SharedVoice);
195 void stop(const SharedVoice &);
196 void stop(VoiceId);
197
198 template <typename Visitor>
200 {
202 static_assert(std::is_reference_v<visitorArg>);
203
204 // we need to prevent that the visitor function is going to be destroyed on the real-time
205 // thread, unless:
206 // * it can be trivially destroyed
207 // * it is sufficiently small to fit into the small-buffer-optimization of std::function.
208 // we don't know what the value is, so we are conservative and assume only the size of a
209 // pointer (we could relax it with something like std::inplace_function)
210 constexpr size_t smallBufferOptimizationEstimate = 2 * sizeof(void *);
213
215 visitor(static_cast<visitorArg>(voice));
216 };
218 }
219
220 template <typename Visitor>
225
226 static VoiceId allocateVoiceId();
227
229
230 // testing
232 const auto &voices() const { return m_voices; }
233
236
237private:
239
240 void audioCallback(QSpan<float>) noexcept QT_MM_NONBLOCKING;
242
243 void runRtCommands() noexcept QT_MM_NONBLOCKING;
248
252
254
256
257 // Application side
259
260 // Rt memory
261 // Note: when the memory pool is exhausted, we fall back to the system allocator. Not great for
262 // real-time uses, but a simple fallback strategy
263 static constexpr size_t poolSize = 128 * 1024; // 128kb
265
266 // Voice registry on the real-time thread:
267 // invariant: every voice in m_rtVoiceRegistry is also in m_voices
272 };
273
274 // rt/nrt communication
275 static constexpr size_t commandBuffersSize = 2048;
281 };
287
290};
291
292} // namespace QtMultimediaPrivate
293
294QT_END_NAMESPACE
295
296#endif // QRTAUDIOENGINE_P_H
typename Impl::visitor_arg< F >::type visitor_arg_t
static uint64_t getId(const QRtAudioEngineVoice &arg)
bool operator()(const Lhs &lhs, const Rhs &rhs) const