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 To play a file:
32
33 Starting to play an audio stream is simply a matter of calling
34 start() with a QIODevice. QAudioSink will then fetch the data it
35 needs from the io device. So playing back an audio file is as
36 simple as:
37
38 \snippet multimedia-snippets/audio.cpp Audio output class members
39
40 \snippet multimedia-snippets/audio.cpp Audio output setup
41
42 The file will start playing assuming that the audio system and
43 output device support it. If you run out of luck, check what's
44 up with the error() function.
45
46 After the file has finished playing, we need to stop the device:
47
48 \snippet multimedia-snippets/audio.cpp Audio output stop
49
50 At any given time, the QAudioSink will be in one of four states:
51 active, suspended, stopped, or idle. These states are described
52 by the QtAudio::State enum.
53 State changes are reported through the stateChanged() signal. You
54 can use this signal to, for instance, update the GUI of the
55 application; the mundane example here being changing the state of
56 a \c { play/pause } button. You request a state change directly
57 with suspend(), stop(), reset(), resume(), and start().
58
59 If an error occurs, you can fetch the \l{QtAudio::Error}{error
60 type} with the error() function. Please see the QtAudio::Error enum
61 for a description of the possible errors that are reported. The QAudioSink
62 will enter the \l{QtAudio::}{StoppedState} when an error is encountered.
63
64 You can check for errors by connecting to the stateChanged()
65 signal:
66
67 \snippet multimedia-snippets/audio.cpp Audio output state changed
68
69 \sa QAudioSource, QAudioDevice
70*/
71
72/*!
73 Construct a new audio output and attach it to \a parent.
74 The default audio output device is used with the output
75 \a format parameters. If \a format is default-initialized,
76 the format will be set to the preferred format of the audio device.
77*/
78QAudioSink::QAudioSink(const QAudioFormat &format, QObject *parent)
79 : QAudioSink({}, format, parent)
80{
81}
82
83/*!
84 Construct a new audio output and attach it to \a parent.
85 The device referenced by \a audioDevice is used with the output
86 \a format parameters. If \a format is default-initialized,
87 the format will be set to the preferred format of \a audioDevice.
88*/
89QAudioSink::QAudioSink(const QAudioDevice &audioDevice, const QAudioFormat &format, QObject *parent):
90 QObject(parent)
91{
92 d = QPlatformMediaIntegration::instance()->audioDevices()->audioOutputDevice(format,
93 audioDevice, this);
94 if (d)
95 connect(d, &QPlatformAudioSink::stateChanged, this, &QAudioSink::stateChanged);
96 else
97 qWarning("No audio device detected");
98}
99
100/*!
101 \fn bool QAudioSink::isNull() const
102
103 Returns \c true is the QAudioSink instance is \c null, otherwise returns
104 \c false.
105*/
106
107/*!
108 Destroys this audio output.
109
110 This will release any system resources used and free any buffers.
111*/
112QAudioSink::~QAudioSink()
113{
114 delete d;
115}
116
117/*!
118 Returns the QAudioFormat being used.
119
120*/
121QAudioFormat QAudioSink::format() const
122{
123 return d ? d->format() : QAudioFormat();
124}
125
126static bool validateFormatAtStart(QPlatformAudioSink *d)
127{
128 if (!d->format().isValid()) {
129 qWarning() << "QAudioSink::start: QAudioFormat not valid";
130 d->setError(QAudio::OpenError);
131 return false;
132 }
133
134 if (!d->isFormatSupported(d->format())) {
135 qWarning() << "QAudioSink::start: QAudioFormat not supported by QAudioDevice";
136 d->setError(QAudio::OpenError);
137 return false;
138 }
139 return true;
140};
141
142/*!
143 Starts transferring audio data from the \a device to the system's audio output.
144 The \a device must have been opened in the \l{QIODevice::ReadOnly}{ReadOnly} or
145 \l{QIODevice::ReadWrite}{ReadWrite} modes.
146
147 If the QAudioSink is able to successfully output audio data, state() returns
148 QtAudio::ActiveState, error() returns QtAudio::NoError
149 and the stateChanged() signal is emitted.
150
151 If a problem occurs during this process, error() returns QtAudio::OpenError,
152 state() returns QtAudio::StoppedState and the stateChanged() signal is emitted.
153
154 \sa QIODevice
155*/
156void QAudioSink::start(QIODevice* device)
157{
158 if (!d)
159 return;
160
161 d->setError(QAudio::NoError);
162
163 if (!device->isReadable()) {
164 qWarning() << "QAudioSink::start: QIODevice is not readable";
165 d->setError(QAudio::OpenError);
166 return;
167 }
168
169 if (!validateFormatAtStart(d))
170 return;
171
172 d->elapsedTime.start();
173 d->start(device);
174}
175
176/*!
177 Returns a pointer to the internal QIODevice being used to transfer data to
178 the system's audio output. The device will already be open and
179 \l{QIODevice::write()}{write()} can write data directly to it.
180
181 \note The pointer will become invalid after the stream is stopped or
182 if you start another stream.
183
184 If the QAudioSink is able to access the system's audio device, state() returns
185 QtAudio::IdleState, 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
192*/
193QIODevice* QAudioSink::start()
194{
195 if (!d)
196 return nullptr;
197
198 d->setError(QAudio::NoError);
199
200 if (!validateFormatAtStart(d))
201 return nullptr;
202
203 d->elapsedTime.start();
204 return d->start();
205}
206
207/*!
208 Stops the audio output, detaching from the system resource.
209
210 Sets error() to QtAudio::NoError, state() to QtAudio::StoppedState and
211 emit stateChanged() signal.
212
213 \note On Linux, and Darwin, this operation synchronously drains the
214 underlying audio buffer, which may cause delays accordingly to the
215 buffer payload. To reset all the buffers immediately, use the method
216 \l reset instead.
217 \sa reset()
218*/
219void QAudioSink::stop()
220{
221 if (d)
222 d->stop();
223}
224
225/*!
226 Immediately halts audio output and discards any audio data currently in the buffers. All pending
227 audio data pushed to QIODevice is ignored.
228
229 \sa stop()
230*/
231void QAudioSink::reset()
232{
233 if (d)
234 d->reset();
235}
236
237/*!
238 Stops processing audio data, preserving buffered audio data.
239
240 Sets error() to QtAudio::NoError, state() to QtAudio::SuspendedState and
241 emits stateChanged() signal.
242*/
243void QAudioSink::suspend()
244{
245 if (d)
246 d->suspend();
247}
248
249/*!
250 Resumes processing audio data after a suspend().
251
252 Sets state() to the state the sink had when suspend() was called, and sets
253 error() to QAudioError::NoError. This function does nothing if the audio sink's
254 state is not QtAudio::SuspendedState.
255*/
256void QAudioSink::resume()
257{
258 if (d)
259 d->resume();
260}
261
262/*!
263 Returns the number of free bytes available in the audio buffer.
264
265 \note The returned value is only valid while in QtAudio::ActiveState or QtAudio::IdleState
266 state, otherwise returns zero.
267
268 \sa framesFree
269*/
270qsizetype QAudioSink::bytesFree() const
271{
272 return d ? d->bytesFree() : 0;
273}
274
275/*!
276 Returns the number of free frames available in the audio buffer.
277
278 \note The returned value is only valid while in QtAudio::ActiveState or QtAudio::IdleState
279 state, otherwise returns zero.
280
281 \sa bytesFree
282 \since 6.10
283*/
284
285qsizetype QAudioSink::framesFree() const
286{
287 return d ? d->format().framesForBytes(bytesFree()) : 0;
288}
289
290/*!
291 Sets the audio buffer size to \a value in bytes.
292
293 \note This function can be called anytime before start(). Calls to this
294 are ignored after start(). It should not be assumed that the buffer size
295 set is the actual buffer size used - call bufferSize() anytime after start()
296 to return the actual buffer size being used.
297
298 \sa setBufferFrameCount
299*/
300void QAudioSink::setBufferSize(qsizetype value)
301{
302 if (d)
303 d->setBufferSize(value);
304}
305
306/*!
307 Returns the audio buffer size in bytes.
308
309 If called before \l start(), returns platform default value.
310 If called before \c start() but \l setBufferSize() or \l setBufferFrameCount() was called prior, returns
311 value set by \c setBufferSize() or \c setBufferFrameCount(). If called after \c start(), returns the actual
312 buffer size being used. This may not be what was set previously by
313 \c setBufferSize() or \c setBufferFrameCount().
314
315 \sa bufferFrameCount
316*/
317qsizetype QAudioSink::bufferSize() const
318{
319 return d ? d->bufferSize() : 0;
320}
321
322/*!
323 Sets the audio buffer size to \a value in frame count.
324
325 \note This function can be called anytime before start(). Calls to this
326 are ignored after start(). It should not be assumed that the buffer size
327 set is the actual buffer size used - call bufferFrameCount() anytime after
328 start() to return the actual buffer size being used.
329
330 \sa setBufferSize
331 \since 6.10
332*/
333
334void QAudioSink::setBufferFrameCount(qsizetype value)
335{
336 if (d)
337 setBufferSize(d->format().bytesForFrames(value));
338}
339
340/*!
341 Returns the audio buffer size in frames.
342
343 If called before \l start(), returns platform default value.
344 If called before \c start() but \l setBufferSize() or \l setBufferFrameCount() was called prior, returns
345 value set by \c setBufferSize() or \c setBufferFrameCount(). If called after \c start(), returns the actual
346 buffer size being used. This may not be what was set previously by
347 \c setBufferSize() or \c setBufferFrameCount().
348
349 \sa bufferSize
350 \since 6.10
351*/
352
353qsizetype QAudioSink::bufferFrameCount() const
354{
355 return d ? d->format().framesForBytes(bufferSize()) : 0;
356}
357
358/*!
359 Returns the amount of audio data processed since start()
360 was called (in microseconds).
361*/
362qint64 QAudioSink::processedUSecs() const
363{
364 return d ? d->processedUSecs() : 0;
365}
366
367/*!
368 Returns the microseconds since start() was called, including time in Idle and
369 Suspend states.
370*/
371qint64 QAudioSink::elapsedUSecs() const
372{
373 return state() == QAudio::StoppedState ? 0 : d->elapsedTime.nsecsElapsed()/1000;
374}
375
376/*!
377 Returns the error state.
378*/
379QtAudio::Error QAudioSink::error() const
380{
381 return d ? d->error() : QAudio::OpenError;
382}
383
384/*!
385 Returns the state of audio processing.
386*/
387QtAudio::State QAudioSink::state() const
388{
389 return d ? d->state() : QAudio::StoppedState;
390}
391
392/*!
393 Sets the output volume to \a volume.
394
395 The volume is scaled linearly from \c 0.0 (silence) to \c 1.0 (full volume).
396 Values outside this range will be clamped.
397
398 The default volume is \c 1.0.
399
400 \note Adjustments to the volume will change the volume of this audio stream,
401 not the global volume.
402
403 UI volume controls should usually be scaled non-linearly. For example, using
404 a logarithmic scale will produce linear changes in perceived loudness, which
405 is what a user would normally expect from a volume control. See
406 QtAudio::convertVolume() for more details.
407*/
408void QAudioSink::setVolume(qreal volume)
409{
410 if (!d)
411 return;
412
413 std::optional<float> newVolume = QAudioHelperInternal::sanitizeVolume(volume, this->volume());
414 if (newVolume)
415 d->setVolume(*newVolume);
416}
417
418/*!
419 Returns the volume between 0.0 and 1.0 inclusive.
420*/
421qreal QAudioSink::volume() const
422{
423 return d ? d->volume() : 1.0;
424}
425
426/*!
427 \fn QAudioSink::stateChanged(QtAudio::State state)
428 This signal is emitted when the device \a state has changed.
429 This is the current state of the audio output.
430
431 \note The QtAudio namespace was named QAudio up to and including Qt 6.6.
432 String-based connections to this signal have to use \c{QAudio::State} as
433 the parameter type: \c{connect(source, SIGNAL(stateChanged(QAudio::State)), ...);}
434*/
435
436QT_END_NAMESPACE
437
438#include "moc_qaudiosink.cpp"
static bool validateFormatAtStart(QPlatformAudioSink *d)