Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
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
/
qtmm_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
15
QT_BEGIN_NAMESPACE
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
*/
117
QAudioSink::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
*/
128
QAudioSink::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
*/
151
QAudioSink::~QAudioSink()
152
{
153
delete
d;
154
}
155
156
/*!
157
Returns the QAudioFormat being used.
158
159
*/
160
QAudioFormat QAudioSink::format()
const
161
{
162
return
d ? d->format() : QAudioFormat();
163
}
164
165
static
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
*/
195
void
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
*/
232
QIODevice* 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
266
template
<
typename
T>
267
void
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
291
void
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
*/
308
void
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
*/
320
void
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
*/
331
void
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
*/
343
void
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
*/
357
qsizetype 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
372
qsizetype 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
*/
390
void
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
*/
407
qsizetype 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
427
void
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
446
qsizetype 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
*/
472
void
QAudioSink::setNativePeriodFrameCount(qsizetype 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 %lld "
487
"(must be -1 or a power of 2 between 32 and 4096)"
,
488
qint64(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
*/
508
qsizetype QAudioSink::nativePeriodFrameCount()
const
509
{
510
std::optional hwbf = d ? d->nativePeriodFrames() : std::nullopt;
511
return
hwbf ? qsizetype(qToUnderlying(*hwbf)) : qsizetype(-1);
512
}
513
514
/*!
515
Returns the amount of audio data processed since start()
516
was called (in microseconds).
517
*/
518
qint64 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
*/
527
qint64 QAudioSink::elapsedUSecs()
const
528
{
529
return
state() == QAudio::StoppedState ? 0 : d->elapsedTime.nsecsElapsed()/1000;
530
}
531
532
/*!
533
Returns the error state.
534
*/
535
QtAudio::Error QAudioSink::error()
const
536
{
537
return
d ? d->error() : QAudio::OpenError;
538
}
539
540
/*!
541
Returns the state of audio processing.
542
*/
543
QtAudio::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
*/
564
void
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
*/
577
qreal 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
592
QT_END_NAMESPACE
593
594
#
include
"moc_qaudiosink.cpp"
QT_BEGIN_NAMESPACE
Combined button and popup list for selecting options.
Definition
qsequentialanimationgroup.cpp:47
validateFormatAtStart
static bool validateFormatAtStart(QPlatformAudioSink *d)
Definition
qaudiosink.cpp:165
qtmultimedia
src
multimedia
audio
qaudiosink.cpp
Generated on
for Qt by
1.18.0