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/qtmultimediaglobal.h>
19
20#include <QtMultimedia/private/qaudiosystem_p.h>
21#include <QtMultimedia/private/qaudiosystem_platform_stream_support_p.h>
22
23QT_BEGIN_NAMESPACE
24
25namespace QtMultimediaPrivate {
26
27///////////////////////////////////////////////////////////////////////////////////////////////////
28
29#ifdef __cpp_concepts
30// clang-format off
31template <typename T>
35{
37 T,
39 const QAudioFormat&,
41 typename T::SinkType*,
42 float,
45 >;
46
47 { t.open() } -> std::same_as<bool>;
48
49 { t.start() } -> std::same_as<QIODevice *>;
50 { t.start(device) } -> std::same_as<bool>;
51 { t.start(std::move(callback)) } -> std::same_as<bool>;
52
53 { t.suspend() } -> std::same_as<void>;
54 { t.resume() } -> std::same_as<void>;
55 { t.stop(shutdownPolicy) } -> std::same_as<void>;
56
57 { t.setVolume(0.0f) } -> std::same_as<void>;
58 { t.bytesFree() } -> std::same_as<quint64>;
59
61};
62
63// clang-format on
64# define STREAM_TYPE_ARG QPlatformSinkStream StreamType
65#else
66# define STREAM_TYPE_ARG typename StreamType
67#endif
68
69template <STREAM_TYPE_ARG, typename DerivedType = void>
71{
72public:
73 QPlatformAudioSinkImplementation(QAudioDevice device, const QAudioFormat &format,
74 QObject *parent);
76
77 void start(QIODevice *device) override;
78 void start(AudioCallback &&) override;
79 QIODevice *start() override;
80
83
86
87 qsizetype bytesFree() const override;
88 void setBufferSize(qsizetype value) override;
89 qsizetype bufferSize() const override;
92 qint64 processedUSecs() const override;
93
94 void setVolume(float volume) override;
96 void setRole(AudioEndpointRole role) override;
97
98protected:
100 friend StreamType;
102
106
108
111};
112
113template <STREAM_TYPE_ARG, typename DerivedType>
115 QAudioDevice device, const QAudioFormat &format, QObject *parent)
117{
118}
119
120template <STREAM_TYPE_ARG, typename DerivedType>
125
126template <STREAM_TYPE_ARG, typename DerivedType>
127void QPlatformAudioSinkImplementation<StreamType, DerivedType>::start(QIODevice *device)
128{
129 if (!device) {
130 setError(QAudio::IOError);
131 return;
132 }
133
134 if (m_stream) {
135 qWarning("QAudioSink::start() called while already started");
136 return;
137 }
138
139 m_stream = std::make_shared<StreamType>(m_audioDevice, m_format, m_internalBufferSize,
140 static_cast<ConcreteSinkType *>(this), volume(),
141 m_hardwareBufferFrames, m_role);
142
143 if (!m_stream->open()) {
144 setError(QAudio::OpenError);
145 m_stream = {};
146 return;
147 }
148
149 m_stream->start(device);
150 updateStreamState(QAudio::ActiveState);
151}
152
153template <STREAM_TYPE_ARG, typename DerivedType>
154void QPlatformAudioSinkImplementation<StreamType, DerivedType>::start(AudioCallback &&audioCallback)
155{
156 using namespace QtMultimediaPrivate;
157 if (!validateAudioCallback(audioCallback, m_format)) {
158 setError(QAudio::OpenError);
159 return;
160 }
161
162 if (m_stream) {
163 qWarning("QAudioSink::start() called while already started");
164 return;
165 }
166
167 m_stream = std::make_shared<StreamType>(m_audioDevice, m_format, m_internalBufferSize,
168 static_cast<ConcreteSinkType *>(this), volume(),
169 m_hardwareBufferFrames, m_role);
170
171 if (!m_stream->open()) {
172 setError(QAudio::OpenError);
173 m_stream = {};
174 return;
175 }
176
177 bool started = m_stream->start(std::move(audioCallback));
178 if (!started) {
179 setError(QAudio::OpenError);
180 m_stream = {};
181 return;
182 }
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()) {
200 setError(QAudio::OpenError);
201 m_stream = {};
202 return nullptr;
203 }
204
205 QIODevice *device = m_stream->start();
206 QObject::connect(device, &QIODevice::readyRead, this, [this] {
207 updateStreamIdle(false);
208 });
209 updateStreamIdle(true);
210 updateStreamState(QAudio::ActiveState);
211 return device;
212}
213
214template <STREAM_TYPE_ARG, typename DerivedType>
215void QPlatformAudioSinkImplementation<StreamType, DerivedType>::stop()
216{
217 if (m_stream) {
218 m_stream->stop(ShutdownPolicy::DrainRingbuffer);
219 m_stream = {};
220 updateStreamState(QAudio::StoppedState);
221 }
222}
223
224template <STREAM_TYPE_ARG, typename DerivedType>
225void QPlatformAudioSinkImplementation<StreamType, DerivedType>::reset()
226{
227 if (m_stream) {
228 m_stream->stop(ShutdownPolicy::DiscardRingbuffer);
229 m_stream = {};
230 updateStreamState(QAudio::StoppedState);
231 }
232}
233
234template <STREAM_TYPE_ARG, typename DerivedType>
235void QPlatformAudioSinkImplementation<StreamType, DerivedType>::suspend()
236{
237 if (m_stream) {
238 m_stream->suspend();
239 updateStreamState(QAudio::SuspendedState);
240 }
241}
242
243template <STREAM_TYPE_ARG, typename DerivedType>
244void QPlatformAudioSinkImplementation<StreamType, DerivedType>::resume()
245{
246 if (m_stream) {
247 updateStreamState(QAudio::ActiveState);
248 m_stream->resume();
249 }
250}
251
252template <STREAM_TYPE_ARG, typename DerivedType>
253qsizetype QPlatformAudioSinkImplementation<StreamType, DerivedType>::bytesFree() const
254{
255 if (m_stream)
256 return m_stream->bytesFree();
257 return 0;
258}
259
260template <STREAM_TYPE_ARG, typename DerivedType>
261void QPlatformAudioSinkImplementation<StreamType, DerivedType>::setBufferSize(qsizetype value)
262{
263 m_internalBufferSize = value;
264}
265
266template <STREAM_TYPE_ARG, typename DerivedType>
268{
269 if (m_stream)
270 return m_stream->ringbufferSizeInBytes();
271
272 return QPlatformAudioIOStream::inferRingbufferBytes(m_internalBufferSize,
273 m_hardwareBufferFrames, m_format);
274}
275
276template <STREAM_TYPE_ARG, typename DerivedType>
277void QPlatformAudioSinkImplementation<StreamType, DerivedType>::setHardwareBufferFrames(int32_t arg)
278{
279 if (arg > 0)
280 m_hardwareBufferFrames = arg;
281 else
282 m_hardwareBufferFrames = std::nullopt;
283}
284
285template <STREAM_TYPE_ARG, typename DerivedType>
287{
288 return m_hardwareBufferFrames.value_or(-1);
289}
290
291template <STREAM_TYPE_ARG, typename DerivedType>
293{
294 if (m_stream)
295 return m_stream->processedDuration().count();
296
297 return 0;
298}
299
300template <STREAM_TYPE_ARG, typename DerivedType>
301void QPlatformAudioSinkImplementation<StreamType, DerivedType>::setVolume(float volume)
302{
303 QPlatformAudioEndpointBase::setVolume(volume);
304 if (m_stream)
305 m_stream->setVolume(volume);
306}
307
308template <STREAM_TYPE_ARG, typename DerivedType>
309bool QPlatformAudioSinkImplementation<StreamType, DerivedType>::hasCallbackAPI()
310{
311 return true;
312}
313
314template <STREAM_TYPE_ARG, typename DerivedType>
315void QPlatformAudioSinkImplementation<StreamType, DerivedType>::setRole(AudioEndpointRole role)
316{
317 m_role = role;
318}
319
320#undef STREAM_TYPE_ARG
321
322///////////////////////////////////////////////////////////////////////////////////////////////////
323
324#ifdef __cpp_concepts
325// clang-format off
326template <typename T>
329{
331 T,
333 const QAudioFormat&,
335 typename T::SourceType*,
336 float,
338 >;
339
340 { t.open() } -> std::same_as<bool>;
341
342 { t.start() } -> std::same_as<QIODevice *>;
343 { t.start(device) } -> std::same_as<bool>;
344
345 { t.suspend() } -> std::same_as<void>;
346 { t.resume() } -> std::same_as<void>;
347 { t.stop(shutdownPolicy) } -> std::same_as<void>;
348
349 { t.setVolume(0.0f) } -> std::same_as<void>;
350 { t.bytesReady() } -> std::same_as<qsizetype>;
351 { t.deviceIsRingbufferReader() } -> std::same_as<bool>;
352
354};
355
356// clang-format on
357# define STREAM_TYPE_ARG QPlatformSourceStream StreamType
358#else
359# define STREAM_TYPE_ARG typename StreamType
360#endif
361
362template <STREAM_TYPE_ARG, typename DerivedType = void>
402
403template <STREAM_TYPE_ARG, typename DerivedType>
405 QAudioDevice device, const QAudioFormat &format, QObject *parent)
407{
408}
409
410template <STREAM_TYPE_ARG, typename DerivedType>
415
416template <STREAM_TYPE_ARG, typename DerivedType>
417void QPlatformAudioSourceImplementation<StreamType, DerivedType>::start(QIODevice *device)
418{
419 if (!device) {
420 setError(QAudio::IOError);
421 return;
422 }
423
424 if (m_stream) {
425 qWarning("QAudioSource::start() called while already started");
426 return;
427 }
428
429 m_stream = std::make_shared<StreamType>(m_audioDevice, m_format, m_internalBufferSize,
430 static_cast<ConcreteSourceType *>(this), volume(),
431 m_hardwareBufferFrames);
432
433 if (!m_stream->open()) {
434 setError(QAudio::OpenError);
435 m_stream = {};
436 return;
437 }
438
439 m_stream->start(device);
440 updateStreamState(QAudio::ActiveState);
441}
442
443template <STREAM_TYPE_ARG, typename DerivedType>
445{
446 if (m_stream) {
447 qWarning("QAudioSource::start() called while already started");
448 return nullptr;
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()) {
456 setError(QAudio::OpenError);
457 m_stream = {};
458 return nullptr;
459 }
460
461 QIODevice *device = m_stream->start();
462 QObject::connect(device, &QIODevice::readyRead, this, [this] {
463 updateStreamIdle(false);
464 });
465 updateStreamIdle(true, EmitStateSignal::False);
466 updateStreamState(QAudio::ActiveState);
467 return device;
468}
469
470template <STREAM_TYPE_ARG, typename DerivedType>
471void QPlatformAudioSourceImplementation<StreamType, DerivedType>::stop()
472{
473 if (!m_stream)
474 return;
475
476 if (m_stream->deviceIsRingbufferReader())
477 // we own the qiodevice, so let's keep it alive to allow users to drain the ringbuffer
478 m_retiredStream = m_stream;
479
480 m_stream->stop(ShutdownPolicy::DrainRingbuffer);
481 m_stream = {};
482 updateStreamState(QAudio::StoppedState);
483}
484
485template <STREAM_TYPE_ARG, typename DerivedType>
486void QPlatformAudioSourceImplementation<StreamType, DerivedType>::reset()
487{
488 m_retiredStream = {};
489
490 if (!m_stream)
491 return;
492
493 m_stream->stop(ShutdownPolicy::DiscardRingbuffer);
494 m_stream = {};
495 updateStreamState(QAudio::StoppedState);
496}
497
498template <STREAM_TYPE_ARG, typename DerivedType>
499void QPlatformAudioSourceImplementation<StreamType, DerivedType>::suspend()
500{
501 if (m_stream) {
502 m_stream->suspend();
503 updateStreamState(QAudio::SuspendedState);
504 }
505}
506
507template <STREAM_TYPE_ARG, typename DerivedType>
508void QPlatformAudioSourceImplementation<StreamType, DerivedType>::resume()
509{
510 if (m_stream) {
511 updateStreamState(QAudio::ActiveState);
512 m_stream->resume();
513 }
514}
515
516template <STREAM_TYPE_ARG, typename DerivedType>
518{
519 return m_stream ? m_stream->bytesReady() : 0;
520}
521
522template <STREAM_TYPE_ARG, typename DerivedType>
523void QPlatformAudioSourceImplementation<StreamType, DerivedType>::setBufferSize(qsizetype value)
524{
525 m_internalBufferSize = value;
526}
527
528template <STREAM_TYPE_ARG, typename DerivedType>
530{
531 if (m_stream)
532 return m_stream->ringbufferSizeInBytes();
533
534 return QPlatformAudioIOStream::inferRingbufferBytes(m_internalBufferSize,
535 m_hardwareBufferFrames, m_format);
536}
537
538template <STREAM_TYPE_ARG, typename DerivedType>
540 int32_t arg)
541{
542 if (arg > 0)
543 m_hardwareBufferFrames = arg;
544 else
545 m_hardwareBufferFrames = std::nullopt;
546}
547
548template <STREAM_TYPE_ARG, typename DerivedType>
550{
551 return m_hardwareBufferFrames.value_or(-1);
552}
553
554template <STREAM_TYPE_ARG, typename DerivedType>
556{
557 return m_stream ? m_stream->processedDuration().count() : 0;
558}
559
560template <STREAM_TYPE_ARG, typename DerivedType>
561void QPlatformAudioSourceImplementation<StreamType, DerivedType>::setVolume(float volume)
562{
563 QPlatformAudioEndpointBase::setVolume(volume);
564 if (m_stream)
565 m_stream->setVolume(volume);
566}
567
568///////////////////////////////////////////////////////////////////////////////////////////////////
569
570template <STREAM_TYPE_ARG, typename DerivedType = void>
572 : public QPlatformAudioSourceImplementation<StreamType, DerivedType>
573{
574protected:
575 using BaseClass = QPlatformAudioSourceImplementation<StreamType, DerivedType>;
576 using BaseClass::BaseClass;
577 using BaseClass::start;
578
579 QT_WARNING_PUSH
580 QT_WARNING_DISABLE_CLANG("-Woverloaded-virtual")
583 bool hasCallbackAPI() override { return true; }
584};
585
586template <STREAM_TYPE_ARG, typename DerivedType>
587void QPlatformAudioSourceImplementationWithCallback<StreamType, DerivedType>::start(
588 QPlatformAudioSource::AudioCallback &&audioCallback)
589{
590 using namespace QtMultimediaPrivate;
591 if (!validateAudioCallback(audioCallback, BaseClass::m_format)) {
592 BaseClass::setError(QAudio::OpenError);
593 return;
594 }
595
596 BaseClass::m_stream = std::make_shared<StreamType>(
597 BaseClass::m_audioDevice, BaseClass::m_format, BaseClass::m_internalBufferSize,
598 static_cast<typename BaseClass::ConcreteSourceType *>(this), BaseClass::volume(),
599 BaseClass::m_hardwareBufferFrames);
600
601 if (!BaseClass::m_stream->open()) {
602 BaseClass::setError(QAudio::OpenError);
603 BaseClass::m_stream = {};
604 return;
605 }
606
607 bool started = BaseClass::m_stream->start(std::move(audioCallback));
608 if (!started) {
609 BaseClass::setError(QAudio::OpenError);
610 BaseClass::m_stream = {};
611 return;
612 }
613
614 BaseClass::updateStreamState(QAudio::ActiveState);
615}
616
617#undef STREAM_TYPE_ARG
618
619} // namespace QtMultimediaPrivate
620
621QT_END_NAMESPACE
622
623#endif // QAUDIO_PLATFORM_IMPLEMENTATION_SUPPORT_P_H
QPlatformAudioSinkImplementation(QAudioDevice device, const QAudioFormat &format, QObject *parent)
QPlatformAudioSourceImplementation(QAudioDevice device, const QAudioFormat &format, QObject *parent)