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
qsoundeffectwithplayer.cpp
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
#
include
"qsoundeffectwithplayer_p.h"
5
6
#
include
<
QtCore
/
q20map
.
h
>
7
#
include
<
QtCore
/
qmutex
.
h
>
8
#
include
<
QtCore
/
private
/
qobject_p
.
h
>
9
10
#
include
<
utility
>
11
12
QT_BEGIN_NAMESPACE
13
14
namespace
QtMultimediaPrivate
{
15
16
namespace
{
17
18
QSpan<
const
float
> toFloatSpan(QSpan<
const
char
> byteArray)
19
{
20
return
QSpan{
21
reinterpret_cast
<
const
float
*>(byteArray.data()),
22
qsizetype(byteArray.size_bytes() /
sizeof
(
float
)),
23
};
24
}
25
26
struct
AudioDeviceFormatLess
27
{
28
bool
operator()(
const
std::pair<QAudioDevice, QAudioFormat> &lhs,
29
const
std::pair<QAudioDevice, QAudioFormat> &rhs)
const
30
{
31
auto
cmp = qCompareThreeWay(lhs.first.id(), rhs.first.id());
32
if
(cmp == Qt::strong_ordering::less)
33
return
true
;
34
if
(cmp == Qt::strong_ordering::greater)
35
return
false
;
36
37
return
std
::tuple(lhs.second.sampleRate(), lhs.second.sampleFormat(),
38
lhs.second.channelCount())
39
<
std
::tuple(rhs.second.sampleRate(), rhs.second.sampleFormat(),
40
rhs.second.channelCount());
41
}
42
};
43
44
}
// namespace
45
46
// we keep a pool of engines with one engine per device/format
47
std
::
shared_ptr
<
QRtAudioEngine
>
48
QSoundEffectPrivateWithPlayer
::
getEngineFor
(
const
QAudioDevice
&
device
,
const
QAudioFormat
&
format
)
49
{
50
if
(
device
.
isNull
()) {
51
qWarning
() <<
"QRtAudioEngine needs to be called with a valid device"
;
52
return
nullptr
;
53
}
54
55
if
(
format
.
sampleFormat
() !=
QAudioFormat
::
Float
) {
56
qWarning
() <<
"QRtAudioEngine requires floating point samples"
;
57
return
nullptr
;
58
}
59
60
if
(!
device
.
isFormatSupported
(
format
)) {
61
qWarning
() <<
"QRtAudioEngine needs to be called with a supported fromat"
;
62
return
nullptr
;
63
}
64
65
static
QMutex
s_playerRegistryMutex
;
66
static
std
::
map
<
std
::
pair
<
QAudioDevice
,
QAudioFormat
>,
std
::
weak_ptr
<
QRtAudioEngine
>,
67
AudioDeviceFormatLess
>
68
s_playerRegistry
;
69
70
auto
guard
=
std
::
lock_guard
{
s_playerRegistryMutex
};
71
72
auto
key
=
std
::
pair
{
device
,
format
};
73
auto
found
=
s_playerRegistry
.
find
(
key
);
74
if
(
found
!=
s_playerRegistry
.
end
()) {
75
auto
player
=
found
->
second
.
lock
();
76
if
(
player
)
77
return
player
;
78
}
79
80
// lazy clean up
81
q20
::
erase_if
(
s_playerRegistry
, [](
auto
&&
keyValuePair
) {
82
return
keyValuePair
.
second
.
expired
();
83
});
84
85
// SoundEffect can prevent the stream from appear in an OS mixer
86
auto
endpointRole
=
AudioEndpointRole
::
SoundEffect
;
87
88
auto
player
=
std
::
shared_ptr
<
QRtAudioEngine
>(
89
new
QRtAudioEngine
{
QRtAudioEngine
::
MoveToApplicationThread
::
Yes
,
device
,
format
,
90
endpointRole
},
91
&
QObjectPrivate
::
deleteInOwnThread
);
92
s_playerRegistry
.
emplace
(
key
,
player
);
93
94
return
player
;
95
}
96
97
///////////////////////////////////////////////////////////////////////////////////////////////////
98
99
QSoundEffectVoice
::QSoundEffectVoice(VoiceId voiceId, std::shared_ptr<
const
QSample> sample,
100
float
volume,
bool
muted,
int
totalLoopCount,
101
QAudioFormat engineFormat)
102
: QRtAudioEngineVoice{ voiceId },
103
m_sample{ std::move(sample) },
104
m_engineFormat{ engineFormat },
105
m_volume
{ volume },
106
m_muted
{ muted },
107
m_loopsRemaining{ totalLoopCount }
108
{
109
}
110
111
QSoundEffectVoice
::~
QSoundEffectVoice
() =
default
;
112
113
VoicePlayResult
QSoundEffectVoice
::
play
(
QSpan
<
float
>
outputBuffer
)
noexcept
Q_DECL_NONBLOCKING_FUNCTION
114
{
115
qsizetype
playedFrames
=
playVoice
(
outputBuffer
);
116
m_currentFrame
+=
playedFrames
;
117
118
if
(
m_currentFrame
==
m_totalFrames
) {
119
const
bool
isInfiniteLoop
=
loopsRemaining
() ==
QSoundEffect
::
Infinite
;
120
bool
continuePlaying
=
isInfiniteLoop
;
121
122
if
(!
isInfiniteLoop
)
123
continuePlaying
=
m_loopsRemaining
.
fetch_sub
(1,
std
::
memory_order_relaxed
) > 1;
124
125
if
(
continuePlaying
) {
126
if
(!
isInfiniteLoop
)
127
m_currentLoopChanged
.
set
();
128
m_currentFrame
= 0;
129
QSpan
remainingOutputBuffer
=
130
drop
(
outputBuffer
,
playedFrames
*
m_engineFormat
.
channelCount
());
131
return
play
(
remainingOutputBuffer
);
132
}
133
return
VoicePlayResult
::
Finished
;
134
}
135
return
VoicePlayResult
::
Playing
;
136
}
137
138
qsizetype
QSoundEffectVoice
::
playVoice
(
QSpan
<
float
>
outputBuffer
)
noexcept
Q_DECL_NONBLOCKING_FUNCTION
139
{
140
const
QAudioFormat
&
format
=
m_sample
->
format
();
141
const
int
totalSamples
=
m_totalFrames
*
format
.
channelCount
();
142
const
int
currentSample
=
format
.
channelCount
() *
m_currentFrame
;
143
144
const
QSpan
fullSample
=
toFloatSpan
(
m_sample
->
data
());
145
const
QSpan
playbackRange
=
take
(
drop
(
fullSample
,
currentSample
),
totalSamples
);
146
147
Q_ASSERT
(!
playbackRange
.
empty
());
148
149
const
int
sampleCh
=
format
.
channelCount
();
150
const
int
engineCh
=
m_engineFormat
.
channelCount
();
151
const
qsizetype
sampleSamples
=
playbackRange
.
size
();
152
const
qsizetype
outputSamples
=
outputBuffer
.
size
();
153
const
qsizetype
maxFrames
=
std
::
min
(
sampleSamples
/
sampleCh
,
outputSamples
/
engineCh
);
154
const
qsizetype
framesToPlay
=
maxFrames
;
155
156
enum
ConversionType
:
uint8_t
{
SameChannels
,
MonoToStereo
,
StereoToMono
};
157
const
ConversionType
conversion
= [&] {
158
if
(
sampleCh
==
engineCh
)
159
return
SameChannels
;
160
if
(
sampleCh
== 1 &&
engineCh
== 2)
161
return
MonoToStereo
;
162
if
(
sampleCh
== 2 &&
engineCh
== 1)
163
return
StereoToMono
;
164
Q_UNREACHABLE_RETURN
(
SameChannels
);
165
}();
166
167
if
(
m_muted
||
m_volume
== 0.f)
168
return
framesToPlay
;
169
170
// later: (auto)vectorize?
171
switch
(
conversion
) {
172
case
SameChannels
:
173
for
(
qsizetype
frame
= 0;
frame
<
framesToPlay
; ++
frame
) {
174
const
qsizetype
sampleBase
=
frame
*
sampleCh
;
175
const
qsizetype
outputBase
=
frame
*
engineCh
;
176
for
(
int
ch
= 0;
ch
<
sampleCh
; ++
ch
) {
177
outputBuffer
[
outputBase
+
ch
] +=
playbackRange
[
sampleBase
+
ch
] *
m_volume
;
178
}
179
}
180
break
;
181
case
MonoToStereo
:
182
for
(
qsizetype
frame
= 0;
frame
<
framesToPlay
; ++
frame
) {
183
const
qsizetype
sampleBase
=
frame
*
sampleCh
;
184
const
qsizetype
outputBase
=
frame
*
engineCh
;
185
const
float
val
=
playbackRange
[
sampleBase
] *
m_volume
;
186
outputBuffer
[
outputBase
] +=
val
;
187
outputBuffer
[
outputBase
+ 1] +=
val
;
188
}
189
break
;
190
case
StereoToMono
:
191
float
scale
= 0.5f *
m_volume
;
192
for
(
qsizetype
frame
= 0;
frame
<
framesToPlay
; ++
frame
) {
193
const
qsizetype
sampleBase
=
frame
*
sampleCh
;
194
const
qsizetype
outputBase
=
frame
*
engineCh
;
195
const
float
val
= (
playbackRange
[
sampleBase
] +
playbackRange
[
sampleBase
+ 1]) *
scale
;
196
outputBuffer
[
outputBase
] +=
val
;
197
}
198
break
;
199
}
200
201
return
framesToPlay
;
202
}
203
204
bool
QSoundEffectVoice
::
isActive
()
noexcept
Q_DECL_NONBLOCKING_FUNCTION
205
{
206
if
(
m_currentFrame
!=
m_totalFrames
)
207
return
true
;
208
209
return
loopsRemaining
() != 0;
210
}
211
212
std
::
shared_ptr
<
QSoundEffectVoice
>
213
QSoundEffectVoice
::
clone
(
std
::
optional
<
QAudioFormat
>
newEngineFormat
)
const
214
{
215
auto
clone
=
std
::
make_shared
<
QSoundEffectVoice
>(
QRtAudioEngine
::
allocateVoiceId
(),
m_sample
,
216
m_volume
,
m_muted
,
loopsRemaining
(),
217
newEngineFormat
.
value_or
(
m_engineFormat
));
218
219
// caveat: reading frame is not atomic, so we may have a race here ... is is rare, though,
220
// not sure if we really care
221
clone
->
m_currentFrame
=
m_currentFrame
;
222
return
clone
;
223
}
224
225
///////////////////////////////////////////////////////////////////////////////////////////////////
226
227
QSoundEffectPrivateWithPlayer
::
QSoundEffectPrivateWithPlayer
(
QSoundEffect
*
q
,
228
QAudioDevice
audioDevice
)
229
:
q_ptr
{
q
},
m_audioDevice
{
std
::
move
(
audioDevice
) }
230
{
231
resolveAudioDevice
();
232
233
QObject
::
connect
(&
m_mediaDevices
, &
QMediaDevices
::
audioOutputsChanged
,
this
, [
this
] {
234
const
QAudioDevice
defaultAudioDevice
=
QMediaDevices
::
defaultAudioOutput
();
235
if
(
defaultAudioDevice
==
m_defaultAudioDevice
)
236
return
;
237
238
m_defaultAudioDevice
=
defaultAudioDevice
;
239
if
(
m_audioDevice
.
isNull
())
240
setResolvedAudioDevice
(
m_defaultAudioDevice
);
241
});
242
243
m_playerReleaseTimer
.
setTimerType
(
Qt
::
VeryCoarseTimer
);
244
m_playerReleaseTimer
.
setSingleShot
(
true
);
245
}
246
247
QSoundEffectPrivateWithPlayer
::~
QSoundEffectPrivateWithPlayer
()
248
{
249
stop
();
250
if
(
m_sampleLoadFuture
)
251
m_sampleLoadFuture
->
cancelChain
();
252
}
253
254
bool
QSoundEffectPrivateWithPlayer
::
setAudioDevice
(
QAudioDevice
device
)
255
{
256
if
(
device
==
m_audioDevice
)
257
return
false
;
258
259
m_audioDevice
=
std
::
move
(
device
);
260
resolveAudioDevice
();
261
return
true
;
262
}
263
264
void
QSoundEffectPrivateWithPlayer
::
setResolvedAudioDevice
(
QAudioDevice
device
)
265
{
266
if
(
m_resolvedAudioDevice
==
device
)
267
return
;
268
269
m_resolvedAudioDevice
=
std
::
move
(
device
);
270
271
if
(
m_player
)
272
for
(
const
auto
&
voice
:
m_voices
)
273
m_player
->
stop
(
voice
);
274
275
std
::
vector
<
std
::
shared_ptr
<
QSoundEffectVoice
>>
voices
{
276
std
::
make_move_iterator
(
m_voices
.
begin
()),
std
::
make_move_iterator
(
m_voices
.
end
())
277
};
278
m_voices
.
clear
();
279
280
if
(
m_sample
) {
281
bool
hasPlayer
=
updatePlayer
(
m_sample
);
282
if
(!
hasPlayer
) {
283
setStatus
(
QSoundEffect
::
Error
);
284
return
;
285
}
286
287
for
(
const
auto
&
voice
:
voices
)
288
// we re-allocate a new voice ID and play on the new player
289
play
(
voice
->
clone
(
m_player
->
audioSink
().
format
()));
290
291
setStatus
(
QSoundEffect
::
Ready
);
292
}
else
{
293
setStatus
(
m_sampleLoadFuture
?
QSoundEffect
::
Loading
:
QSoundEffect
::
Null
);
294
}
295
}
296
297
void
QSoundEffectPrivateWithPlayer
::
resolveAudioDevice
()
298
{
299
if
(
m_audioDevice
.
isNull
())
300
m_defaultAudioDevice
=
QMediaDevices
::
defaultAudioOutput
();
301
setResolvedAudioDevice
(
m_audioDevice
.
isNull
() ?
m_defaultAudioDevice
:
m_audioDevice
);
302
}
303
304
QAudioDevice
QSoundEffectPrivateWithPlayer
::
audioDevice
()
const
305
{
306
return
m_audioDevice
;
307
}
308
309
void
QSoundEffectPrivateWithPlayer
::
setSource
(
QUrl
url
,
QSampleCache
&
sampleCache
)
310
{
311
if
(
m_sampleLoadFuture
) {
312
m_sampleLoadFuture
->
cancelChain
();
313
m_sampleLoadFuture
=
std
::
nullopt
;
314
}
315
316
if
(
m_player
) {
317
QObject
::
disconnect
(
m_voiceFinishedConnection
);
318
m_playerReleaseTimer
.
callOnTimeout
(
this
, [
player
=
std
::
move
(
m_player
)] {
319
// we keep the player referenced for a little longer, so that later calls to
320
// getEngineFor will be able to reuse the existing instance
321
},
Qt
::
SingleShotConnection
);
322
m_playerReleaseTimer
.
start
();
323
}
324
325
m_sample
= {};
326
327
if
(
url
.
isEmpty
()) {
328
setStatus
(
QSoundEffect
::
Null
);
329
return
;
330
}
331
332
if
(!
url
.
isValid
()) {
333
setStatus
(
QSoundEffect
::
Error
);
334
return
;
335
}
336
337
setStatus
(
QSoundEffect
::
Loading
);
338
339
m_sampleLoadFuture
=
340
sampleCache
.
requestSampleFuture
(
url
).
then
(
this
, [
this
,
url
](
SharedSamplePtr
result
) {
341
if
(
result
) {
342
if
(!
formatIsSupported
(
result
->
format
())) {
343
qWarning
(
"QSoundEffect: QSoundEffect only supports mono or stereo files"
);
344
setStatus
(
QSoundEffect
::
Error
);
345
return
;
346
}
347
348
bool
hasPlayer
=
updatePlayer
(
result
);
349
m_sample
=
std
::
move
(
result
);
350
if
(!
hasPlayer
) {
351
qWarning
(
"QSoundEffect: playback of this format is not supported on the selected "
352
"audio device"
);
353
setStatus
(
QSoundEffect
::
Error
);
354
return
;
355
}
356
357
setStatus
(
QSoundEffect
::
Ready
);
358
if
(
std
::
exchange
(
m_playPending
,
false
)) {
359
play
();
360
}
361
}
else
{
362
qWarning
(
"QSoundEffect: Error decoding source %ls"
,
qUtf16Printable
(
url
.
toString
()));
363
setStatus
(
QSoundEffect
::
Error
);
364
}
365
});
366
}
367
368
void
QSoundEffectPrivateWithPlayer
::
setStatus
(
QSoundEffect
::
Status
status
)
369
{
370
if
(
status
==
m_status
)
371
return
;
372
m_status
=
status
;
373
emit
q_ptr
->
statusChanged
();
374
}
375
376
QSoundEffect
::
Status
QSoundEffectPrivateWithPlayer
::
status
()
const
377
{
378
return
m_status
;
379
}
380
381
int
QSoundEffectPrivateWithPlayer
::
loopCount
()
const
382
{
383
return
m_loopCount
;
384
}
385
386
bool
QSoundEffectPrivateWithPlayer
::
setLoopCount
(
int
loopCount
)
387
{
388
if
(
loopCount
== 0)
389
loopCount
= 1;
390
391
if
(
loopCount
==
m_loopCount
)
392
return
false
;
393
394
m_loopCount
=
loopCount
;
395
396
if
(
m_voices
.
empty
())
397
return
true
;
398
399
const
std
::
shared_ptr
<
QSoundEffectVoice
> &
voice
= *
m_voices
.
rbegin
();
400
voice
->
m_loopsRemaining
.
store
(
loopCount
,
std
::
memory_order_relaxed
);
401
402
setLoopsRemaining
(
loopCount
);
403
404
return
true
;
405
}
406
407
int
QSoundEffectPrivateWithPlayer
::
loopsRemaining
()
const
408
{
409
if
(
m_voices
.
empty
())
410
return
0;
411
412
return
m_loopsRemaining
;
413
}
414
415
float
QSoundEffectPrivateWithPlayer
::
volume
()
const
416
{
417
return
m_volume
;
418
}
419
420
bool
QSoundEffectPrivateWithPlayer
::
setVolume
(
float
volume
)
421
{
422
if
(
m_volume
==
volume
)
423
return
false
;
424
425
m_volume
=
volume
;
426
for
(
const
auto
&
voice
:
m_voices
) {
427
m_player
->
visitVoiceRt
(
voice
, [
volume
](
QSoundEffectVoice
&
voice
) {
428
voice
.
m_volume
=
volume
;
429
});
430
}
431
return
true
;
432
}
433
434
bool
QSoundEffectPrivateWithPlayer
::
muted
()
const
435
{
436
return
m_muted
;
437
}
438
439
bool
QSoundEffectPrivateWithPlayer
::
setMuted
(
bool
muted
)
440
{
441
if
(
m_muted
==
muted
)
442
return
false
;
443
444
m_muted
=
muted
;
445
for
(
const
auto
&
voice
:
m_voices
) {
446
m_player
->
visitVoiceRt
(
voice
, [
muted
](
QSoundEffectVoice
&
voice
) {
447
voice
.
m_muted
=
muted
;
448
});
449
}
450
return
true
;
451
}
452
453
void
QSoundEffectPrivateWithPlayer
::
play
()
454
{
455
if
(!
m_sample
) {
456
m_playPending
=
true
;
457
return
;
458
}
459
460
if
(
status
() !=
QSoundEffect
::
Ready
)
461
return
;
462
463
Q_ASSERT
(
m_player
);
464
465
// each `play` will start a new voice
466
auto
voice
=
std
::
make_shared
<
QSoundEffectVoice
>(
QRtAudioEngine
::
allocateVoiceId
(),
m_sample
,
467
m_volume
,
m_muted
,
m_loopCount
,
468
m_player
->
audioSink
().
format
());
469
470
play
(
std
::
move
(
voice
));
471
}
472
473
void
QSoundEffectPrivateWithPlayer
::
stop
()
474
{
475
size_t
activeVoices
=
m_voices
.
size
();
476
for
(
const
auto
&
voice
:
m_voices
)
477
m_player
->
stop
(
voice
->
voiceId
());
478
setLoopsRemaining
(0);
479
480
m_voices
.
clear
();
481
m_playPending
=
false
;
482
if
(
activeVoices
)
483
emit
q_ptr
->
playingChanged
();
484
}
485
486
bool
QSoundEffectPrivateWithPlayer
::
playing
()
const
487
{
488
return
!
m_voices
.
empty
();
489
}
490
491
void
QSoundEffectPrivateWithPlayer
::
play
(
std
::
shared_ptr
<
QSoundEffectVoice
>
voice
)
492
{
493
QObject
::
connect
(&
voice
->
m_currentLoopChanged
, &
QAutoResetEvent
::
activated
,
this
,
494
[
this
,
voiceId
=
voice
->
voiceId
()] {
495
auto
foundVoice
=
m_voices
.
find
(
voiceId
);
496
if
(
foundVoice
==
m_voices
.
end
())
497
return
;
498
499
if
(
voiceId
!=
activeVoice
())
500
return
;
501
502
setLoopsRemaining
((*
foundVoice
)->
loopsRemaining
());
503
});
504
505
m_player
->
play
(
voice
);
506
m_voices
.
insert
(
std
::
move
(
voice
));
507
setLoopsRemaining
(
m_loopCount
);
508
if
(
m_voices
.
size
() == 1)
509
emit
q_ptr
->
playingChanged
();
510
}
511
512
bool
QSoundEffectPrivateWithPlayer
::
updatePlayer
(
const
SharedSamplePtr
&
sample
)
513
{
514
Q_ASSERT
(
sample
);
515
Q_ASSERT
(
m_voices
.
empty
());
516
QObject
::
disconnect
(
m_voiceFinishedConnection
);
517
518
m_player
= {};
519
if
(
m_resolvedAudioDevice
.
isNull
())
520
return
false
;
521
522
m_player
= [&]() ->
std
::
shared_ptr
<
QRtAudioEngine
> {
523
auto
player
=
getEngineFor
(
m_resolvedAudioDevice
,
sample
->
format
());
524
if
(
player
)
525
return
player
;
526
527
QAudioFormat
alternativeFormat
=
sample
->
format
();
528
switch
(
sample
->
format
().
channelCount
()) {
529
case
1:
530
alternativeFormat
.
setChannelCount
(2);
531
break
;
532
case
2:
533
alternativeFormat
.
setChannelCount
(1);
534
break
;
535
default
:
536
Q_UNREACHABLE_RETURN
({});
537
}
538
539
return
std
::
make_shared
<
QRtAudioEngine
>(
m_resolvedAudioDevice
,
alternativeFormat
);
540
}();
541
542
if
(!
m_player
)
543
return
false
;
544
545
m_voiceFinishedConnection
=
QObject
::
connect
(
m_player
.
get
(), &
QRtAudioEngine
::
voiceFinished
,
546
this
, [
this
](
VoiceId
voiceId
) {
547
if
(
voiceId
==
activeVoice
())
548
setLoopsRemaining
(0);
549
550
auto
found
=
m_voices
.
find
(
voiceId
);
551
if
(
found
!=
m_voices
.
end
()) {
552
m_voices
.
erase
(
found
);
553
if
(
m_voices
.
empty
())
554
emit
q_ptr
->
playingChanged
();
555
}
556
});
557
return
true
;
558
}
559
560
std
::
optional
<
VoiceId
>
QSoundEffectPrivateWithPlayer
::
activeVoice
()
const
561
{
562
if
(
m_voices
.
empty
())
563
return
std
::
nullopt
;
564
return
(*
m_voices
.
rbegin
())->
voiceId
();
565
}
566
567
bool
QSoundEffectPrivateWithPlayer
::
formatIsSupported
(
const
QAudioFormat
&
fmt
)
568
{
569
switch
(
fmt
.
channelCount
()) {
570
case
1:
571
case
2:
572
return
true
;
573
default
:
574
return
false
;
575
}
576
}
577
578
void
QSoundEffectPrivateWithPlayer
::
setLoopsRemaining
(
int
loopsRemaining
)
579
{
580
if
(
loopsRemaining
==
m_loopsRemaining
)
581
return
;
582
m_loopsRemaining
=
loopsRemaining
;
583
emit
q_ptr
->
loopsRemainingChanged
();
584
}
585
586
}
// namespace QtMultimediaPrivate
587
588
QT_END_NAMESPACE
QtMultimediaPrivate::QSoundEffectVoice::~QSoundEffectVoice
Q_MULTIMEDIA_EXPORT ~QSoundEffectVoice()
QtMultimediaPrivate::QSoundEffectVoice::m_volume
float m_volume
Definition
qsoundeffectwithplayer_p.h:59
QtMultimediaPrivate::QSoundEffectVoice::m_muted
bool m_muted
Definition
qsoundeffectwithplayer_p.h:60
QtMultimediaPrivate::QSoundEffectVoice::m_totalFrames
const int m_totalFrames
Definition
qsoundeffectwithplayer_p.h:53
QtMultimediaPrivate::impl
Definition
qaudiosystem_p.h:257
std
[33]
Definition
src_corelib_tools_qhash.cpp:421
qtmultimedia
src
multimedia
audio
qsoundeffectwithplayer.cpp
Generated on
for Qt by
1.16.1