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 (m_stream) {
170 qWarning("QAudioSink::start() called while already started");
171 return;
172 }
173
174 m_stream = std::make_shared<StreamType>(m_audioDevice, m_format, m_internalBufferSize,
175 static_cast<ConcreteSinkType *>(this), volume(),
176 m_hardwareBufferFrames, m_role);
177
178 if (!m_stream->open())
180
181 bool started = m_stream->start(std::move(audioCallback));
182 if (!started)
184
185 updateStreamState(QAudio::ActiveState);
186}
187
188template <STREAM_TYPE_ARG, typename DerivedType>
190{
191 if (m_stream) {
192 qWarning("QAudioSink::start() called while already started");
193 return nullptr;
194 }
195
196 m_stream = std::make_shared<StreamType>(m_audioDevice, m_format, m_internalBufferSize,
197 static_cast<ConcreteSinkType *>(this), volume(),
198 m_hardwareBufferFrames, m_role);
199
200 if (!m_stream->open()) {
202 return nullptr;
203 }
204
205 QIODevice *device = m_stream->start();
206 if (!device) {
208 return nullptr;
209 }
210
211 QObject::connect(device, &QIODevice::readyRead, this, [this] {
212 updateStreamIdle(false);
213 });
214 updateStreamIdle(true);
215 updateStreamState(QAudio::ActiveState);
216 return device;
217}
218
219template <STREAM_TYPE_ARG, typename DerivedType>
220void QPlatformAudioSinkImplementation<StreamType, DerivedType>::stop()
221{
222 if (m_stream) {
223 m_stream->stop(ShutdownPolicy::DrainRingbuffer);
224 m_stream = {};
225 updateStreamState(QAudio::StoppedState);
226 }
227 setError(QtAudio::NoError);
228}
229
230template <STREAM_TYPE_ARG, typename DerivedType>
231void QPlatformAudioSinkImplementation<StreamType, DerivedType>::reset()
232{
233 if (m_stream) {
234 m_stream->stop(ShutdownPolicy::DiscardRingbuffer);
235 m_stream = {};
236 updateStreamState(QAudio::StoppedState);
237 }
238 setError(QtAudio::NoError);
239}
240
241template <STREAM_TYPE_ARG, typename DerivedType>
242void QPlatformAudioSinkImplementation<StreamType, DerivedType>::suspend()
243{
244 if (m_stream) {
245 if (state() == QAudio::SuspendedState)
246 return;
247
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 if (state() != QAudio::SuspendedState)
258 return;
259
260 updateStreamState(QAudio::ActiveState);
261 m_stream->resume();
262 }
263}
264
265template <STREAM_TYPE_ARG, typename DerivedType>
266qsizetype QPlatformAudioSinkImplementation<StreamType, DerivedType>::bytesFree() const
267{
268 if (m_stream)
269 return m_stream->bytesFree();
270 return 0;
271}
272
273template <STREAM_TYPE_ARG, typename DerivedType>
274void QPlatformAudioSinkImplementation<StreamType, DerivedType>::setBufferSize(qsizetype value)
275{
276 m_internalBufferSize = value;
277}
278
279template <STREAM_TYPE_ARG, typename DerivedType>
281{
282 if (m_stream)
283 return m_stream->ringbufferSizeInBytes();
284
285 return QPlatformAudioIOStream::inferRingbufferBytes(m_internalBufferSize,
286 m_hardwareBufferFrames, m_format);
287}
288
289template <STREAM_TYPE_ARG, typename DerivedType>
290void QPlatformAudioSinkImplementation<StreamType, DerivedType>::setHardwareBufferFrames(int32_t arg)
291{
292 if (arg > 0)
293 m_hardwareBufferFrames = arg;
294 else
295 m_hardwareBufferFrames = std::nullopt;
296}
297
298template <STREAM_TYPE_ARG, typename DerivedType>
300{
301 return m_hardwareBufferFrames.value_or(-1);
302}
303
304template <STREAM_TYPE_ARG, typename DerivedType>
306{
307 if (m_stream)
308 return m_stream->processedDuration().count();
309
310 return 0;
311}
312
313template <STREAM_TYPE_ARG, typename DerivedType>
314void QPlatformAudioSinkImplementation<StreamType, DerivedType>::setVolume(float volume)
315{
316 QPlatformAudioEndpointBase::setVolume(volume);
317 if (m_stream)
318 m_stream->setVolume(volume);
319}
320
321template <STREAM_TYPE_ARG, typename DerivedType>
322bool QPlatformAudioSinkImplementation<StreamType, DerivedType>::hasCallbackAPI()
323{
324 return true;
325}
326
327template <STREAM_TYPE_ARG, typename DerivedType>
328void QPlatformAudioSinkImplementation<StreamType, DerivedType>::setRole(AudioEndpointRole role)
329{
330 m_role = role;
331}
332
333#undef STREAM_TYPE_ARG
334
335///////////////////////////////////////////////////////////////////////////////////////////////////
336
337#ifdef __cpp_concepts
338// clang-format off
339template <typename T>
342{
344 T,
346 const QAudioFormat&,
348 typename T::SourceType*,
349 float,
351 >;
352
353 { t.open() } -> std::same_as<bool>;
354
355 { t.start() } -> std::same_as<QIODevice *>;
356 { t.start(device) } -> std::same_as<bool>;
357
358 { t.suspend() } -> std::same_as<void>;
359 { t.resume() } -> std::same_as<void>;
360 { t.stop(shutdownPolicy) } -> std::same_as<void>;
361
362 { t.setVolume(0.0f) } -> std::same_as<void>;
363 { t.bytesReady() } -> std::same_as<qsizetype>;
364 { t.deviceIsRingbufferReader() } -> std::same_as<bool>;
365
367};
368
369// clang-format on
370# define STREAM_TYPE_ARG QPlatformSourceStream StreamType
371#else
372# define STREAM_TYPE_ARG typename StreamType
373#endif
374
375template <STREAM_TYPE_ARG, typename DerivedType = void>
417
418template <STREAM_TYPE_ARG, typename DerivedType>
420 QAudioDevice device, const QAudioFormat &format, QObject *parent)
422{
423}
424
425template <STREAM_TYPE_ARG, typename DerivedType>
430
431template <STREAM_TYPE_ARG, typename DerivedType>
433{
434 m_stream->requestStop();
435 setError(QAudio::OpenError);
436 m_stream = {};
437}
438
439template <STREAM_TYPE_ARG, typename DerivedType>
440void QPlatformAudioSourceImplementation<StreamType, DerivedType>::start(QIODevice *device)
441{
442 if (!device) {
443 setError(QAudio::IOError);
444 return;
445 }
446
447 if (m_stream) {
448 qWarning("QAudioSource::start() called while already started");
449 return;
450 }
451
452 m_stream = std::make_shared<StreamType>(m_audioDevice, m_format, m_internalBufferSize,
453 static_cast<ConcreteSourceType *>(this), volume(),
454 m_hardwareBufferFrames);
455
456 if (!m_stream->open())
458
459 bool started = m_stream->start(device);
460 if (!started)
462
463 updateStreamState(QAudio::ActiveState);
464}
465
466template <STREAM_TYPE_ARG, typename DerivedType>
468{
469 if (m_stream) {
470 qWarning("QAudioSource::start() called while already started");
471 return nullptr;
472 }
473
474 m_stream = std::make_shared<StreamType>(m_audioDevice, m_format, m_internalBufferSize,
475 static_cast<ConcreteSourceType *>(this), volume(),
476 m_hardwareBufferFrames);
477
478 if (!m_stream->open()) {
480 return nullptr;
481 }
482
483 QIODevice *device = m_stream->start();
484 if (!device) {
486 return nullptr;
487 }
488
489 QObject::connect(device, &QIODevice::readyRead, this, [this] {
490 updateStreamIdle(false);
491 });
492 updateStreamIdle(true, EmitStateSignal::False);
493 updateStreamState(QAudio::ActiveState);
494 return device;
495}
496
497template <STREAM_TYPE_ARG, typename DerivedType>
498void QPlatformAudioSourceImplementation<StreamType, DerivedType>::stop()
499{
500 if (!m_stream)
501 return;
502
503 if (m_stream->deviceIsRingbufferReader())
504 // we own the qiodevice, so let's keep it alive to allow users to drain the ringbuffer
505 m_retiredStream = m_stream;
506
507 m_stream->stop(ShutdownPolicy::DrainRingbuffer);
508 m_stream = {};
509 setError(QtAudio::NoError);
510 updateStreamState(QAudio::StoppedState);
511}
512
513template <STREAM_TYPE_ARG, typename DerivedType>
514void QPlatformAudioSourceImplementation<StreamType, DerivedType>::reset()
515{
516 m_retiredStream = {};
517
518 if (!m_stream)
519 return;
520
521 m_stream->stop(ShutdownPolicy::DiscardRingbuffer);
522 m_stream = {};
523 setError(QtAudio::NoError);
524 updateStreamState(QAudio::StoppedState);
525}
526
527template <STREAM_TYPE_ARG, typename DerivedType>
528void QPlatformAudioSourceImplementation<StreamType, DerivedType>::suspend()
529{
530 if (m_stream) {
531 m_stream->suspend();
532 updateStreamState(QAudio::SuspendedState);
533 }
534}
535
536template <STREAM_TYPE_ARG, typename DerivedType>
537void QPlatformAudioSourceImplementation<StreamType, DerivedType>::resume()
538{
539 if (m_stream) {
540 updateStreamState(QAudio::ActiveState);
541 m_stream->resume();
542 }
543}
544
545template <STREAM_TYPE_ARG, typename DerivedType>
547{
548 return m_stream ? m_stream->bytesReady() : 0;
549}
550
551template <STREAM_TYPE_ARG, typename DerivedType>
552void QPlatformAudioSourceImplementation<StreamType, DerivedType>::setBufferSize(qsizetype value)
553{
554 m_internalBufferSize = value;
555}
556
557template <STREAM_TYPE_ARG, typename DerivedType>
559{
560 if (m_stream)
561 return m_stream->ringbufferSizeInBytes();
562
563 return QPlatformAudioIOStream::inferRingbufferBytes(m_internalBufferSize,
564 m_hardwareBufferFrames, m_format);
565}
566
567template <STREAM_TYPE_ARG, typename DerivedType>
569 int32_t arg)
570{
571 if (arg > 0)
572 m_hardwareBufferFrames = arg;
573 else
574 m_hardwareBufferFrames = std::nullopt;
575}
576
577template <STREAM_TYPE_ARG, typename DerivedType>
579{
580 return m_hardwareBufferFrames.value_or(-1);
581}
582
583template <STREAM_TYPE_ARG, typename DerivedType>
585{
586 return m_stream ? m_stream->processedDuration().count() : 0;
587}
588
589template <STREAM_TYPE_ARG, typename DerivedType>
590void QPlatformAudioSourceImplementation<StreamType, DerivedType>::setVolume(float volume)
591{
592 QPlatformAudioEndpointBase::setVolume(volume);
593 if (m_stream)
594 m_stream->setVolume(volume);
595}
596
597///////////////////////////////////////////////////////////////////////////////////////////////////
598
599template <STREAM_TYPE_ARG, typename DerivedType = void>
601 : public QPlatformAudioSourceImplementation<StreamType, DerivedType>
602{
603protected:
604 using BaseClass = QPlatformAudioSourceImplementation<StreamType, DerivedType>;
605 using BaseClass::BaseClass;
606 using BaseClass::start;
607
608 QT_WARNING_PUSH
609 QT_WARNING_DISABLE_CLANG("-Woverloaded-virtual")
612 bool hasCallbackAPI() override { return true; }
613};
614
615template <STREAM_TYPE_ARG, typename DerivedType>
616void QPlatformAudioSourceImplementationWithCallback<StreamType, DerivedType>::start(
617 QPlatformAudioSource::AudioCallback &&audioCallback)
618{
619 using namespace QtMultimediaPrivate;
620
621 BaseClass::m_stream = std::make_shared<StreamType>(
622 BaseClass::m_audioDevice, BaseClass::m_format, BaseClass::m_internalBufferSize,
623 static_cast<typename BaseClass::ConcreteSourceType *>(this), BaseClass::volume(),
624 BaseClass::m_hardwareBufferFrames);
625
626 if (!BaseClass::m_stream->open())
627 return BaseClass::handleStreamOpenError();
628
629 bool started = BaseClass::m_stream->start(std::move(audioCallback));
630 if (!started)
631 return BaseClass::handleStreamOpenError();
632
633 BaseClass::updateStreamState(QAudio::ActiveState);
634}
635
636#undef STREAM_TYPE_ARG
637
638} // namespace QtMultimediaPrivate
639
640QT_END_NAMESPACE
641
642#endif // QAUDIO_PLATFORM_IMPLEMENTATION_SUPPORT_P_H
QPlatformAudioSinkImplementation(QAudioDevice device, const QAudioFormat &format, QObject *parent)
QPlatformAudioSourceImplementation(QAudioDevice device, const QAudioFormat &format, QObject *parent)