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/q_pmr_emulation_p.h>
19#include <QtMultimedia/private/qaudio_rtsan_support_p.h>
20#include <QtMultimedia/private/qaudioringbuffer_p.h>
21#include <QtMultimedia/private/qaudiosystem_p.h>
22#include <QtMultimedia/private/qautoresetevent_p.h>
23#include <QtMultimedia/qaudiosink.h>
24#include <QtMultimedia/qtmultimediaglobal.h>
25#include <QtCore/qmutex.h>
26#include <QtCore/qtclasshelpermacros.h>
27#include <QtCore/qtimer.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:
191
192 // play/stop/visitVoiceRT are thread-safe
193 void play(SharedVoice);
194 void stop(const SharedVoice &);
195 void stop(VoiceId);
196
197 template <typename Visitor>
199 {
201 static_assert(std::is_reference_v<visitorArg>);
202
203 // we need to prevent that the visitor function is going to be destroyed on the real-time
204 // thread, unless:
205 // * it can be trivially destroyed
206 // * it is sufficiently small to fit into the small-buffer-optimization of std::function.
207 // we don't know what the value is, so we are conservative and assume only the size of a
208 // pointer (we could relax it with something like std::inplace_function)
209 constexpr size_t smallBufferOptimizationEstimate = 2 * sizeof(void *);
212
213 auto wrapped = [visitor = std::move(visitor)](QRtAudioEngineVoice &voice) mutable {
214 visitor(static_cast<visitorArg>(voice));
215 };
217 }
218
219 template <typename Visitor>
224
225 static VoiceId allocateVoiceId();
226
228
229 // testing
231 const auto &voices() const { return m_voices; }
232
235
236private:
238
239 void audioCallback(QSpan<float>) noexcept QT_MM_NONBLOCKING;
241
242 void runRtCommands() noexcept QT_MM_NONBLOCKING;
247
251
253
255
256 // Application side
258
259 // Rt memory
260 // Note: when the memory pool is exhausted, we fall back to the system allocator. Not great for
261 // real-time uses, but a simple fallback strategy
262 static constexpr size_t poolSize = 128 * 1024; // 128kb
264
265 // Voice registry on the real-time thread:
266 // invariant: every voice in m_rtVoiceRegistry is also in m_voices
271 };
272
273 // rt/nrt communication
274 static constexpr size_t commandBuffersSize = 2048;
280 };
286
289};
290
291} // namespace QtMultimediaPrivate
292
293QT_END_NAMESPACE
294
295#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