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
102 friend StreamType;
104
108
110
113};
114
115template <STREAM_TYPE_ARG, typename DerivedType>
117 QAudioDevice device, const QAudioFormat &format, QObject *parent)
119{
120}
121
122template <STREAM_TYPE_ARG, typename DerivedType>
127
128template <STREAM_TYPE_ARG, typename DerivedType>
129void QPlatformAudioSinkImplementation<StreamType, DerivedType>::start(QIODevice *device)
130{
131 if (!device) {
132 setError(QAudio::IOError);
133 return;
134 }
135
136 if (m_stream) {
137 qWarning("QAudioSink::start() called while already started");
138 return;
139 }
140
141 m_stream = std::make_shared<StreamType>(m_audioDevice, m_format, m_internalBufferSize,
142 static_cast<ConcreteSinkType *>(this), volume(),
143 m_hardwareBufferFrames, m_role);
144
145 if (!m_stream->open())
147
148 bool started = m_stream->start(device);
149 if (!started)
151
152 updateStreamState(QAudio::ActiveState);
153}
154
155
156template <STREAM_TYPE_ARG, typename DerivedType>
158{
159 m_stream->requestStop();
160 setError(QAudio::OpenError);
161 m_stream = {};
162}
163
164
165template <STREAM_TYPE_ARG, typename DerivedType>
166void QPlatformAudioSinkImplementation<StreamType, DerivedType>::start(AudioCallback &&audioCallback)
167{
168 using namespace QtMultimediaPrivate;
169 if (!validateAudioCallback(audioCallback, m_format)) {
170 setError(QAudio::OpenError);
171 return;
172 }
173
174 if (m_stream) {
175 qWarning("QAudioSink::start() called while already started");
176 return;
177 }
178
179 m_stream = std::make_shared<StreamType>(m_audioDevice, m_format, m_internalBufferSize,
180 static_cast<ConcreteSinkType *>(this), volume(),
181 m_hardwareBufferFrames, m_role);
182
183 if (!m_stream->open())
185
186 bool started = m_stream->start(std::move(audioCallback));
187 if (!started)
189
190 updateStreamState(QAudio::ActiveState);
191}
192
193template <STREAM_TYPE_ARG, typename DerivedType>
195{
196 if (m_stream) {
197 qWarning("QAudioSink::start() called while already started");
198 return nullptr;
199 }
200
201 m_stream = std::make_shared<StreamType>(m_audioDevice, m_format, m_internalBufferSize,
202 static_cast<ConcreteSinkType *>(this), volume(),
203 m_hardwareBufferFrames, m_role);
204
205 if (!m_stream->open()) {
207 return nullptr;
208 }
209
210 QIODevice *device = m_stream->start();
211 if (!device) {
213 return nullptr;
214 }
215
216 QObject::connect(device, &QIODevice::readyRead, this, [this] {
217 updateStreamIdle(false);
218 });
219 updateStreamIdle(true);
220 updateStreamState(QAudio::ActiveState);
221 return device;
222}
223
224template <STREAM_TYPE_ARG, typename DerivedType>
225void QPlatformAudioSinkImplementation<StreamType, DerivedType>::stop()
226{
227 if (m_stream) {
228 m_stream->stop(ShutdownPolicy::DrainRingbuffer);
229 m_stream = {};
230 updateStreamState(QAudio::StoppedState);
231 }
232}
233
234template <STREAM_TYPE_ARG, typename DerivedType>
235void QPlatformAudioSinkImplementation<StreamType, DerivedType>::reset()
236{
237 if (m_stream) {
238 m_stream->stop(ShutdownPolicy::DiscardRingbuffer);
239 m_stream = {};
240 updateStreamState(QAudio::StoppedState);
241 }
242}
243
244template <STREAM_TYPE_ARG, typename DerivedType>
245void QPlatformAudioSinkImplementation<StreamType, DerivedType>::suspend()
246{
247 if (m_stream) {
248 m_stream->suspend();
249 updateStreamState(QAudio::SuspendedState);
250 }
251}
252
253template <STREAM_TYPE_ARG, typename DerivedType>
254void QPlatformAudioSinkImplementation<StreamType, DerivedType>::resume()
255{
256 if (m_stream) {
257 updateStreamState(QAudio::ActiveState);
258 m_stream->resume();
259 }
260}
261
262template <STREAM_TYPE_ARG, typename DerivedType>
263qsizetype QPlatformAudioSinkImplementation<StreamType, DerivedType>::bytesFree() const
264{
265 if (m_stream)
266 return m_stream->bytesFree();
267 return 0;
268}
269
270template <STREAM_TYPE_ARG, typename DerivedType>
271void QPlatformAudioSinkImplementation<StreamType, DerivedType>::setBufferSize(qsizetype value)
272{
273 m_internalBufferSize = value;
274}
275
276template <STREAM_TYPE_ARG, typename DerivedType>
278{
279 if (m_stream)
280 return m_stream->ringbufferSizeInBytes();
281
282 return QPlatformAudioIOStream::inferRingbufferBytes(m_internalBufferSize,
283 m_hardwareBufferFrames, m_format);
284}
285
286template <STREAM_TYPE_ARG, typename DerivedType>
287void QPlatformAudioSinkImplementation<StreamType, DerivedType>::setHardwareBufferFrames(int32_t arg)
288{
289 if (arg > 0)
290 m_hardwareBufferFrames = arg;
291 else
292 m_hardwareBufferFrames = std::nullopt;
293}
294
295template <STREAM_TYPE_ARG, typename DerivedType>
297{
298 return m_hardwareBufferFrames.value_or(-1);
299}
300
301template <STREAM_TYPE_ARG, typename DerivedType>
303{
304 if (m_stream)
305 return m_stream->processedDuration().count();
306
307 return 0;
308}
309
310template <STREAM_TYPE_ARG, typename DerivedType>
311void QPlatformAudioSinkImplementation<StreamType, DerivedType>::setVolume(float volume)
312{
313 QPlatformAudioEndpointBase::setVolume(volume);
314 if (m_stream)
315 m_stream->setVolume(volume);
316}
317
318template <STREAM_TYPE_ARG, typename DerivedType>
319bool QPlatformAudioSinkImplementation<StreamType, DerivedType>::hasCallbackAPI()
320{
321 return true;
322}
323
324template <STREAM_TYPE_ARG, typename DerivedType>
325void QPlatformAudioSinkImplementation<StreamType, DerivedType>::setRole(AudioEndpointRole role)
326{
327 m_role = role;
328}
329
330#undef STREAM_TYPE_ARG
331
332///////////////////////////////////////////////////////////////////////////////////////////////////
333
334#ifdef __cpp_concepts
335// clang-format off
336template <typename T>
339{
341 T,
343 const QAudioFormat&,
345 typename T::SourceType*,
346 float,
348 >;
349
350 { t.open() } -> std::same_as<bool>;
351
352 { t.start() } -> std::same_as<QIODevice *>;
353 { t.start(device) } -> std::same_as<bool>;
354
355 { t.suspend() } -> std::same_as<void>;
356 { t.resume() } -> std::same_as<void>;
357 { t.stop(shutdownPolicy) } -> std::same_as<void>;
358
359 { t.setVolume(0.0f) } -> std::same_as<void>;
360 { t.bytesReady() } -> std::same_as<qsizetype>;
361 { t.deviceIsRingbufferReader() } -> std::same_as<bool>;
362
364};
365
366// clang-format on
367# define STREAM_TYPE_ARG QPlatformSourceStream StreamType
368#else
369# define STREAM_TYPE_ARG typename StreamType
370#endif
371
372template <STREAM_TYPE_ARG, typename DerivedType = void>
414
415template <STREAM_TYPE_ARG, typename DerivedType>
417 QAudioDevice device, const QAudioFormat &format, QObject *parent)
419{
420}
421
422template <STREAM_TYPE_ARG, typename DerivedType>
427
428template <STREAM_TYPE_ARG, typename DerivedType>
430{
431 m_stream->requestStop();
432 setError(QAudio::OpenError);
433 m_stream = {};
434}
435
436template <STREAM_TYPE_ARG, typename DerivedType>
437void QPlatformAudioSourceImplementation<StreamType, DerivedType>::start(QIODevice *device)
438{
439 if (!device) {
440 setError(QAudio::IOError);
441 return;
442 }
443
444 if (m_stream) {
445 qWarning("QAudioSource::start() called while already started");
446 return;
447 }
448
449 m_stream = std::make_shared<StreamType>(m_audioDevice, m_format, m_internalBufferSize,
450 static_cast<ConcreteSourceType *>(this), volume(),
451 m_hardwareBufferFrames);
452
453 if (!m_stream->open())
455
456 bool started = m_stream->start(device);
457 if (!started)
459
460 updateStreamState(QAudio::ActiveState);
461}
462
463template <STREAM_TYPE_ARG, typename DerivedType>
465{
466 if (m_stream) {
467 qWarning("QAudioSource::start() called while already started");
468 return nullptr;
469 }
470
471 m_stream = std::make_shared<StreamType>(m_audioDevice, m_format, m_internalBufferSize,
472 static_cast<ConcreteSourceType *>(this), volume(),
473 m_hardwareBufferFrames);
474
475 if (!m_stream->open()) {
477 return nullptr;
478 }
479
480 QIODevice *device = m_stream->start();
481 if (!device) {
483 return nullptr;
484 }
485
486 QObject::connect(device, &QIODevice::readyRead, this, [this] {
487 updateStreamIdle(false);
488 });
489 updateStreamIdle(true, EmitStateSignal::False);
490 updateStreamState(QAudio::ActiveState);
491 return device;
492}
493
494template <STREAM_TYPE_ARG, typename DerivedType>
495void QPlatformAudioSourceImplementation<StreamType, DerivedType>::stop()
496{
497 if (!m_stream)
498 return;
499
500 if (m_stream->deviceIsRingbufferReader())
501 // we own the qiodevice, so let's keep it alive to allow users to drain the ringbuffer
502 m_retiredStream = m_stream;
503
504 m_stream->stop(ShutdownPolicy::DrainRingbuffer);
505 m_stream = {};
506 updateStreamState(QAudio::StoppedState);
507}
508
509template <STREAM_TYPE_ARG, typename DerivedType>
510void QPlatformAudioSourceImplementation<StreamType, DerivedType>::reset()
511{
512 m_retiredStream = {};
513
514 if (!m_stream)
515 return;
516
517 m_stream->stop(ShutdownPolicy::DiscardRingbuffer);
518 m_stream = {};
519 updateStreamState(QAudio::StoppedState);
520}
521
522template <STREAM_TYPE_ARG, typename DerivedType>
523void QPlatformAudioSourceImplementation<StreamType, DerivedType>::suspend()
524{
525 if (m_stream) {
526 m_stream->suspend();
527 updateStreamState(QAudio::SuspendedState);
528 }
529}
530
531template <STREAM_TYPE_ARG, typename DerivedType>
532void QPlatformAudioSourceImplementation<StreamType, DerivedType>::resume()
533{
534 if (m_stream) {
535 updateStreamState(QAudio::ActiveState);
536 m_stream->resume();
537 }
538}
539
540template <STREAM_TYPE_ARG, typename DerivedType>
542{
543 return m_stream ? m_stream->bytesReady() : 0;
544}
545
546template <STREAM_TYPE_ARG, typename DerivedType>
547void QPlatformAudioSourceImplementation<StreamType, DerivedType>::setBufferSize(qsizetype value)
548{
549 m_internalBufferSize = value;
550}
551
552template <STREAM_TYPE_ARG, typename DerivedType>
554{
555 if (m_stream)
556 return m_stream->ringbufferSizeInBytes();
557
558 return QPlatformAudioIOStream::inferRingbufferBytes(m_internalBufferSize,
559 m_hardwareBufferFrames, m_format);
560}
561
562template <STREAM_TYPE_ARG, typename DerivedType>
564 int32_t arg)
565{
566 if (arg > 0)
567 m_hardwareBufferFrames = arg;
568 else
569 m_hardwareBufferFrames = std::nullopt;
570}
571
572template <STREAM_TYPE_ARG, typename DerivedType>
574{
575 return m_hardwareBufferFrames.value_or(-1);
576}
577
578template <STREAM_TYPE_ARG, typename DerivedType>
580{
581 return m_stream ? m_stream->processedDuration().count() : 0;
582}
583
584template <STREAM_TYPE_ARG, typename DerivedType>
585void QPlatformAudioSourceImplementation<StreamType, DerivedType>::setVolume(float volume)
586{
587 QPlatformAudioEndpointBase::setVolume(volume);
588 if (m_stream)
589 m_stream->setVolume(volume);
590}
591
592///////////////////////////////////////////////////////////////////////////////////////////////////
593
594template <STREAM_TYPE_ARG, typename DerivedType = void>
596 : public QPlatformAudioSourceImplementation<StreamType, DerivedType>
597{
598protected:
599 using BaseClass = QPlatformAudioSourceImplementation<StreamType, DerivedType>;
600 using BaseClass::BaseClass;
601 using BaseClass::start;
602
603 QT_WARNING_PUSH
604 QT_WARNING_DISABLE_CLANG("-Woverloaded-virtual")
607 bool hasCallbackAPI() override { return true; }
608};
609
610template <STREAM_TYPE_ARG, typename DerivedType>
611void QPlatformAudioSourceImplementationWithCallback<StreamType, DerivedType>::start(
612 QPlatformAudioSource::AudioCallback &&audioCallback)
613{
614 using namespace QtMultimediaPrivate;
615 if (!validateAudioCallback(audioCallback, BaseClass::m_format)) {
616 BaseClass::setError(QAudio::OpenError);
617 return;
618 }
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)