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