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
34
QT_BEGIN_NAMESPACE
35
36
namespace
QtMultimediaPrivate
{
37
38
///////////////////////////////////////////////////////////////////////////////////////////////////
39
40
// ID to uniquely identify a voice
41
enum
class
VoiceId
:
uint64_t
42
{
43
};
44
45
enum
class
VoicePlayResult
:
uint8_t
46
{
47
Playing
,
48
Finished
,
49
};
50
51
///////////////////////////////////////////////////////////////////////////////////////////////////
52
53
class
Q_MULTIMEDIA_EXPORT
QRtAudioEngineVoice
54
{
55
public
:
56
using
VoicePlayResult
=
QtMultimediaPrivate
::
VoicePlayResult
;
57
using
VoiceId
=
QtMultimediaPrivate
::
VoiceId
;
58
59
explicit
QRtAudioEngineVoice
(
VoiceId
id
) :
m_voiceId
{
id
} { }
60
Q_DISABLE_COPY_MOVE
(
QRtAudioEngineVoice
)
61
virtual
~
QRtAudioEngineVoice
() =
default
;
62
63
// once play() returns finished or isActive is false, the QAudioPlaybackEngine will stop the
64
// voice
65
[[
nodiscard
]]
virtual
VoicePlayResult
play
(
QSpan
<
float
>)
noexcept
Q_DECL_NONBLOCKING_FUNCTION
= 0;
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
72
private
:
73
const
VoiceId
m_voiceId
;
74
};
75
76
struct
QRtAudioEngineVoiceCompare
:
std
::
less
<
uint64_t
>
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
89
using
is_transparent
=
std
::
true_type
;
90
};
91
92
///////////////////////////////////////////////////////////////////////////////////////////////////
93
94
namespace
Impl
{
95
template
<
typename
T>
96
struct
visitor_arg
;
97
98
template
<
typename
R,
typename
Arg>
99
struct
visitor_arg
<R(Arg)>
100
{
101
using
type
= Arg;
102
};
103
104
template
<
typename
R,
typename
Arg>
105
struct
visitor_arg
<R (*)(Arg)>
106
{
107
using
type
= Arg;
108
};
109
110
template
<
typename
F>
111
struct
visitor_arg
:
visitor_arg
<
decltype
(&F::operator())>
112
{
113
};
114
115
template
<
typename
C,
typename
R,
typename
Arg>
116
struct
visitor_arg
<R (
C
::*)(Arg)
const
>
117
{
118
using
type
= Arg;
119
};
120
121
template
<
typename
C,
typename
R,
typename
Arg>
122
struct
visitor_arg
<R (
C
::*)(Arg)>
123
{
124
using
type
= Arg;
125
};
126
127
}
// namespace Impl
128
129
template
<
typename
F>
130
using
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
136
class
Q_MULTIMEDIA_EXPORT QRtAudioEngine
final
:
public
QObject
137
{
138
public
:
139
using
RtVoiceVisitor
=
std
::
function
<
void
(
QRtAudioEngineVoice
&)>;
140
using
SharedVoice
=
std
::
shared_ptr
<
QRtAudioEngineVoice
>;
141
142
Q_OBJECT
143
144
// commands (app->rt)
145
struct
PlayCommand
146
{
147
SharedVoice
voice
;
148
};
149
150
struct
StopCommand
151
{
152
const
VoiceId
voiceId
;
153
};
154
155
// visitors are sent back to the non-rt thread, so that they are destroyed in a safe context
156
struct
VisitCommand
157
{
158
const
VoiceId
voiceId
;
159
RtVoiceVisitor
callback
;
160
};
161
162
// "trivial" visitors are not sent back to the non-rt thread
163
struct
VisitCommandTrivial
164
{
165
const
VoiceId
voiceId
;
166
RtVoiceVisitor
callback
;
167
};
168
169
using
RtCommand
=
std
::
variant
<
PlayCommand
,
StopCommand
,
VisitCommand
,
VisitCommandTrivial
>;
170
171
// notifications (rt->app)
172
struct
StopNotification
173
{
174
SharedVoice
voice
;
175
};
176
177
struct
VisitReply
178
{
179
RtVoiceVisitor
callback
;
180
};
181
182
using
Notification
=
std
::
variant
<
StopNotification
,
VisitReply
>;
183
184
public
:
185
QRtAudioEngine
(
const
QAudioDevice
&,
const
QAudioFormat
&,
186
std
::
optional
<
AudioEndpointRole
> =
std
::
nullopt
,
187
std
::
optional
<
NativePeriodFrames
>
nativePeriodFrames
=
std
::
nullopt
);
188
Q_DISABLE_COPY_MOVE
(
QRtAudioEngine
)
189
~
QRtAudioEngine
()
override
;
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
>
197
void
visitVoiceRt
(
VoiceId
id
,
Visitor
visitor
)
198
{
199
using
visitorArg
=
visitor_arg_t
<
Visitor
>;
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
*);
209
constexpr
bool
visitorIsTrivial
=
std
::
is_trivially_destructible_v
<
std
::
decay_t
<
Visitor
>>
210
&&
sizeof
(
Visitor
) <=
smallBufferOptimizationEstimate
;
211
212
auto
wrapped
= [
visitor
=
std
::
move
(
visitor
)](
QRtAudioEngineVoice
&
voice
)
mutable
{
213
visitor
(
static_cast
<
visitorArg
>(
voice
));
214
};
215
visitVoiceRt
(
id
,
RtVoiceVisitor
{
wrapped
},
visitorIsTrivial
);
216
}
217
218
template
<
typename
Visitor
>
219
void
visitVoiceRt
(
const
SharedVoice
&
voice
,
Visitor
visitor
)
220
{
221
visitVoiceRt
(
voice
->
voiceId
(),
std
::
move
(
visitor
));
222
}
223
224
static
VoiceId
allocateVoiceId
();
225
226
std
::
unique_ptr
<
pmr
::
memory_resource
> &
rtMemoryResource
() {
return
m_rtMemoryPool
; }
227
228
// testing
229
QAudioSink
&
audioSink
() {
return
m_sink
; }
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
235
Q_SIGNALS
:
236
void
stateChanged
(
QtAudio
::
State
);
237
void
voiceFinished
(
VoiceId
);
238
239
private
:
240
void
visitVoiceRt
(
VoiceId
,
RtVoiceVisitor
,
bool
visitorIsTrivial
);
241
242
void
audioCallback
(
QSpan
<
float
>)
noexcept
Q_DECL_NONBLOCKING_FUNCTION
;
243
void
cleanupRetiredVoices
()
noexcept
Q_DECL_NONBLOCKING_FUNCTION
;
244
245
void
runRtCommands
()
noexcept
Q_DECL_NONBLOCKING_FUNCTION
;
246
void
runRtCommand
(
PlayCommand
)
noexcept
Q_DECL_NONBLOCKING_FUNCTION
;
247
void
runRtCommand
(
StopCommand
)
noexcept
Q_DECL_NONBLOCKING_FUNCTION
;
248
void
runRtCommand
(
VisitCommand
)
noexcept
Q_DECL_NONBLOCKING_FUNCTION
;
249
void
runRtCommand
(
VisitCommandTrivial
)
noexcept
Q_DECL_NONBLOCKING_FUNCTION
;
250
251
void
runNonRtNotifications
();
252
void
runNonRtNotification
(
StopNotification
);
253
void
runNonRtNotification
(
VisitReply
);
254
255
QAudioSink
m_sink
;
256
257
QMutex
m_mutex
;
258
259
// Application side
260
std
::
set
<
SharedVoice
,
QRtAudioEngineVoiceCompare
>
m_voices
;
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
266
std
::
unique_ptr
<
pmr
::
memory_resource
>
m_rtMemoryPool
;
267
268
// Voice registry on the real-time thread:
269
// invariant: every voice in m_rtVoiceRegistry is also in m_voices
270
using
VoiceRegistry
=
std
::
set
<
SharedVoice
,
QRtAudioEngineVoiceCompare
,
271
pmr
::
polymorphic_allocator
<
SharedVoice
>>;
272
VoiceRegistry
m_rtVoiceRegistry
{
273
m_rtMemoryPool
.
get
(),
274
};
275
276
// rt/nrt communication
277
static
constexpr
size_t
commandBuffersSize
= 2048;
278
QtPrivate
::
QAudioRingBuffer
<
RtCommand
>
m_appToRt
{
commandBuffersSize
};
279
QtPrivate
::
QAudioRingBuffer
<
Notification
>
m_rtToApp
{
commandBuffersSize
};
280
std
::
deque
<
RtCommand
>
m_appToRtOverflowBuffer
;
281
std
::
deque
<
Notification
,
pmr
::
polymorphic_allocator
<
Notification
>>
m_rtToAppOverflowBuffer
{
282
m_rtMemoryPool
.
get
(),
283
};
284
void
sendAppToRtCommand
(
RtCommand
cmd
);
285
bool
sendRtToAppNotification
(
Notification
cmd
);
286
void
sendPendingRtCommands
();
287
bool
sendPendingAppNotifications
();
288
QTimer
m_pendingCommandsTimer
;
289
290
QtPrivate
::
QAutoResetEvent
m_notificationEvent
;
291
std
::
vector
<
VoiceId
>
m_finishedVoices
;
292
};
293
294
}
// namespace QtMultimediaPrivate
295
296
QT_END_NAMESPACE
297
298
#
endif
// QRTAUDIOENGINE_P_H
QtMultimediaPrivate::QRtAudioEngineVoice
Definition
qrtaudioengine_p.h:54
QtMultimediaPrivate::Impl
Definition
qrtaudioengine_p.h:94
QtMultimediaPrivate
Definition
qaudiosystem_p.h:51
QtMultimediaPrivate::visitor_arg_t
typename Impl::visitor_arg< F >::type visitor_arg_t
Definition
qrtaudioengine_p.h:130
QtMultimediaPrivate::VoiceId
VoiceId
Definition
qrtaudioengine_p.h:42
QtMultimediaPrivate::VoicePlayResult
VoicePlayResult
Definition
qrtaudioengine_p.h:46
QtMultimediaPrivate::VoicePlayResult::Finished
@ Finished
Definition
qrtaudioengine_p.h:48
QtMultimediaPrivate::VoicePlayResult::Playing
@ Playing
Definition
qrtaudioengine_p.h:47
QtMultimediaPrivate::sinkStateToEngineState
static QtAudio::State sinkStateToEngineState(QtAudio::State state)
Definition
qrtaudioengine.cpp:30
std
[33]
Definition
src_corelib_tools_qhash.cpp:421
QtMultimediaPrivate::Impl::visitor_arg< R(Arg)>
Definition
qrtaudioengine_p.h:100
QtMultimediaPrivate::Impl::visitor_arg< R(Arg)>::type
Arg type
Definition
qrtaudioengine_p.h:101
QtMultimediaPrivate::Impl::visitor_arg< R(C::*)(Arg) const >
Definition
qrtaudioengine_p.h:117
QtMultimediaPrivate::Impl::visitor_arg< R(C::*)(Arg) const >::type
Arg type
Definition
qrtaudioengine_p.h:118
QtMultimediaPrivate::Impl::visitor_arg< R(*)(Arg)>
Definition
qrtaudioengine_p.h:106
QtMultimediaPrivate::Impl::visitor_arg< R(*)(Arg)>::type
Arg type
Definition
qrtaudioengine_p.h:107
QtMultimediaPrivate::Impl::visitor_arg
Definition
qrtaudioengine_p.h:112
QtMultimediaPrivate::QRtAudioEngineVoiceCompare
Definition
qrtaudioengine_p.h:77
QtMultimediaPrivate::QRtAudioEngineVoiceCompare::getId
static uint64_t getId(VoiceId arg)
Definition
qrtaudioengine_p.h:85
QtMultimediaPrivate::QRtAudioEngineVoiceCompare::getId
static uint64_t getId(const QRtAudioEngineVoice &arg)
Definition
qrtaudioengine_p.h:86
QtMultimediaPrivate::QRtAudioEngineVoiceCompare::operator()
bool operator()(const Lhs &lhs, const Rhs &rhs) const
Definition
qrtaudioengine_p.h:80
qtmultimedia
src
multimedia
audio
qrtaudioengine_p.h
Generated on
for Qt by
1.16.1