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
qaudio_platform_implementation_support_p.h
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#ifndef QAUDIO_PLATFORM_IMPLEMENTATION_SUPPORT_P_H
5#define QAUDIO_PLATFORM_IMPLEMENTATION_SUPPORT_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QtMultimedia/private/qaudiosystem_p.h>
19#include <QtMultimedia/private/qaudiosystem_platform_stream_support_p.h>
20#include <QtMultimedia/qtmultimediaglobal.h>
21
22QT_BEGIN_NAMESPACE
23
24namespace QtMultimediaPrivate {
25
26///////////////////////////////////////////////////////////////////////////////////////////////////
27
28#ifdef __cpp_concepts
29// clang-format off
30template <typename T>
34{
36 T,
38 const QAudioFormat&,
40 typename T::SinkType*,
41 float,
44 >;
45
46 { t.open() } -> std::same_as<bool>;
47
48 { t.start() } -> std::same_as<QIODevice *>;
49 { t.start(device) } -> std::same_as<bool>;
50 { t.start(std::move(callback)) } -> std::same_as<bool>;
51
52 { t.suspend() } -> std::same_as<void>;
53 { t.resume() } -> std::same_as<void>;
54 { t.stop(shutdownPolicy) } -> std::same_as<void>;
55
56 { t.setVolume(0.0f) } -> std::same_as<void>;
57 { t.bytesFree() } -> std::same_as<quint64>;
58
60};
61
62// clang-format on
63# define STREAM_TYPE_ARG QPlatformSinkStream StreamType
64#else
65# define STREAM_TYPE_ARG typename StreamType
66#endif
67
68template <STREAM_TYPE_ARG, typename DerivedType = void>
70{
71public:
72 QPlatformAudioSinkImplementation(QAudioDevice device, const QAudioFormat &format,
73 QObject *parent);
75
76 void start(QIODevice *device) override;
77 void start(AudioCallback &&) override;
78 QIODevice *start() override;
79
82
85
86 qsizetype bytesFree() const override;
87 void setBufferSize(qsizetype value) override;
88 qsizetype bufferSize() const override;
91 qint64 processedUSecs() const override;
92
93 void setVolume(float volume) override;
95 void setRole(AudioEndpointRole role) override;
96
97protected:
99
101 friend StreamType;
103
107
109
112};
113
114template <STREAM_TYPE_ARG, typename DerivedType>
116 QAudioDevice device, const QAudioFormat &format, QObject *parent)
118{
119}
120
121template <STREAM_TYPE_ARG, typename DerivedType>
126
127template <STREAM_TYPE_ARG, typename DerivedType>
128void QPlatformAudioSinkImplementation<StreamType, DerivedType>::start(QIODevice *device)
129{
130 if (!device) {
131 setError(QAudio::IOError);
132 return;
133 }
134
135 if (m_stream) {
136 qWarning("QAudioSink::start() called while already started");
137 return;
138 }
139
140 m_stream = std::make_shared<StreamType>(m_audioDevice, m_format, m_internalBufferSize,
141 static_cast<ConcreteSinkType *>(this), volume(),
142 m_hardwareBufferFrames, m_role);
143
144 if (!m_stream->open())
146
147 bool started = m_stream->start(device);
148 if (!started)
150
151 updateStreamState(QAudio::ActiveState);
152}
153
154
155template <STREAM_TYPE_ARG, typename DerivedType>
157{
158 m_stream->requestStop();
159 setError(QAudio::OpenError);
160 m_stream = {};
161}
162
163
164template <STREAM_TYPE_ARG, typename DerivedType>
165void QPlatformAudioSinkImplementation<StreamType, DerivedType>::start(AudioCallback &&audioCallback)
166{
167 using namespace QtMultimediaPrivate;
168 if (m_stream) {
169 qWarning("QAudioSink::start() called while already started");
170 return;
171 }
172
173 m_stream = std::make_shared<StreamType>(m_audioDevice, m_format, m_internalBufferSize,
174 static_cast<ConcreteSinkType *>(this), volume(),
175 m_hardwareBufferFrames, m_role);
176
177 if (!m_stream->open())
179
180 bool started = m_stream->start(std::move(audioCallback));
181 if (!started)
183
184 updateStreamState(QAudio::ActiveState);
185}
186
187template <STREAM_TYPE_ARG, typename DerivedType>
189{
190 if (m_stream) {
191 qWarning("QAudioSink::start() called while already started");
192 return nullptr;
193 }
194
195 m_stream = std::make_shared<StreamType>(m_audioDevice, m_format, m_internalBufferSize,
196 static_cast<ConcreteSinkType *>(this), volume(),
197 m_hardwareBufferFrames, m_role);
198
199 if (!m_stream->open()) {
201 return nullptr;
202 }
203
204 QIODevice *device = m_stream->start();
205 if (!device) {
207 return nullptr;
208 }
209
210 QObject::connect(device, &QIODevice::readyRead, this, [this] {
211 updateStreamIdle(false);
212 });
213 updateStreamIdle(true);
214 updateStreamState(QAudio::ActiveState);
215 return device;
216}
217
218template <STREAM_TYPE_ARG, typename DerivedType>
219void QPlatformAudioSinkImplementation<StreamType, DerivedType>::stop()
220{
221 if (m_stream) {
222 m_stream->stop(ShutdownPolicy::DrainRingbuffer);
223 m_stream = {};
224 updateStreamState(QAudio::StoppedState);
225 }
226 setError(QtAudio::NoError);
227}
228
229template <STREAM_TYPE_ARG, typename DerivedType>
230void QPlatformAudioSinkImplementation<StreamType, DerivedType>::reset()
231{
232 if (m_stream) {
233 m_stream->stop(ShutdownPolicy::DiscardRingbuffer);
234 m_stream = {};
235 updateStreamState(QAudio::StoppedState);
236 }
237 setError(QtAudio::NoError);
238}
239
240template <STREAM_TYPE_ARG, typename DerivedType>
241void QPlatformAudioSinkImplementation<StreamType, DerivedType>::suspend()
242{
243 if (m_stream) {
244 if (state() == QAudio::SuspendedState)
245 return;
246
247 m_stream->suspend();
248 updateStreamState(QAudio::SuspendedState);
249 }
250}
251
252template <STREAM_TYPE_ARG, typename DerivedType>
253void QPlatformAudioSinkImplementation<StreamType, DerivedType>::resume()
254{
255 if (m_stream) {
256 if (state() != QAudio::SuspendedState)
257 return;
258
259 updateStreamState(QAudio::ActiveState);
260 m_stream->resume();
261 }
262}
263
264template <STREAM_TYPE_ARG, typename DerivedType>
265qsizetype QPlatformAudioSinkImplementation<StreamType, DerivedType>::bytesFree() const
266{
267 if (m_stream)
268 return m_stream->bytesFree();
269 return 0;
270}
271
272template <STREAM_TYPE_ARG, typename DerivedType>
273void QPlatformAudioSinkImplementation<StreamType, DerivedType>::setBufferSize(qsizetype value)
274{
275 m_internalBufferSize = value;
276}
277
278template <STREAM_TYPE_ARG, typename DerivedType>
280{
281 if (m_stream)
282 return m_stream->ringbufferSizeInBytes();
283
284 return QPlatformAudioIOStream::inferRingbufferBytes(m_internalBufferSize,
285 m_hardwareBufferFrames, m_format);
286}
287
288template <STREAM_TYPE_ARG, typename DerivedType>
289void QPlatformAudioSinkImplementation<StreamType, DerivedType>::setHardwareBufferFrames(int32_t arg)
290{
291 if (arg > 0)
292 m_hardwareBufferFrames = arg;
293 else
294 m_hardwareBufferFrames = std::nullopt;
295}
296
297template <STREAM_TYPE_ARG, typename DerivedType>
299{
300 return m_hardwareBufferFrames.value_or(-1);
301}
302
303template <STREAM_TYPE_ARG, typename DerivedType>
305{
306 if (m_stream)
307 return m_stream->processedDuration().count();
308
309 return 0;
310}
311
312template <STREAM_TYPE_ARG, typename DerivedType>
313void QPlatformAudioSinkImplementation<StreamType, DerivedType>::setVolume(float volume)
314{
315 QPlatformAudioEndpointBase::setVolume(volume);
316 if (m_stream)
317 m_stream->setVolume(volume);
318}
319
320template <STREAM_TYPE_ARG, typename DerivedType>
321bool QPlatformAudioSinkImplementation<StreamType, DerivedType>::hasCallbackAPI()
322{
323 return true;
324}
325
326template <STREAM_TYPE_ARG, typename DerivedType>
327void QPlatformAudioSinkImplementation<StreamType, DerivedType>::setRole(AudioEndpointRole role)
328{
329 m_role = role;
330}
331
332#undef STREAM_TYPE_ARG
333
334///////////////////////////////////////////////////////////////////////////////////////////////////
335
336#ifdef __cpp_concepts
337// clang-format off
338template <typename T>
341{
343 T,
345 const QAudioFormat&,
347 typename T::SourceType*,
348 float,
350 >;
351
352 { t.open() } -> std::same_as<bool>;
353
354 { t.start() } -> std::same_as<QIODevice *>;
355 { t.start(device) } -> std::same_as<bool>;
356
357 { t.suspend() } -> std::same_as<void>;
358 { t.resume() } -> std::same_as<void>;
359 { t.stop(shutdownPolicy) } -> std::same_as<void>;
360
361 { t.setVolume(0.0f) } -> std::same_as<void>;
362 { t.bytesReady() } -> std::same_as<qsizetype>;
363 { t.deviceIsRingbufferReader() } -> std::same_as<bool>;
364
366};
367
368// clang-format on
369# define STREAM_TYPE_ARG QPlatformSourceStream StreamType
370#else
371# define STREAM_TYPE_ARG typename StreamType
372#endif
373
374template <STREAM_TYPE_ARG, typename DerivedType = void>
416
417template <STREAM_TYPE_ARG, typename DerivedType>
419 QAudioDevice device, const QAudioFormat &format, QObject *parent)
421{
422}
423
424template <STREAM_TYPE_ARG, typename DerivedType>
429
430template <STREAM_TYPE_ARG, typename DerivedType>
432{
433 m_stream->requestStop();
434 setError(QAudio::OpenError);
435 m_stream = {};
436}
437
438template <STREAM_TYPE_ARG, typename DerivedType>
439void QPlatformAudioSourceImplementation<StreamType, DerivedType>::start(QIODevice *device)
440{
441 if (!device) {
442 setError(QAudio::IOError);
443 return;
444 }
445
446 if (m_stream) {
447 qWarning("QAudioSource::start() called while already started");
448 return;
449 }
450
451 m_stream = std::make_shared<StreamType>(m_audioDevice, m_format, m_internalBufferSize,
452 static_cast<ConcreteSourceType *>(this), volume(),
453 m_hardwareBufferFrames);
454
455 if (!m_stream->open())
457
458 bool started = m_stream->start(device);
459 if (!started)
461
462 updateStreamState(QAudio::ActiveState);
463}
464
465template <STREAM_TYPE_ARG, typename DerivedType>
467{
468 if (m_stream) {
469 qWarning("QAudioSource::start() called while already started");
470 return nullptr;
471 }
472
473 m_stream = std::make_shared<StreamType>(m_audioDevice, m_format, m_internalBufferSize,
474 static_cast<ConcreteSourceType *>(this), volume(),
475 m_hardwareBufferFrames);
476
477 if (!m_stream->open()) {
479 return nullptr;
480 }
481
482 QIODevice *device = m_stream->start();
483 if (!device) {
485 return nullptr;
486 }
487
488 QObject::connect(device, &QIODevice::readyRead, this, [this] {
489 updateStreamIdle(false);
490 });
491 updateStreamIdle(true, EmitStateSignal::False);
492 updateStreamState(QAudio::ActiveState);
493 return device;
494}
495
496template <STREAM_TYPE_ARG, typename DerivedType>
497void QPlatformAudioSourceImplementation<StreamType, DerivedType>::stop()
498{
499 if (!m_stream)
500 return;
501
502 if (m_stream->deviceIsRingbufferReader())
503 // we own the qiodevice, so let's keep it alive to allow users to drain the ringbuffer
504 m_retiredStream = m_stream;
505
506 m_stream->stop(ShutdownPolicy::DrainRingbuffer);
507 m_stream = {};
508 setError(QtAudio::NoError);
509 updateStreamState(QAudio::StoppedState);
510}
511
512template <STREAM_TYPE_ARG, typename DerivedType>
513void QPlatformAudioSourceImplementation<StreamType, DerivedType>::reset()
514{
515 m_retiredStream = {};
516
517 if (!m_stream)
518 return;
519
520 m_stream->stop(ShutdownPolicy::DiscardRingbuffer);
521 m_stream = {};
522 setError(QtAudio::NoError);
523 updateStreamState(QAudio::StoppedState);
524}
525
526template <STREAM_TYPE_ARG, typename DerivedType>
527void QPlatformAudioSourceImplementation<StreamType, DerivedType>::suspend()
528{
529 if (m_stream) {
530 m_stream->suspend();
531 updateStreamState(QAudio::SuspendedState);
532 }
533}
534
535template <STREAM_TYPE_ARG, typename DerivedType>
536void QPlatformAudioSourceImplementation<StreamType, DerivedType>::resume()
537{
538 if (m_stream) {
539 updateStreamState(QAudio::ActiveState);
540 m_stream->resume();
541 }
542}
543
544template <STREAM_TYPE_ARG, typename DerivedType>
546{
547 return m_stream ? m_stream->bytesReady() : 0;
548}
549
550template <STREAM_TYPE_ARG, typename DerivedType>
551void QPlatformAudioSourceImplementation<StreamType, DerivedType>::setBufferSize(qsizetype value)
552{
553 m_internalBufferSize = value;
554}
555
556template <STREAM_TYPE_ARG, typename DerivedType>
558{
559 if (m_stream)
560 return m_stream->ringbufferSizeInBytes();
561
562 return QPlatformAudioIOStream::inferRingbufferBytes(m_internalBufferSize,
563 m_hardwareBufferFrames, m_format);
564}
565
566template <STREAM_TYPE_ARG, typename DerivedType>
568 int32_t arg)
569{
570 if (arg > 0)
571 m_hardwareBufferFrames = arg;
572 else
573 m_hardwareBufferFrames = std::nullopt;
574}
575
576template <STREAM_TYPE_ARG, typename DerivedType>
578{
579 return m_hardwareBufferFrames.value_or(-1);
580}
581
582template <STREAM_TYPE_ARG, typename DerivedType>
584{
585 return m_stream ? m_stream->processedDuration().count() : 0;
586}
587
588template <STREAM_TYPE_ARG, typename DerivedType>
589void QPlatformAudioSourceImplementation<StreamType, DerivedType>::setVolume(float volume)
590{
591 QPlatformAudioEndpointBase::setVolume(volume);
592 if (m_stream)
593 m_stream->setVolume(volume);
594}
595
596///////////////////////////////////////////////////////////////////////////////////////////////////
597
598template <STREAM_TYPE_ARG, typename DerivedType = void>
600 : public QPlatformAudioSourceImplementation<StreamType, DerivedType>
601{
602protected:
603 using BaseClass = QPlatformAudioSourceImplementation<StreamType, DerivedType>;
604 using BaseClass::BaseClass;
605 using BaseClass::start;
606
607 QT_WARNING_PUSH
608 QT_WARNING_DISABLE_CLANG("-Woverloaded-virtual")
611 bool hasCallbackAPI() override { return true; }
612};
613
614template <STREAM_TYPE_ARG, typename DerivedType>
615void QPlatformAudioSourceImplementationWithCallback<StreamType, DerivedType>::start(
616 QPlatformAudioSource::AudioCallback &&audioCallback)
617{
618 using namespace QtMultimediaPrivate;
619
620 BaseClass::m_stream = std::make_shared<StreamType>(
621 BaseClass::m_audioDevice, BaseClass::m_format, BaseClass::m_internalBufferSize,
622 static_cast<typename BaseClass::ConcreteSourceType *>(this), BaseClass::volume(),
623 BaseClass::m_hardwareBufferFrames);
624
625 if (!BaseClass::m_stream->open())
626 return BaseClass::handleStreamOpenError();
627
628 bool started = BaseClass::m_stream->start(std::move(audioCallback));
629 if (!started)
630 return BaseClass::handleStreamOpenError();
631
632 BaseClass::updateStreamState(QAudio::ActiveState);
633}
634
635#undef STREAM_TYPE_ARG
636
637} // namespace QtMultimediaPrivate
638
639QT_END_NAMESPACE
640
641#endif // QAUDIO_PLATFORM_IMPLEMENTATION_SUPPORT_P_H
QPlatformAudioSinkImplementation(QAudioDevice device, const QAudioFormat &format, QObject *parent)
QPlatformAudioSourceImplementation(QAudioDevice device, const QAudioFormat &format, QObject *parent)