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
5#include "qaudio.h"
6#include "qaudiodevice.h"
7#include "qaudiosystem_p.h"
8#include "qaudiosink.h"
9
10#include <private/qplatformmediadevices_p.h>
11#include <private/qplatformmediaintegration_p.h>
12
13QT_BEGIN_NAMESPACE
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 state changed
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 QAudio::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{QAudio::Error}{error
60 type} with the error() function. Please see the QAudio::Error enum
61 for a description of the possible errors that are reported. When
62 QAudio::UnderrunError is encountered, the state changes to QAudio::IdleState,
63 when another error is encountered, the state changes to QAudio::StoppedState.
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.
76*/
77QAudioSink::QAudioSink(const QAudioFormat &format, QObject *parent)
78 : QAudioSink({}, format, parent)
79{
80}
81
82/*!
83 Construct a new audio output and attach it to \a parent.
84 The device referenced by \a audioDevice is used with the output
85 \a format parameters.
86*/
87QAudioSink::QAudioSink(const QAudioDevice &audioDevice, const QAudioFormat &format, QObject *parent):
88 QObject(parent)
89{
90 d = QPlatformMediaDevices::instance()->audioOutputDevice(format, deviceInfo: audioDevice, parent);
91 if (d)
92 connect(sender: d, signal: &QPlatformAudioSink::stateChanged, context: this, slot: [this](QAudio::State state) {
93 // if the signal has been emitted from another thread,
94 // the state may be already changed by main one
95 if (state == d->state())
96 emit stateChanged(state);
97 });
98 else
99 qWarning() << ("No audio device detected");
100}
101
102/*!
103 \fn bool QAudioSink::isNull() const
104
105 Returns \c true is the QAudioSink instance is \c null, otherwise returns
106 \c false.
107*/
108
109/*!
110 Destroys this audio output.
111
112 This will release any system resources used and free any buffers.
113*/
114QAudioSink::~QAudioSink()
115{
116 delete d;
117}
118
119/*!
120 Returns the QAudioFormat being used.
121
122*/
123QAudioFormat QAudioSink::format() const
124{
125 return d ? d->format() : QAudioFormat();
126}
127
128/*!
129 Starts transferring audio data from the \a device to the system's audio output.
130 The \a device must have been opened in the \l{QIODevice::ReadOnly}{ReadOnly} or
131 \l{QIODevice::ReadWrite}{ReadWrite} modes.
132
133 If the QAudioSink is able to successfully output audio data, state() returns
134 QAudio::ActiveState, error() returns QAudio::NoError
135 and the stateChanged() signal is emitted.
136
137 If a problem occurs during this process, error() returns QAudio::OpenError,
138 state() returns QAudio::StoppedState and the stateChanged() signal is emitted.
139
140 \sa QIODevice
141*/
142void QAudioSink::start(QIODevice* device)
143{
144 if (!d)
145 return;
146 d->elapsedTime.restart();
147 d->start(device);
148}
149
150/*!
151 Returns a pointer to the internal QIODevice being used to transfer data to
152 the system's audio output. The device will already be open and
153 \l{QIODevice::write()}{write()} can write data directly to it.
154
155 \note The pointer will become invalid after the stream is stopped or
156 if you start another stream.
157
158 If the QAudioSink is able to access the system's audio device, state() returns
159 QAudio::IdleState, error() returns QAudio::NoError
160 and the stateChanged() signal is emitted.
161
162 If a problem occurs during this process, error() returns QAudio::OpenError,
163 state() returns QAudio::StoppedState and the stateChanged() signal is emitted.
164
165 \sa QIODevice
166*/
167QIODevice* QAudioSink::start()
168{
169 if (!d)
170 return nullptr;
171 d->elapsedTime.restart();
172 return d->start();
173}
174
175/*!
176 Stops the audio output, detaching from the system resource.
177
178 Sets error() to QAudio::NoError, state() to QAudio::StoppedState and
179 emit stateChanged() signal.
180*/
181void QAudioSink::stop()
182{
183 if (d)
184 d->stop();
185}
186
187/*!
188 Drops all audio data in the buffers, resets buffers to zero.
189
190*/
191void QAudioSink::reset()
192{
193 if (d)
194 d->reset();
195}
196
197/*!
198 Stops processing audio data, preserving buffered audio data.
199
200 Sets error() to QAudio::NoError, state() to QAudio::SuspendedState and
201 emits stateChanged() signal.
202*/
203void QAudioSink::suspend()
204{
205 if (d)
206 d->suspend();
207}
208
209/*!
210 Resumes processing audio data after a suspend().
211
212 Sets state() to the state the sink had when suspend() was called, and sets
213 error() to QAudioError::NoError. This function does nothing if the audio sink's
214 state is not QAudio::SuspendedState.
215*/
216void QAudioSink::resume()
217{
218 if (d)
219 d->resume();
220}
221
222/*!
223 Returns the number of free bytes available in the audio buffer.
224
225 \note The returned value is only valid while in QAudio::ActiveState or QAudio::IdleState
226 state, otherwise returns zero.
227*/
228qsizetype QAudioSink::bytesFree() const
229{
230 return d ? d->bytesFree() : 0;
231}
232
233/*!
234 Sets the audio buffer size to \a value in bytes.
235
236 \note This function can be called anytime before start(). Calls to this
237 are ignored after start(). It should not be assumed that the buffer size
238 set is the actual buffer size used - call bufferSize() anytime after start()
239 to return the actual buffer size being used.
240*/
241void QAudioSink::setBufferSize(qsizetype value)
242{
243 if (d)
244 d->setBufferSize(value);
245}
246
247/*!
248 Returns the audio buffer size in bytes.
249
250 If called before start(), returns platform default value.
251 If called before start() but setBufferSize() was called prior, returns value set by setBufferSize().
252 If called after start(), returns the actual buffer size being used. This may not be what was set previously
253 by setBufferSize().
254
255*/
256qsizetype QAudioSink::bufferSize() const
257{
258 return d ? d->bufferSize() : 0;
259}
260
261/*!
262 Returns the amount of audio data processed since start()
263 was called (in microseconds).
264*/
265qint64 QAudioSink::processedUSecs() const
266{
267 return d ? d->processedUSecs() : 0;
268}
269
270/*!
271 Returns the microseconds since start() was called, including time in Idle and
272 Suspend states.
273*/
274qint64 QAudioSink::elapsedUSecs() const
275{
276 return state() == QAudio::StoppedState ? 0 : d->elapsedTime.nsecsElapsed()/1000;
277}
278
279/*!
280 Returns the error state.
281*/
282QAudio::Error QAudioSink::error() const
283{
284 return d ? d->error() : QAudio::OpenError;
285}
286
287/*!
288 Returns the state of audio processing.
289*/
290QAudio::State QAudioSink::state() const
291{
292 return d ? d->state() : QAudio::StoppedState;
293}
294
295/*!
296 Sets the output volume to \a volume.
297
298 The volume is scaled linearly from \c 0.0 (silence) to \c 1.0 (full volume).
299 Values outside this range will be clamped.
300
301 The default volume is \c 1.0.
302
303 \note Adjustments to the volume will change the volume of this audio stream,
304 not the global volume.
305
306 UI volume controls should usually be scaled non-linearly. For example, using
307 a logarithmic scale will produce linear changes in perceived loudness, which
308 is what a user would normally expect from a volume control. See
309 QAudio::convertVolume() for more details.
310*/
311void QAudioSink::setVolume(qreal volume)
312{
313 if (!d)
314 return;
315 qreal v = qBound(min: qreal(0.0), val: volume, max: qreal(1.0));
316 d->setVolume(v);
317}
318
319/*!
320 Returns the volume between 0.0 and 1.0 inclusive.
321*/
322qreal QAudioSink::volume() const
323{
324 return d ? d->volume() : 1.0;
325}
326
327/*!
328 \fn QAudioSink::stateChanged(QAudio::State state)
329 This signal is emitted when the device \a state has changed.
330 This is the current state of the audio output.
331*/
332
333QT_END_NAMESPACE
334
335#include "moc_qaudiosink.cpp"
336

source code of qtmultimedia/src/multimedia/audio/qaudiosink.cpp