diff options
author | Christian Strømme <christian.stromme@digia.com> | 2013-11-23 00:14:15 +0100 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2014-01-30 18:15:36 +0100 |
commit | 2d54da2d39217e7b21ccafa9594513d554352a24 (patch) | |
tree | b9a620c05741fc81875c11fa351ea25713b1583c /src/plugins/alsa/qalsaaudiodeviceinfo.cpp | |
parent | 0ab81ef59f35d103ec8174834c4fc2a4dcced453 (diff) |
Move win32 and Alsa audio backends into plugins.
Change-Id: I9835cf5ee97900569f26421a19543b485e933051
Reviewed-by: Yoann Lopes <yoann.lopes@digia.com>
Diffstat (limited to 'src/plugins/alsa/qalsaaudiodeviceinfo.cpp')
-rw-r--r-- | src/plugins/alsa/qalsaaudiodeviceinfo.cpp | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/src/plugins/alsa/qalsaaudiodeviceinfo.cpp b/src/plugins/alsa/qalsaaudiodeviceinfo.cpp new file mode 100644 index 000000000..1e75c4661 --- /dev/null +++ b/src/plugins/alsa/qalsaaudiodeviceinfo.cpp @@ -0,0 +1,464 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// INTERNAL USE ONLY: Do NOT use for any other purpose. +// + +#include "qalsaaudiodeviceinfo.h" + +#include <alsa/version.h> + +QT_BEGIN_NAMESPACE + +QAlsaAudioDeviceInfo::QAlsaAudioDeviceInfo(QByteArray dev, QAudio::Mode mode) +{ + handle = 0; + + device = QLatin1String(dev); + this->mode = mode; + + checkSurround(); +} + +QAlsaAudioDeviceInfo::~QAlsaAudioDeviceInfo() +{ + close(); +} + +bool QAlsaAudioDeviceInfo::isFormatSupported(const QAudioFormat& format) const +{ + return testSettings(format); +} + +QAudioFormat QAlsaAudioDeviceInfo::preferredFormat() const +{ + QAudioFormat nearest; + if(mode == QAudio::AudioOutput) { + nearest.setSampleRate(44100); + nearest.setChannelCount(2); + nearest.setByteOrder(QAudioFormat::LittleEndian); + nearest.setSampleType(QAudioFormat::SignedInt); + nearest.setSampleSize(16); + nearest.setCodec(QLatin1String("audio/pcm")); + } else { + nearest.setSampleRate(8000); + nearest.setChannelCount(1); + nearest.setSampleType(QAudioFormat::UnSignedInt); + nearest.setSampleSize(8); + nearest.setCodec(QLatin1String("audio/pcm")); + if(!testSettings(nearest)) { + nearest.setChannelCount(2); + nearest.setSampleSize(16); + nearest.setSampleType(QAudioFormat::SignedInt); + } + } + return nearest; +} + +QString QAlsaAudioDeviceInfo::deviceName() const +{ + return device; +} + +QStringList QAlsaAudioDeviceInfo::supportedCodecs() +{ + updateLists(); + return codecz; +} + +QList<int> QAlsaAudioDeviceInfo::supportedSampleRates() +{ + updateLists(); + return sampleRatez; +} + +QList<int> QAlsaAudioDeviceInfo::supportedChannelCounts() +{ + updateLists(); + return channelz; +} + +QList<int> QAlsaAudioDeviceInfo::supportedSampleSizes() +{ + updateLists(); + return sizez; +} + +QList<QAudioFormat::Endian> QAlsaAudioDeviceInfo::supportedByteOrders() +{ + updateLists(); + return byteOrderz; +} + +QList<QAudioFormat::SampleType> QAlsaAudioDeviceInfo::supportedSampleTypes() +{ + updateLists(); + return typez; +} + +bool QAlsaAudioDeviceInfo::open() +{ + int err = 0; + QString dev = device; + QList<QByteArray> devices = availableDevices(mode); + + if(dev.compare(QLatin1String("default")) == 0) { +#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) + if (devices.size() > 0) + dev = QLatin1String(devices.first().constData()); + else + return false; +#else + dev = QLatin1String("hw:0,0"); +#endif + } else { +#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) + dev = device; +#else + int idx = 0; + char *name; + + QString shortName = device.mid(device.indexOf(QLatin1String("="),0)+1); + + while (snd_card_get_name(idx,&name) == 0) { + if(dev.contains(QLatin1String(name))) + break; + idx++; + } + dev = QString(QLatin1String("hw:%1,0")).arg(idx); +#endif + } + if(mode == QAudio::AudioOutput) { + err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0); + } else { + err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0); + } + if(err < 0) { + handle = 0; + return false; + } + return true; +} + +void QAlsaAudioDeviceInfo::close() +{ + if(handle) + snd_pcm_close(handle); + handle = 0; +} + +bool QAlsaAudioDeviceInfo::testSettings(const QAudioFormat& format) const +{ + // Set nearest to closest settings that do work. + // See if what is in settings will work (return value). + int err = -1; + snd_pcm_t* pcmHandle; + snd_pcm_hw_params_t *params; + QString dev; + +#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) + dev = device; + if (dev.compare(QLatin1String("default")) == 0) { + QList<QByteArray> devices = availableDevices(QAudio::AudioOutput); + if (!devices.isEmpty()) + dev = QLatin1String(devices.first().constData()); + } +#else + if (dev.compare(QLatin1String("default")) == 0) { + dev = QLatin1String("hw:0,0"); + } else { + int idx = 0; + char *name; + + QString shortName = device.mid(device.indexOf(QLatin1String("="),0)+1); + + while(snd_card_get_name(idx,&name) == 0) { + if(shortName.compare(QLatin1String(name)) == 0) + break; + idx++; + } + dev = QString(QLatin1String("hw:%1,0")).arg(idx); + } +#endif + + snd_pcm_stream_t stream = mode == QAudio::AudioOutput + ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE; + + if (snd_pcm_open(&pcmHandle, dev.toLocal8Bit().constData(), stream, 0) < 0) + return false; + + snd_pcm_nonblock(pcmHandle, 0); + snd_pcm_hw_params_alloca(¶ms); + snd_pcm_hw_params_any(pcmHandle, params); + + // set the values! + snd_pcm_hw_params_set_channels(pcmHandle, params, format.channelCount()); + snd_pcm_hw_params_set_rate(pcmHandle, params, format.sampleRate(), 0); + + snd_pcm_format_t pcmFormat = SND_PCM_FORMAT_UNKNOWN; + switch (format.sampleSize()) { + case 8: + if (format.sampleType() == QAudioFormat::SignedInt) + pcmFormat = SND_PCM_FORMAT_S8; + else if (format.sampleType() == QAudioFormat::UnSignedInt) + pcmFormat = SND_PCM_FORMAT_U8; + break; + case 16: + if (format.sampleType() == QAudioFormat::SignedInt) { + pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian + ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_S16_BE; + } else if (format.sampleType() == QAudioFormat::UnSignedInt) { + pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian + ? SND_PCM_FORMAT_U16_LE : SND_PCM_FORMAT_U16_BE; + } + break; + case 32: + if (format.sampleType() == QAudioFormat::SignedInt) { + pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian + ? SND_PCM_FORMAT_S32_LE : SND_PCM_FORMAT_S32_BE; + } else if (format.sampleType() == QAudioFormat::UnSignedInt) { + pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian + ? SND_PCM_FORMAT_U32_LE : SND_PCM_FORMAT_U32_BE; + } else if (format.sampleType() == QAudioFormat::Float) { + pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian + ? SND_PCM_FORMAT_FLOAT_LE : SND_PCM_FORMAT_FLOAT_BE; + } + } + + if (pcmFormat != SND_PCM_FORMAT_UNKNOWN) + err = snd_pcm_hw_params_set_format(pcmHandle, params, pcmFormat); + + // For now, just accept only audio/pcm codec + if (!format.codec().startsWith(QLatin1String("audio/pcm"))) + err = -1; + + if (err >= 0 && format.channelCount() != -1) { + err = snd_pcm_hw_params_test_channels(pcmHandle, params, format.channelCount()); + if (err >= 0) + err = snd_pcm_hw_params_set_channels(pcmHandle, params, format.channelCount()); + } + + if (err >= 0 && format.sampleRate() != -1) { + err = snd_pcm_hw_params_test_rate(pcmHandle, params, format.sampleRate(), 0); + if (err >= 0) + err = snd_pcm_hw_params_set_rate(pcmHandle, params, format.sampleRate(), 0); + } + + if (err >= 0 && pcmFormat != SND_PCM_FORMAT_UNKNOWN) + err = snd_pcm_hw_params_set_format(pcmHandle, params, pcmFormat); + + if (err >= 0) + err = snd_pcm_hw_params(pcmHandle, params); + + snd_pcm_close(pcmHandle); + + return (err == 0); +} + +void QAlsaAudioDeviceInfo::updateLists() +{ + // redo all lists based on current settings + sampleRatez.clear(); + channelz.clear(); + sizez.clear(); + byteOrderz.clear(); + typez.clear(); + codecz.clear(); + + if(!handle) + open(); + + if(!handle) + return; + + for(int i=0; i<(int)MAX_SAMPLE_RATES; i++) { + //if(snd_pcm_hw_params_test_rate(handle, params, SAMPLE_RATES[i], dir) == 0) + sampleRatez.append(SAMPLE_RATES[i]); + } + channelz.append(1); + channelz.append(2); + if (surround40) channelz.append(4); + if (surround51) channelz.append(6); + if (surround71) channelz.append(8); + sizez.append(8); + sizez.append(16); + sizez.append(32); + byteOrderz.append(QAudioFormat::LittleEndian); + byteOrderz.append(QAudioFormat::BigEndian); + typez.append(QAudioFormat::SignedInt); + typez.append(QAudioFormat::UnSignedInt); + typez.append(QAudioFormat::Float); + codecz.append(QLatin1String("audio/pcm")); + close(); +} + +QList<QByteArray> QAlsaAudioDeviceInfo::availableDevices(QAudio::Mode mode) +{ + QList<QByteArray> devices; + QByteArray filter; + +#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) + // Create a list of all current audio devices that support mode + void **hints; + char *name, *descr, *io; + int card = -1; + + if(mode == QAudio::AudioInput) { + filter = "Input"; + } else { + filter = "Output"; + } + + while (snd_card_next(&card) == 0 && card >= 0) { + if (snd_device_name_hint(card, "pcm", &hints) < 0) + continue; + + void **n = hints; + while (*n != NULL) { + name = snd_device_name_get_hint(*n, "NAME"); + if (name != 0 && qstrcmp(name, "null") != 0) { + descr = snd_device_name_get_hint(*n, "DESC"); + io = snd_device_name_get_hint(*n, "IOID"); + + if ((descr != NULL) && ((io == NULL) || (io == filter))) { + QString deviceName = QLatin1String(name); + QString deviceDescription = QLatin1String(descr); + if (deviceDescription.contains(QLatin1String("Default Audio Device"))) + devices.prepend(deviceName.toLocal8Bit().constData()); + else + devices.append(deviceName.toLocal8Bit().constData()); + } + + free(descr); + free(io); + } + free(name); + ++n; + } + + snd_device_name_free_hint(hints); + } +#else + int idx = 0; + char* name; + + while(snd_card_get_name(idx,&name) == 0) { + devices.append(name); + idx++; + } +#endif + + if (devices.size() > 0) + devices.append("default"); + + return devices; +} + +QByteArray QAlsaAudioDeviceInfo::defaultInputDevice() +{ + QList<QByteArray> devices = availableDevices(QAudio::AudioInput); + if(devices.size() == 0) + return QByteArray(); + + return devices.first(); +} + +QByteArray QAlsaAudioDeviceInfo::defaultOutputDevice() +{ + QList<QByteArray> devices = availableDevices(QAudio::AudioOutput); + if(devices.size() == 0) + return QByteArray(); + + return devices.first(); +} + +void QAlsaAudioDeviceInfo::checkSurround() +{ + surround40 = false; + surround51 = false; + surround71 = false; + + void **hints; + char *name, *descr, *io; + int card = -1; + + while (snd_card_next(&card) == 0 && card >= 0) { + if (snd_device_name_hint(card, "pcm", &hints) < 0) + continue; + + void **n = hints; + while (*n != NULL) { + name = snd_device_name_get_hint(*n, "NAME"); + descr = snd_device_name_get_hint(*n, "DESC"); + io = snd_device_name_get_hint(*n, "IOID"); + if((name != NULL) && (descr != NULL)) { + QString deviceName = QLatin1String(name); + if (mode == QAudio::AudioOutput) { + if(deviceName.contains(QLatin1String("surround40"))) + surround40 = true; + if(deviceName.contains(QLatin1String("surround51"))) + surround51 = true; + if(deviceName.contains(QLatin1String("surround71"))) + surround71 = true; + } + } + if(name != NULL) + free(name); + if(descr != NULL) + free(descr); + if(io != NULL) + free(io); + ++n; + } + + snd_device_name_free_hint(hints); + } +} + +QT_END_NAMESPACE |