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_nativePeriodFrames, 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_nativePeriodFrames, 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_nativePeriodFrames, 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_nativePeriodFrames, m_format);
286}
287
288template <STREAM_TYPE_ARG, typename DerivedType>
289void QPlatformAudioSinkImplementation<StreamType, DerivedType>::setNativePeriodFrames(
291{
293}
294
295template <STREAM_TYPE_ARG, typename DerivedType>
298{
299 return m_nativePeriodFrames;
300}
301
302template <STREAM_TYPE_ARG, typename DerivedType>
304{
305 if (m_stream)
306 return m_stream->processedDuration().count();
307
308 return 0;
309}
310
311template <STREAM_TYPE_ARG, typename DerivedType>
312void QPlatformAudioSinkImplementation<StreamType, DerivedType>::setVolume(float volume)
313{
314 QPlatformAudioEndpointBase::setVolume(volume);
315 if (m_stream)
316 m_stream->setVolume(volume);
317}
318
319template <STREAM_TYPE_ARG, typename DerivedType>
320bool QPlatformAudioSinkImplementation<StreamType, DerivedType>::hasCallbackAPI()
321{
322 return true;
323}
324
325template <STREAM_TYPE_ARG, typename DerivedType>
326void QPlatformAudioSinkImplementation<StreamType, DerivedType>::setRole(AudioEndpointRole role)
327{
328 m_role = role;
329}
330
331#undef STREAM_TYPE_ARG
332
333///////////////////////////////////////////////////////////////////////////////////////////////////
334
335#ifdef __cpp_concepts
336// clang-format off
337template <typename T>
340{
342 T,
344 const QAudioFormat&,
346 typename T::SourceType*,
347 float,
349 >;
350
351 { t.open() } -> std::same_as<bool>;
352
353 { t.start() } -> std::same_as<QIODevice *>;
354 { t.start(device) } -> std::same_as<bool>;
355
356 { t.suspend() } -> std::same_as<void>;
357 { t.resume() } -> std::same_as<void>;
358 { t.stop(shutdownPolicy) } -> std::same_as<void>;
359
360 { t.setVolume(0.0f) } -> std::same_as<void>;
361 { t.bytesReady() } -> std::same_as<qsizetype>;
362 { t.deviceIsRingbufferReader() } -> std::same_as<bool>;
363
365};
366
367// clang-format on
368# define STREAM_TYPE_ARG QPlatformSourceStream StreamType
369#else
370# define STREAM_TYPE_ARG typename StreamType
371#endif
372
373template <STREAM_TYPE_ARG, typename DerivedType = void>
415
416template <STREAM_TYPE_ARG, typename DerivedType>
418 QAudioDevice device, const QAudioFormat &format, QObject *parent)
420{
421}
422
423template <STREAM_TYPE_ARG, typename DerivedType>
428
429template <STREAM_TYPE_ARG, typename DerivedType>
431{
432 m_stream->requestStop();
433 setError(QAudio::OpenError);
434 m_stream = {};
435}
436
437template <STREAM_TYPE_ARG, typename DerivedType>
438void QPlatformAudioSourceImplementation<StreamType, DerivedType>::start(QIODevice *device)
439{
440 if (!device) {
441 setError(QAudio::IOError);
442 return;
443 }
444
445 if (m_stream) {
446 qWarning("QAudioSource::start() called while already started");
447 return;
448 }
449
450 m_stream = std::make_shared<StreamType>(m_audioDevice, m_format, m_internalBufferSize,
451 static_cast<ConcreteSourceType *>(this), volume(),
452 m_nativePeriodFrames);
453
454 if (!m_stream->open())
456
457 bool started = m_stream->start(device);
458 if (!started)
460
461 updateStreamState(QAudio::ActiveState);
462}
463
464template <STREAM_TYPE_ARG, typename DerivedType>
466{
467 if (m_stream) {
468 qWarning("QAudioSource::start() called while already started");
469 return nullptr;
470 }
471
472 m_stream = std::make_shared<StreamType>(m_audioDevice, m_format, m_internalBufferSize,
473 static_cast<ConcreteSourceType *>(this), volume(),
474 m_nativePeriodFrames);
475
476 if (!m_stream->open()) {
478 return nullptr;
479 }
480
481 QIODevice *device = m_stream->start();
482 if (!device) {
484 return nullptr;
485 }
486
487 QObject::connect(device, &QIODevice::readyRead, this, [this] {
488 updateStreamIdle(false);
489 });
490 updateStreamIdle(true, EmitStateSignal::False);
491 updateStreamState(QAudio::ActiveState);
492 return device;
493}
494
495template <STREAM_TYPE_ARG, typename DerivedType>
496void QPlatformAudioSourceImplementation<StreamType, DerivedType>::stop()
497{
498 if (!m_stream)
499 return;
500
501 if (m_stream->deviceIsRingbufferReader())
502 // we own the qiodevice, so let's keep it alive to allow users to drain the ringbuffer
503 m_retiredStream = m_stream;
504
505 m_stream->stop(ShutdownPolicy::DrainRingbuffer);
506 m_stream = {};
507 setError(QtAudio::NoError);
508 updateStreamState(QAudio::StoppedState);
509}
510
511template <STREAM_TYPE_ARG, typename DerivedType>
512void QPlatformAudioSourceImplementation<StreamType, DerivedType>::reset()
513{
514 m_retiredStream = {};
515
516 if (!m_stream)
517 return;
518
519 m_stream->stop(ShutdownPolicy::DiscardRingbuffer);
520 m_stream = {};
521 setError(QtAudio::NoError);
522 updateStreamState(QAudio::StoppedState);
523}
524
525template <STREAM_TYPE_ARG, typename DerivedType>
526void QPlatformAudioSourceImplementation<StreamType, DerivedType>::suspend()
527{
528 if (m_stream) {
529 m_stream->suspend();
530 updateStreamState(QAudio::SuspendedState);
531 }
532}
533
534template <STREAM_TYPE_ARG, typename DerivedType>
535void QPlatformAudioSourceImplementation<StreamType, DerivedType>::resume()
536{
537 if (m_stream) {
538 updateStreamState(QAudio::ActiveState);
539 m_stream->resume();
540 }
541}
542
543template <STREAM_TYPE_ARG, typename DerivedType>
545{
546 return m_stream ? m_stream->bytesReady() : 0;
547}
548
549template <STREAM_TYPE_ARG, typename DerivedType>
550void QPlatformAudioSourceImplementation<StreamType, DerivedType>::setBufferSize(qsizetype value)
551{
552 m_internalBufferSize = value;
553}
554
555template <STREAM_TYPE_ARG, typename DerivedType>
557{
558 if (m_stream)
559 return m_stream->ringbufferSizeInBytes();
560
561 return QPlatformAudioIOStream::inferRingbufferBytes(m_internalBufferSize,
562 m_nativePeriodFrames, m_format);
563}
564
565template <STREAM_TYPE_ARG, typename DerivedType>
566void QPlatformAudioSourceImplementation<StreamType, DerivedType>::setNativePeriodFrames(
568{
570}
571
572template <STREAM_TYPE_ARG, typename DerivedType>
575{
576 return m_nativePeriodFrames;
577}
578
579template <STREAM_TYPE_ARG, typename DerivedType>
581{
582 return m_stream ? m_stream->processedDuration().count() : 0;
583}
584
585template <STREAM_TYPE_ARG, typename DerivedType>
586void QPlatformAudioSourceImplementation<StreamType, DerivedType>::setVolume(float volume)
587{
588 QPlatformAudioEndpointBase::setVolume(volume);
589 if (m_stream)
590 m_stream->setVolume(volume);
591}
592
593///////////////////////////////////////////////////////////////////////////////////////////////////
594
595template <STREAM_TYPE_ARG, typename DerivedType = void>
597 : public QPlatformAudioSourceImplementation<StreamType, DerivedType>
598{
599protected:
600 using BaseClass = QPlatformAudioSourceImplementation<StreamType, DerivedType>;
601 using BaseClass::BaseClass;
602 using BaseClass::start;
603
604 QT_WARNING_PUSH
605 QT_WARNING_DISABLE_CLANG("-Woverloaded-virtual")
608 bool hasCallbackAPI() override { return true; }
609};
610
611template <STREAM_TYPE_ARG, typename DerivedType>
612void QPlatformAudioSourceImplementationWithCallback<StreamType, DerivedType>::start(
613 QPlatformAudioSource::AudioCallback &&audioCallback)
614{
615 using namespace QtMultimediaPrivate;
616
617 BaseClass::m_stream = std::make_shared<StreamType>(
618 BaseClass::m_audioDevice, BaseClass::m_format, BaseClass::m_internalBufferSize,
619 static_cast<typename BaseClass::ConcreteSourceType *>(this), BaseClass::volume(),
620 BaseClass::m_nativePeriodFrames);
621
622 if (!BaseClass::m_stream->open())
623 return BaseClass::handleStreamOpenError();
624
625 bool started = BaseClass::m_stream->start(std::move(audioCallback));
626 if (!started)
627 return BaseClass::handleStreamOpenError();
628
629 BaseClass::updateStreamState(QAudio::ActiveState);
630}
631
632#undef STREAM_TYPE_ARG
633
634} // namespace QtMultimediaPrivate
635
636QT_END_NAMESPACE
637
638#endif // QAUDIO_PLATFORM_IMPLEMENTATION_SUPPORT_P_H
void setNativePeriodFrames(std::optional< NativePeriodFrames >) override
QPlatformAudioSinkImplementation(QAudioDevice device, const QAudioFormat &format, QObject *parent)
QPlatformAudioSourceImplementation(QAudioDevice device, const QAudioFormat &format, QObject *parent)
void setNativePeriodFrames(std::optional< NativePeriodFrames >) override