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 error() to QtAudio::NoError.
344 Sets state() to QtAudio::ActiveState if you previously called start(QIODevice*).
345 Sets state() to QtAudio::IdleState if you previously called start().
346 emits stateChanged() signal.
347*/
348
349void QAudioSource::resume()
350{
351 if (d)
352 d->resume();
353}
354
355/*!
356 Sets the audio buffer size to \a value bytes.
357
358 \note This function can be called anytime before start(), calls to this
359 are ignored after start(). It should not be assumed that the buffer size
360 set is the actual buffer size used, calling bufferSize() anytime after start()
361 will return the actual buffer size being used.
362
363 \sa setBufferFrameCount
364 \since 6.10
365*/
366
367void QAudioSource::setBufferSize(qsizetype value)
368{
369 if (d)
370 d->setBufferSize(value);
371}
372
373/*!
374 Returns the audio buffer size in bytes.
375
376 If called before \l start(), returns platform default value.
377 If called before \c start() but \l setBufferSize() or \l setBufferFrameCount() was called prior, returns
378 value set by \c setBufferSize() or \c setBufferFrameCount(). If called after \c start(), returns the actual
379 buffer size being used. This may not be what was set previously by
380 \c setBufferSize() or \c setBufferFrameCount().
381
382 \sa bufferFrameCount
383 \since 6.10
384*/
385
386qsizetype QAudioSource::bufferSize() const
387{
388 return d ? d->bufferSize() : 0;
389}
390
391/*!
392 Sets the audio buffer size to \a value in frame count.
393
394 \note This function can be called anytime before start(). Calls to this
395 are ignored after start(). It should not be assumed that the buffer size
396 set is the actual buffer size used - call bufferFrameCount() anytime
397 after start() to return the actual buffer size being used.
398
399 \sa setBufferSize
400*/
401
402void QAudioSource::setBufferFrameCount(qsizetype value)
403{
404 if (d)
405 setBufferSize(d->format().bytesForFrames(value));
406}
407
408/*!
409 Returns the audio buffer size in frames.
410
411 If called before \l start(), returns platform default value.
412 If called before \c start() but \l setBufferSize() or \l setBufferFrameCount() was called prior, returns
413 value set by \c setBufferSize() or \c setBufferFrameCount(). If called after \c start(), returns the actual
414 buffer size being used. This may not be what was set previously by
415 \c setBufferSize() or \c setBufferFrameCount().
416
417 \sa bufferSize
418*/
419
420qsizetype QAudioSource::bufferFrameCount() const
421{
422 return d ? d->format().framesForBytes(bufferSize()) : 0;
423}
424
425/*!
426 Returns the amount of audio data available to read in bytes.
427
428 \note returned value is only valid while in QtAudio::ActiveState or QtAudio::IdleState
429 state, otherwise returns zero.
430
431 \sa framesAvailable
432*/
433
434qsizetype QAudioSource::bytesAvailable() const
435{
436 return d ? d->bytesReady() : 0;
437}
438
439/*!
440 Returns the amount of audio data available to read in frames.
441
442 Note: returned value is only valid while in QtAudio::ActiveState or QtAudio::IdleState
443 state, otherwise returns zero.
444
445 \sa bytesAvailable
446 \since 6.10
447*/
448
449qsizetype QAudioSource::framesAvailable() const
450{
451
452 return d ? d->format().framesForBytes(bytesAvailable()) : 0;
453}
454
455/*!
456 Sets the input volume to \a volume.
457
458 The volume is scaled linearly from \c 0.0 (silence) to \c 1.0 (full volume). Values outside this
459 range will be clamped.
460
461 If the device does not support adjusting the input
462 volume then \a volume will be ignored and the input
463 volume will remain at 1.0.
464
465 The default volume is \c 1.0.
466
467 \note Adjustments to the volume will change the volume of this audio stream, not the global
468 volume.
469*/
470void QAudioSource::setVolume(qreal volume)
471{
472 if (!d)
473 return;
474
475 std::optional<float> newVolume = QAudioHelperInternal::sanitizeVolume(volume, this->volume());
476 if (newVolume)
477 d->setVolume(*newVolume);
478}
479
480/*!
481 Returns the input volume.
482
483 If the device does not support adjusting the input volume
484 the returned value will be 1.0.
485*/
486qreal QAudioSource::volume() const
487{
488 return d ? d->volume() : 1.0;
489}
490
491/*!
492 Returns the amount of audio data processed since start()
493 was called in microseconds.
494*/
495
496qint64 QAudioSource::processedUSecs() const
497{
498 return d ? d->processedUSecs() : 0;
499}
500
501/*!
502 Returns the microseconds since start() was called, including time in Idle and
503 Suspend states.
504*/
505
506qint64 QAudioSource::elapsedUSecs() const
507{
508 return state() == QAudio::StoppedState ? 0 : d->elapsedTime.nsecsElapsed()/1000;
509}
510
511/*!
512 Returns the error state.
513*/
514
515QtAudio::Error QAudioSource::error() const
516{
517 return d ? d->error() : QAudio::OpenError;
518}
519
520/*!
521 Returns the state of audio processing.
522*/
523
524QtAudio::State QAudioSource::state() const
525{
526 return d ? d->state() : QAudio::StoppedState;
527}
528
529/*!
530 \fn QAudioSource::stateChanged(QtAudio::State state)
531 This signal is emitted when the device \a state has changed.
532
533 \note The QtAudio namespace was named QAudio up to and including Qt 6.6.
534 String-based connections to this signal have to use \c{QAudio::State} as
535 the parameter type: \c{connect(source, SIGNAL(stateChanged(QAudio::State)), ...);}
536*/
537
538QT_END_NAMESPACE
539
540#include "moc_qaudiosource.cpp"
Combined button and popup list for selecting options.
static bool validateFormatAtStart(QPlatformAudioSource *d)