From ad70c99d961876b3f0916382eeed4128eebd2ade Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Fri, 26 Jun 2015 00:06:34 +0200 Subject: [PATCH 1/5] Populate and show a combo box of available ALSA cards and devices Shows a combo box with the available ALSA cards and devices instead of a line edit. The problem currently is that the widgets are nested classes of AudioDevice and therefore the macro Q_OBJECT does not work which means that its not possible to define slots that react to retrieved signals. --- include/AudioAlsa.h | 5 + src/core/audio/AudioAlsa.cpp | 255 ++++++++++++++++++++++++++++++++++- 2 files changed, 257 insertions(+), 3 deletions(-) diff --git a/include/AudioAlsa.h b/include/AudioAlsa.h index 88438a105..4043aafd5 100644 --- a/include/AudioAlsa.h +++ b/include/AudioAlsa.h @@ -37,6 +37,7 @@ #include "AudioDevice.h" +class QComboBox; class LcdSpinBox; class QLineEdit; @@ -64,7 +65,11 @@ public: virtual void saveSettings(); + public slots: + void onCurrentIndexChanged(int index); + private: + QComboBox * m_deviceComboBox; QLineEdit * m_device; LcdSpinBox * m_channels; diff --git a/src/core/audio/AudioAlsa.cpp b/src/core/audio/AudioAlsa.cpp index 240674bce..8438b5177 100644 --- a/src/core/audio/AudioAlsa.cpp +++ b/src/core/audio/AudioAlsa.cpp @@ -22,6 +22,7 @@ * */ +#include #include #include @@ -36,6 +37,8 @@ #include "gui_templates.h" #include "templates.h" +#include +#include AudioAlsa::AudioAlsa( bool & _success_ful, Mixer* _mixer ) : @@ -493,13 +496,253 @@ int AudioAlsa::setSWParams() +void testPrintCards() +{ + std::cout << "Listing cards: " << std::endl; + char **hints; + + /* Enumerate sound devices */ + int err = snd_device_name_hint(-1, "pcm", (void***)&hints); + if (err != 0) + return;//Error! Just return + + char** n = hints; + while (*n != NULL) { + + char *name = snd_device_name_get_hint(*n, "NAME"); + + if (name != NULL && 0 != strcmp("null", name)) { + //Copy name to another buffer and then free it + std::cout << "Name: " << name << std::endl; + + free(name); + } + + char *description = snd_device_name_get_hint(*n, "DESC"); + + if (description != NULL && 0 != strcmp("null", name)) { + //Copy name to another buffer and then free it + std::cout << "Description: " << description << std::endl; + + free(description); + } + else + { + std::cout << "No description" << std::endl; + } + + std::cout << std::endl; + + n++; + }//End of while + + //Free hint buffer too + snd_device_name_free_hint((void**)hints); +} + + + +void device_list(void) +{ + 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); + + card = -1; + if (snd_card_next(&card) < 0 || card < 0) { + return; + } + 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; + } + } +} + + + +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() {} + + 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); } + +private: + int m_cardNumber; + int m_deviceNumber; + QString m_cardName; + QString m_pcmName; + QString m_cardId; + QString m_pcmId; +}; + + + +void populateDeviceInfos(std::vector &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; + + // 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) + { + 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); + } +} + + AudioAlsa::setupWidget::setupWidget( QWidget * _parent ) : AudioDevice::setupWidget( AudioAlsa::name(), _parent ) { - m_device = new QLineEdit( AudioAlsa::probeDevice(), this ); - m_device->setGeometry( 10, 20, 160, 20 ); + typedef std::vector DeviceInfoCollection; + DeviceInfoCollection deviceInfos; + populateDeviceInfos(deviceInfos); + + // Implements the "-l" from aplay + //device_list(); + + //testPrintCards(); + + m_deviceComboBox = new QComboBox(this); + for (size_t i = 0; i < deviceInfos.size(); ++i) + { + DeviceInfo const & currentDeviceInfo = deviceInfos[i]; + QString comboBoxText = currentDeviceInfo.getHWString() + " [" + currentDeviceInfo.getCardName() + " | " + currentDeviceInfo.getPcmName() + "]"; + m_deviceComboBox->addItem(comboBoxText, QVariant(static_cast(i))); + m_deviceComboBox->setItemData(i, comboBoxText, Qt::ToolTipRole); + } + + m_deviceComboBox->setGeometry( 10, 20, 160, 20 ); + connect(m_deviceComboBox, 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() ) ); @@ -538,5 +781,11 @@ void AudioAlsa::setupWidget::saveSettings() } -#endif +void AudioAlsa::setupWidget::onCurrentIndexChanged(int index) +{ + +} + + +#endif From 79cae31b5bfe0c6615b0cb645347b0ed90b4d680 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Fri, 26 Jun 2015 17:14:47 +0200 Subject: [PATCH 2/5] Some refactoring to enable signals in audio driver setup dialogs Moved AudioDevice::setupWidget into its own class AudioDeviceSetupWidget which logically should belong to the GUI (unfortunately the include structure does not make this obvious). For the ALSA driver there is an implementation AudioAlsaSetupWidget which provides a combo box for selection of the card and device. All other driver widgets have been changed to inherit from AudioAlsaSetupWidget but have not been changed otherwise. SetupDialog has been adjusted to keep a map of AudioAlsaSetupWidgets now. --- include/AudioAlsa.h | 25 --- include/AudioAlsaSetupWidget.h | 103 ++++++++++ include/AudioDevice.h | 26 --- include/AudioDeviceSetupWidget.h | 55 ++++++ include/AudioDummy.h | 5 +- include/AudioJack.h | 3 +- include/AudioOss.h | 3 +- include/AudioPulseAudio.h | 3 +- include/AudioSdl.h | 3 +- include/SetupDialog.h | 4 +- src/core/audio/AudioAlsa.cpp | 294 ----------------------------- src/core/audio/AudioJack.cpp | 2 +- src/core/audio/AudioOss.cpp | 2 +- src/core/audio/AudioPulseAudio.cpp | 2 +- src/core/audio/AudioSdl.cpp | 2 +- src/gui/AudioAlsaSetupWidget.cpp | 267 ++++++++++++++++++++++++++ src/gui/CMakeLists.txt | 1 + src/gui/SetupDialog.cpp | 3 +- 18 files changed, 446 insertions(+), 357 deletions(-) create mode 100644 include/AudioAlsaSetupWidget.h create mode 100644 include/AudioDeviceSetupWidget.h create mode 100644 src/gui/AudioAlsaSetupWidget.cpp diff --git a/include/AudioAlsa.h b/include/AudioAlsa.h index 4043aafd5..a916ad8da 100644 --- a/include/AudioAlsa.h +++ b/include/AudioAlsa.h @@ -37,11 +37,6 @@ #include "AudioDevice.h" -class QComboBox; -class LcdSpinBox; -class QLineEdit; - - class AudioAlsa : public AudioDevice, public QThread { public: @@ -56,26 +51,6 @@ public: static QString probeDevice(); - - class setupWidget : public AudioDevice::setupWidget - { - public: - setupWidget( QWidget * _parent ); - virtual ~setupWidget(); - - virtual void saveSettings(); - - public slots: - void onCurrentIndexChanged(int index); - - private: - QComboBox * m_deviceComboBox; - QLineEdit * m_device; - LcdSpinBox * m_channels; - - } ; - - private: virtual void startProcessing(); virtual void stopProcessing(); diff --git a/include/AudioAlsaSetupWidget.h b/include/AudioAlsaSetupWidget.h new file mode 100644 index 000000000..0e2f9b69c --- /dev/null +++ b/include/AudioAlsaSetupWidget.h @@ -0,0 +1,103 @@ +/* + * AudioAlsa.h - device-class that implements ALSA-PCM-output + * + * Copyright (c) 2004-2009 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef AUDIO_ALSA_SETUP_WIDGET_H +#define AUDIO_ALSA_SETUP_WIDGET_H + +#include "lmmsconfig.h" + +#ifdef LMMS_HAVE_ALSA + +#include "AudioDeviceSetupWidget.h" + +#include + + +class QComboBox; +class LcdSpinBox; +class QLineEdit; + + +class AudioAlsaSetupWidget : public AudioDeviceSetupWidget +{ + Q_OBJECT + +public: + AudioAlsaSetupWidget( QWidget * _parent ); + virtual ~AudioAlsaSetupWidget(); + + virtual void saveSettings(); + +public slots: + void onCurrentIndexChanged(int index); + +private: + 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() {} + + 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); } + + private: + int m_cardNumber; + int m_deviceNumber; + QString m_cardName; + QString m_pcmName; + QString m_cardId; + QString m_pcmId; + }; + + void populateDeviceInfos(std::vector &deviceInfos); + +private: + QComboBox * m_deviceComboBox; + QLineEdit * m_device; + LcdSpinBox * m_channels; + + int m_selectedDevice; + typedef std::vector DeviceInfoCollection; + DeviceInfoCollection m_deviceInfos; +} ; + +#endif + +#endif diff --git a/include/AudioDevice.h b/include/AudioDevice.h index 298279ddb..0782035b7 100644 --- a/include/AudioDevice.h +++ b/include/AudioDevice.h @@ -90,32 +90,6 @@ public: - class setupWidget : public TabWidget - { - public: - setupWidget( const QString & _caption, QWidget * _parent ) : - TabWidget( TabWidget::tr( "Settings for %1" ).arg( - TabWidget::tr( _caption.toLatin1() ) ). - toUpper(), _parent ) - { - } - - virtual ~setupWidget() - { - } - - virtual void saveSettings() = 0; - - virtual void show() - { - parentWidget()->show(); - QWidget::show(); - } - - } ; - - - protected: // subclasses can re-implement this for being used in conjunction with // processNextBuffer() diff --git a/include/AudioDeviceSetupWidget.h b/include/AudioDeviceSetupWidget.h new file mode 100644 index 000000000..95eeaed88 --- /dev/null +++ b/include/AudioDeviceSetupWidget.h @@ -0,0 +1,55 @@ +/* + * AudioDevice.h - base-class for audio-devices, used by LMMS-mixer + * + * Copyright (c) 2004-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef AUDIO_DEVICE_SETUP_WIDGET_H +#define AUDIO_DEVICE_SETUP_WIDGET_H + +#include "TabWidget.h" + + +class AudioDeviceSetupWidget : public TabWidget +{ +public: + AudioDeviceSetupWidget( const QString & _caption, QWidget * _parent ) : + TabWidget( TabWidget::tr( "Settings for %1" ).arg( + TabWidget::tr( _caption.toLatin1() ) ). + toUpper(), _parent ) + { + } + + virtual ~AudioDeviceSetupWidget() + { + } + + virtual void saveSettings() = 0; + + virtual void show() + { + parentWidget()->show(); + QWidget::show(); + } +}; + + +#endif diff --git a/include/AudioDummy.h b/include/AudioDummy.h index 31781e049..993dbccae 100644 --- a/include/AudioDummy.h +++ b/include/AudioDummy.h @@ -26,6 +26,7 @@ #define AUDIO_DUMMY_H #include "AudioDevice.h" +#include "AudioDeviceSetupWidget.h" #include "MicroTimer.h" @@ -49,11 +50,11 @@ public: } - class setupWidget : public AudioDevice::setupWidget + class setupWidget : public AudioDeviceSetupWidget { public: setupWidget( QWidget * _parent ) : - AudioDevice::setupWidget( AudioDummy::name(), _parent ) + AudioDeviceSetupWidget( AudioDummy::name(), _parent ) { } diff --git a/include/AudioJack.h b/include/AudioJack.h index 35801ef03..ec4fc5819 100644 --- a/include/AudioJack.h +++ b/include/AudioJack.h @@ -35,6 +35,7 @@ #include #include "AudioDevice.h" +#include "AudioDeviceSetupWidget.h" class QLineEdit; @@ -55,7 +56,7 @@ public: } - class setupWidget : public AudioDevice::setupWidget + class setupWidget : public AudioDeviceSetupWidget { public: setupWidget( QWidget * _parent ); diff --git a/include/AudioOss.h b/include/AudioOss.h index b40edd58e..76dbeecb8 100644 --- a/include/AudioOss.h +++ b/include/AudioOss.h @@ -30,6 +30,7 @@ #ifdef LMMS_HAVE_OSS #include "AudioDevice.h" +#include "AudioDeviceSetupWidget.h" class LcdSpinBox; @@ -50,7 +51,7 @@ public: static QString probeDevice(); - class setupWidget : public AudioDevice::setupWidget + class setupWidget : public AudioDeviceSetupWidget { public: setupWidget( QWidget * _parent ); diff --git a/include/AudioPulseAudio.h b/include/AudioPulseAudio.h index 5a8504d1f..fc2f77bd4 100644 --- a/include/AudioPulseAudio.h +++ b/include/AudioPulseAudio.h @@ -32,6 +32,7 @@ #include #include "AudioDevice.h" +#include "AudioDeviceSetupWidget.h" class LcdSpinBox; @@ -52,7 +53,7 @@ public: static QString probeDevice(); - class setupWidget : public AudioDevice::setupWidget + class setupWidget : public AudioDeviceSetupWidget { public: setupWidget( QWidget * _parent ); diff --git a/include/AudioSdl.h b/include/AudioSdl.h index 15facb52a..ba2f09faa 100644 --- a/include/AudioSdl.h +++ b/include/AudioSdl.h @@ -33,6 +33,7 @@ #include #include "AudioDevice.h" +#include "AudioDeviceSetupWidget.h" class QLineEdit; @@ -50,7 +51,7 @@ public: } - class setupWidget : public AudioDevice::setupWidget + class setupWidget : public AudioDeviceSetupWidget { public: setupWidget( QWidget * _parent ); diff --git a/include/SetupDialog.h b/include/SetupDialog.h index 3357bf54c..d5e1b6e57 100644 --- a/include/SetupDialog.h +++ b/include/SetupDialog.h @@ -33,6 +33,8 @@ #include "AudioDevice.h" #include "MidiClient.h" +#include "AudioDeviceSetupWidget.h" + class QComboBox; class QLabel; @@ -178,7 +180,7 @@ private: bool m_displayWaveform; bool m_disableAutoQuit; - typedef QMap AswMap; + typedef QMap AswMap; typedef QMap MswMap; typedef QMap trMap; diff --git a/src/core/audio/AudioAlsa.cpp b/src/core/audio/AudioAlsa.cpp index 8438b5177..830effe2b 100644 --- a/src/core/audio/AudioAlsa.cpp +++ b/src/core/audio/AudioAlsa.cpp @@ -494,298 +494,4 @@ int AudioAlsa::setSWParams() return 0; // all ok } - - -void testPrintCards() -{ - std::cout << "Listing cards: " << std::endl; - char **hints; - - /* Enumerate sound devices */ - int err = snd_device_name_hint(-1, "pcm", (void***)&hints); - if (err != 0) - return;//Error! Just return - - char** n = hints; - while (*n != NULL) { - - char *name = snd_device_name_get_hint(*n, "NAME"); - - if (name != NULL && 0 != strcmp("null", name)) { - //Copy name to another buffer and then free it - std::cout << "Name: " << name << std::endl; - - free(name); - } - - char *description = snd_device_name_get_hint(*n, "DESC"); - - if (description != NULL && 0 != strcmp("null", name)) { - //Copy name to another buffer and then free it - std::cout << "Description: " << description << std::endl; - - free(description); - } - else - { - std::cout << "No description" << std::endl; - } - - std::cout << std::endl; - - n++; - }//End of while - - //Free hint buffer too - snd_device_name_free_hint((void**)hints); -} - - - -void device_list(void) -{ - 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); - - card = -1; - if (snd_card_next(&card) < 0 || card < 0) { - return; - } - 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; - } - } -} - - - -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() {} - - 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); } - -private: - int m_cardNumber; - int m_deviceNumber; - QString m_cardName; - QString m_pcmName; - QString m_cardId; - QString m_pcmId; -}; - - - -void populateDeviceInfos(std::vector &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; - - // 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) - { - 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); - } -} - - - - -AudioAlsa::setupWidget::setupWidget( QWidget * _parent ) : - AudioDevice::setupWidget( AudioAlsa::name(), _parent ) -{ - typedef std::vector DeviceInfoCollection; - DeviceInfoCollection deviceInfos; - populateDeviceInfos(deviceInfos); - - // Implements the "-l" from aplay - //device_list(); - - //testPrintCards(); - - m_deviceComboBox = new QComboBox(this); - for (size_t i = 0; i < deviceInfos.size(); ++i) - { - DeviceInfo const & currentDeviceInfo = deviceInfos[i]; - QString comboBoxText = currentDeviceInfo.getHWString() + " [" + currentDeviceInfo.getCardName() + " | " + currentDeviceInfo.getPcmName() + "]"; - m_deviceComboBox->addItem(comboBoxText, QVariant(static_cast(i))); - m_deviceComboBox->setItemData(i, comboBoxText, Qt::ToolTipRole); - } - - m_deviceComboBox->setGeometry( 10, 20, 160, 20 ); - connect(m_deviceComboBox, 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 ); - - LcdSpinBoxModel * m = new LcdSpinBoxModel( /* this */ ); - m->setRange( DEFAULT_CHANNELS, SURROUND_CHANNELS ); - m->setStep( 2 ); - m->setValue( ConfigManager::inst()->value( "audioalsa", - "channels" ).toInt() ); - - m_channels = new LcdSpinBox( 1, this ); - m_channels->setModel( m ); - m_channels->setLabel( tr( "CHANNELS" ) ); - m_channels->move( 180, 20 ); - -} - - - - -AudioAlsa::setupWidget::~setupWidget() -{ - delete m_channels->model(); -} - - - - -void AudioAlsa::setupWidget::saveSettings() -{ - ConfigManager::inst()->setValue( "audioalsa", "device", - m_device->text() ); - ConfigManager::inst()->setValue( "audioalsa", "channels", - QString::number( m_channels->value() ) ); -} - - - -void AudioAlsa::setupWidget::onCurrentIndexChanged(int index) -{ - -} - - #endif diff --git a/src/core/audio/AudioJack.cpp b/src/core/audio/AudioJack.cpp index b491ea826..137c5b445 100644 --- a/src/core/audio/AudioJack.cpp +++ b/src/core/audio/AudioJack.cpp @@ -425,7 +425,7 @@ void AudioJack::shutdownCallback( void * _udata ) AudioJack::setupWidget::setupWidget( QWidget * _parent ) : - AudioDevice::setupWidget( AudioJack::name(), _parent ) + AudioDeviceSetupWidget( AudioJack::name(), _parent ) { QString cn = ConfigManager::inst()->value( "audiojack", "clientname" ); if( cn.isEmpty() ) diff --git a/src/core/audio/AudioOss.cpp b/src/core/audio/AudioOss.cpp index f280da0f9..4a92b3cf5 100644 --- a/src/core/audio/AudioOss.cpp +++ b/src/core/audio/AudioOss.cpp @@ -329,7 +329,7 @@ void AudioOss::run() AudioOss::setupWidget::setupWidget( QWidget * _parent ) : - AudioDevice::setupWidget( AudioOss::name(), _parent ) + AudioDeviceSetupWidget( AudioOss::name(), _parent ) { m_device = new QLineEdit( probeDevice(), this ); m_device->setGeometry( 10, 20, 160, 20 ); diff --git a/src/core/audio/AudioPulseAudio.cpp b/src/core/audio/AudioPulseAudio.cpp index a1d1e740f..4463bab8a 100644 --- a/src/core/audio/AudioPulseAudio.cpp +++ b/src/core/audio/AudioPulseAudio.cpp @@ -280,7 +280,7 @@ void AudioPulseAudio::streamWriteCallback( pa_stream *s, size_t length ) AudioPulseAudio::setupWidget::setupWidget( QWidget * _parent ) : - AudioDevice::setupWidget( AudioPulseAudio::name(), _parent ) + AudioDeviceSetupWidget( AudioPulseAudio::name(), _parent ) { m_device = new QLineEdit( AudioPulseAudio::probeDevice(), this ); m_device->setGeometry( 10, 20, 160, 20 ); diff --git a/src/core/audio/AudioSdl.cpp b/src/core/audio/AudioSdl.cpp index 6285b4d4b..4587a61f3 100644 --- a/src/core/audio/AudioSdl.cpp +++ b/src/core/audio/AudioSdl.cpp @@ -203,7 +203,7 @@ void AudioSdl::sdlAudioCallback( Uint8 * _buf, int _len ) AudioSdl::setupWidget::setupWidget( QWidget * _parent ) : - AudioDevice::setupWidget( AudioSdl::name(), _parent ) + AudioDeviceSetupWidget( AudioSdl::name(), _parent ) { QString dev = ConfigManager::inst()->value( "audiosdl", "device" ); m_device = new QLineEdit( dev, this ); diff --git a/src/gui/AudioAlsaSetupWidget.cpp b/src/gui/AudioAlsaSetupWidget.cpp new file mode 100644 index 000000000..7f74bcecf --- /dev/null +++ b/src/gui/AudioAlsaSetupWidget.cpp @@ -0,0 +1,267 @@ +/* + * AudioAlsa.cpp - device-class which implements ALSA-PCM-output + * + * Copyright (c) 2004-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include + +#include "AudioAlsa.h" +#include "AudioAlsaSetupWidget.h" + +#ifdef LMMS_HAVE_ALSA + +#include "ConfigManager.h" +#include "LcdSpinBox.h" +#include "gui_templates.h" + +#include + + +void device_list(void) +{ + 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); + + card = -1; + if (snd_card_next(&card) < 0 || card < 0) { + return; + } + 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; + } + } +} + + + +void AudioAlsaSetupWidget::populateDeviceInfos(std::vector &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; + + // 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) + { + 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); + } +} + + + + +AudioAlsaSetupWidget::AudioAlsaSetupWidget( QWidget * _parent ) : + AudioDeviceSetupWidget( AudioAlsa::name(), _parent ), + m_selectedDevice(-1) +{ + populateDeviceInfos(m_deviceInfos); + + 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) + { + DeviceInfo const & currentDeviceInfo = m_deviceInfos[i]; + QString comboBoxText = currentDeviceInfo.getHWString() + " [" + currentDeviceInfo.getCardName() + " | " + currentDeviceInfo.getPcmName() + "]"; + m_deviceComboBox->addItem(comboBoxText, QVariant(static_cast(i))); + m_deviceComboBox->setItemData(i, comboBoxText, Qt::ToolTipRole); + + if (currentDeviceInfo.getHWString() == deviceText) + { + m_deviceComboBox->setCurrentIndex(static_cast(i)); + } + } + + m_selectedDevice = m_deviceComboBox->currentIndex(); + + m_deviceComboBox->setGeometry( 10, 20, 160, 20 ); + connect(m_deviceComboBox, + 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 ); + + LcdSpinBoxModel * m = new LcdSpinBoxModel( /* this */ ); + m->setRange( DEFAULT_CHANNELS, SURROUND_CHANNELS ); + m->setStep( 2 ); + m->setValue( ConfigManager::inst()->value( "audioalsa", + "channels" ).toInt() ); + + m_channels = new LcdSpinBox( 1, this ); + m_channels->setModel( m ); + m_channels->setLabel( tr( "CHANNELS" ) ); + m_channels->move( 180, 20 ); + +} + + + + +AudioAlsaSetupWidget::~AudioAlsaSetupWidget() +{ + delete m_channels->model(); +} + + + + +void AudioAlsaSetupWidget::saveSettings() +{ + QString deviceText; + + if (m_selectedDevice != -1) + { + DeviceInfo const & selectedDevice = m_deviceInfos[m_selectedDevice]; + deviceText = selectedDevice.getHWString(); + } + + ConfigManager::inst()->setValue( "audioalsa", "device", deviceText ); + ConfigManager::inst()->setValue( "audioalsa", "channels", + QString::number( m_channels->value() ) ); +} + + + +void AudioAlsaSetupWidget::onCurrentIndexChanged(int index) +{ + m_selectedDevice = index; +} + + +#endif diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index a23c37f81..bfe43f15c 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -2,6 +2,7 @@ SET(LMMS_SRCS ${LMMS_SRCS} gui/AboutDialog.cpp gui/ActionGroup.cpp + gui/AudioAlsaSetupWidget.cpp gui/AutomatableModelView.cpp gui/AutomationPatternView.cpp gui/ControllerConnectionDialog.cpp diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index 7ca9911ce..fd6233df8 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -51,6 +51,7 @@ // platform-specific audio-interface-classes #include "AudioAlsa.h" +#include "AudioAlsaSetupWidget.h" #include "AudioJack.h" #include "AudioOss.h" #include "AudioPortAudio.h" @@ -719,7 +720,7 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : #ifdef LMMS_HAVE_ALSA m_audioIfaceSetupWidgets[AudioAlsa::name()] = - new AudioAlsa::setupWidget( asw ); + new AudioAlsaSetupWidget( asw ); #endif #ifdef LMMS_HAVE_PULSEAUDIO From 27653b713b21558ba4b591049eba468ed2c2c3b7 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sat, 27 Jun 2015 16:42:17 +0200 Subject: [PATCH 3/5] Moves the probing code for available devices into the ALSA driver --- include/AudioAlsa.h | 42 +++++++++ include/AudioAlsaSetupWidget.h | 41 +-------- src/core/audio/AudioAlsa.cpp | 145 +++++++++++++++++++++++++++++++ src/gui/AudioAlsaSetupWidget.cpp | 145 +------------------------------ 4 files changed, 193 insertions(+), 180 deletions(-) diff --git a/include/AudioAlsa.h b/include/AudioAlsa.h index a916ad8da..c051e6eae 100644 --- a/include/AudioAlsa.h +++ b/include/AudioAlsa.h @@ -39,6 +39,46 @@ class AudioAlsa : public AudioDevice, public QThread { + // Public classes and enums +public: + /** + * @brief Contains the relevant information about available ALSA devices + */ + 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() {} + + 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); } + + private: + int m_cardNumber; + int m_deviceNumber; + QString m_cardName; + QString m_pcmName; + QString m_cardId; + QString m_pcmId; + }; + + typedef std::vector DeviceInfoCollection; + public: AudioAlsa( bool & _success_ful, Mixer* mixer ); virtual ~AudioAlsa(); @@ -51,6 +91,8 @@ public: static QString probeDevice(); + static DeviceInfoCollection getAvailableDevices(); + private: virtual void startProcessing(); virtual void stopProcessing(); diff --git a/include/AudioAlsaSetupWidget.h b/include/AudioAlsaSetupWidget.h index 0e2f9b69c..f9224eaca 100644 --- a/include/AudioAlsaSetupWidget.h +++ b/include/AudioAlsaSetupWidget.h @@ -31,6 +31,8 @@ #include "AudioDeviceSetupWidget.h" +#include "AudioAlsa.h" + #include @@ -52,50 +54,13 @@ public: public slots: void onCurrentIndexChanged(int index); -private: - 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() {} - - 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); } - - private: - int m_cardNumber; - int m_deviceNumber; - QString m_cardName; - QString m_pcmName; - QString m_cardId; - QString m_pcmId; - }; - - void populateDeviceInfos(std::vector &deviceInfos); - private: QComboBox * m_deviceComboBox; QLineEdit * m_device; LcdSpinBox * m_channels; int m_selectedDevice; - typedef std::vector DeviceInfoCollection; - DeviceInfoCollection m_deviceInfos; + AudioAlsa::DeviceInfoCollection m_deviceInfos; } ; #endif diff --git a/src/core/audio/AudioAlsa.cpp b/src/core/audio/AudioAlsa.cpp index 830effe2b..13ee39ab9 100644 --- a/src/core/audio/AudioAlsa.cpp +++ b/src/core/audio/AudioAlsa.cpp @@ -141,6 +141,151 @@ QString AudioAlsa::probeDevice() +// TODO Test code. Delete! +void device_list(void) +{ + 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); + + card = -1; + if (snd_card_next(&card) < 0 || card < 0) { + return; + } + 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; + } + } +} + + + + +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; + + // 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) + { + 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; +} + + + int AudioAlsa::handleError( int _err ) { diff --git a/src/gui/AudioAlsaSetupWidget.cpp b/src/gui/AudioAlsaSetupWidget.cpp index 7f74bcecf..8eea54777 100644 --- a/src/gui/AudioAlsaSetupWidget.cpp +++ b/src/gui/AudioAlsaSetupWidget.cpp @@ -38,151 +38,12 @@ #include -void device_list(void) -{ - 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); - - card = -1; - if (snd_card_next(&card) < 0 || card < 0) { - return; - } - 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; - } - } -} - - - -void AudioAlsaSetupWidget::populateDeviceInfos(std::vector &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; - - // 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) - { - 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); - } -} - - - AudioAlsaSetupWidget::AudioAlsaSetupWidget( QWidget * _parent ) : AudioDeviceSetupWidget( AudioAlsa::name(), _parent ), m_selectedDevice(-1) { - populateDeviceInfos(m_deviceInfos); + m_deviceInfos = AudioAlsa::getAvailableDevices(); QString deviceText = ConfigManager::inst()->value( "audioalsa", "device" ); @@ -192,7 +53,7 @@ AudioAlsaSetupWidget::AudioAlsaSetupWidget( QWidget * _parent ) : m_deviceComboBox = new QComboBox(this); for (size_t i = 0; i < m_deviceInfos.size(); ++i) { - DeviceInfo const & currentDeviceInfo = m_deviceInfos[i]; + AudioAlsa::DeviceInfo const & currentDeviceInfo = m_deviceInfos[i]; QString comboBoxText = currentDeviceInfo.getHWString() + " [" + currentDeviceInfo.getCardName() + " | " + currentDeviceInfo.getPcmName() + "]"; m_deviceComboBox->addItem(comboBoxText, QVariant(static_cast(i))); m_deviceComboBox->setItemData(i, comboBoxText, Qt::ToolTipRole); @@ -247,7 +108,7 @@ void AudioAlsaSetupWidget::saveSettings() if (m_selectedDevice != -1) { - DeviceInfo const & selectedDevice = m_deviceInfos[m_selectedDevice]; + AudioAlsa::DeviceInfo const & selectedDevice = m_deviceInfos[m_selectedDevice]; deviceText = selectedDevice.getHWString(); } From 37c4da81b58bb7005d07bc20b74669191d94004d Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sat, 27 Jun 2015 20:07:26 +0200 Subject: [PATCH 4/5] Final version that lets the user select the ALSA device with a combo box This version lets the user select the ALSA device to use with a combo box. It does not check whether the device works for the parameters that LMMS uses (SND_PCM_ACCESS_RW_INTERLEAVED, SND_PCM_FORMAT_S16_LE, etc.). Doing these checks while compiling the list of available devices led to strange effects which are likely caused by the fact that the PCM device has to be opened to query its capabilities. This in turn led to error messages a la "Resource or device busy" when testing the new functionality repeatedly. However, having a combo box to select devices from should be a good step forward compared to a simple line edit. :) --- include/AudioAlsa.h | 31 ++--- include/AudioAlsaSetupWidget.h | 8 +- include/AudioDeviceSetupWidget.h | 4 +- src/core/audio/AudioAlsa.cpp | 215 +++++++++++++------------------ src/gui/AudioAlsaSetupWidget.cpp | 24 ++-- 5 files changed, 113 insertions(+), 169 deletions(-) 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 ); From 5a8dce2650c93781f3630d2e023a6b6337aae53a Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sun, 28 Jun 2015 00:24:23 +0200 Subject: [PATCH 5/5] Fixes most of stuff found in Wallacoloo's code review for #1600 Removal of a superfluous include in AudioAlsaSetupWidget.cpp Removal of the function "bool hasCapabilities(char *device_name)" which was not used anyway. It implemented a test for ALSA device capabilities needed by LMMS (SND_PCM_ACCESS_RW_INTERLEAVED, SND_PCM_FORMAT_S16_LE, etc.). Corrected header name in AudioAlsaSetupWidget.h. Created an implementation file for AudioDeviceSetupWidget to make more clear that it's part of the GUI. Fix build for builds that use Port Audio. The setup widget of AudioPortAudio.h still inherited from AudioDevice::setupWidget instead of the new AudioDeviceSetupWidget. --- include/AudioAlsaSetupWidget.h | 4 +- include/AudioDeviceSetupWidget.h | 17 ++------- include/AudioPortAudio.h | 3 +- src/core/audio/AudioAlsa.cpp | 59 ------------------------------ src/core/audio/AudioPortAudio.cpp | 2 +- src/gui/AudioAlsaSetupWidget.cpp | 2 - src/gui/AudioDeviceSetupWidget.cpp | 41 +++++++++++++++++++++ src/gui/CMakeLists.txt | 1 + 8 files changed, 50 insertions(+), 79 deletions(-) create mode 100644 src/gui/AudioDeviceSetupWidget.cpp diff --git a/include/AudioAlsaSetupWidget.h b/include/AudioAlsaSetupWidget.h index 748536a0e..304870f22 100644 --- a/include/AudioAlsaSetupWidget.h +++ b/include/AudioAlsaSetupWidget.h @@ -1,5 +1,5 @@ /* - * AudioDeviceSetupWidget.h - Implements a setup widget for ALSA-PCM-output + * AudioAlsaSetupWidget.h - Implements a setup widget for ALSA-PCM-output * * Copyright (c) 2004-2015 Tobias Doerffel * @@ -57,7 +57,7 @@ private: int m_selectedDevice; AudioAlsa::DeviceInfoCollection m_deviceInfos; -} ; +}; #endif diff --git a/include/AudioDeviceSetupWidget.h b/include/AudioDeviceSetupWidget.h index 369d4d23c..6e23b3153 100644 --- a/include/AudioDeviceSetupWidget.h +++ b/include/AudioDeviceSetupWidget.h @@ -31,24 +31,13 @@ class AudioDeviceSetupWidget : public TabWidget { public: - AudioDeviceSetupWidget( const QString & _caption, QWidget * _parent ) : - TabWidget( TabWidget::tr( "Settings for %1" ).arg( - TabWidget::tr( _caption.toLatin1() ) ). - toUpper(), _parent ) - { - } + AudioDeviceSetupWidget( const QString & _caption, QWidget * _parent ); - virtual ~AudioDeviceSetupWidget() - { - } + virtual ~AudioDeviceSetupWidget(); virtual void saveSettings() = 0; - virtual void show() - { - parentWidget()->show(); - QWidget::show(); - } + virtual void show(); }; diff --git a/include/AudioPortAudio.h b/include/AudioPortAudio.h index a52ba52be..ab9be60c1 100644 --- a/include/AudioPortAudio.h +++ b/include/AudioPortAudio.h @@ -52,6 +52,7 @@ public: #endif #include "AudioDevice.h" +#include "AudioDeviceSetupWidget.h" #if defined paNeverDropInput || defined paNonInterleaved # define PORTAUDIO_V19 @@ -81,7 +82,7 @@ public: unsigned long _framesPerBuffer ); - class setupWidget : public AudioDevice::setupWidget + class setupWidget : public AudioDeviceSetupWidget { public: setupWidget( QWidget * _parent ); diff --git a/src/core/audio/AudioAlsa.cpp b/src/core/audio/AudioAlsa.cpp index fe26edccb..e903b436c 100644 --- a/src/core/audio/AudioAlsa.cpp +++ b/src/core/audio/AudioAlsa.cpp @@ -142,63 +142,6 @@ QString AudioAlsa::probeDevice() -/** - * @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_t *pcm; // PCM handle - snd_pcm_hw_params_t *hw_params; - int err; - - // 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; - } - - 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. * @@ -232,8 +175,6 @@ AudioAlsa::DeviceInfoCollection AudioAlsa::getAvailableDevices() 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))); diff --git a/src/core/audio/AudioPortAudio.cpp b/src/core/audio/AudioPortAudio.cpp index d23baf277..b2ddf1d97 100644 --- a/src/core/audio/AudioPortAudio.cpp +++ b/src/core/audio/AudioPortAudio.cpp @@ -389,7 +389,7 @@ void AudioPortAudioSetupUtil::updateChannels() AudioPortAudio::setupWidget::setupWidget( QWidget * _parent ) : - AudioDevice::setupWidget( AudioPortAudio::name(), _parent ) + AudioDeviceSetupWidget( AudioPortAudio::name(), _parent ) { m_backend = new ComboBox( this, "BACKEND" ); m_backend->setGeometry( 64, 15, 260, 20 ); diff --git a/src/gui/AudioAlsaSetupWidget.cpp b/src/gui/AudioAlsaSetupWidget.cpp index 0e83922e4..43fe828f3 100644 --- a/src/gui/AudioAlsaSetupWidget.cpp +++ b/src/gui/AudioAlsaSetupWidget.cpp @@ -29,8 +29,6 @@ #ifdef LMMS_HAVE_ALSA -#include "AudioAlsa.h" - #include "ConfigManager.h" #include "LcdSpinBox.h" #include "gui_templates.h" diff --git a/src/gui/AudioDeviceSetupWidget.cpp b/src/gui/AudioDeviceSetupWidget.cpp new file mode 100644 index 000000000..85515aab9 --- /dev/null +++ b/src/gui/AudioDeviceSetupWidget.cpp @@ -0,0 +1,41 @@ +/* + * AudioDeviceSetupWidget.cpp - Base class for audio device setup widgets + * + * Copyright (c) 2004-2015 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "AudioDeviceSetupWidget.h" + +AudioDeviceSetupWidget::AudioDeviceSetupWidget( const QString & _caption, QWidget * _parent ) : + TabWidget( TabWidget::tr( "Settings for %1" ).arg(TabWidget::tr( _caption.toLatin1() ) ).toUpper(), + _parent ) +{ +} + +AudioDeviceSetupWidget::~AudioDeviceSetupWidget() +{ +} + +void AudioDeviceSetupWidget::show() +{ + parentWidget()->show(); + QWidget::show(); +} diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index bfe43f15c..00f9f6296 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -3,6 +3,7 @@ SET(LMMS_SRCS gui/AboutDialog.cpp gui/ActionGroup.cpp gui/AudioAlsaSetupWidget.cpp + gui/AudioDeviceSetupWidget.cpp gui/AutomatableModelView.cpp gui/AutomationPatternView.cpp gui/ControllerConnectionDialog.cpp