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_sample = {};
263
264 if (url.isEmpty()) {
266 return;
267 }
268
269 if (!url.isValid()) {
271 return;
272 }
273
275
278 if (result) {
280 qWarning("QSoundEffect: QSoundEffect only supports mono or stereo files");
282 return;
283 }
284
287 if (!hasPlayer) {
288 qWarning("QSoundEffect: playback of this format is not supported on the selected "
289 "audio device");
291 return;
292 }
293
295 if (std::exchange(m_playPending, false)) {
296 play();
297 }
298 } else {
299 qWarning("QSoundEffect: Error decoding source %ls", qUtf16Printable(url.toString()));
301 }
302 });
303}
304
306{
307 if (status == m_status)
308 return;
311}
312
317
319{
320 return m_loopCount;
321}
322
324{
325 if (loopCount == 0)
326 loopCount = 1;
327
328 if (loopCount == m_loopCount)
329 return false;
330
332
333 if (m_voices.empty())
334 return true;
335
338
340
341 return true;
342}
343
345{
346 if (m_voices.empty())
347 return 0;
348
349 return m_loopsRemaining;
350}
351
353{
354 return m_volume;
355}
356
358{
359 if (m_volume == volume)
360 return false;
361
363 for (const auto &voice : m_voices) {
366 });
367 }
368 return true;
369}
370
372{
373 return m_muted;
374}
375
377{
378 if (m_muted == muted)
379 return false;
380
381 m_muted = muted;
382 for (const auto &voice : m_voices) {
385 });
386 }
387 return true;
388}
389
391{
392 if (!m_sample) {
393 m_playPending = true;
394 return;
395 }
396
397 if (status() != QSoundEffect::Ready)
398 return;
399
401
402 // each `play` will start a new voice
406
407 play(std::move(voice));
408}
409
411{
413 for (const auto &voice : m_voices)
416
417 m_voices.clear();
418 m_playPending = false;
419 if (activeVoices)
421}
422
424{
425 return !m_voices.empty();
426}
427
429{
431 [this, voiceId = voice->voiceId()] {
433 if (foundVoice == m_voices.end())
434 return;
435
436 if (voiceId != activeVoice())
437 return;
438
440 });
441
445 if (m_voices.size() == 1)
447}
448
450{
454
455 m_player = {};
457 return false;
458
461 if (player)
462 return player;
463
465 switch (sample->format().channelCount()) {
466 case 1:
468 break;
469 case 2:
471 break;
472 default:
474 }
475
477 }();
478
479 if (!m_player)
480 return false;
481
483 this, [this](VoiceId voiceId) {
484 if (voiceId == activeVoice())
486
487 auto found = m_voices.find(voiceId);
488 if (found != m_voices.end()) {
490 if (m_voices.empty())
492 }
493 });
494 return true;
495}
496
498{
499 if (m_voices.empty())
500 return std::nullopt;
501 return (*m_voices.rbegin())->voiceId();
502}
503
505{
506 switch (fmt.channelCount()) {
507 case 1:
508 case 2:
509 return true;
510 default:
511 return false;
512 }
513}
514
516{
518 return;
521}
522
523} // namespace QtMultimediaPrivate
524
525QT_END_NAMESPACE
Q_MULTIMEDIA_EXPORT ~QSoundEffectVoice()