Add support for both ALSA hw and plughw

This commit is contained in:
Jonas Kvinge 2018-09-21 23:29:00 +02:00
parent aa83a2b40b
commit c77cb002f3
14 changed files with 323 additions and 100 deletions

View File

@ -99,10 +99,13 @@ QList<DeviceFinder::Device> AlsaDeviceFinder::ListDevices() {
Device device;
device.description = QString("%1 %2").arg(snd_ctl_card_info_get_name(cardinfo)).arg(snd_pcm_info_get_name(pcminfo));
device.value = QString("hw:%1,%2").arg(card).arg(dev);
device.iconname = GuessIconName(device.description);
device.card = card;
device.device = dev;
device.value = QString("hw:%1,%2").arg(card).arg(dev);
ret.append(device);
device.value = QString("plughw:%1,%2").arg(card).arg(dev);
ret.append(device);
}

View File

@ -93,6 +93,7 @@ public:
virtual bool ValidOutput(const QString &output) = 0;
virtual QString DefaultOutput() = 0;
virtual bool CustomDeviceSupport(const QString &output) = 0;
virtual bool ALSADeviceSupport(const QString &output) = 0;
// Plays a media stream represented with the URL 'u' from the given 'beginning' to the given 'end' (usually from 0 to a song's length).
// Both markers should be passed in nanoseconds. 'end' can be negative, indicating that the real length of 'u' stream is unknown.

View File

@ -27,37 +27,33 @@
namespace Engine {
Engine::EngineType EngineTypeFromName(QString enginename) {
QString lower = enginename.toLower();
if (lower == "xine") return Engine::Xine;
else if (lower == "gstreamer") return Engine::GStreamer;
else if (lower == "phonon") return Engine::Phonon;
else if (lower == "vlc") return Engine::VLC;
else return Engine::None;
if (lower == "xine") return Engine::Xine;
else if (lower == "gstreamer") return Engine::GStreamer;
else if (lower == "phonon") return Engine::Phonon;
else if (lower == "vlc") return Engine::VLC;
else return Engine::None;
}
QString EngineName(Engine::EngineType enginetype) {
switch (enginetype) {
case Engine::Xine: return QObject::tr("xine");
case Engine::GStreamer: return QObject::tr("gstreamer");
case Engine::Phonon: return QObject::tr("phonon");
case Engine::VLC: return QObject::tr("vlc");
case Engine::Xine: return QString("xine");
case Engine::GStreamer: return QString("gstreamer");
case Engine::Phonon: return QString("phonon");
case Engine::VLC: return QString("vlc");
case Engine::None:
default: return QObject::tr("None");
default: return QString("None");
}
}
QString EngineDescription(Engine::EngineType enginetype) {
switch (enginetype) {
case Engine::Xine: return QObject::tr("Xine");
case Engine::GStreamer: return QObject::tr("GStreamer");
case Engine::Phonon: return QObject::tr("Phonon");
case Engine::VLC: return QObject::tr("VLC");
case Engine::Xine: return QString("Xine");
case Engine::GStreamer: return QString("GStreamer");
case Engine::Phonon: return QString("Phonon");
case Engine::VLC: return QString("VLC");
case Engine::None:
default: return QObject::tr("None");
default: return QString("None");
}
}

View File

@ -402,6 +402,10 @@ bool GstEngine::CustomDeviceSupport(const QString &output) {
return (output == kALSASink || output == kOpenALSASink || output == kOSSSink || output == kOSS4Sink || output == kPulseSink || output == kA2DPSink || output == kAVDTPSink);
}
bool GstEngine::ALSADeviceSupport(const QString &output) {
return (output == kALSASink);
}
void GstEngine::ReloadSettings() {
Engine::Base::ReloadSettings();

View File

@ -87,6 +87,7 @@ class GstEngine : public Engine::Base, public GstBufferConsumer {
bool ValidOutput(const QString &output);
QString DefaultOutput() { return kAutoSink; }
bool CustomDeviceSupport(const QString &output);
bool ALSADeviceSupport(const QString &output);
void EnsureInitialised() { initialising_.waitForFinished(); }
void InitialiseGStreamer();

View File

@ -189,3 +189,7 @@ bool PhononEngine::ValidOutput(const QString &output) {
bool PhononEngine::CustomDeviceSupport(const QString &output) {
return false;
}
bool PhononEngine::ALSADeviceSupport(const QString &output) {
return false;
}

View File

@ -66,6 +66,7 @@ class PhononEngine : public Engine::Base {
QString DefaultOutput() { return ""; }
bool ValidOutput(const QString &output);
bool CustomDeviceSupport(const QString &output);
bool ALSADeviceSupport(const QString &output);
protected:
void SetVolumeSW( uint percent );

View File

@ -238,6 +238,10 @@ bool VLCEngine::CustomDeviceSupport(const QString &output) {
return (output == "auto" ? false : true);
}
bool VLCEngine::ALSADeviceSupport(const QString &output) {
return (output == "alsa");
}
uint VLCEngine::position() const {
if (!Initialised()) return (0);

View File

@ -64,6 +64,7 @@ class VLCEngine : public Engine::Base {
bool ValidOutput(const QString &output);
QString DefaultOutput() { return ""; }
bool CustomDeviceSupport(const QString &output);
bool ALSADeviceSupport(const QString &output);
private:
libvlc_instance_t *instance_;

View File

@ -345,6 +345,10 @@ bool XineEngine::CustomDeviceSupport(const QString &output) {
return (output == "alsa" || output == "oss" || output == "jack" || output == "pulseaudio");
}
bool XineEngine::ALSADeviceSupport(const QString &output) {
return (output == "alsa");
}
void XineEngine::ReloadSettings() {
Engine::Base::ReloadSettings();

View File

@ -87,6 +87,7 @@ class XineEngine : public Engine::Base {
bool ValidOutput(const QString &output);
QString DefaultOutput() { return "auto"; }
bool CustomDeviceSupport(const QString &output);
bool ALSADeviceSupport(const QString &output);
void ReloadSettings();

View File

@ -31,6 +31,7 @@
#include <QSlider>
#include <QSpinBox>
#include <QLabel>
#include <QListView>
#include "backendsettingspage.h"
@ -112,6 +113,19 @@ void BackendSettingsPage::Load() {
ui_->stickslider_replaygainpreamp->setValue(s_.value("rgpreamp", 0.0).toDouble() * 10 + 150);
ui_->checkbox_replaygaincompression->setChecked(s_.value("rgcompression", true).toBool());
int alsaplug_int = alsa_plugin(s_.value("alsaplugin", 0).toInt());
if (alsa_plugin(alsaplug_int)) {
alsa_plugin alsaplugin = alsa_plugin(alsaplug_int);
switch (alsaplugin) {
case alsa_plugin::alsa_hw:
ui_->radiobutton_alsa_hw->setChecked(true);
break;
case alsa_plugin::alsa_plughw:
ui_->radiobutton_alsa_plughw->setChecked(true);
break;
}
}
if (!EngineInitialised()) return;
if (engine()->state() == Engine::Empty) {
@ -139,6 +153,8 @@ void BackendSettingsPage::ConnectSignals() {
connect(ui_->lineedit_device, SIGNAL(textChanged(const QString &)), SLOT(DeviceStringChanged()));
connect(ui_->slider_bufferminfill, SIGNAL(valueChanged(int)), SLOT(BufferMinFillChanged(int)));
connect(ui_->stickslider_replaygainpreamp, SIGNAL(valueChanged(int)), SLOT(RgPreampChanged(int)));
connect(ui_->radiobutton_alsa_hw, SIGNAL(clicked(bool)), SLOT(radiobutton_alsa_hw_clicked(bool)));
connect(ui_->radiobutton_alsa_plughw, SIGNAL(clicked(bool)), SLOT(radiobutton_alsa_plughw_clicked(bool)));
}
@ -271,6 +287,27 @@ void BackendSettingsPage::Load_Device(QString output, QVariant device) {
ui_->lineedit_device->setEnabled(false);
}
if (engine()->ALSADeviceSupport(output)) {
ui_->radiobutton_alsa_hw->setEnabled(true);
ui_->radiobutton_alsa_plughw->setEnabled(true);
if (device.toString().contains(QRegExp("^plughw:.*"))) {
ui_->radiobutton_alsa_hw->setChecked(false);
ui_->radiobutton_alsa_plughw->setChecked(true);
SwitchALSADevices(alsa_plugin::alsa_plughw);
}
else {
ui_->radiobutton_alsa_plughw->setChecked(false);
ui_->radiobutton_alsa_hw->setChecked(true);
SwitchALSADevices(alsa_plugin::alsa_hw);
}
}
else {
ui_->radiobutton_alsa_hw->setEnabled(false);
ui_->radiobutton_alsa_hw->setChecked(false);
ui_->radiobutton_alsa_plughw->setEnabled(false);
ui_->radiobutton_alsa_plughw->setChecked(false);
}
bool found(false);
for (int i = 0; i < ui_->combobox_device->count(); ++i) {
QVariant d = ui_->combobox_device->itemData(i).value<QVariant>();
@ -285,17 +322,12 @@ void BackendSettingsPage::Load_Device(QString output, QVariant device) {
if (engine()->CustomDeviceSupport(output) && device.type() == QVariant::String && !device.toString().isEmpty()) {
ui_->lineedit_device->setText(device.toString());
if (!found) {
bool have_custom(false);
int index_custom = 0;
for (int i = 0; i < ui_->combobox_device->count(); ++i) {
if (ui_->combobox_device->itemText(i) == "Custom") {
have_custom = true;
index_custom = i;
if (ui_->combobox_device->currentText() != "Custom") ui_->combobox_device->setCurrentIndex(i);
break;
}
}
if (have_custom) ui_->combobox_device->setItemData(index_custom, QVariant(device.toString()));
}
}
@ -315,7 +347,9 @@ void BackendSettingsPage::Save() {
EngineBase::OutputDetails output = ui_->combobox_output->itemData(ui_->combobox_output->currentIndex()).value<EngineBase::OutputDetails>();
output_name = output.name;
}
if (ui_->combobox_device->currentText().isEmpty()) device_value = QVariant();
else if (ui_->combobox_device->currentText() == "Custom") device_value = ui_->lineedit_device->text();
else device_value = ui_->combobox_device->itemData(ui_->combobox_device->currentIndex()).value<QVariant>();
s_.setValue("engine", EngineName(enginetype));
@ -330,6 +364,10 @@ void BackendSettingsPage::Save() {
s_.setValue("rgpreamp", float(ui_->stickslider_replaygainpreamp->value()) / 10 - 15);
s_.setValue("rgcompression", ui_->checkbox_replaygaincompression->isChecked());
if (ui_->radiobutton_alsa_hw->isChecked()) s_.setValue("alsaplugin", static_cast<int>(alsa_plugin::alsa_hw));
else if (ui_->radiobutton_alsa_plughw->isChecked()) s_.setValue("alsaplugin", static_cast<int>(alsa_plugin::alsa_plughw));
else s_.remove("alsaplugin");
// If engine has not been changed, but output or device has been changed,
// then set_output_changed(true) to reinitialize engine when dialog closes.
if (enginetype == enginetype_current_ && (output_name != output_current_ || device_value != device_current_)) dialog()->set_output_changed(true);
@ -347,7 +385,7 @@ void BackendSettingsPage::Cancel() {
void BackendSettingsPage::EngineChanged(int index) {
if (!configloaded_ || !EngineInitialised()) return;
QVariant v = ui_->combobox_engine->itemData(index);
Engine::EngineType enginetype = v.value<Engine::EngineType>();
@ -386,12 +424,7 @@ void BackendSettingsPage::DeviceSelectionChanged(int index) {
if (engine()->CustomDeviceSupport(output.name)) {
ui_->lineedit_device->setEnabled(true);
if (ui_->combobox_device->currentText() == "Custom") {
ui_->combobox_device->setItemData(index, QVariant(ui_->lineedit_device->text()));
}
else {
if (device.type() == QVariant::String) ui_->lineedit_device->setText(device.toString());
}
if (ui_->combobox_device->currentText() != "Custom" && device.type() == QVariant::String) ui_->lineedit_device->setText(device.toString());
}
else {
ui_->lineedit_device->setEnabled(false);
@ -409,10 +442,22 @@ void BackendSettingsPage::DeviceStringChanged() {
EngineBase::OutputDetails output = ui_->combobox_output->itemData(ui_->combobox_output->currentIndex()).value<EngineBase::OutputDetails>();
bool found(false);
if (engine()->ALSADeviceSupport(output.name)) {
if (ui_->lineedit_device->text().contains(QRegExp("^hw:.*")) && !ui_->radiobutton_alsa_hw->isChecked()) {
ui_->radiobutton_alsa_hw->setChecked(true);
SwitchALSADevices(alsa_plugin::alsa_hw);
}
else if (ui_->lineedit_device->text().contains(QRegExp("^plughw:.*")) && !ui_->radiobutton_alsa_plughw->isChecked()) {
ui_->radiobutton_alsa_plughw->setChecked(true);
SwitchALSADevices(alsa_plugin::alsa_plughw);
}
}
for (int i = 0; i < ui_->combobox_device->count(); ++i) {
QVariant device = ui_->combobox_device->itemData(i).value<QVariant>();
if (device.type() != QVariant::String) continue;
if (device.toString().isEmpty()) continue;
if (ui_->combobox_device->itemText(i) == "Custom") continue;
if (device.toString() == ui_->lineedit_device->text()) {
if (ui_->combobox_device->currentIndex() != i) ui_->combobox_device->setCurrentIndex(i);
found = true;
@ -430,7 +475,6 @@ void BackendSettingsPage::DeviceStringChanged() {
}
}
if (ui_->combobox_device->currentText() == "Custom") {
ui_->combobox_device->setItemData(ui_->combobox_device->currentIndex(), QVariant(ui_->lineedit_device->text()));
if ((ui_->lineedit_device->text().isEmpty()) && (ui_->combobox_device->count() > 0) && (ui_->combobox_device->currentIndex() != 0)) ui_->combobox_device->setCurrentIndex(0);
}
}
@ -502,3 +546,73 @@ void BackendSettingsPage::XineWarning() {
xinewarning_ = true;
}
void BackendSettingsPage::SwitchALSADevices(alsa_plugin alsaplugin) {
// All ALSA devices are listed twice, one for "hw" and one for "plughw"
// Only show one of them by making the other ones invisible based on the alsa plugin radiobuttons
for (int i = 0; i < ui_->combobox_device->count(); ++i) {
QListView *view = qobject_cast<QListView *>(ui_->combobox_device->view());
if (!view) continue;
if (alsaplugin == alsa_plugin::alsa_hw && ui_->combobox_device->itemData(i).toString().contains(QRegExp("^plughw:.*"))) {
view->setRowHidden(i, true);
}
else if (alsaplugin == alsa_plugin::alsa_plughw && ui_->combobox_device->itemData(i).toString().contains(QRegExp("^hw:.*"))) {
view->setRowHidden(i, true);
}
else {
view->setRowHidden(i, false);
}
}
}
void BackendSettingsPage::radiobutton_alsa_hw_clicked(bool checked) {
if (!configloaded_ || !EngineInitialised()) return;
EngineBase::OutputDetails output = ui_->combobox_output->itemData(ui_->combobox_output->currentIndex()).value<EngineBase::OutputDetails>();
if (!engine()->ALSADeviceSupport(output.name)) return;
if (ui_->lineedit_device->text().contains(QRegExp("^plughw:.*"))) {
SwitchALSADevices(alsa_plugin::alsa_hw);
QString device_new = ui_->lineedit_device->text().replace(QRegExp("^plughw:"), "hw:");
bool found(false);
for (int i = 0; i < ui_->combobox_device->count(); ++i) {
QVariant device = ui_->combobox_device->itemData(i).value<QVariant>();
if (device.type() != QVariant::String) continue;
if (device.toString().isEmpty()) continue;
if (device.toString() == device_new) {
if (ui_->combobox_device->currentIndex() != i) ui_->combobox_device->setCurrentIndex(i);
found = true;
}
}
if (!found) ui_->lineedit_device->setText(device_new);
}
}
void BackendSettingsPage::radiobutton_alsa_plughw_clicked(bool checked) {
if (!configloaded_ || !EngineInitialised()) return;
EngineBase::OutputDetails output = ui_->combobox_output->itemData(ui_->combobox_output->currentIndex()).value<EngineBase::OutputDetails>();
if (!engine()->ALSADeviceSupport(output.name)) return;
if (ui_->lineedit_device->text().contains(QRegExp("^hw:.*"))) {
SwitchALSADevices(alsa_plugin::alsa_plughw);
QString device_new = ui_->lineedit_device->text().replace(QRegExp("^hw:"), "plughw:");
bool found(false);
for (int i = 0; i < ui_->combobox_device->count(); ++i) {
QVariant device = ui_->combobox_device->itemData(i).value<QVariant>();
if (device.type() != QVariant::String) continue;
if (device.toString().isEmpty()) continue;
if (device.toString() == device_new) {
if (ui_->combobox_device->currentIndex() != i) ui_->combobox_device->setCurrentIndex(i);
found = true;
}
}
if (!found) ui_->lineedit_device->setText(device_new);
}
}

View File

@ -63,8 +63,16 @@ public:
void DeviceStringChanged();
void RgPreampChanged(int value);
void BufferMinFillChanged(int value);
void radiobutton_alsa_hw_clicked(bool checked);
void radiobutton_alsa_plughw_clicked(bool checked);
private:
enum alsa_plugin {
alsa_hw = 1,
alsa_plughw = 2
};
Ui_BackendSettingsPage *ui_;
void ConnectSignals();
@ -78,6 +86,7 @@ private:
void ShowWarning(QString text);
void ResetWarning();
void XineWarning();
void SwitchALSADevices(alsa_plugin alsaplugin);
QSettings s_;
bool configloaded_;

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>596</width>
<height>638</height>
<height>720</height>
</rect>
</property>
<property name="windowTitle">
@ -19,67 +19,84 @@
<property name="title">
<string>Audio output</string>
</property>
<layout class="QFormLayout" name="formLayout_3">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_engine">
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Engine</string>
</property>
</widget>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_engine">
<item>
<widget class="QLabel" name="label_engine">
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Engine</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="combobox_engine">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="spacer_engine">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</spacer>
</item>
</layout>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="combobox_engine"/>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_output">
<item>
<widget class="QLabel" name="label_output">
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Output</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="combobox_output">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="spacer_output">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</spacer>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_output">
<property name="enabled">
<bool>true</bool>
</property>
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Output</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="combobox_output">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_device">
<property name="enabled">
<bool>true</bool>
</property>
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Device</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_device" stretch="0,0">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_device" stretch="0,0,0,0">
<item>
<widget class="QLabel" name="label_device">
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Device</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="combobox_device">
<property name="enabled">
@ -92,6 +109,12 @@
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>160</width>
@ -118,6 +141,69 @@
</property>
</widget>
</item>
<item>
<spacer name="spacer_device">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_alsaplugin">
<item>
<widget class="QLabel" name="label_alsaplugin">
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>ALSA plugin</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radiobutton_alsa_hw">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
<property name="text">
<string>hw</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radiobutton_alsa_plughw">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
<property name="text">
<string>plughw</string>
</property>
</widget>
</item>
<item>
<spacer name="spacer_alsaplugin">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
@ -155,7 +241,7 @@
<item row="2" column="0">
<widget class="QLabel" name="label_bufferminfill">
<property name="text">
<string>Minimum buffer fill</string>
<string>&amp;Minimum buffer fill</string>
</property>
</widget>
</item>
@ -370,12 +456,6 @@
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>