diff --git a/include/AudioAlsa.h b/include/AudioAlsa.h index c051e6eae..570b86005 100644 --- a/include/AudioAlsa.h +++ b/include/AudioAlsa.h @@ -47,34 +47,19 @@ public: class DeviceInfo { public: - DeviceInfo(int cardNumber, int deviceNumber, - QString const & cardName, QString const & pcmName, - QString const & cardId, QString const & pcmId) : - m_cardNumber(cardNumber), - m_deviceNumber(deviceNumber), - m_cardName(cardName), - m_pcmName(pcmName), - m_cardId(cardId), - m_pcmId(pcmId) + DeviceInfo(QString const & deviceName, QString const & deviceDescription) : + m_deviceName(deviceName), + m_deviceDescription(deviceDescription) {} ~DeviceInfo() {} - int getCardNumber() const { return m_cardNumber; } - int getDeviceNumber() const { return m_deviceNumber; } - QString const & getCardName() const { return m_cardName; } - QString const & getPcmName() const { return m_pcmName; } - QString const & getCardId() const { return m_cardId; } - QString const & getPcmId() const { return m_pcmId; } - - QString getHWString() const { return QString("hw:%1,%2").arg(m_cardNumber).arg(m_deviceNumber); } + QString const & getDeviceName() const { return m_deviceName; } + QString const & getDeviceDescription() const { return m_deviceDescription; } private: - int m_cardNumber; - int m_deviceNumber; - QString m_cardName; - QString m_pcmName; - QString m_cardId; - QString m_pcmId; + QString m_deviceName; + QString m_deviceDescription; + }; typedef std::vector DeviceInfoCollection; diff --git a/include/AudioAlsaSetupWidget.h b/include/AudioAlsaSetupWidget.h index f9224eaca..748536a0e 100644 --- a/include/AudioAlsaSetupWidget.h +++ b/include/AudioAlsaSetupWidget.h @@ -1,7 +1,7 @@ /* - * AudioAlsa.h - device-class that implements ALSA-PCM-output + * AudioDeviceSetupWidget.h - Implements a setup widget for ALSA-PCM-output * - * Copyright (c) 2004-2009 Tobias Doerffel + * Copyright (c) 2004-2015 Tobias Doerffel * * This file is part of LMMS - http://lmms.io * @@ -33,12 +33,9 @@ #include "AudioAlsa.h" -#include - class QComboBox; class LcdSpinBox; -class QLineEdit; class AudioAlsaSetupWidget : public AudioDeviceSetupWidget @@ -56,7 +53,6 @@ public slots: private: QComboBox * m_deviceComboBox; - QLineEdit * m_device; LcdSpinBox * m_channels; int m_selectedDevice; diff --git a/include/AudioDeviceSetupWidget.h b/include/AudioDeviceSetupWidget.h index 95eeaed88..369d4d23c 100644 --- a/include/AudioDeviceSetupWidget.h +++ b/include/AudioDeviceSetupWidget.h @@ -1,7 +1,7 @@ /* - * AudioDevice.h - base-class for audio-devices, used by LMMS-mixer + * AudioDeviceSetupWidget.h - Base class for audio device setup widgets * - * Copyright (c) 2004-2014 Tobias Doerffel + * Copyright (c) 2004-2015 Tobias Doerffel * * This file is part of LMMS - http://lmms.io * diff --git a/src/core/audio/AudioAlsa.cpp b/src/core/audio/AudioAlsa.cpp index 13ee39ab9..fe26edccb 100644 --- a/src/core/audio/AudioAlsa.cpp +++ b/src/core/audio/AudioAlsa.cpp @@ -141,146 +141,113 @@ QString AudioAlsa::probeDevice() -// TODO Test code. Delete! -void device_list(void) + +/** + * @brief Checks whether the ALSA device with the given name has the needed + * capabilities for LMMS. + * @param deviceName Name of the device that is checked. + * @return If the device is usable for LMMS true is returned. + */ +bool hasCapabilities(char *device_name) { - snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK; - snd_ctl_t *handle; - int card, err, dev, idx; - snd_ctl_card_info_t *info; - snd_pcm_info_t *pcminfo; - snd_ctl_card_info_alloca(&info); - snd_pcm_info_alloca(&pcminfo); + snd_pcm_t *pcm; // PCM handle + snd_pcm_hw_params_t *hw_params; + int err; - card = -1; - if (snd_card_next(&card) < 0 || card < 0) { - return; + // Implicit check for SND_PCM_STREAM_PLAYBACK + err = snd_pcm_open(&pcm, device_name, SND_PCM_STREAM_PLAYBACK, 0); + if (err < 0) + { + std::cerr << "Cannot open device '" << device_name << "': " << snd_strerror(err) << std::endl; + return false; } - std::cout << "**** List of " << snd_pcm_stream_name(stream) << " Hardware Devices ****\n"; - while (card >= 0) { - char name[32]; - sprintf(name, "hw:%d", card); - if ((err = snd_ctl_open(&handle, name, 0)) < 0) { - // TODO Error handling - //error("control open (%i): %s", card, snd_strerror(err)); - goto next_card; - } - if ((err = snd_ctl_card_info(handle, info)) < 0) { - // TODO Error handling - //error("control hardware info (%i): %s", card, snd_strerror(err)); - snd_ctl_close(handle); - goto next_card; - } - dev = -1; - while (1) { - unsigned int count; - if (snd_ctl_pcm_next_device(handle, &dev)<0) - // TODO Error handling - //error("snd_ctl_pcm_next_device"); - std::cerr << "snd_ctl_pcm_next_device"; - if (dev < 0) - break; - snd_pcm_info_set_device(pcminfo, dev); - snd_pcm_info_set_subdevice(pcminfo, 0); - snd_pcm_info_set_stream(pcminfo, stream); - if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) { - if (err != -ENOENT) - // TODO Error handling - //error("control digital audio info (%i): %s", card, snd_strerror(err)); - std::cerr << "Error\n"; - continue; - } - //std::cout << "card " << card << ": " << snd_ctl_card_info_get_id(info) << " [" << snd_ctl_card_info_get_name(info) << "], device " << dev << - // ": " << snd_pcm_info_get_id(pcminfo) << " [" << snd_pcm_info_get_name(pcminfo) << "]\n"; - std::cout << "card hw" << card << ":" << dev << " - " << "[" << snd_ctl_card_info_get_name(info) << "/" << snd_pcm_info_get_name(pcminfo) << "], " << - snd_ctl_card_info_get_id(info) << "," << snd_pcm_info_get_id(pcminfo) << "\n"; - - count = snd_pcm_info_get_subdevices_count(pcminfo); - std::cout << " Subdevices: " << snd_pcm_info_get_subdevices_avail(pcminfo) << "/" << count << std::endl; - for (idx = 0; idx < (int)count; idx++) { - snd_pcm_info_set_subdevice(pcminfo, idx); - if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) { - // TODO Error handling - //error("control digital audio playback info (%i): %s", card, snd_strerror(err)); - std::cerr << "Error\n"; - } else { - std::cout << " Subdevice #" << idx << ": " << snd_pcm_info_get_subdevice_name(pcminfo) << std::endl; - } - } - } - snd_ctl_close(handle); -next_card: - if (snd_card_next(&card) < 0) { - // TODO Error handling - //error("snd_card_next"); - break; - } + snd_pcm_hw_params_alloca(&hw_params); + err = snd_pcm_hw_params_any(pcm, hw_params); + if (err < 0) + { + std::cerr << "Cannot get hardware parameters: " << snd_strerror(err) << std::endl; + snd_pcm_close(pcm); + return false; } + + // Checks for SND_PCM_ACCESS_RW_INTERLEAVED + err = snd_pcm_hw_params_test_access(pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); + if (err < 0) + { + std::cerr << "Interleaved access not possible for '" << device_name << "': " << snd_strerror(err) << std::endl; + snd_pcm_close(pcm); + return false; + } + + // Check for SND_PCM_FORMAT_S16_LE or SND_PCM_FORMAT_S16_BE + bool validFormatFound = false; + + validFormatFound |= !snd_pcm_hw_params_test_format(pcm, hw_params, SND_PCM_FORMAT_S16_LE); + validFormatFound |= !snd_pcm_hw_params_test_format(pcm, hw_params, SND_PCM_FORMAT_S16_BE); + + if (!validFormatFound) + { + std::cerr << "Device " << device_name << " does not not support SND_PCM_FORMAT_S16_LE or SND_PCM_FORMAT_S16_BE!" << std::endl; + snd_pcm_close(pcm); + return false; + } + + snd_pcm_close(pcm); + + return true; } - - +/** + * @brief Creates a list of all available devices. + * + * Uses the hints API of ALSA to collect all devices. This also includes plug + * devices. The reason to collect these and not the raw hardware devices + * (e.g. hw:0,0) is that hardware devices often have a very limited number of + * supported formats, etc. Plugs on the other hand are software components that + * map all types of formats and inputs to the hardware and therefore they are + * much more flexible and more what we want. + * + * Further helpful info http://jan.newmarch.name/LinuxSound/Sampled/Alsa/. + * + * @return A collection of devices found on the system. + */ AudioAlsa::DeviceInfoCollection AudioAlsa::getAvailableDevices() { DeviceInfoCollection deviceInfos; - snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK; - snd_ctl_t *handle; - snd_ctl_card_info_t *info; - snd_pcm_info_t *pcminfo; + char **hints; - // Allocate memory for the info structs - snd_ctl_card_info_alloca(&info); - snd_pcm_info_alloca(&pcminfo); - - int card = -1; - - while (!snd_card_next(&card) && card >= 0) + /* Enumerate sound devices */ + int err = snd_device_name_hint(-1, "pcm", (void***)&hints); + if (err != 0) { - std::cout << "Card: " << card << " found!" << std::endl; - - char name[32]; - sprintf(name, "hw:%d", card); - - if (snd_ctl_open(&handle, name, 0) < 0) - { - std::cerr << "Error opening ALSA card " << name << std::endl; - continue; - } - if (snd_ctl_card_info(handle, info) < 0) - { - snd_ctl_close(handle); - std::cerr << "Could not retrieve info for ALSA card " << name << std::endl; - continue; - } - - int dev = -1; - - while (!snd_ctl_pcm_next_device(handle, &dev) && dev >= 0) - { - snd_pcm_info_set_device(pcminfo, dev); - snd_pcm_info_set_subdevice(pcminfo, 0); - snd_pcm_info_set_stream(pcminfo, stream); - if (!snd_ctl_pcm_info(handle, pcminfo)) - { - QString cardName(snd_ctl_card_info_get_name(info)); - QString pcmName(snd_pcm_info_get_name(pcminfo)); - QString cardId(snd_ctl_card_info_get_id(info)); - QString pcmId(snd_pcm_info_get_id(pcminfo)); - - DeviceInfo currentDevice(card, dev, cardName, pcmName, cardId, pcmId); - deviceInfos.push_back(currentDevice); - - std::cout << "card hw" << card << ":" << dev << " - " << "[" << snd_ctl_card_info_get_name(info) << " | " << snd_pcm_info_get_name(pcminfo) << "], " << - snd_ctl_card_info_get_id(info) << ", " << snd_pcm_info_get_id(pcminfo) << std::endl; - } - } - - snd_ctl_close(handle); + return deviceInfos; } + char** n = hints; + while (*n != NULL) + { + char *name = snd_device_name_get_hint(*n, "NAME"); + char *description = snd_device_name_get_hint(*n, "DESC"); + + // We could call hasCapabilities(name) here but this gives strange + // results + if (name != 0 && description != 0) + { + deviceInfos.push_back(DeviceInfo(QString(name), QString(description))); + } + + free(name); + free(description); + + n++; + } + + //Free the hint buffer + snd_device_name_free_hint((void**)hints); + return deviceInfos; } diff --git a/src/gui/AudioAlsaSetupWidget.cpp b/src/gui/AudioAlsaSetupWidget.cpp index 8eea54777..0e83922e4 100644 --- a/src/gui/AudioAlsaSetupWidget.cpp +++ b/src/gui/AudioAlsaSetupWidget.cpp @@ -1,7 +1,7 @@ /* - * AudioAlsa.cpp - device-class which implements ALSA-PCM-output + * AudioAlsaSetupWidget.cpp - Implements a setup widget for ALSA-PCM-output * - * Copyright (c) 2004-2014 Tobias Doerffel + * Copyright (c) 2004-2015 Tobias Doerffel * * This file is part of LMMS - http://lmms.io * @@ -23,14 +23,14 @@ */ #include -#include #include -#include "AudioAlsa.h" #include "AudioAlsaSetupWidget.h" #ifdef LMMS_HAVE_ALSA +#include "AudioAlsa.h" + #include "ConfigManager.h" #include "LcdSpinBox.h" #include "gui_templates.h" @@ -47,18 +47,17 @@ AudioAlsaSetupWidget::AudioAlsaSetupWidget( QWidget * _parent ) : QString deviceText = ConfigManager::inst()->value( "audioalsa", "device" ); - // Implements the "-l" from aplay - //device_list(); - m_deviceComboBox = new QComboBox(this); for (size_t i = 0; i < m_deviceInfos.size(); ++i) { AudioAlsa::DeviceInfo const & currentDeviceInfo = m_deviceInfos[i]; - QString comboBoxText = currentDeviceInfo.getHWString() + " [" + currentDeviceInfo.getCardName() + " | " + currentDeviceInfo.getPcmName() + "]"; + QString comboBoxText = currentDeviceInfo.getDeviceName(); m_deviceComboBox->addItem(comboBoxText, QVariant(static_cast(i))); - m_deviceComboBox->setItemData(i, comboBoxText, Qt::ToolTipRole); - if (currentDeviceInfo.getHWString() == deviceText) + QString toolTipText = currentDeviceInfo.getDeviceDescription(); + m_deviceComboBox->setItemData(i, toolTipText, Qt::ToolTipRole); + + if (currentDeviceInfo.getDeviceName() == deviceText) { m_deviceComboBox->setCurrentIndex(static_cast(i)); } @@ -71,9 +70,6 @@ AudioAlsaSetupWidget::AudioAlsaSetupWidget( QWidget * _parent ) : SIGNAL(currentIndexChanged(int)), SLOT(onCurrentIndexChanged(int))); - //m_device = new QLineEdit( AudioAlsa::probeDevice(), this ); - //m_device->setGeometry( 10, 20, 160, 20 ); - QLabel * dev_lbl = new QLabel( tr( "DEVICE" ), this ); dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) ); dev_lbl->setGeometry( 10, 40, 160, 10 ); @@ -109,7 +105,7 @@ void AudioAlsaSetupWidget::saveSettings() if (m_selectedDevice != -1) { AudioAlsa::DeviceInfo const & selectedDevice = m_deviceInfos[m_selectedDevice]; - deviceText = selectedDevice.getHWString(); + deviceText = selectedDevice.getDeviceName(); } ConfigManager::inst()->setValue( "audioalsa", "device", deviceText );