source: trunk/src/multimedia/audio/qaudiodeviceinfo_alsa_p.cpp@ 568

Last change on this file since 568 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

  • Property svn:eol-style set to native
File size: 15.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtMultimedia module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42//
43// W A R N I N G
44// -------------
45//
46// This file is not part of the Qt API. It exists for the convenience
47// of other Qt classes. This header file may change from version to
48// version without notice, or even be removed.
49//
50// We mean it.
51//
52
53#include "qaudiodeviceinfo_alsa_p.h"
54
55#include <alsa/version.h>
56
57QT_BEGIN_NAMESPACE
58
59QAudioDeviceInfoInternal::QAudioDeviceInfoInternal(QByteArray dev, QAudio::Mode mode)
60{
61 handle = 0;
62
63 device = QLatin1String(dev);
64 this->mode = mode;
65}
66
67QAudioDeviceInfoInternal::~QAudioDeviceInfoInternal()
68{
69 close();
70}
71
72bool QAudioDeviceInfoInternal::isFormatSupported(const QAudioFormat& format) const
73{
74 return testSettings(format);
75}
76
77QAudioFormat QAudioDeviceInfoInternal::preferredFormat() const
78{
79 QAudioFormat nearest;
80 if(mode == QAudio::AudioOutput) {
81 nearest.setFrequency(44100);
82 nearest.setChannels(2);
83 nearest.setByteOrder(QAudioFormat::LittleEndian);
84 nearest.setSampleType(QAudioFormat::SignedInt);
85 nearest.setSampleSize(16);
86 nearest.setCodec(QLatin1String("audio/pcm"));
87 } else {
88 nearest.setFrequency(8000);
89 nearest.setChannels(1);
90 nearest.setSampleType(QAudioFormat::UnSignedInt);
91 nearest.setSampleSize(8);
92 nearest.setCodec(QLatin1String("audio/pcm"));
93 if(!testSettings(nearest)) {
94 nearest.setChannels(2);
95 nearest.setSampleSize(16);
96 nearest.setSampleType(QAudioFormat::SignedInt);
97 }
98 }
99 return nearest;
100}
101
102QAudioFormat QAudioDeviceInfoInternal::nearestFormat(const QAudioFormat& format) const
103{
104 if(testSettings(format))
105 return format;
106 else
107 return preferredFormat();
108}
109
110QString QAudioDeviceInfoInternal::deviceName() const
111{
112 return device;
113}
114
115QStringList QAudioDeviceInfoInternal::codecList()
116{
117 updateLists();
118 return codecz;
119}
120
121QList<int> QAudioDeviceInfoInternal::frequencyList()
122{
123 updateLists();
124 return freqz;
125}
126
127QList<int> QAudioDeviceInfoInternal::channelsList()
128{
129 updateLists();
130 return channelz;
131}
132
133QList<int> QAudioDeviceInfoInternal::sampleSizeList()
134{
135 updateLists();
136 return sizez;
137}
138
139QList<QAudioFormat::Endian> QAudioDeviceInfoInternal::byteOrderList()
140{
141 updateLists();
142 return byteOrderz;
143}
144
145QList<QAudioFormat::SampleType> QAudioDeviceInfoInternal::sampleTypeList()
146{
147 updateLists();
148 return typez;
149}
150
151bool QAudioDeviceInfoInternal::open()
152{
153 int err = 0;
154 QString dev = device;
155 QList<QByteArray> devices = availableDevices(mode);
156
157 if(dev.compare(QLatin1String("default")) == 0) {
158#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
159 dev = QLatin1String(devices.first().constData());
160#else
161 dev = QLatin1String("hw:0,0");
162#endif
163 } else {
164#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
165 dev = device;
166#else
167 int idx = 0;
168 char *name;
169
170 QString shortName = device.mid(device.indexOf(QLatin1String("="),0)+1);
171
172 while(snd_card_get_name(idx,&name) == 0) {
173 if(dev.contains(QLatin1String(name)))
174 break;
175 idx++;
176 }
177 dev = QString(QLatin1String("hw:%1,0")).arg(idx);
178#endif
179 }
180 if(mode == QAudio::AudioOutput) {
181 err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0);
182 } else {
183 err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0);
184 }
185 if(err < 0) {
186 handle = 0;
187 return false;
188 }
189 return true;
190}
191
192void QAudioDeviceInfoInternal::close()
193{
194 if(handle)
195 snd_pcm_close(handle);
196 handle = 0;
197}
198
199bool QAudioDeviceInfoInternal::testSettings(const QAudioFormat& format) const
200{
201 // Set nearest to closest settings that do work.
202 // See if what is in settings will work (return value).
203 int err = 0;
204 snd_pcm_t* handle;
205 snd_pcm_hw_params_t *params;
206 QString dev = device;
207
208 QList<QByteArray> devices = QAudioDeviceInfoInternal::availableDevices(QAudio::AudioOutput);
209
210 if(dev.compare(QLatin1String("default")) == 0) {
211#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
212 dev = QLatin1String(devices.first().constData());
213#else
214 dev = QLatin1String("hw:0,0");
215#endif
216 } else {
217#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
218 dev = device;
219#else
220 int idx = 0;
221 char *name;
222
223 QString shortName = device.mid(device.indexOf(QLatin1String("="),0)+1);
224
225 while(snd_card_get_name(idx,&name) == 0) {
226 if(shortName.compare(QLatin1String(name)) == 0)
227 break;
228 idx++;
229 }
230 dev = QString(QLatin1String("hw:%1,0")).arg(idx);
231#endif
232 }
233 if(mode == QAudio::AudioOutput) {
234 err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0);
235 } else {
236 err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0);
237 }
238 if(err < 0) {
239 handle = 0;
240 return false;
241 }
242
243 bool testChannel = false;
244 bool testCodec = false;
245 bool testFreq = false;
246 bool testType = false;
247 bool testSize = false;
248
249 int dir = 0;
250
251 snd_pcm_nonblock( handle, 0 );
252 snd_pcm_hw_params_alloca( &params );
253 snd_pcm_hw_params_any( handle, params );
254
255 // set the values!
256 snd_pcm_hw_params_set_channels(handle,params,format.channels());
257 snd_pcm_hw_params_set_rate(handle,params,format.frequency(),dir);
258 switch(format.sampleSize()) {
259 case 8:
260 if(format.sampleType() == QAudioFormat::SignedInt)
261 snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S8);
262 else if(format.sampleType() == QAudioFormat::UnSignedInt)
263 snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U8);
264 break;
265 case 16:
266 if(format.sampleType() == QAudioFormat::SignedInt) {
267 if(format.byteOrder() == QAudioFormat::LittleEndian)
268 snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_LE);
269 else if(format.byteOrder() == QAudioFormat::BigEndian)
270 snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_BE);
271 } else if(format.sampleType() == QAudioFormat::UnSignedInt) {
272 if(format.byteOrder() == QAudioFormat::LittleEndian)
273 snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_LE);
274 else if(format.byteOrder() == QAudioFormat::BigEndian)
275 snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_BE);
276 }
277 break;
278 case 32:
279 if(format.sampleType() == QAudioFormat::SignedInt) {
280 if(format.byteOrder() == QAudioFormat::LittleEndian)
281 snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_LE);
282 else if(format.byteOrder() == QAudioFormat::BigEndian)
283 snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_BE);
284 } else if(format.sampleType() == QAudioFormat::UnSignedInt) {
285 if(format.byteOrder() == QAudioFormat::LittleEndian)
286 snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_LE);
287 else if(format.byteOrder() == QAudioFormat::BigEndian)
288 snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_BE);
289 }
290 }
291
292 // For now, just accept only audio/pcm codec
293 if(!format.codec().startsWith(QLatin1String("audio/pcm"))) {
294 err=-1;
295 } else
296 testCodec = true;
297
298 if(err>=0 && format.channels() != -1) {
299 err = snd_pcm_hw_params_test_channels(handle,params,format.channels());
300 if(err>=0)
301 err = snd_pcm_hw_params_set_channels(handle,params,format.channels());
302 if(err>=0)
303 testChannel = true;
304 }
305
306 if(err>=0 && format.frequency() != -1) {
307 err = snd_pcm_hw_params_test_rate(handle,params,format.frequency(),0);
308 if(err>=0)
309 err = snd_pcm_hw_params_set_rate(handle,params,format.frequency(),dir);
310 if(err>=0)
311 testFreq = true;
312 }
313
314 if((err>=0 && format.sampleSize() != -1) &&
315 (format.sampleType() != QAudioFormat::Unknown)) {
316 switch(format.sampleSize()) {
317 case 8:
318 if(format.sampleType() == QAudioFormat::SignedInt)
319 err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S8);
320 else if(format.sampleType() == QAudioFormat::UnSignedInt)
321 err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U8);
322 break;
323 case 16:
324 if(format.sampleType() == QAudioFormat::SignedInt) {
325 if(format.byteOrder() == QAudioFormat::LittleEndian)
326 err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_LE);
327 else if(format.byteOrder() == QAudioFormat::BigEndian)
328 err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_BE);
329 } else if(format.sampleType() == QAudioFormat::UnSignedInt) {
330 if(format.byteOrder() == QAudioFormat::LittleEndian)
331 err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_LE);
332 else if(format.byteOrder() == QAudioFormat::BigEndian)
333 err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_BE);
334 }
335 break;
336 case 32:
337 if(format.sampleType() == QAudioFormat::SignedInt) {
338 if(format.byteOrder() == QAudioFormat::LittleEndian)
339 err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_LE);
340 else if(format.byteOrder() == QAudioFormat::BigEndian)
341 err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_BE);
342 } else if(format.sampleType() == QAudioFormat::UnSignedInt) {
343 if(format.byteOrder() == QAudioFormat::LittleEndian)
344 err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_LE);
345 else if(format.byteOrder() == QAudioFormat::BigEndian)
346 err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_BE);
347 }
348 }
349 if(err>=0) {
350 testSize = true;
351 testType = true;
352 }
353 }
354 if(err>=0)
355 err = snd_pcm_hw_params(handle, params);
356
357 if(err == 0) {
358 // settings work
359 // close()
360 if(handle)
361 snd_pcm_close(handle);
362 return true;
363 }
364 if(handle)
365 snd_pcm_close(handle);
366
367 return false;
368}
369
370void QAudioDeviceInfoInternal::updateLists()
371{
372 // redo all lists based on current settings
373 freqz.clear();
374 channelz.clear();
375 sizez.clear();
376 byteOrderz.clear();
377 typez.clear();
378 codecz.clear();
379
380 if(!handle)
381 open();
382
383 if(!handle)
384 return;
385
386 for(int i=0; i<(int)MAX_SAMPLE_RATES; i++) {
387 //if(snd_pcm_hw_params_test_rate(handle, params, SAMPLE_RATES[i], dir) == 0)
388 freqz.append(SAMPLE_RATES[i]);
389 }
390 channelz.append(1);
391 channelz.append(2);
392 sizez.append(8);
393 sizez.append(16);
394 sizez.append(32);
395 byteOrderz.append(QAudioFormat::LittleEndian);
396 byteOrderz.append(QAudioFormat::BigEndian);
397 typez.append(QAudioFormat::SignedInt);
398 typez.append(QAudioFormat::UnSignedInt);
399 typez.append(QAudioFormat::Float);
400 codecz.append(QLatin1String("audio/pcm"));
401 close();
402}
403
404QList<QByteArray> QAudioDeviceInfoInternal::availableDevices(QAudio::Mode mode)
405{
406 QList<QByteArray> allDevices;
407 QList<QByteArray> devices;
408 QByteArray filter;
409
410#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
411 // Create a list of all current audio devices that support mode
412 void **hints, **n;
413 char *name, *descr, *io;
414
415 if(snd_device_name_hint(-1, "pcm", &hints) < 0) {
416 qWarning() << "no alsa devices available";
417 return devices;
418 }
419 n = hints;
420
421 if(mode == QAudio::AudioInput) {
422 filter = "Input";
423 } else {
424 filter = "Output";
425 }
426
427 while (*n != NULL) {
428 name = snd_device_name_get_hint(*n, "NAME");
429 descr = snd_device_name_get_hint(*n, "DESC");
430 io = snd_device_name_get_hint(*n, "IOID");
431 if((name != NULL) && (descr != NULL) && ((io == NULL) || (io == filter))) {
432 QString deviceName = QLatin1String(name);
433 QString deviceDescription = QLatin1String(descr);
434 allDevices.append(deviceName.toLocal8Bit().constData());
435 if(deviceDescription.contains(QLatin1String("Default Audio Device")))
436 devices.append(deviceName.toLocal8Bit().constData());
437 }
438 if(name != NULL)
439 free(name);
440 if(descr != NULL)
441 free(descr);
442 if(io != NULL)
443 free(io);
444 ++n;
445 }
446 snd_device_name_free_hint(hints);
447
448 if(devices.size() > 0) {
449 devices.append("default");
450 }
451#else
452 int idx = 0;
453 char* name;
454
455 while(snd_card_get_name(idx,&name) == 0) {
456 devices.append(name);
457 idx++;
458 }
459 if (idx > 0)
460 devices.append("default");
461#endif
462 if (devices.size() == 0 && allDevices.size() > 0)
463 return allDevices;
464
465 return devices;
466}
467
468QByteArray QAudioDeviceInfoInternal::defaultInputDevice()
469{
470 QList<QByteArray> devices = availableDevices(QAudio::AudioInput);
471 if(devices.size() == 0)
472 return QByteArray();
473
474 return devices.first();
475}
476
477QByteArray QAudioDeviceInfoInternal::defaultOutputDevice()
478{
479 QList<QByteArray> devices = availableDevices(QAudio::AudioOutput);
480 if(devices.size() == 0)
481 return QByteArray();
482
483 return devices.first();
484}
485
486QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.