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
qaudiosource.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 "qaudiosource.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 QAudioSource
17 \brief The QAudioSource class provides an interface for receiving audio data from an audio input
18 device.
19
20 \inmodule QtMultimedia
21 \ingroup multimedia
22 \ingroup multimedia_audio
23
24 You can construct an audio input with the system's
25 default audio input device. It is also possible to
26 create QAudioSource with a specific QAudioDevice. When
27 you create the audio input, you should also send in the
28 QAudioFormat to be used for the recording (see the QAudioFormat
29 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 QAudioSource lets you record audio with an audio input device. The
40 default constructor of this class will use the systems default
41 audio device, but you can also specify a QAudioDevice for a
42 specific device. You also need to pass in the QAudioFormat in
43 which you wish to record.
44
45 Starting up the QAudioSource is simply a matter of calling start()
46 with a QIODevice opened for writing. For instance, to record to a
47 file, you can:
48
49 \snippet multimedia-snippets/audio.cpp Audio input class members
50
51 \snippet multimedia-snippets/audio.cpp Audio input setup
52
53 This will start recording if the format specified is supported by
54 the input device (you can check this with
55 QAudioDevice::isFormatSupported(). In case there are any
56 snags, use the error() function to check what went wrong. We stop
57 recording in the \c stopRecording() slot.
58
59 \snippet multimedia-snippets/audio.cpp Audio input stop recording
60
61 At any point in time, QAudioSource will be in one of four states:
62 active, suspended, stopped, or idle. These states are specified by
63 the QtAudio::State enum.
64
65 QAudioSource provides several ways of measuring the time that has
66 passed since the start() of the recording. The \c processedUSecs()
67 function returns the length of the stream in microseconds written,
68 i.e., it leaves out the times the audio input was suspended or idle.
69 The elapsedUSecs() function returns the time elapsed since start() was called regardless of
70 which states the QAudioSource has been in.
71
72 \section2 Threading model and buffering
73
74 The QIODevice interface is designed to be used from the application thread.
75 A wait-free ringbuffer is used to communicate to the audio thread. The size
76 of this ringbuffer can be configured with setBufferSize() and defaults to
77 250ms. The state of this buffer can be queried with bytesFree(). If the
78 ringbuffer is full because the application does not read from the QIODevice in time,
79 the state will change to QtAudio::IdleState and resume to QtAudio::ActiveState
80 once the application has read data from the QIODevice. Note that this state change will drop
81 audio data, so you should always read from the QIODevice as fast as possible to avoid dropouts.
82
83 \section1 Callback interface
84
85 The preferred way to achieve low audio latencies is to use the callback based interface.
86 It allows you to read audio data directly from the audio device without having to go through
87 a QIODevice. This is done by calling start() with a callback function that will be called
88 from the audio thread. This callback function will be called with a QSpan<const SampleType>
89 whenever the audio backend produces data.
90
91 \snippet multimedia-snippets/audio.cpp Audio callback capture class members
92 \snippet multimedia-snippets/audio.cpp Audio callback capture setup peak meter
93
94 Unlike the QIODevice-based interface, the QAudioSource can only be in the states active,
95 suspendend and stopped. The setBufferSize() API is not available when using the callback,
96 the size of the callback argument is determined by the audio backend.
97
98 \qtmmaudiocallbacksupportednote
99 \qtmmaudiocallbacknote
100
101
102 \section1 State and error handling
103
104 State changes are reported through the stateChanged() signal. You can request a state change
105 directly through suspend(), resume(), stop(), reset(), and start().
106
107 The QAudioSource will enter the \l{QtAudio::}{StoppedState} when an error is encountered.
108 The \l{QtAudio::Error}{error type} can be retrieved error() function. Please see the
109 QtAudio::Error enum for a description of the possible errors that are reported. Calling stop()
110 or reset() will reset the error state to \l{QtAudio::Error}{NoError}.
111
112 \snippet multimedia-snippets/audio.cpp Audio input state changed
113
114 \sa QAudioSink, QAudioDevice
115*/
116
117/*!
118 Construct a new audio input and attach it to \a parent.
119 The default audio input device is used with the output
120 \a format parameters. If \a format is default-initialized,
121 the format will be set to the preferred format of the audio device.
122*/
123
124QAudioSource::QAudioSource(const QAudioFormat &format, QObject *parent)
125 : QAudioSource({}, format, parent)
126{
127}
128
129/*!
130 Construct a new audio input and attach it to \a parent.
131 The device referenced by \a audioDevice is used with the input
132 \a format parameters. If \a format is default-initialized,
133 the format will be set to the preferred format of \a audioDevice.
134*/
135
136QAudioSource::QAudioSource(const QAudioDevice &audioDevice, const QAudioFormat &format, QObject *parent):
137 QObject(parent)
138{
139 d = QPlatformMediaIntegration::instance()->audioDevices()->audioInputDevice(format, audioDevice,
140 this);
141 if (d)
142 connect(d, &QPlatformAudioSource::stateChanged, this, &QAudioSource::stateChanged);
143 else
144 qWarning("No audio device detected");
145}
146
147/*!
148 \fn bool QAudioSource::isNull() const
149
150 Returns \c true if the audio source is \c null, otherwise returns \c false.
151*/
152
153/*!
154 Destroy this audio input.
155*/
156
157QAudioSource::~QAudioSource()
158{
159 delete d;
160}
161
162static bool validateFormatAtStart(QPlatformAudioSource *d)
163{
164 if (!d->format().isValid()) {
165 qWarning() << "QAudioSource::start: QAudioFormat not valid";
166 d->setError(QAudio::OpenError);
167 return false;
168 }
169
170 if (!d->isFormatSupported(d->format())) {
171 qWarning() << "QAudioSource::start: QAudioFormat not supported by QAudioDevice";
172 d->setError(QAudio::OpenError);
173 return false;
174 }
175 return true;
176};
177
178/*!
179 Starts transferring audio data from the system's audio input to the \a device.
180 The \a device must have been opened in the \l{QIODevice::WriteOnly}{WriteOnly},
181 \l{QIODevice::Append}{Append} or \l{QIODevice::ReadWrite}{ReadWrite} modes.
182
183 If the QAudioSource is able to successfully get audio data, state() returns
184 either QtAudio::ActiveState or QtAudio::IdleState, error() returns QtAudio::NoError
185 and the stateChanged() signal is emitted.
186
187 If a problem occurs during this process, error() returns QtAudio::OpenError,
188 state() returns QtAudio::StoppedState and the stateChanged() signal is emitted.
189
190 \sa QIODevice {QAudioSource#Callback interface}{QIODevice interface}
191*/
192
193void QAudioSource::start(QIODevice* device)
194{
195 if (!d)
196 return;
197
198 d->setError(QAudio::NoError);
199
200 if (!device->isWritable()) {
201 qWarning() << "QAudioSource::start: QIODevice is not writable";
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 from
215 the system's audio input. The device will already be open and
216 \l{QIODevice::read()}{read()} can read data directly from 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 QAudioSource 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 {QAudioSource#Callback interface}{QIODevice interface}
229*/
230
231QIODevice* QAudioSource::start()
232{
233 if (!d)
234 return nullptr;
235
236 d->setError(QAudio::NoError);
237
238 if (!validateFormatAtStart(d))
239 return nullptr;
240
241 d->elapsedTime.start();
242 return d->start();
243}
244
245/*!
246 \fn template <typename Callback, QtAudio::if_audio_source_callback<Callback> = true> void QAudioSource::start(Callback &&)
247
248 Starts the QAudioSource with a callback function that will be called on a soft-realtime audio
249 thread. The callback is a callable that takes a QSpan<const SampleType> as an argument,
250 SampleType has to match the QAudioFormat::SampleFormat of the QAudioSource's format. The span
251 contains the interleaved audio data.
252
253 If the QAudioSource is able to successfully start, error() returns QtAudio::NoError.
254
255 If a problem occurs during this process, error() returns QtAudio::OpenError, state() returns
256 QtAudio::StoppedState and the stateChanged() signal is emitted.
257
258 \qtmmaudiocallbacksupportednote
259 \qtmmaudiocallbacknote
260
261 \sa {QAudioSource#Callback interface}{Callback interface}
262 \since 6.11
263 */
264
265template <typename T>
266void QAudioSource::startImpl(T &&callback)
267{
268 if (!d)
269 return;
270
271 if (!d->hasCallbackAPI()) {
272 qWarning() << "QAudioSource::start: Callback API not supported on this platform";
273 d->setError(QAudio::OpenError);
274 return;
275 }
276
277 using namespace QtMultimediaPrivate;
278 if (!validateAudioCallback(callback, format())) {
279 d->setError(QAudio::OpenError);
280 return;
281 }
282
283 if (!validateFormatAtStart(d))
284 return;
285
286 d->elapsedTime.start();
287 d->start(std::forward<T>(callback));
288}
289
290void QAudioSource::startABIImpl(QtAudioPrivate::AudioSourceCallback &&callback)
291{
292 return QAudioSource::startImpl(QtMultimediaPrivate::asAudioSourceCallback(std::move(callback)));
293}
294
295/*!
296 Returns the QAudioFormat being used.
297*/
298
299QAudioFormat QAudioSource::format() const
300{
301 return d ? d->format() : QAudioFormat();
302}
303
304/*!
305 Stops the audio input, detaching from the system resource.
306
307 Sets error() to QtAudio::NoError, state() to QtAudio::StoppedState and
308 emit stateChanged() signal.
309*/
310
311void QAudioSource::stop()
312{
313 if (d)
314 d->stop();
315}
316
317/*!
318 Drops all audio data in the buffers, resets buffers to zero.
319*/
320
321void QAudioSource::reset()
322{
323 if (d)
324 d->reset();
325}
326
327/*!
328 Stops processing audio data, preserving buffered audio data.
329
330 Sets error() to QtAudio::NoError, state() to QtAudio::SuspendedState and
331 emit stateChanged() signal.
332*/
333
334void QAudioSource::suspend()
335{
336 if (d)
337 d->suspend();
338}
339
340/*!
341 Resumes processing audio data after a suspend().
342
343 Sets state() to the state the sink had when suspend() was called. This function does nothing if
344 the audio sink's state is not QtAudio::SuspendedState.
345*/
346
347void QAudioSource::resume()
348{
349 if (d)
350 d->resume();
351}
352
353/*!
354 Sets the audio buffer size to \a value bytes.
355
356 \note This function can be called anytime before start(), calls to this
357 are ignored after start(). It should not be assumed that the buffer size
358 set is the actual buffer size used, calling bufferSize() anytime after start()
359 will return the actual buffer size being used.
360
361 \sa setBufferFrameCount
362 \since 6.10
363*/
364
365void QAudioSource::setBufferSize(qsizetype value)
366{
367 if (d)
368 d->setBufferSize(value);
369}
370
371/*!
372 Returns the audio buffer size in bytes.
373
374 If called before \l start(), returns platform default value.
375 If called before \c start() but \l setBufferSize() or \l setBufferFrameCount() was called prior, returns
376 value set by \c setBufferSize() or \c setBufferFrameCount(). If called after \c start(), returns the actual
377 buffer size being used. This may not be what was set previously by
378 \c setBufferSize() or \c setBufferFrameCount().
379
380 \sa bufferFrameCount
381 \since 6.10
382*/
383
384qsizetype QAudioSource::bufferSize() const
385{
386 return d ? d->bufferSize() : 0;
387}
388
389/*!
390 Sets the audio buffer size to \a value in frame count.
391
392 \note This function can be called anytime before start(). Calls to this
393 are ignored after start(). It should not be assumed that the buffer size
394 set is the actual buffer size used - call bufferFrameCount() anytime
395 after start() to return the actual buffer size being used.
396
397 \sa setBufferSize
398*/
399
400void QAudioSource::setBufferFrameCount(qsizetype value)
401{
402 if (d)
403 setBufferSize(d->format().bytesForFrames(value));
404}
405
406/*!
407 Returns the audio buffer size in frames.
408
409 If called before \l start(), returns platform default value.
410 If called before \c start() but \l setBufferSize() or \l setBufferFrameCount() was called prior, returns
411 value set by \c setBufferSize() or \c setBufferFrameCount(). If called after \c start(), returns the actual
412 buffer size being used. This may not be what was set previously by
413 \c setBufferSize() or \c setBufferFrameCount().
414
415 \sa bufferSize
416*/
417
418qsizetype QAudioSource::bufferFrameCount() const
419{
420 return d ? d->format().framesForBytes(bufferSize()) : 0;
421}
422
423/*!
424 Returns the amount of audio data available to read in bytes.
425
426 \note returned value is only valid while in QtAudio::ActiveState or QtAudio::IdleState
427 state, otherwise returns zero.
428
429 \sa framesAvailable
430*/
431
432qsizetype QAudioSource::bytesAvailable() const
433{
434 return d ? d->bytesReady() : 0;
435}
436
437/*!
438 Returns the amount of audio data available to read in frames.
439
440 Note: returned value is only valid while in QtAudio::ActiveState or QtAudio::IdleState
441 state, otherwise returns zero.
442
443 \sa bytesAvailable
444 \since 6.10
445*/
446
447qsizetype QAudioSource::framesAvailable() const
448{
449
450 return d ? d->format().framesForBytes(bytesAvailable()) : 0;
451}
452
453/*!
454 Sets the input volume to \a volume.
455
456 The volume is scaled linearly from \c 0.0 (silence) to \c 1.0 (full volume). Values outside this
457 range will be clamped.
458
459 If the device does not support adjusting the input
460 volume then \a volume will be ignored and the input
461 volume will remain at 1.0.
462
463 The default volume is \c 1.0.
464
465 \note Adjustments to the volume will change the volume of this audio stream, not the global
466 volume.
467*/
468void QAudioSource::setVolume(qreal volume)
469{
470 if (!d)
471 return;
472
473 std::optional<float> newVolume = QAudioHelperInternal::sanitizeVolume(volume, this->volume());
474 if (newVolume)
475 d->setVolume(*newVolume);
476}
477
478/*!
479 Returns the input volume.
480
481 If the device does not support adjusting the input volume
482 the returned value will be 1.0.
483*/
484qreal QAudioSource::volume() const
485{
486 return d ? d->volume() : 1.0;
487}
488
489/*!
490 Returns the amount of audio data processed since start()
491 was called in microseconds.
492*/
493
494qint64 QAudioSource::processedUSecs() const
495{
496 return d ? d->processedUSecs() : 0;
497}
498
499/*!
500 Returns the microseconds since start() was called, including time in Idle and
501 Suspend states.
502*/
503
504qint64 QAudioSource::elapsedUSecs() const
505{
506 return state() == QAudio::StoppedState ? 0 : d->elapsedTime.nsecsElapsed()/1000;
507}
508
509/*!
510 Returns the error state.
511*/
512
513QtAudio::Error QAudioSource::error() const
514{
515 return d ? d->error() : QAudio::OpenError;
516}
517
518/*!
519 Returns the state of audio processing.
520*/
521
522QtAudio::State QAudioSource::state() const
523{
524 return d ? d->state() : QAudio::StoppedState;
525}
526
527/*!
528 \fn QAudioSource::stateChanged(QtAudio::State state)
529 This signal is emitted when the device \a state has changed.
530
531 \note The QtAudio namespace was named QAudio up to and including Qt 6.6.
532 String-based connections to this signal have to use \c{QAudio::State} as
533 the parameter type: \c{connect(source, SIGNAL(stateChanged(QAudio::State)), ...);}
534*/
535
536QT_END_NAMESPACE
537
538#include "moc_qaudiosource.cpp"
Combined button and popup list for selecting options.
static bool validateFormatAtStart(QPlatformAudioSource *d)