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 "qaudiosource.h" |
9 | |
10 | #include <private/qplatformmediadevices_p.h> |
11 | #include <private/qplatformmediaintegration_p.h> |
12 | |
13 | QT_BEGIN_NAMESPACE |
14 | |
15 | /*! |
16 | \class QAudioSource |
17 | \brief The QAudioSource class provides an interface for receiving audio data from an audio input device. |
18 | |
19 | \inmodule QtMultimedia |
20 | \ingroup multimedia |
21 | \ingroup multimedia_audio |
22 | |
23 | You can construct an audio input with the system's |
24 | default audio input device. It is also possible to |
25 | create QAudioSource with a specific QAudioDevice. When |
26 | you create the audio input, you should also send in the |
27 | QAudioFormat to be used for the recording (see the QAudioFormat |
28 | class description for details). |
29 | |
30 | To record to a file: |
31 | |
32 | QAudioSource lets you record audio with an audio input device. The |
33 | default constructor of this class will use the systems default |
34 | audio device, but you can also specify a QAudioDevice for a |
35 | specific device. You also need to pass in the QAudioFormat in |
36 | which you wish to record. |
37 | |
38 | Starting up the QAudioSource is simply a matter of calling start() |
39 | with a QIODevice opened for writing. For instance, to record to a |
40 | file, you can: |
41 | |
42 | \snippet multimedia-snippets/audio.cpp Audio input class members |
43 | |
44 | \snippet multimedia-snippets/audio.cpp Audio input setup |
45 | |
46 | This will start recording if the format specified is supported by |
47 | the input device (you can check this with |
48 | QAudioDevice::isFormatSupported(). In case there are any |
49 | snags, use the error() function to check what went wrong. We stop |
50 | recording in the \c stopRecording() slot. |
51 | |
52 | \snippet multimedia-snippets/audio.cpp Audio input stop recording |
53 | |
54 | At any point in time, QAudioSource will be in one of four states: |
55 | active, suspended, stopped, or idle. These states are specified by |
56 | the QAudio::State enum. You can request a state change directly through |
57 | suspend(), resume(), stop(), reset(), and start(). The current |
58 | state is reported by state(). QAudioSink will also signal you |
59 | when the state changes (stateChanged()). |
60 | |
61 | QAudioSource provides several ways of measuring the time that has |
62 | passed since the start() of the recording. The \c processedUSecs() |
63 | function returns the length of the stream in microseconds written, |
64 | i.e., it leaves out the times the audio input was suspended or idle. |
65 | The elapsedUSecs() function returns the time elapsed since start() was called regardless of |
66 | which states the QAudioSource has been in. |
67 | |
68 | If an error should occur, you can fetch its reason with error(). |
69 | The possible error reasons are described by the QAudio::Error |
70 | enum. The QAudioSource will enter the \l{QAudio::}{StoppedState} when |
71 | an error is encountered. Connect to the stateChanged() signal to |
72 | handle the error: |
73 | |
74 | \snippet multimedia-snippets/audio.cpp Audio input state changed |
75 | |
76 | \sa QAudioSink, QAudioDevice |
77 | */ |
78 | |
79 | /*! |
80 | Construct a new audio input and attach it to \a parent. |
81 | The default audio input device is used with the output |
82 | \a format parameters. |
83 | */ |
84 | |
85 | QAudioSource::QAudioSource(const QAudioFormat &format, QObject *parent) |
86 | : QAudioSource({}, format, parent) |
87 | { |
88 | } |
89 | |
90 | /*! |
91 | Construct a new audio input and attach it to \a parent. |
92 | The device referenced by \a audioDevice is used with the input |
93 | \a format parameters. |
94 | */ |
95 | |
96 | QAudioSource::QAudioSource(const QAudioDevice &audioDevice, const QAudioFormat &format, QObject *parent): |
97 | QObject(parent) |
98 | { |
99 | d = QPlatformMediaDevices::instance()->audioInputDevice(format, deviceInfo: audioDevice, parent); |
100 | if (d) { |
101 | connect(sender: d, signal: &QPlatformAudioSource::stateChanged, context: this, slot: [this](QAudio::State state) { |
102 | // if the signal has been emitted from another thread, |
103 | // the state may be already changed by main one |
104 | if (state == d->state()) |
105 | emit stateChanged(state); |
106 | }); |
107 | } |
108 | else |
109 | qWarning() << ("No audio device detected" ); |
110 | |
111 | } |
112 | |
113 | /*! |
114 | \fn bool QAudioSource::isNull() const |
115 | |
116 | Returns \c true if the audio source is \c null, otherwise returns \c false. |
117 | */ |
118 | |
119 | /*! |
120 | Destroy this audio input. |
121 | */ |
122 | |
123 | QAudioSource::~QAudioSource() |
124 | { |
125 | delete d; |
126 | } |
127 | |
128 | /*! |
129 | Starts transferring audio data from the system's audio input to the \a device. |
130 | The \a device must have been opened in the \l{QIODevice::WriteOnly}{WriteOnly}, |
131 | \l{QIODevice::Append}{Append} or \l{QIODevice::ReadWrite}{ReadWrite} modes. |
132 | |
133 | If the QAudioSource is able to successfully get audio data, state() returns |
134 | either QAudio::ActiveState or QAudio::IdleState, 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 | */ |
142 | |
143 | void QAudioSource::start(QIODevice* device) |
144 | { |
145 | if (!d) |
146 | return; |
147 | d->elapsedTime.start(); |
148 | d->start(device); |
149 | } |
150 | |
151 | /*! |
152 | Returns a pointer to the internal QIODevice being used to transfer data from |
153 | the system's audio input. The device will already be open and |
154 | \l{QIODevice::read()}{read()} can read data directly from it. |
155 | |
156 | \note The pointer will become invalid after the stream is stopped or |
157 | if you start another stream. |
158 | |
159 | If the QAudioSource is able to access the system's audio device, state() returns |
160 | QAudio::IdleState, error() returns QAudio::NoError |
161 | and the stateChanged() signal is emitted. |
162 | |
163 | If a problem occurs during this process, error() returns QAudio::OpenError, |
164 | state() returns QAudio::StoppedState and the stateChanged() signal is emitted. |
165 | |
166 | \sa QIODevice |
167 | */ |
168 | |
169 | QIODevice* QAudioSource::start() |
170 | { |
171 | if (!d) |
172 | return nullptr; |
173 | d->elapsedTime.start(); |
174 | return d->start(); |
175 | } |
176 | |
177 | /*! |
178 | Returns the QAudioFormat being used. |
179 | */ |
180 | |
181 | QAudioFormat QAudioSource::format() const |
182 | { |
183 | return d ? d->format() : QAudioFormat(); |
184 | } |
185 | |
186 | /*! |
187 | Stops the audio input, detaching from the system resource. |
188 | |
189 | Sets error() to QAudio::NoError, state() to QAudio::StoppedState and |
190 | emit stateChanged() signal. |
191 | */ |
192 | |
193 | void QAudioSource::stop() |
194 | { |
195 | if (d) |
196 | d->stop(); |
197 | } |
198 | |
199 | /*! |
200 | Drops all audio data in the buffers, resets buffers to zero. |
201 | */ |
202 | |
203 | void QAudioSource::reset() |
204 | { |
205 | if (d) |
206 | d->reset(); |
207 | } |
208 | |
209 | /*! |
210 | Stops processing audio data, preserving buffered audio data. |
211 | |
212 | Sets error() to QAudio::NoError, state() to QAudio::SuspendedState and |
213 | emit stateChanged() signal. |
214 | */ |
215 | |
216 | void QAudioSource::suspend() |
217 | { |
218 | if (d) |
219 | d->suspend(); |
220 | } |
221 | |
222 | /*! |
223 | Resumes processing audio data after a suspend(). |
224 | |
225 | Sets error() to QAudio::NoError. |
226 | Sets state() to QAudio::ActiveState if you previously called start(QIODevice*). |
227 | Sets state() to QAudio::IdleState if you previously called start(). |
228 | emits stateChanged() signal. |
229 | */ |
230 | |
231 | void QAudioSource::resume() |
232 | { |
233 | if (d) |
234 | d->resume(); |
235 | } |
236 | |
237 | /*! |
238 | Sets the audio buffer size to \a value bytes. |
239 | |
240 | Note: This function can be called anytime before start(), calls to this |
241 | are ignored after start(). It should not be assumed that the buffer size |
242 | set is the actual buffer size used, calling bufferSize() anytime after start() |
243 | will return the actual buffer size being used. |
244 | |
245 | */ |
246 | |
247 | void QAudioSource::setBufferSize(qsizetype value) |
248 | { |
249 | if (d) |
250 | d->setBufferSize(value); |
251 | } |
252 | |
253 | /*! |
254 | Returns the audio buffer size in bytes. |
255 | |
256 | If called before start(), returns platform default value. |
257 | If called before start() but setBufferSize() was called prior, returns value set by setBufferSize(). |
258 | If called after start(), returns the actual buffer size being used. This may not be what was set previously |
259 | by setBufferSize(). |
260 | |
261 | */ |
262 | |
263 | qsizetype QAudioSource::bufferSize() const |
264 | { |
265 | return d ? d->bufferSize() : 0; |
266 | } |
267 | |
268 | /*! |
269 | Returns the amount of audio data available to read in bytes. |
270 | |
271 | Note: returned value is only valid while in QAudio::ActiveState or QAudio::IdleState |
272 | state, otherwise returns zero. |
273 | */ |
274 | |
275 | qsizetype QAudioSource::bytesAvailable() const |
276 | { |
277 | /* |
278 | -If not ActiveState|IdleState, return 0 |
279 | -return amount of audio data available to read |
280 | */ |
281 | return d ? d->bytesReady() : 0; |
282 | } |
283 | |
284 | /*! |
285 | Sets the input volume to \a volume. |
286 | |
287 | The volume is scaled linearly from \c 0.0 (silence) to \c 1.0 (full volume). Values outside this |
288 | range will be clamped. |
289 | |
290 | If the device does not support adjusting the input |
291 | volume then \a volume will be ignored and the input |
292 | volume will remain at 1.0. |
293 | |
294 | The default volume is \c 1.0. |
295 | |
296 | Note: Adjustments to the volume will change the volume of this audio stream, not the global volume. |
297 | */ |
298 | void QAudioSource::setVolume(qreal volume) |
299 | { |
300 | if (!d) |
301 | return; |
302 | qreal v = qBound(min: qreal(0.0), val: volume, max: qreal(1.0)); |
303 | d->setVolume(v); |
304 | } |
305 | |
306 | /*! |
307 | Returns the input volume. |
308 | |
309 | If the device does not support adjusting the input volume |
310 | the returned value will be 1.0. |
311 | */ |
312 | qreal QAudioSource::volume() const |
313 | { |
314 | return d ? d->volume() : 1.0; |
315 | } |
316 | |
317 | /*! |
318 | Returns the amount of audio data processed since start() |
319 | was called in microseconds. |
320 | */ |
321 | |
322 | qint64 QAudioSource::processedUSecs() const |
323 | { |
324 | return d ? d->processedUSecs() : 0; |
325 | } |
326 | |
327 | /*! |
328 | Returns the microseconds since start() was called, including time in Idle and |
329 | Suspend states. |
330 | */ |
331 | |
332 | #include <qdebug.h> |
333 | |
334 | qint64 QAudioSource::elapsedUSecs() const |
335 | { |
336 | return state() == QAudio::StoppedState ? 0 : d->elapsedTime.nsecsElapsed()/1000; |
337 | } |
338 | |
339 | /*! |
340 | Returns the error state. |
341 | */ |
342 | |
343 | QAudio::Error QAudioSource::error() const |
344 | { |
345 | return d ? d->error() : QAudio::OpenError; |
346 | } |
347 | |
348 | /*! |
349 | Returns the state of audio processing. |
350 | */ |
351 | |
352 | QAudio::State QAudioSource::state() const |
353 | { |
354 | return d ? d->state() : QAudio::StoppedState; |
355 | } |
356 | |
357 | /*! |
358 | \fn QAudioSource::stateChanged(QAudio::State state) |
359 | This signal is emitted when the device \a state has changed. |
360 | */ |
361 | |
362 | QT_END_NAMESPACE |
363 | |
364 | #include "moc_qaudiosource.cpp" |
365 | |
366 | |