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