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/qaudio_rtsan_support_p.h>
7#include <QtMultimedia/private/qaudiosystem_p.h>
8#include <QtMultimedia/private/qmemory_resource_tlsf_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
96
98{
99 m_sink.reset();
100
101 // consume the ringbuffers
102 m_appToRt.consumeAll([](auto) {
103 });
104 m_rtToApp.consumeAll([](auto) {
105 });
106}
107
109{
110 auto lock = std::lock_guard{ m_mutex };
111
112 // TODO: where do we expect reampling to happen?
114
115 if (m_voices.empty())
116 m_sink.resume();
117
119
121 std::move(voice),
122 });
123}
124
126{
127 stop(voice->voiceId());
128}
129
135
137{
138 auto lock = std::lock_guard{ m_mutex };
139
140 if (visitorIsTrivial) {
142 voiceId,
143 std::move(fn),
144 });
145
146 } else {
148 voiceId,
149 std::move(fn),
150 });
151 }
152}
153
159
164
166{
169
171
174 };
175
176 for (const SharedVoice &voice : m_rtVoiceRegistry) {
177 Q_ASSERT(voice.use_count() >= 2); // voice in both m_rtVoiceRegistry and m_voices
178
182 }
183
184 if (!finishedVoices.empty()) {
186 for (const SharedVoice &voice : finishedVoices) {
189 if (stopSent)
190 sendNotification = true;
191 }
192 });
193 }
194
195 // TODO: we should probably (soft)clip the output buffer
196
200}
201
203{
204 bool notifyApp = false;
205
206#if __cpp_lib_erase_if >= 202002L
207 using std::erase_if;
208#else
209 auto erase_if = [](auto &c, auto &&pred) {
210 auto old_size = c.size();
211 for (auto first = c.begin(), last = c.end(); first != last;) {
212 if (pred(*first))
213 first = c.erase(first);
214 else
215 ++first;
216 }
217 return old_size - c.size();
218 };
219#endif
222 bool voiceIsActive = voice->isActive();
223 if (!voiceIsActive)
225
226 return !voiceIsActive;
227 });
228 });
229
230 if (notifyApp)
232}
233
235{
237 for (RtCommand &cmd : commands) {
238 std::visit([&](auto cmd) {
240 }, std::move(cmd));
241 }
242 });
243}
244
246{
249 });
250}
251
253{
255 if (it == m_rtVoiceRegistry.end())
256 return;
257
260
262 std::move(voice),
263 });
264 if (emitNotify)
266}
267
269{
271 if (it == m_rtVoiceRegistry.end())
272 return;
273
274 cmd.callback(**it);
275
276 // send callback back to application for destruction
279 });
280 if (emitNotify)
282}
283
285{
287 if (it == m_rtVoiceRegistry.end())
288 return;
289
290 cmd.callback(**it);
291}
292
294{
296 {
297 auto lock = std::lock_guard{ m_mutex };
300 std::visit([&](auto notification) {
302 }, std::move(notification));
303 }
304 });
305
308 }
309
310 // emit voiceFinished outside of the lock
313}
314
316{
318 if (m_voices.empty())
319 m_sink.suspend();
321}
322
324{
325 // nop (just making sure to delete on the application thread);
326}
327
329{
330 // first write all pending commands from overflow buffer
332
333 bool written = m_appToRt.produceOne([&] {
334 return std::move(cmd);
335 });
336
337 if (written)
338 return;
339
341
345 });
346}
347
349{
350 // first write all pending commands from overflow buffer
352
353 bool written = m_rtToApp.produceOne([&] {
354 return std::move(cmd);
355 });
356
357 if (written)
358 return true;
359
361
362 return emitNotification;
363}
364
366{
367 while (!m_appToRtOverflowBuffer.empty()) {
369 bool written = m_appToRt.produceOne([&] {
371 });
372 if (!written)
373 return;
374
376 }
377}
378
380{
381 bool emitNotification = false;
382 while (!m_rtToAppOverflowBuffer.empty()) {
384
385 // first write all pending commands from overflow buffer
386 bool written = m_rtToApp.produceOne([&] {
388 });
389 if (!written)
390 break;
391
393 emitNotification = true;
394 }
395
396 return emitNotification;
397}
398
399} // namespace QtMultimediaPrivate
400
401QT_END_NAMESPACE
402
403#ifdef Q_CC_MINGW
404QT_WARNING_POP
405#endif
406
407#include "moc_qrtaudioengine_p.cpp"
static QtAudio::State sinkStateToEngineState(QtAudio::State state)