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 <QtMultimedia/private/qaudioringbuffer_p.h>
19#include <QtMultimedia/private/qaudiosystem_p.h>
20#include <QtMultimedia/private/qautoresetevent_p.h>
21#include <QtMultimedia/private/qtmm_pmr_p.h>
22#include <QtMultimedia/qaudiosink.h>
23#include <QtMultimedia/qtmultimediaglobal.h>
24#include <QtCore/qmutex.h>
25#include <QtCore/qtclasshelpermacros.h>
26#include <QtCore/qtimer.h>
27
28#include <cstdint>
29#include <deque>
30#include <set>
31#include <variant>
32#include <vector>
33
34QT_BEGIN_NAMESPACE
35
36namespace QtMultimediaPrivate {
37
38///////////////////////////////////////////////////////////////////////////////////////////////////
39
40// ID to uniquely identify a voice
41enum class VoiceId : uint64_t
42{
43};
44
50
51///////////////////////////////////////////////////////////////////////////////////////////////////
52
53class Q_MULTIMEDIA_EXPORT QRtAudioEngineVoice
54{
55public:
58
61 virtual ~QRtAudioEngineVoice() = default;
62
63 // once play() returns finished or isActive is false, the QAudioPlaybackEngine will stop the
64 // voice
65 //
66 // additive mixing constraint: QRtAudioEngine zero-fills the output buffer once per callback
67 // and then invokes play() on every active voice with that same buffer, so implementations
68 // must mix additively (e.g. `+=`) rather than overwrite/clear its contents.
70 virtual bool isActive() noexcept Q_DECL_NONBLOCKING_FUNCTION = 0;
71
72 virtual const QAudioFormat &format() noexcept = 0;
73
74 VoiceId voiceId() const { return m_voiceId; }
75
76private:
77 const VoiceId m_voiceId;
78};
79
81{
82 using std::less<uint64_t>::operator();
83 template <typename Lhs, typename Rhs>
84 bool operator()(const Lhs &lhs, const Rhs &rhs) const
85 {
86 return operator()(getId(lhs), getId(rhs));
87 }
88
89 static uint64_t getId(VoiceId arg) { return qToUnderlying(arg); }
90 static uint64_t getId(const QRtAudioEngineVoice &arg) { return getId(arg.voiceId()); }
91 static uint64_t getId(const std::shared_ptr<QRtAudioEngineVoice> &arg) { return getId(*arg); }
92
94};
95
96///////////////////////////////////////////////////////////////////////////////////////////////////
97
98namespace Impl {
99template <typename T>
100struct visitor_arg;
101
102template <typename R, typename Arg>
103struct visitor_arg<R(Arg)>
104{
105 using type = Arg;
106};
107
108template <typename R, typename Arg>
109struct visitor_arg<R (*)(Arg)>
110{
111 using type = Arg;
112};
113
114template <typename F>
115struct visitor_arg : visitor_arg<decltype(&F::operator())>
116{
117};
118
119template <typename C, typename R, typename Arg>
120struct visitor_arg<R (C::*)(Arg) const>
121{
122 using type = Arg;
123};
124
125template <typename C, typename R, typename Arg>
126struct visitor_arg<R (C::*)(Arg)>
127{
128 using type = Arg;
129};
130
131} // namespace Impl
132
133template <typename F>
134using visitor_arg_t = typename Impl::visitor_arg<F>::type;
135
136///////////////////////////////////////////////////////////////////////////////////////////////////
137
138// playback engine for QRtAudioEngineVoice instances
139// keeps a QAudioSink alive, but in a suspended state if no voices are playing
140class Q_MULTIMEDIA_EXPORT QRtAudioEngine final : public QObject
141{
142public:
145
147
148 // commands (app->rt)
150 {
152 };
153
155 {
157 };
158
159 // visitors are sent back to the non-rt thread, so that they are destroyed in a safe context
165
166 // "trivial" visitors are not sent back to the non-rt thread
172
174
175 // notifications (rt->app)
180
185
187
188public:
189 enum class MoveToApplicationThread : bool {
192 };
193
202
203 // play/stop/visitVoiceRT are thread-safe
204 void play(SharedVoice);
205 void stop(const SharedVoice &);
206 void stop(VoiceId);
207
208 template <typename Visitor>
210 {
212 static_assert(std::is_reference_v<visitorArg>);
213
214 // we need to prevent that the visitor function is going to be destroyed on the real-time
215 // thread, unless:
216 // * it can be trivially destroyed
217 // * it is sufficiently small to fit into the small-buffer-optimization of std::function.
218 // we don't know what the value is, so we are conservative and assume only the size of a
219 // pointer (we could relax it with something like std::inplace_function)
220 constexpr size_t smallBufferOptimizationEstimate = 2 * sizeof(void *);
223
224 auto wrapped = [visitor = std::move(visitor)](QRtAudioEngineVoice &voice) mutable {
225 visitor(static_cast<visitorArg>(voice));
226 };
228 }
229
230 template <typename Visitor>
235
236 static VoiceId allocateVoiceId();
237
239
240 // testing
242 const auto &voices() const { return m_voices; }
244
245 // will return QtAudio::Stopped sink cannot be started or an error occurred
246 QtAudio::State audioState() const;
247
251
252private:
254
255 void audioCallback(QSpan<float>) noexcept Q_DECL_NONBLOCKING_FUNCTION;
257
263
267
269
271
272 // Application side
274
275 // Rt memory
276 // Note: when the memory pool is exhausted, we fall back to the system allocator. Not great for
277 // real-time uses, but a simple fallback strategy
278 static constexpr size_t poolSize = 128 * 1024; // 128kb
280
281 // Voice registry on the real-time thread:
282 // invariant: every voice in m_rtVoiceRegistry is also in m_voices
287 };
288
289 // rt/nrt communication
290 static constexpr size_t commandBuffersSize = 2048;
296 };
302
305};
306
307} // namespace QtMultimediaPrivate
308
309QT_END_NAMESPACE
310
311#endif // QRTAUDIOENGINE_P_H
typename Impl::visitor_arg< F >::type visitor_arg_t
static QtAudio::State sinkStateToEngineState(QtAudio::State state)
static uint64_t getId(const QRtAudioEngineVoice &arg)
bool operator()(const Lhs &lhs, const Rhs &rhs) const