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
5
6#include <QtCore/qmutex.h>
7#include <QtCore/q20map.h>
8
9#include <utility>
10
11QT_BEGIN_NAMESPACE
12
13namespace QtMultimediaPrivate {
14
15namespace {
16
17QSpan<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} // namespace
26
27///////////////////////////////////////////////////////////////////////////////////////////////////
28
29QSoundEffectVoice::QSoundEffectVoice(VoiceId voiceId, std::shared_ptr<const QSample> sample,
30 float volume, bool muted, int totalLoopCount,
31 QAudioFormat engineFormat)
32 : QRtAudioEngineVoice{ voiceId },
33 m_sample{ std::move(sample) },
34 m_engineFormat{ engineFormat },
35 m_volume{ volume },
36 m_muted{ muted },
37 m_loopsRemaining{ totalLoopCount }
38{
39}
40
42
67
69{
73
76
78
79 const int sampleCh = format.channelCount();
86
88 const ConversionType conversion = [&] {
89 if (sampleCh == engineCh)
90 return SameChannels;
91 if (sampleCh == 1 && engineCh == 2)
92 return MonoToStereo;
93 if (sampleCh == 2 && engineCh == 1)
94 return StereoToMono;
96 }();
97
98 if (m_muted || m_volume == 0.f) {
100 return framesToPlay;
101 }
102
103 // later: (auto)vectorize?
104 switch (conversion) {
105 case SameChannels:
106 for (qsizetype frame = 0; frame < framesToPlay; ++frame) {
109 for (int ch = 0; ch < sampleCh; ++ch) {
111 }
112 }
113 break;
114 case MonoToStereo:
115 for (qsizetype frame = 0; frame < framesToPlay; ++frame) {
118 const float val = playbackRange[sampleBase] * m_volume;
121 }
122 break;
123 case StereoToMono:
124 float scale = 0.5f * m_volume;
125 for (qsizetype frame = 0; frame < framesToPlay; ++frame) {
128 const float val = (playbackRange[sampleBase] + playbackRange[sampleBase + 1]) * scale;
130 }
131 break;
132 }
133
134 return framesToPlay;
135}
136
138{
140 return true;
141
142 return loopsRemaining() != 0;
143}
144
147{
151
152 // caveat: reading frame is not atomic, so we may have a race here ... is is rare, though,
153 // not sure if we really care
155 return clone;
156}
157
158///////////////////////////////////////////////////////////////////////////////////////////////////
159
179
186
188{
189 if (device == m_audioDevice)
190 return false;
191
194 return true;
195}
196
198{
200 return;
201
203
204 if (m_player)
205 for (const auto &voice : m_voices)
207
210 };
211 m_voices.clear();
212
213 if (m_sample) {
215 if (!hasPlayer) {
217 return;
218 }
219
220 for (const auto &voice : voices)
221 // we re-allocate a new voice ID and play on the new player
223
225
226 for (const auto &voice : voices)
227 // we re-allocate a new voice ID and play on the new player
228 play(voice->clone());
229 } else {
231 }
232}
233
235{
236 if (m_audioDevice.isNull())
239}
240
245
247{
248 if (m_sampleLoadFuture) {
251 }
252
253 if (m_player) {
256 // we keep the player referenced for a little longer, so that later calls to
257 // QRtAudioEngine::getEngineFor will be able to reuse the existing instance
260 }
261
262 m_url = url;
263 m_sample = {};
264
265 if (url.isEmpty()) {
267 return false;
268 }
269
270 if (!url.isValid()) {
272 return false;
273 }
274
276
279 if (result) {
281 qWarning("QSoundEffect: QSoundEffect only supports mono or stereo files");
283 return;
284 }
285
288 if (!hasPlayer) {
289 qWarning("QSoundEffect: playback of this format is not supported on the selected "
290 "audio device");
292 return;
293 }
294
296 if (std::exchange(m_playPending, false)) {
297 play();
298 }
299 } else {
300 qWarning("QSoundEffect: Error decoding source %ls", qUtf16Printable(m_url.toString()));
302 }
303 });
304
305 return true;
306}
307
309{
310 return m_url;
311}
312
314{
315 if (status == m_status)
316 return;
319}
320
325
327{
328 return m_loopCount;
329}
330
332{
333 if (loopCount == 0)
334 loopCount = 1;
335
336 if (loopCount == m_loopCount)
337 return false;
338
340
341 if (m_voices.empty())
342 return true;
343
346
348
349 return true;
350}
351
353{
354 if (m_voices.empty())
355 return 0;
356
357 return m_loopsRemaining;
358}
359
361{
362 return m_volume;
363}
364
366{
367 if (m_volume == volume)
368 return false;
369
371 for (const auto &voice : m_voices) {
374 });
375 }
376 return true;
377}
378
380{
381 return m_muted;
382}
383
385{
386 if (m_muted == muted)
387 return false;
388
389 m_muted = muted;
390 for (const auto &voice : m_voices) {
393 });
394 }
395 return true;
396}
397
399{
400 if (!m_sample) {
401 m_playPending = true;
402 return;
403 }
404
405 if (status() != QSoundEffect::Ready)
406 return;
407
409
410 // each `play` will start a new voice
414
415 play(std::move(voice));
416}
417
419{
421 for (const auto &voice : m_voices)
424
425 m_voices.clear();
426 m_playPending = false;
427 if (activeVoices)
429}
430
432{
433 return !m_voices.empty();
434}
435
437{
439 [this, voiceId = voice->voiceId()] {
441 if (foundVoice == m_voices.end())
442 return;
443
444 if (voiceId != activeVoice())
445 return;
446
448 });
449
453 if (m_voices.size() == 1)
455}
456
458{
462
463 m_player = {};
465 return false;
466
469 if (player)
470 return player;
471
473 switch (sample->format().channelCount()) {
474 case 1:
476 break;
477 case 2:
479 break;
480 default:
482 }
483
485 }();
486
487 if (!m_player)
488 return false;
489
491 this, [this](VoiceId voiceId) {
492 if (voiceId == activeVoice())
494
495 auto found = m_voices.find(voiceId);
496 if (found != m_voices.end()) {
498 if (m_voices.empty())
500 }
501 });
502 return true;
503}
504
506{
507 if (m_voices.empty())
508 return std::nullopt;
509 return (*m_voices.rbegin())->voiceId();
510}
511
513{
514 switch (fmt.channelCount()) {
515 case 1:
516 case 2:
517 return true;
518 default:
519 return false;
520 }
521}
522
524{
526 return;
529}
530
531} // namespace QtMultimediaPrivate
532
533QT_END_NAMESPACE
Q_MULTIMEDIA_EXPORT ~QSoundEffectVoice()