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. :)
This commit is contained in:
@@ -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<DeviceInfo> DeviceInfoCollection;
|
||||
|
||||
@@ -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 <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2004-2015 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
@@ -33,12 +33,9 @@
|
||||
|
||||
#include "AudioAlsa.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
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;
|
||||
|
||||
@@ -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 <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2004-2015 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
|
||||
@@ -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 <tt>true</tt> 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2004-2015 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
@@ -23,14 +23,14 @@
|
||||
*/
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QLineEdit>
|
||||
#include <QLabel>
|
||||
|
||||
#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<uint>(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<int>(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 );
|
||||
|
||||
Reference in New Issue
Block a user