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