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
qaudiosink.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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#include "qaudiosink.h"
5
6#include <QtMultimedia/private/qaudio_alignment_support_p.h>
7#include <QtMultimedia/private/qaudiosystem_p.h>
8#include <QtMultimedia/private/qaudiohelpers_p.h>
9#include <QtMultimedia/private/qaudiosystem_p.h>
10#include <QtMultimedia/private/qplatformaudiodevices_p.h>
11#include <QtMultimedia/private/qplatformmediaintegration_p.h>
12#include <QtMultimedia/qaudio.h>
13#include <QtMultimedia/qaudiodevice.h>
14
16
17/*!
18 \class QAudioSink
19 \brief The QAudioSink class provides an interface for sending audio data to
20 an audio output device.
21
22 \inmodule QtMultimedia
23 \ingroup multimedia
24 \ingroup multimedia_audio
25
26 You can construct an audio output with the system's
27 default audio output device. It is also possible to
28 create QAudioSink with a specific QAudioDevice. When
29 you create the audio output, you should also send in
30 the QAudioFormat to be used for the playback (see
31 the QAudioFormat class description for details).
32
33 QAudioSink can be used in two different modes:
34 \list
35 \li Using a QIODevice from an application thread
36 \li Using a callback-based interface from the audio thread
37 \endlist
38
39 \section1 QIODevice interface
40
41 Starting to play an audio stream is simply a matter of calling
42 start() with a QIODevice. QAudioSink will then fetch the data it
43 needs from the io device. So playing back an audio file is as
44 simple as:
45
46 \snippet multimedia-snippets/audio.cpp Audio output class members
47
48 \snippet multimedia-snippets/audio.cpp Audio output setup
49
50 The file will start playing assuming that the audio system and
51 output device support it. If you run out of luck, check what's
52 up with the error() function.
53
54 After the file has finished playing, we need to stop the device:
55
56 \snippet multimedia-snippets/audio.cpp Audio output stop
57
58 At any given time, the QAudioSink will be in one of four states:
59 active, suspended, stopped, or idle. These states are described
60 by the QtAudio::State enum.
61
62 \section2 Threading model and buffering
63
64 The QIODevice interface is designed to be used from the application thread.
65 A wait-free ringbuffer is used to communicate to the audio thread. The size
66 of this ringbuffer can be configured with setBufferSize() and defaults to
67 250ms. The state of this buffer can be queried with bytesFree(). If the
68 ringbuffer runs out of data, the audio thread will send silence to the audio
69 device and the state will change to QtAudio::IdleState and resume to QtAudio::ActiveState
70 when more data is available from the QIODevice.
71
72 \section1 Callback interface
73
74 The preferred way to achieve low audio latency is to use the callback-based interface.
75 It allows you to write audio data directly to the audio device without having to go through
76 a QIODevice. This is done by calling start() with a callback function that will be called
77 from the audio thread. This callback function will be called with a QSpan<SampleType> whenever
78 the audio backend requires data.
79
80 \snippet multimedia-snippets/audio.cpp Audio callback output class members
81 \snippet multimedia-snippets/audio.cpp Audio callback output setup sine
82
83 Unlike the QIODevice-based interface, the QAudioSink can only be in the states active,
84 suspendend and stopped. The setBufferSize() API is not available when using the callback,
85 the size of the callback argument is determined by the audio backend.
86
87 \qtmmaudiocallbacksupportednote
88 \qtmmaudiocallbacknote
89
90 \section1 State and error handling
91
92 State changes are reported through the stateChanged() signal. You
93 can use this signal to, for instance, update the GUI of the
94 application; the mundane example here being changing the state of
95 a \c { play/pause } button. You request a state change directly
96 with suspend(), stop(), reset(), resume(), and start().
97
98 The QAudioSink will enter the \l{QtAudio::}{StoppedState} when an error is encountered.
99 The \l{QtAudio::Error}{error type} can be retrieved with the error() function. Please see the
100 QtAudio::Error enum for a description of the possible errors that are reported. Calling stop()
101 or reset() will reset the error state to \l{QtAudio::Error}{NoError}.
102
103 You can check for errors by connecting to the stateChanged()
104 signal:
105
106 \snippet multimedia-snippets/audio.cpp Audio output state changed
107
108 \sa QAudioSource, QAudioDevice
109*/
110
111/*!
112 Construct a new audio output and attach it to \a parent.
113 The default audio output device is used with the output
114 \a format parameters. If \a format is default-initialized,
115 the format will be set to the preferred format of the audio device.
116*/
117QAudioSink::QAudioSink(const QAudioFormat &format, QObject *parent)
118 : QAudioSink({}, format, parent)
119{
120}
121
122/*!
123 Construct a new audio output and attach it to \a parent.
124 The device referenced by \a audioDevice is used with the output
125 \a format parameters. If \a format is default-initialized,
126 the format will be set to the preferred format of \a audioDevice.
127*/
128QAudioSink::QAudioSink(const QAudioDevice &audioDevice, const QAudioFormat &format, QObject *parent):
129 QObject(parent)
130{
131 d = QPlatformMediaIntegration::instance()->audioDevices()->audioOutputDevice(format,
132 audioDevice, this);
133 if (d)
134 connect(d, &QPlatformAudioSink::stateChanged, this, &QAudioSink::stateChanged);
135 else
136 qWarning("No audio device detected");
137}
138
139/*!
140 \fn bool QAudioSink::isNull() const
141
142 Returns \c true is the QAudioSink instance is \c null, otherwise returns
143 \c false.
144*/
145
146/*!
147 Destroys this audio output.
148
149 This will release any system resources used and free any buffers.
150*/
151QAudioSink::~QAudioSink()
152{
153 delete d;
154}
155
156/*!
157 Returns the QAudioFormat being used.
158
159*/
160QAudioFormat QAudioSink::format() const
161{
162 return d ? d->format() : QAudioFormat();
163}
164
165static bool validateFormatAtStart(QPlatformAudioSink *d)
166{
167 if (!d->format().isValid()) {
168 qWarning() << "QAudioSink::start: QAudioFormat not valid";
169 d->setError(QAudio::OpenError);
170 return false;
171 }
172
173 if (!d->isFormatSupported(d->format())) {
174 qWarning() << "QAudioSink::start: QAudioFormat not supported by QAudioDevice";
175 d->setError(QAudio::OpenError);
176 return false;
177 }
178 return true;
179};
180
181/*!
182 Starts transferring audio data from the \a device to the system's audio output.
183 The \a device must have been opened in the \l{QIODevice::ReadOnly}{ReadOnly} or
184 \l{QIODevice::ReadWrite}{ReadWrite} modes.
185
186 If the QAudioSink is able to successfully output audio data, state() returns
187 QtAudio::ActiveState, error() returns QtAudio::NoError
188 and the stateChanged() signal is emitted.
189
190 If a problem occurs during this process, error() returns QtAudio::OpenError,
191 state() returns QtAudio::StoppedState and the stateChanged() signal is emitted.
192
193 \sa QIODevice, {QAudioSink#Callback interface}{QIODevice interface}
194*/
195void QAudioSink::start(QIODevice* device)
196{
197 if (!d)
198 return;
199
200 d->setError(QAudio::NoError);
201
202 if (!device->isReadable()) {
203 qWarning() << "QAudioSink::start: QIODevice is not readable";
204 d->setError(QAudio::OpenError);
205 return;
206 }
207
208 if (!validateFormatAtStart(d))
209 return;
210
211 d->elapsedTime.start();
212 d->start(device);
213}
214
215/*!
216 Returns a pointer to the internal QIODevice being used to transfer data to
217 the system's audio output. The device will already be open and
218 \l{QIODevice::write()}{write()} can write data directly to it.
219
220 \note The pointer will become invalid after the stream is stopped or
221 if you start another stream.
222
223 If the QAudioSink is able to access the system's audio device, state() returns
224 QtAudio::IdleState, error() returns QtAudio::NoError
225 and the stateChanged() signal is emitted.
226
227 If a problem occurs during this process, error() returns QtAudio::OpenError,
228 state() returns QtAudio::StoppedState and the stateChanged() signal is emitted.
229
230 \sa QIODevice, {QAudioSink#Callback interface}{QIODevice interface}
231*/
232QIODevice* QAudioSink::start()
233{
234 if (!d)
235 return nullptr;
236
237 d->setError(QAudio::NoError);
238
239 if (!validateFormatAtStart(d))
240 return nullptr;
241
242 d->elapsedTime.start();
243 return d->start();
244}
245
246/*!
247 \fn template <typename Callback, QtAudio::if_audio_sink_callback<Callback> = true> void QAudioSink::start(Callback &&)
248
249 Starts the QAudioSink with a callback function that will be called on a soft-realtime audio
250 thread. The callback is a callable that takes a QSpan<SampleType> as an argument, SampleType has
251 to match the QAudioFormat::SampleFormat of the QAudioSink's format. The span needs to be filled
252 with interleaved audio data.
253
254 If the QAudioSink is able to successfully start, error() returns QtAudio::NoError.
255
256 If a problem occurs during this process, error() returns QtAudio::OpenError, state() returns
257 QtAudio::StoppedState and the stateChanged() signal is emitted.
258
259 \qtmmaudiocallbacksupportednote
260 \qtmmaudiocallbacknote
261
262 \sa {QAudioSink#Callback interface}{Callback interface}
263 \since 6.11
264*/
265
266template <typename T>
267void QAudioSink::startImpl(T &&callback)
268{
269 if (!d)
270 return;
271
272 if (!d->hasCallbackAPI()) {
273 qWarning() << "QAudioSink::start: Callback API not supported on this platform";
274 d->setError(QAudio::OpenError);
275 return;
276 }
277
278 using namespace QtMultimediaPrivate;
279 if (!validateAudioCallback(callback, format())) {
280 d->setError(QAudio::OpenError);
281 return;
282 }
283
284 if (!validateFormatAtStart(d))
285 return;
286
287 d->elapsedTime.start();
288 d->start(std::forward<T>(callback));
289}
290
291void QAudioSink::startABIImpl(QtAudioPrivate::AudioSinkCallback &&callback)
292{
293 return QAudioSink::startImpl(QtMultimediaPrivate::asAudioSinkCallback(std::move(callback)));
294}
295
296/*!
297 Stops the audio output, detaching from the system resource.
298
299 Sets error() to QtAudio::NoError, state() to QtAudio::StoppedState and
300 emit stateChanged() signal.
301
302 \note On Linux, and Darwin, this operation synchronously drains the
303 underlying audio buffer, which may cause delays accordingly to the
304 buffer payload. To reset all the buffers immediately, use the method
305 \l reset instead.
306 \sa reset()
307*/
308void QAudioSink::stop()
309{
310 if (d)
311 d->stop();
312}
313
314/*!
315 Immediately halts audio output and discards any audio data currently in the buffers. All pending
316 audio data pushed to QIODevice is ignored.
317
318 \sa stop()
319*/
320void QAudioSink::reset()
321{
322 if (d)
323 d->reset();
324}
325
326/*!
327 Stops processing audio data, preserving buffered audio data.
328
329 Sets state() to QtAudio::SuspendedState and emits stateChanged() signal.
330*/
331void QAudioSink::suspend()
332{
333 if (d)
334 d->suspend();
335}
336
337/*!
338 Resumes processing audio data after a suspend().
339
340 Sets state() to the state the sink had when suspend() was called. This function does nothing if
341 the audio sink's state is not QtAudio::SuspendedState.
342*/
343void QAudioSink::resume()
344{
345 if (d)
346 d->resume();
347}
348
349/*!
350 Returns the number of free bytes available in the audio buffer.
351
352 \note The returned value is only valid while in QtAudio::ActiveState or QtAudio::IdleState
353 state, otherwise returns zero.
354
355 \sa framesFree
356*/
357qsizetype QAudioSink::bytesFree() const
358{
359 return d ? d->bytesFree() : 0;
360}
361
362/*!
363 Returns the number of free frames available in the audio buffer.
364
365 \note The returned value is only valid while in QtAudio::ActiveState or QtAudio::IdleState
366 state, otherwise returns zero.
367
368 \sa bytesFree
369 \since 6.10
370*/
371
372qsizetype QAudioSink::framesFree() const
373{
374 return d ? d->format().framesForBytes(bytesFree()) : 0;
375}
376
377/*!
378 Sets the audio buffer size to \a value in bytes.
379
380 \note This function can be called anytime before start(). Calls to this
381 are ignored after start(). It should not be assumed that the buffer size
382 set is the actual buffer size used - call bufferSize() anytime after start()
383 to return the actual buffer size being used.
384
385 The bufferFrameCount() and bufferSize() properties denote the ringbuffer size
386 when using the QIODevice APIs. The native period size is controlled via setNativePeriodFrameCount().
387
388 \sa setBufferFrameCount
389*/
390void QAudioSink::setBufferSize(qsizetype value)
391{
392 if (d)
393 d->setBufferSize(value);
394}
395
396/*!
397 Returns the audio buffer size in bytes.
398
399 If called before \l start(), returns platform default value.
400 If called before \c start() but \l setBufferSize() or \l setBufferFrameCount() was called prior, returns
401 value set by \c setBufferSize() or \c setBufferFrameCount(). If called after \c start(), returns the actual
402 buffer size being used. This may not be what was set previously by
403 \c setBufferSize() or \c setBufferFrameCount().
404
405 \sa bufferFrameCount
406*/
407qsizetype QAudioSink::bufferSize() const
408{
409 return d ? d->bufferSize() : 0;
410}
411
412/*!
413 Sets the audio buffer size to \a value in frame count.
414
415 \note This function can be called anytime before start(). Calls to this
416 are ignored after start(). It should not be assumed that the buffer size
417 set is the actual buffer size used - call bufferFrameCount() anytime after
418 start() to return the actual buffer size being used.
419
420 The bufferFrameCount() and bufferSize() properties denote the ringbuffer size
421 when using the QIODevice APIs. The native period size is controlled via setNativePeriodFrameCount().
422
423 \sa setBufferSize
424 \since 6.10
425*/
426
427void QAudioSink::setBufferFrameCount(qsizetype value)
428{
429 if (d)
430 setBufferSize(d->format().bytesForFrames(value));
431}
432
433/*!
434 Returns the audio buffer size in frames.
435
436 If called before \l start(), returns platform default value.
437 If called before \c start() but \l setBufferSize() or \l setBufferFrameCount() was called prior, returns
438 value set by \c setBufferSize() or \c setBufferFrameCount(). If called after \c start(), returns the actual
439 buffer size being used. This may not be what was set previously by
440 \c setBufferSize() or \c setBufferFrameCount().
441
442 \sa bufferSize
443 \since 6.10
444*/
445
446qsizetype QAudioSink::bufferFrameCount() const
447{
448 return d ? d->format().framesForBytes(bufferSize()) : 0;
449}
450
451/*!
452 Sets the native period frame count to \a frameCount.
453
454 Tunes the audio buffer size used by the operating system and hardware. By default (when not set),
455 the system uses a platform-dependent value, typically 1024 frames (~23ms at 44100 Hz).
456
457 Use smaller values (64-256) to reduce latency, or larger values (2048-4096) to reduce the risk of
458 buffer underruns (dropouts).
459
460 Valid values are powers of 2 between 32 and 4096 (inclusive), or -1 to unset. Can only be called
461 when the audio sink is stopped.
462
463 Maps to platform-specific settings: \c{kAudioDevicePropertyBufferFrameSize} (macOS),
464 \c{IAudioClient::bufferDuration} (Windows), \c{PW_KEY_NODE_FORCE_QUANTUM} (PipeWire/Linux),
465 and \c{AAudioStreamBuilder_setBufferCapacityInFrames} (Android).
466
467 \note This is separate from the ringbuffer configured with setBufferSize() or setBufferFrameCount().
468
469 \since 6.12
470 \sa nativePeriodFrameCount
471*/
472void QAudioSink::setNativePeriodFrameCount(int frameCount)
473{
474 if (!d)
475 return;
476
477 // Allow -1 as special case (unset)
478 if (frameCount != -1) {
479 if (state() != QAudio::StoppedState) {
480 qWarning("QAudioSink::setNativePeriodFrameCount: can only be set when stopped");
481 return;
482 }
483
484 if (frameCount < 32 || frameCount > 4096
485 || !QtMultimediaPrivate::isPowerOfTwo(frameCount)) {
486 qWarning("QAudioSink::setNativePeriodFrameCount: invalid frame count %d "
487 "(must be -1 or a power of 2 between 32 and 4096)",
488 frameCount);
489 return;
490 }
491 }
492
493 if (frameCount != -1)
494 d->setNativePeriodFrames(QtMultimediaPrivate::NativePeriodFrames(frameCount));
495 else
496 d->setNativePeriodFrames(std::nullopt);
497}
498
499/*!
500 Returns the native period frame count.
501
502 Returns -1 if not set, otherwise returns the value previously set with
503 setNativePeriodFrameCount().
504
505 \since 6.12
506 \sa setNativePeriodFrameCount
507*/
508int QAudioSink::nativePeriodFrameCount() const
509{
510 std::optional hwbf = d ? d->nativePeriodFrames() : std::nullopt;
511 return hwbf ? qToUnderlying(*hwbf) : -1;
512}
513
514/*!
515 Returns the amount of audio data processed since start()
516 was called (in microseconds).
517*/
518qint64 QAudioSink::processedUSecs() const
519{
520 return d ? d->processedUSecs() : 0;
521}
522
523/*!
524 Returns the microseconds since start() was called, including time in Idle and
525 Suspend states.
526*/
527qint64 QAudioSink::elapsedUSecs() const
528{
529 return state() == QAudio::StoppedState ? 0 : d->elapsedTime.nsecsElapsed()/1000;
530}
531
532/*!
533 Returns the error state.
534*/
535QtAudio::Error QAudioSink::error() const
536{
537 return d ? d->error() : QAudio::OpenError;
538}
539
540/*!
541 Returns the state of audio processing.
542*/
543QtAudio::State QAudioSink::state() const
544{
545 return d ? d->state() : QAudio::StoppedState;
546}
547
548/*!
549 Sets the output volume to \a volume.
550
551 The volume is scaled linearly from \c 0.0 (silence) to \c 1.0 (full volume).
552 Values outside this range will be clamped.
553
554 The default volume is \c 1.0.
555
556 \note Adjustments to the volume will change the volume of this audio stream,
557 not the global volume.
558
559 UI volume controls should usually be scaled non-linearly. For example, using
560 a logarithmic scale will produce linear changes in perceived loudness, which
561 is what a user would normally expect from a volume control. See
562 QtAudio::convertVolume() for more details.
563*/
564void QAudioSink::setVolume(qreal volume)
565{
566 if (!d)
567 return;
568
569 std::optional<float> newVolume = QAudioHelperInternal::sanitizeVolume(volume, this->volume());
570 if (newVolume)
571 d->setVolume(*newVolume);
572}
573
574/*!
575 Returns the volume between 0.0 and 1.0 inclusive.
576*/
577qreal QAudioSink::volume() const
578{
579 return d ? d->volume() : 1.0;
580}
581
582/*!
583 \fn QAudioSink::stateChanged(QtAudio::State state)
584 This signal is emitted when the device \a state has changed.
585 This is the current state of the audio output.
586
587 \note The QtAudio namespace was named QAudio up to and including Qt 6.6.
588 String-based connections to this signal have to use \c{QAudio::State} as
589 the parameter type: \c{connect(source, SIGNAL(stateChanged(QAudio::State)), ...);}
590*/
591
592QT_END_NAMESPACE
593
594#include "moc_qaudiosink.cpp"
Combined button and popup list for selecting options.
static bool validateFormatAtStart(QPlatformAudioSink *d)