1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qaudiodevicefactory_p.h" |
41 | #include "qaudiosystem.h" |
42 | #include "qaudiodeviceinfo.h" |
43 | |
44 | #include <QtCore/qmap.h> |
45 | |
46 | QT_BEGIN_NAMESPACE |
47 | |
48 | static void qRegisterAudioDeviceInfoMetaTypes() |
49 | { |
50 | qRegisterMetaType<QAudioDeviceInfo>(); |
51 | } |
52 | |
53 | Q_CONSTRUCTOR_FUNCTION(qRegisterAudioDeviceInfoMetaTypes) |
54 | |
55 | class QAudioDeviceInfoPrivate : public QSharedData |
56 | { |
57 | public: |
58 | QAudioDeviceInfoPrivate() |
59 | : mode(QAudio::AudioOutput) |
60 | , info(nullptr) |
61 | { |
62 | } |
63 | |
64 | QAudioDeviceInfoPrivate(const QString &r, const QByteArray &h, QAudio::Mode m): |
65 | realm(r), handle(h), mode(m) |
66 | { |
67 | if (!handle.isEmpty()) |
68 | info = QAudioDeviceFactory::audioDeviceInfo(realm, handle, mode); |
69 | else |
70 | info = nullptr; |
71 | } |
72 | |
73 | QAudioDeviceInfoPrivate(const QAudioDeviceInfoPrivate &other): |
74 | QSharedData(other), |
75 | realm(other.realm), handle(other.handle), mode(other.mode) |
76 | { |
77 | info = QAudioDeviceFactory::audioDeviceInfo(realm, handle, mode); |
78 | } |
79 | |
80 | QAudioDeviceInfoPrivate& operator=(const QAudioDeviceInfoPrivate &other) |
81 | { |
82 | delete info; |
83 | |
84 | realm = other.realm; |
85 | handle = other.handle; |
86 | mode = other.mode; |
87 | info = QAudioDeviceFactory::audioDeviceInfo(realm, handle, mode); |
88 | return *this; |
89 | } |
90 | |
91 | ~QAudioDeviceInfoPrivate() |
92 | { |
93 | delete info; |
94 | } |
95 | |
96 | QString realm; |
97 | QByteArray handle; |
98 | QAudio::Mode mode; |
99 | QAbstractAudioDeviceInfo* info; |
100 | }; |
101 | |
102 | |
103 | /*! |
104 | \class QAudioDeviceInfo |
105 | \brief The QAudioDeviceInfo class provides an interface to query audio devices and their functionality. |
106 | \inmodule QtMultimedia |
107 | \ingroup multimedia |
108 | \ingroup multimedia_audio |
109 | |
110 | QAudioDeviceInfo lets you query for audio devices--such as sound |
111 | cards and USB headsets--that are currently available on the system. |
112 | The audio devices available are dependent on the platform or audio plugins installed. |
113 | |
114 | A QAudioDeviceInfo is used by Qt to construct |
115 | classes that communicate with the device--such as |
116 | QAudioInput, and QAudioOutput. |
117 | |
118 | You can also query each device for the formats it supports. A |
119 | format in this context is a set consisting of a specific byte |
120 | order, channel, codec, frequency, sample rate, and sample type. A |
121 | format is represented by the QAudioFormat class. |
122 | |
123 | The values supported by the device for each of these |
124 | parameters can be fetched with |
125 | supportedByteOrders(), supportedChannelCounts(), supportedCodecs(), |
126 | supportedSampleRates(), supportedSampleSizes(), and |
127 | supportedSampleTypes(). The combinations supported are dependent on the platform, |
128 | audio plugins installed and the audio device capabilities. If you need a |
129 | specific format, you can check if |
130 | the device supports it with isFormatSupported(), or fetch a |
131 | supported format that is as close as possible to the format with |
132 | nearestFormat(). For instance: |
133 | |
134 | \snippet multimedia-snippets/audio.cpp Setting audio format |
135 | |
136 | The static |
137 | functions defaultInputDevice(), defaultOutputDevice(), and |
138 | availableDevices() let you get a list of all available |
139 | devices. Devices are fetched according to the value of mode |
140 | this is specified by the \l {QAudio}::Mode enum. |
141 | The QAudioDeviceInfo returned are only valid for the \l {QAudio}::Mode. |
142 | |
143 | For instance: |
144 | |
145 | \snippet multimedia-snippets/audio.cpp Dumping audio formats |
146 | |
147 | In this code sample, we loop through all devices that are able to output |
148 | sound, i.e., play an audio stream in a supported format. For each device we |
149 | find, we simply print the deviceName(). |
150 | |
151 | \sa QAudioOutput, QAudioInput |
152 | */ |
153 | |
154 | /*! |
155 | Constructs an empty QAudioDeviceInfo object. |
156 | */ |
157 | QAudioDeviceInfo::QAudioDeviceInfo(): |
158 | d(new QAudioDeviceInfoPrivate) |
159 | { |
160 | } |
161 | |
162 | /*! |
163 | Constructs a copy of \a other. |
164 | */ |
165 | QAudioDeviceInfo::QAudioDeviceInfo(const QAudioDeviceInfo& other): |
166 | d(other.d) |
167 | { |
168 | } |
169 | |
170 | /*! |
171 | Destroy this audio device info. |
172 | */ |
173 | QAudioDeviceInfo::~QAudioDeviceInfo() |
174 | { |
175 | } |
176 | |
177 | /*! |
178 | Sets the QAudioDeviceInfo object to be equal to \a other. |
179 | */ |
180 | QAudioDeviceInfo& QAudioDeviceInfo::operator=(const QAudioDeviceInfo &other) |
181 | { |
182 | d = other.d; |
183 | return *this; |
184 | } |
185 | |
186 | /*! |
187 | Returns true if this QAudioDeviceInfo class represents the |
188 | same audio device as \a other. |
189 | */ |
190 | bool QAudioDeviceInfo::operator ==(const QAudioDeviceInfo &other) const |
191 | { |
192 | if (d == other.d) |
193 | return true; |
194 | if (d->realm == other.d->realm |
195 | && d->mode == other.d->mode |
196 | && d->handle == other.d->handle |
197 | && deviceName() == other.deviceName()) |
198 | return true; |
199 | return false; |
200 | } |
201 | |
202 | /*! |
203 | Returns true if this QAudioDeviceInfo class represents a |
204 | different audio device than \a other |
205 | */ |
206 | bool QAudioDeviceInfo::operator !=(const QAudioDeviceInfo &other) const |
207 | { |
208 | return !operator==(other); |
209 | } |
210 | |
211 | /*! |
212 | Returns whether this QAudioDeviceInfo object holds a valid device definition. |
213 | */ |
214 | bool QAudioDeviceInfo::isNull() const |
215 | { |
216 | return d->info == nullptr; |
217 | } |
218 | |
219 | /*! |
220 | Returns the human readable name of the audio device. |
221 | |
222 | Device names vary depending on the platform/audio plugin being used. |
223 | |
224 | They are a unique string identifier for the audio device. |
225 | |
226 | eg. default, Intel, U0x46d0x9a4 |
227 | */ |
228 | QString QAudioDeviceInfo::deviceName() const |
229 | { |
230 | return isNull() ? QString() : d->info->deviceName(); |
231 | } |
232 | |
233 | /*! |
234 | Returns true if the supplied \a settings are supported by the audio |
235 | device described by this QAudioDeviceInfo. |
236 | */ |
237 | bool QAudioDeviceInfo::isFormatSupported(const QAudioFormat &settings) const |
238 | { |
239 | return isNull() ? false : d->info->isFormatSupported(format: settings); |
240 | } |
241 | |
242 | /*! |
243 | Returns the default audio format settings for this device. |
244 | |
245 | These settings are provided by the platform/audio plugin being used. |
246 | |
247 | They are also dependent on the \l {QAudio}::Mode being used. |
248 | |
249 | A typical audio system would provide something like: |
250 | \list |
251 | \li Input settings: 8000Hz mono 8 bit. |
252 | \li Output settings: 44100Hz stereo 16 bit little endian. |
253 | \endlist |
254 | */ |
255 | QAudioFormat QAudioDeviceInfo::preferredFormat() const |
256 | { |
257 | return isNull() ? QAudioFormat() : d->info->preferredFormat(); |
258 | } |
259 | |
260 | /*! |
261 | Returns the closest QAudioFormat to the supplied \a settings that the system supports. |
262 | |
263 | These settings are provided by the platform/audio plugin being used. |
264 | |
265 | They are also dependent on the \l {QAudio}::Mode being used. |
266 | */ |
267 | QAudioFormat QAudioDeviceInfo::nearestFormat(const QAudioFormat &settings) const |
268 | { |
269 | if (isFormatSupported(settings)) |
270 | return settings; |
271 | |
272 | QAudioFormat nearest = settings; |
273 | |
274 | QList<QString> testCodecs = supportedCodecs(); |
275 | QList<int> testChannels = supportedChannelCounts(); |
276 | QList<QAudioFormat::Endian> testByteOrders = supportedByteOrders(); |
277 | QList<QAudioFormat::SampleType> testSampleTypes; |
278 | QList<QAudioFormat::SampleType> sampleTypesAvailable = supportedSampleTypes(); |
279 | QMap<int,int> testSampleRates; |
280 | QList<int> sampleRatesAvailable = supportedSampleRates(); |
281 | QMap<int,int> testSampleSizes; |
282 | QList<int> sampleSizesAvailable = supportedSampleSizes(); |
283 | |
284 | // Get sorted lists for checking |
285 | if (testCodecs.contains(t: settings.codec())) { |
286 | testCodecs.removeAll(t: settings.codec()); |
287 | testCodecs.insert(i: 0, t: settings.codec()); |
288 | } |
289 | testChannels.removeAll(t: settings.channelCount()); |
290 | testChannels.insert(i: 0, t: settings.channelCount()); |
291 | testByteOrders.removeAll(t: settings.byteOrder()); |
292 | testByteOrders.insert(i: 0, t: settings.byteOrder()); |
293 | |
294 | if (sampleTypesAvailable.contains(t: settings.sampleType())) |
295 | testSampleTypes.append(t: settings.sampleType()); |
296 | if (sampleTypesAvailable.contains(t: QAudioFormat::SignedInt)) |
297 | testSampleTypes.append(t: QAudioFormat::SignedInt); |
298 | if (sampleTypesAvailable.contains(t: QAudioFormat::UnSignedInt)) |
299 | testSampleTypes.append(t: QAudioFormat::UnSignedInt); |
300 | if (sampleTypesAvailable.contains(t: QAudioFormat::Float)) |
301 | testSampleTypes.append(t: QAudioFormat::Float); |
302 | |
303 | if (sampleSizesAvailable.contains(t: settings.sampleSize())) |
304 | testSampleSizes.insert(akey: 0,avalue: settings.sampleSize()); |
305 | sampleSizesAvailable.removeAll(t: settings.sampleSize()); |
306 | for (int size : qAsConst(t&: sampleSizesAvailable)) { |
307 | int larger = (size > settings.sampleSize()) ? size : settings.sampleSize(); |
308 | int smaller = (size > settings.sampleSize()) ? settings.sampleSize() : size; |
309 | bool isMultiple = ( 0 == (larger % smaller)); |
310 | int diff = larger - smaller; |
311 | testSampleSizes.insert(akey: (isMultiple ? diff : diff+100000), avalue: size); |
312 | } |
313 | if (sampleRatesAvailable.contains(t: settings.sampleRate())) |
314 | testSampleRates.insert(akey: 0,avalue: settings.sampleRate()); |
315 | sampleRatesAvailable.removeAll(t: settings.sampleRate()); |
316 | for (int sampleRate : qAsConst(t&: sampleRatesAvailable)) { |
317 | int larger = (sampleRate > settings.sampleRate()) ? sampleRate : settings.sampleRate(); |
318 | int smaller = (sampleRate > settings.sampleRate()) ? settings.sampleRate() : sampleRate; |
319 | bool isMultiple = ( 0 == (larger % smaller)); |
320 | int diff = larger - smaller; |
321 | testSampleRates.insert(akey: (isMultiple ? diff : diff+100000), avalue: sampleRate); |
322 | } |
323 | |
324 | // Try to find nearest |
325 | for (const QString &codec : qAsConst(t&: testCodecs)) { |
326 | nearest.setCodec(codec); |
327 | for (QAudioFormat::Endian order : qAsConst(t&: testByteOrders)) { |
328 | nearest.setByteOrder(order); |
329 | for (QAudioFormat::SampleType sample : qAsConst(t&: testSampleTypes)) { |
330 | nearest.setSampleType(sample); |
331 | for (int sampleSize : qAsConst(t&: testSampleSizes)) { |
332 | nearest.setSampleSize(sampleSize); |
333 | for (int channel : qAsConst(t&: testChannels)) { |
334 | nearest.setChannelCount(channel); |
335 | for (int sampleRate : qAsConst(t&: testSampleRates)) { |
336 | nearest.setSampleRate(sampleRate); |
337 | if (isFormatSupported(settings: nearest)) |
338 | return nearest; |
339 | } |
340 | } |
341 | } |
342 | } |
343 | } |
344 | } |
345 | //Fallback |
346 | return preferredFormat(); |
347 | } |
348 | |
349 | /*! |
350 | Returns a list of supported codecs. |
351 | |
352 | All platform and plugin implementations should provide support for: |
353 | |
354 | "audio/pcm" - Linear PCM |
355 | |
356 | For writing plugins to support additional codecs refer to: |
357 | |
358 | http://www.iana.org/assignments/media-types/audio/ |
359 | */ |
360 | QStringList QAudioDeviceInfo::supportedCodecs() const |
361 | { |
362 | return isNull() ? QStringList() : d->info->supportedCodecs(); |
363 | } |
364 | |
365 | /*! |
366 | Returns a list of supported sample rates (in Hertz). |
367 | |
368 | */ |
369 | QList<int> QAudioDeviceInfo::supportedSampleRates() const |
370 | { |
371 | return isNull() ? QList<int>() : d->info->supportedSampleRates(); |
372 | } |
373 | |
374 | /*! |
375 | Returns a list of supported channel counts. |
376 | |
377 | This is typically 1 for mono sound, or 2 for stereo sound. |
378 | |
379 | */ |
380 | QList<int> QAudioDeviceInfo::supportedChannelCounts() const |
381 | { |
382 | return isNull() ? QList<int>() : d->info->supportedChannelCounts(); |
383 | } |
384 | |
385 | /*! |
386 | Returns a list of supported sample sizes (in bits). |
387 | |
388 | Typically this will include 8 and 16 bit sample sizes. |
389 | |
390 | */ |
391 | QList<int> QAudioDeviceInfo::supportedSampleSizes() const |
392 | { |
393 | return isNull() ? QList<int>() : d->info->supportedSampleSizes(); |
394 | } |
395 | |
396 | /*! |
397 | Returns a list of supported byte orders. |
398 | */ |
399 | QList<QAudioFormat::Endian> QAudioDeviceInfo::supportedByteOrders() const |
400 | { |
401 | return isNull() ? QList<QAudioFormat::Endian>() : d->info->supportedByteOrders(); |
402 | } |
403 | |
404 | /*! |
405 | Returns a list of supported sample types. |
406 | */ |
407 | QList<QAudioFormat::SampleType> QAudioDeviceInfo::supportedSampleTypes() const |
408 | { |
409 | return isNull() ? QList<QAudioFormat::SampleType>() : d->info->supportedSampleTypes(); |
410 | } |
411 | |
412 | /*! |
413 | Returns the information for the default input audio device. |
414 | All platform and audio plugin implementations provide a default audio device to use. |
415 | */ |
416 | QAudioDeviceInfo QAudioDeviceInfo::defaultInputDevice() |
417 | { |
418 | return QAudioDeviceFactory::defaultDevice(mode: QAudio::AudioInput); |
419 | } |
420 | |
421 | /*! |
422 | Returns the information for the default output audio device. |
423 | All platform and audio plugin implementations provide a default audio device to use. |
424 | */ |
425 | QAudioDeviceInfo QAudioDeviceInfo::defaultOutputDevice() |
426 | { |
427 | return QAudioDeviceFactory::defaultDevice(mode: QAudio::AudioOutput); |
428 | } |
429 | |
430 | /*! |
431 | Returns a list of audio devices that support \a mode. |
432 | */ |
433 | QList<QAudioDeviceInfo> QAudioDeviceInfo::availableDevices(QAudio::Mode mode) |
434 | { |
435 | return QAudioDeviceFactory::availableDevices(mode); |
436 | } |
437 | |
438 | |
439 | /*! |
440 | \internal |
441 | */ |
442 | QAudioDeviceInfo::QAudioDeviceInfo(const QString &realm, const QByteArray &handle, QAudio::Mode mode): |
443 | d(new QAudioDeviceInfoPrivate(realm, handle, mode)) |
444 | { |
445 | } |
446 | |
447 | /*! |
448 | Returns the key that represents the audio plugin. |
449 | |
450 | \since 5.14 |
451 | \sa QAudioSystemPlugin |
452 | */ |
453 | QString QAudioDeviceInfo::realm() const |
454 | { |
455 | return d->realm; |
456 | } |
457 | |
458 | /*! |
459 | \internal |
460 | */ |
461 | QByteArray QAudioDeviceInfo::handle() const |
462 | { |
463 | return d->handle; |
464 | } |
465 | |
466 | |
467 | /*! |
468 | \internal |
469 | */ |
470 | QAudio::Mode QAudioDeviceInfo::mode() const |
471 | { |
472 | return d->mode; |
473 | } |
474 | |
475 | QT_END_NAMESPACE |
476 | |
477 | |