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/qaudioringbuffer_p.h>
20#include <QtMultimedia/private/qaudiosystem_p.h>
21#include <QtMultimedia/private/qautoresetevent_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
66 virtual bool isActive() noexcept Q_DECL_NONBLOCKING_FUNCTION = 0;
67
68 virtual const QAudioFormat &format() noexcept = 0;
69
70 VoiceId voiceId() const { return m_voiceId; }
71
72private:
73 const VoiceId m_voiceId;
74};
75
77{
78 using std::less<uint64_t>::operator();
79 template <typename Lhs, typename Rhs>
80 bool operator()(const Lhs &lhs, const Rhs &rhs) const
81 {
82 return operator()(getId(lhs), getId(rhs));
83 }
84
85 static uint64_t getId(VoiceId arg) { return qToUnderlying(arg); }
86 static uint64_t getId(const QRtAudioEngineVoice &arg) { return getId(arg.voiceId()); }
87 static uint64_t getId(const std::shared_ptr<QRtAudioEngineVoice> &arg) { return getId(*arg); }
88
90};
91
92///////////////////////////////////////////////////////////////////////////////////////////////////
93
94namespace Impl {
95template <typename T>
96struct visitor_arg;
97
98template <typename R, typename Arg>
99struct visitor_arg<R(Arg)>
100{
101 using type = Arg;
102};
103
104template <typename R, typename Arg>
105struct visitor_arg<R (*)(Arg)>
106{
107 using type = Arg;
108};
109
110template <typename F>
111struct visitor_arg : visitor_arg<decltype(&F::operator())>
112{
113};
114
115template <typename C, typename R, typename Arg>
116struct visitor_arg<R (C::*)(Arg) const>
117{
118 using type = Arg;
119};
120
121template <typename C, typename R, typename Arg>
122struct visitor_arg<R (C::*)(Arg)>
123{
124 using type = Arg;
125};
126
127} // namespace Impl
128
129template <typename F>
130using visitor_arg_t = typename Impl::visitor_arg<F>::type;
131
132///////////////////////////////////////////////////////////////////////////////////////////////////
133
134// playback engine for QRtAudioEngineVoice instances
135// keeps a QAudioSink alive, but in a suspended state if no voices are playing
136class Q_MULTIMEDIA_EXPORT QRtAudioEngine final : public QObject
137{
138public:
141
143
144 // commands (app->rt)
146 {
148 };
149
151 {
153 };
154
155 // visitors are sent back to the non-rt thread, so that they are destroyed in a safe context
161
162 // "trivial" visitors are not sent back to the non-rt thread
168
170
171 // notifications (rt->app)
176
181
183
184public:
190
191 // play/stop/visitVoiceRT are thread-safe
192 void play(SharedVoice);
193 void stop(const SharedVoice &);
194 void stop(VoiceId);
195
196 template <typename Visitor>
198 {
200 static_assert(std::is_reference_v<visitorArg>);
201
202 // we need to prevent that the visitor function is going to be destroyed on the real-time
203 // thread, unless:
204 // * it can be trivially destroyed
205 // * it is sufficiently small to fit into the small-buffer-optimization of std::function.
206 // we don't know what the value is, so we are conservative and assume only the size of a
207 // pointer (we could relax it with something like std::inplace_function)
208 constexpr size_t smallBufferOptimizationEstimate = 2 * sizeof(void *);
211
212 auto wrapped = [visitor = std::move(visitor)](QRtAudioEngineVoice &voice) mutable {
213 visitor(static_cast<visitorArg>(voice));
214 };
216 }
217
218 template <typename Visitor>
223
224 static VoiceId allocateVoiceId();
225
227
228 // testing
230 const auto &voices() const { return m_voices; }
231
232 // will return QtAudio::Stopped sink cannot be started or an error occurred
233 QtAudio::State audioState() const;
234
238
239private:
241
242 void audioCallback(QSpan<float>) noexcept Q_DECL_NONBLOCKING_FUNCTION;
244
250
254
256
258
259 // Application side
261
262 // Rt memory
263 // Note: when the memory pool is exhausted, we fall back to the system allocator. Not great for
264 // real-time uses, but a simple fallback strategy
265 static constexpr size_t poolSize = 128 * 1024; // 128kb
267
268 // Voice registry on the real-time thread:
269 // invariant: every voice in m_rtVoiceRegistry is also in m_voices
274 };
275
276 // rt/nrt communication
277 static constexpr size_t commandBuffersSize = 2048;
283 };
289
292};
293
294} // namespace QtMultimediaPrivate
295
296QT_END_NAMESPACE
297
298#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