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
qrtaudioengine.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 <QtMultimedia/private/qaudiosystem_p.h>
7#include <QtMultimedia/private/qmemory_resource_tlsf_p.h>
8#include <QtMultimedia/private/qtmm_rtsan_support_p.h>
9#include <QtCore/q20map.h>
10#include <QtCore/qcoreapplication.h>
11#include <QtCore/qdebug.h>
12#include <QtCore/qmutex.h>
13#include <QtCore/qthread.h>
14
15#include <mutex>
16
17#ifdef Q_CC_MINGW
18// mingw-13.1 seems to have a false positive when using std::function inside a std::variant
19QT_WARNING_PUSH
20QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
21#endif
22
23QT_BEGIN_NAMESPACE
24
25namespace QtMultimediaPrivate {
26
27using namespace QtPrivate;
28using namespace std::chrono_literals;
29
30static QtAudio::State sinkStateToEngineState(QtAudio::State state)
31{
32 switch (state) {
33 case QtAudio::ActiveState:
34 case QtAudio::IdleState:
35 case QtAudio::SuspendedState:
36 return QtAudio::ActiveState;
37 case QtAudio::StoppedState:
38 return QtAudio::StoppedState;
39 default:
40 Q_UNREACHABLE_RETURN(QtAudio::StoppedState);
41 }
42}
43
44///////////////////////////////////////////////////////////////////////////////////////////////////
45
52
57 : m_sink{
58 device,
59 format,
60 },
63 }
64{
67 });
68
75 }
76
80 auto lock = std::lock_guard{ m_mutex };
84 });
85
87
88 if (role)
92
93 m_sink.start([this](QSpan<float> outputBuffer) {
95 });
96
97 // we start suspended
99
102 });
103}
104
106{
107 m_sink.reset();
108
109 // consume the ringbuffers
110 m_appToRt.consumeAll([](auto) {
111 });
112 m_rtToApp.consumeAll([](auto) {
113 });
114}
115
117{
118 auto lock = std::lock_guard{ m_mutex };
119
120 // TODO: where do we expect reampling to happen?
122
123 if (m_voices.empty())
124 m_sink.resume();
125
127
129 std::move(voice),
130 });
131}
132
134{
135 stop(voice->voiceId());
136}
137
143
145{
146 auto lock = std::lock_guard{ m_mutex };
147
148 if (visitorIsTrivial) {
150 voiceId,
151 std::move(fn),
152 });
153
154 } else {
156 voiceId,
157 std::move(fn),
158 });
159 }
160}
161
167
172
174{
177
179
182 };
183
184 for (const SharedVoice &voice : m_rtVoiceRegistry) {
185 Q_ASSERT(voice.use_count() >= 2); // voice in both m_rtVoiceRegistry and m_voices
186
190 }
191
192 if (!finishedVoices.empty()) {
194 for (const SharedVoice &voice : finishedVoices) {
197 if (stopSent)
198 sendNotification = true;
199 }
200 });
201 }
202
203 // TODO: we should probably (soft)clip the output buffer
204
208}
209
211{
212 bool notifyApp = false;
213
214#if __cpp_lib_erase_if >= 202002L
215 using std::erase_if;
216#else
217 auto erase_if = [](auto &c, auto &&pred) {
218 auto old_size = c.size();
219 for (auto first = c.begin(), last = c.end(); first != last;) {
220 if (pred(*first))
221 first = c.erase(first);
222 else
223 ++first;
224 }
225 return old_size - c.size();
226 };
227#endif
230 bool voiceIsActive = voice->isActive();
231 if (!voiceIsActive)
233
234 return !voiceIsActive;
235 });
236 });
237
238 if (notifyApp)
240}
241
243{
245 for (RtCommand &cmd : commands) {
246 std::visit([&](auto cmd) {
248 }, std::move(cmd));
249 }
250 });
251}
252
254{
257 });
258}
259
261{
263 if (it == m_rtVoiceRegistry.end())
264 return;
265
268
270 std::move(voice),
271 });
272 if (emitNotify)
274}
275
277{
279 if (it == m_rtVoiceRegistry.end())
280 return;
281
282 cmd.callback(**it);
283
284 // send callback back to application for destruction
287 });
288 if (emitNotify)
290}
291
293{
295 if (it == m_rtVoiceRegistry.end())
296 return;
297
298 cmd.callback(**it);
299}
300
302{
304 {
305 auto lock = std::lock_guard{ m_mutex };
308 std::visit([&](auto notification) {
310 }, std::move(notification));
311 }
312 });
313
316 }
317
318 // emit voiceFinished outside of the lock
321}
322
324{
326 if (m_voices.empty())
327 m_sink.suspend();
329}
330
332{
333 // nop (just making sure to delete on the application thread);
334}
335
337{
338 // first write all pending commands from overflow buffer
340
341 bool written = m_appToRt.produceOne([&] {
342 return std::move(cmd);
343 });
344
345 if (written)
346 return;
347
349
353 });
354}
355
357{
358 // first write all pending commands from overflow buffer
360
361 bool written = m_rtToApp.produceOne([&] {
362 return std::move(cmd);
363 });
364
365 if (written)
366 return true;
367
369
370 return emitNotification;
371}
372
374{
375 while (!m_appToRtOverflowBuffer.empty()) {
377 bool written = m_appToRt.produceOne([&] {
379 });
380 if (!written)
381 return;
382
384 }
385}
386
388{
389 bool emitNotification = false;
390 while (!m_rtToAppOverflowBuffer.empty()) {
392
393 // first write all pending commands from overflow buffer
394 bool written = m_rtToApp.produceOne([&] {
396 });
397 if (!written)
398 break;
399
401 emitNotification = true;
402 }
403
404 return emitNotification;
405}
406
407} // namespace QtMultimediaPrivate
408
409QT_END_NAMESPACE
410
411#ifdef Q_CC_MINGW
412QT_WARNING_POP
413#endif
414
415#include "moc_qrtaudioengine_p.cpp"
static QtAudio::State sinkStateToEngineState(QtAudio::State state)