diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3627d40..8684251 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,67 +1,68 @@ include_directories(${PULSEAUDIO_INCLUDE_DIR} ${GLIB2_INCLUDE_DIR}) set(cpp_SRCS card.cpp client.cpp context.cpp device.cpp maps.cpp operation.cpp port.cpp profile.cpp pulseaudio.cpp pulseobject.cpp sink.cpp sinkinput.cpp modulemanager.cpp source.cpp sourceoutput.cpp stream.cpp volumeobject.cpp debug.cpp server.cpp streamrestore.cpp module.cpp + canberracontext.cpp qml/globalactioncollection.cpp qml/plugin.cpp qml/volumeosd.cpp qml/volumefeedback.cpp ) set(qml_SRCS qml/qmldir qml/PulseObjectFilterModel.qml ) set_property(SOURCE qml/dbus/osdService.xml APPEND PROPERTY CLASSNAME OsdServiceInterface) qt5_add_dbus_interface(dbus_SRCS qml/dbus/osdService.xml osdservice) if (HAVE_GCONF) include_directories(${GCONF_INCLUDE_DIRS} ${GOBJECT_INCLUDE_DIRS}) set(cpp_SRCS ${cpp_SRCS} gconfitem.cpp) endif() add_library(plasma-volume-declarative SHARED ${dbus_SRCS} ${cpp_SRCS} ${qml_SRCS}) target_link_libraries(plasma-volume-declarative Qt5::Core Qt5::Gui Qt5::DBus Qt5::Quick KF5::GlobalAccel ${PULSEAUDIO_LIBRARY} ${PULSEAUDIO_MAINLOOP_LIBRARY} ${CANBERRA_LIBRARIES} ) if (HAVE_GCONF) target_link_libraries(plasma-volume-declarative ${GCONF_LDFLAGS} ${GOBJECT_LDFLAGS} ) endif() set(PRIVATE_QML_INSTALL_DIR ${KDE_INSTALL_QMLDIR}/org/kde/plasma/private/volume) install(TARGETS plasma-volume-declarative DESTINATION ${PRIVATE_QML_INSTALL_DIR}) install(FILES ${qml_SRCS} DESTINATION ${PRIVATE_QML_INSTALL_DIR}) add_subdirectory(kcm) diff --git a/src/sink.h b/src/canberracontext.cpp similarity index 54% copy from src/sink.h copy to src/canberracontext.cpp index f3f2a2c..4c485a4 100644 --- a/src/sink.h +++ b/src/canberracontext.cpp @@ -1,47 +1,68 @@ /* - Copyright 2014-2015 Harald Sitter + Copyright 2018 Nicolas Fella This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ -#ifndef SINK_H -#define SINK_H - -#include "device.h" +#include "canberracontext.h" +#include namespace QPulseAudio { -class Sink : public Device +CanberraContext *CanberraContext::s_context = nullptr; + +CanberraContext *CanberraContext::instance() { - Q_OBJECT -public: - explicit Sink(QObject *parent); + if (!s_context) { + s_context = new CanberraContext; + } + return s_context; +} - void update(const pa_sink_info *info); - void setVolume(qint64 volume) override; - void setMuted(bool muted) override; - void setActivePortIndex(quint32 port_index) override; - void setChannelVolume(int channel, qint64 volume) override; +CanberraContext::CanberraContext(QObject *parent) + : QObject(parent) +{ + ca_context_create(&m_canberra); +} - bool isDefault() const override; - void setDefault(bool enable) override; -}; -} // QPulseAudio +CanberraContext::~CanberraContext() +{ + if (m_canberra) { + ca_context_destroy(m_canberra); + } +} -#endif // SINK_H +ca_context *CanberraContext::canberra() +{ + return m_canberra; +} + +void CanberraContext::ref() +{ + ++m_references; +} + +void CanberraContext::unref() +{ + if (--m_references == 0) { + delete this; + s_context = nullptr; + } +} +} diff --git a/src/qml/volumefeedback.h b/src/canberracontext.h similarity index 69% copy from src/qml/volumefeedback.h copy to src/canberracontext.h index 3e38f94..1c1c314 100644 --- a/src/qml/volumefeedback.h +++ b/src/canberracontext.h @@ -1,46 +1,51 @@ /* - Copyright 2016 David Rosca + Copyright 2018 Nicolas Fella This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ +#pragma once -#ifndef VOLUMEFEEDBACK_H -#define VOLUMEFEEDBACK_H - +#include #include -#include +namespace QPulseAudio +{ -class VolumeFeedback : public QObject +class CanberraContext : public QObject { Q_OBJECT - Q_PROPERTY(bool valid READ isValid CONSTANT) public: - explicit VolumeFeedback(QObject *parent = nullptr); - ~VolumeFeedback() override; + explicit CanberraContext(QObject *parent = nullptr); + virtual ~CanberraContext(); - bool isValid() const; + static CanberraContext *instance(); -public slots: - void play(quint32 sinkIndex); + ca_context *canberra(); + + void ref(); + void unref(); private: - ca_context *m_context = nullptr; + ca_context *m_canberra = nullptr; + int m_references = 0; + + static CanberraContext *s_context; + }; -#endif // VOLUMEFEEDBACK_H +} diff --git a/src/kcm/package/contents/ui/Advanced.qml b/src/kcm/package/contents/ui/Advanced.qml index 0cd9eb9..e208006 100644 --- a/src/kcm/package/contents/ui/Advanced.qml +++ b/src/kcm/package/contents/ui/Advanced.qml @@ -1,97 +1,245 @@ /* Copyright 2014-2015 Harald Sitter 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) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. 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. If not, see . */ import QtQuick 2.0 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.3 import org.kde.plasma.private.volume 0.1 +import org.kde.kcoreaddons 1.0 as KCoreAddons + ScrollView { id: scrollView ColumnLayout { Component.onCompleted: { // Normal binding causes binding loops width = Qt.binding(function() { return scrollView.viewport.width; }); } Header { Layout.fillWidth: true enabled: view.count > 0 text: i18n("Device Profiles") disabledText: i18nc("@label", "No Device Profiles Available") } ListView { id: view Layout.fillWidth: true Layout.preferredHeight: contentHeight Layout.margins: units.gridUnit / 2 interactive: false spacing: units.smallSpacing * 2 model: CardModel {} delegate: CardListItem {} } Header { Layout.fillWidth: true text: i18n("Advanced Output Configuration") visible: moduleManager.settingsSupported } ModuleManager { id: moduleManager } CheckBox { Layout.fillWidth: true Layout.topMargin: units.smallSpacing Layout.leftMargin: units.gridUnit / 2 Layout.rightMargin: units.gridUnit / 2 text: i18n("Add virtual output device for simultaneous output on all local sound cards") checked: moduleManager.combineSinks onCheckedChanged: moduleManager.combineSinks = checked; enabled: moduleManager.loadedModules.indexOf("module-gconf") != -1 visible: moduleManager.settingsSupported } CheckBox { Layout.fillWidth: true Layout.leftMargin: units.gridUnit / 2 Layout.rightMargin: units.gridUnit / 2 text: i18n("Automatically switch all running streams when a new output becomes available") checked: moduleManager.switchOnConnect onCheckedChanged: moduleManager.switchOnConnect = checked; enabled: moduleManager.loadedModules.indexOf("module-gconf") != -1 visible: moduleManager.settingsSupported } Label { Layout.alignment: Qt.AlignHCenter enabled: false font.italic: true text: i18n("Requires 'module-gconf' PulseAudio module") visible: moduleManager.settingsSupported && moduleManager.loadedModules.indexOf("module-gconf") == -1 } + + Header { + Layout.fillWidth: true + text: i18n("Speaker Placement and Testing") + } + + RowLayout { + Layout.margins: units.gridUnit / 2 + visible: sinks.count > 1 + + Label { + text: i18nc("@label", "Output:") + font.bold: true + } + + ComboBox { + id: sinks + + property var pulseObject: null + + Layout.fillWidth: true + textRole: "Description" + model: SinkModel { + onRowsInserted: sinks.updatePulseObject() + onRowsRemoved: sinks.updatePulseObject() + onDataChanged: sinks.updatePulseObject() + } + onCurrentIndexChanged: updatePulseObject() + onCurrentTextChanged: updatePulseObject() + Component.onCompleted: updatePulseObject() + + function updatePulseObject() { + Qt.callLater(function() { + pulseObject = model.data(model.index(sinks.currentIndex, 0), model.role("PulseObject")); + }); + } + } + } + + Grid { + id: grid + columns: 3 + spacing: 5 + Layout.fillWidth: true + + Item { + width: grid.width/3 + height: 50 + + Button{ + text: i18n("Front Left") + anchors.centerIn: parent + visible: sinks.pulseObject ? sinks.pulseObject.channels.indexOf("Front Left") > -1 : false + onClicked: sinks.pulseObject.testChannel("Front Left") + } + } + Item { + width: grid.width/3 + height: 50 + + Button{ + text: i18n("Front Center") + anchors.centerIn: parent + visible: sinks.pulseObject ? sinks.pulseObject.channels.indexOf("Front Center") > -1 : false + onClicked: sinks.pulseObject.testChannel("Front Center") + } + } + Item { + width: grid.width/3 + height: 50 + + Button{ + text: i18n("Front Right") + anchors.centerIn: parent + visible: sinks.pulseObject ? sinks.pulseObject.channels.indexOf("Front Right") > -1 : false + onClicked: sinks.pulseObject.testChannel("Front Right") + } + } + Item { + width: grid.width/3 + height: 50 + + Button{ + text: i18n("Side Left") + anchors.centerIn: parent + visible: sinks.pulseObject ? sinks.pulseObject.channels.indexOf("Side Left") > -1 : false + onClicked: sinks.pulseObject.testChannel("Side Left") + + } + } + Item { + width: grid.width/3 + height: 50 + + KCoreAddons.KUser { + id: kuser + } + + Image { + source: kuser.faceIconUrl + anchors.centerIn: parent + width: 50 + height: 50 + } + } + Item { + width: grid.width/3 + height: 50 + Button{ + text: i18n("Side Right") + anchors.centerIn: parent + visible: sinks.pulseObject ? sinks.pulseObject.channels.indexOf("Side Right") > -1 : false + onClicked: sinks.pulseObject.testChannel("Side Right") + } + } + Item { + width: grid.width/3 + height: 50 + Button{ + text: i18n("Rear Left") + anchors.centerIn: parent + visible: sinks.pulseObject ? sinks.pulseObject.channels.indexOf("Rear Left") > -1 : false + onClicked: sinks.pulseObject.testChannel("Rear Left") + } + } + Item { + width: grid.width/3 + height: 50 + Button{ + text: i18n("Subwoofer") + anchors.centerIn: parent + visible: sinks.pulseObject ? sinks.pulseObject.channels.indexOf("Subwoofer") > -1 : false + onClicked: sinks.pulseObject.testChannel("Subwoofer") + } + } + Item { + width: grid.width/3 + height: 50 + Button{ + text: i18n("Rear Right") + anchors.centerIn: parent + visible: sinks.pulseObject ? sinks.pulseObject.channels.indexOf("Rear Right") > -1 : false + onClicked: sinks.pulseObject.testChannel("Rear Right") + } + } + } } } diff --git a/src/qml/volumefeedback.cpp b/src/qml/volumefeedback.cpp index 2c9f6c7..69bc260 100644 --- a/src/qml/volumefeedback.cpp +++ b/src/qml/volumefeedback.cpp @@ -1,83 +1,79 @@ /* Copyright 2008 Helio Chissini de Castro Copyright 2016 David Rosca This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "volumefeedback.h" +#include "canberracontext.h" VolumeFeedback::VolumeFeedback(QObject *parent) : QObject(parent) { - if (ca_context_create(&m_context) < 0) { - m_context = nullptr; - return; - } - if (ca_context_set_driver(m_context, "pulse") < 0) { - ca_context_destroy(m_context); - m_context = nullptr; + QPulseAudio::CanberraContext::instance()->ref(); + if (ca_context_set_driver(QPulseAudio::CanberraContext::instance()->canberra(), "pulse") < 0) { return; } } VolumeFeedback::~VolumeFeedback() { - if (m_context) { - ca_context_destroy(m_context); - } + QPulseAudio::CanberraContext::instance()->unref(); } bool VolumeFeedback::isValid() const { - return m_context; + return QPulseAudio::CanberraContext::instance()->canberra(); } void VolumeFeedback::play(quint32 sinkIndex) { - if (!m_context) { + auto context = QPulseAudio::CanberraContext::instance()->canberra(); + + if (!context) { return; } int playing = 0; const int cindex = 2; // Note "2" is simply the index we've picked. It's somewhat irrelevant. - ca_context_playing(m_context, cindex, &playing); + ca_context_playing(context, cindex, &playing); // NB Depending on how this is desired to work, we may want to simply // skip playing, or cancel the currently playing sound and play our // new one... for now, let's do the latter. if (playing) { - ca_context_cancel(m_context, cindex); + ca_context_cancel(context, cindex); } char dev[64]; snprintf(dev, sizeof(dev), "%lu", (unsigned long) sinkIndex); - ca_context_change_device(m_context, dev); + ca_context_change_device(context, dev); // Ideally we'd use something like ca_gtk_play_for_widget()... ca_context_play( - m_context, + context, cindex, CA_PROP_EVENT_DESCRIPTION, "Volume Control Feedback Sound", CA_PROP_EVENT_ID, "audio-volume-change", CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", CA_PROP_CANBERRA_ENABLE, "1", nullptr ); - ca_context_change_device(m_context, nullptr); + ca_context_change_device(context, nullptr); } diff --git a/src/qml/volumefeedback.h b/src/qml/volumefeedback.h index 3e38f94..9c58f34 100644 --- a/src/qml/volumefeedback.h +++ b/src/qml/volumefeedback.h @@ -1,46 +1,43 @@ /* Copyright 2016 David Rosca This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef VOLUMEFEEDBACK_H #define VOLUMEFEEDBACK_H #include #include class VolumeFeedback : public QObject { Q_OBJECT Q_PROPERTY(bool valid READ isValid CONSTANT) public: explicit VolumeFeedback(QObject *parent = nullptr); ~VolumeFeedback() override; bool isValid() const; public slots: void play(quint32 sinkIndex); - -private: - ca_context *m_context = nullptr; }; #endif // VOLUMEFEEDBACK_H diff --git a/src/sink.cpp b/src/sink.cpp index 8058a8a..b183c3a 100644 --- a/src/sink.cpp +++ b/src/sink.cpp @@ -1,77 +1,198 @@ /* Copyright 2014-2015 Harald Sitter This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "sink.h" #include "context.h" #include "server.h" +#include "canberracontext.h" +#include namespace QPulseAudio { Sink::Sink(QObject *parent) : Device(parent) { connect(context()->server(), &Server::defaultSinkChanged, this, &Sink::defaultChanged); + CanberraContext::instance()->ref(); +} + +Sink::~Sink() +{ + CanberraContext::instance()->unref(); } void Sink::update(const pa_sink_info *info) { updateDevice(info); } void Sink::setVolume(qint64 volume) { context()->setGenericVolume(index(), -1, volume, cvolume(), &pa_context_set_sink_volume_by_index); } void Sink::setMuted(bool muted) { context()->setGenericMute(m_index, muted, &pa_context_set_sink_mute_by_index); } void Sink::setActivePortIndex(quint32 port_index) { Port *port = qobject_cast(ports().at(port_index)); if (!port) { qCWarning(PLASMAPA) << "invalid port set request" << port_index; return; } context()->setGenericPort(index(), port->name(), &pa_context_set_sink_port_by_index); } void Sink::setChannelVolume(int channel, qint64 volume) { context()->setGenericVolume(index(), channel, volume, cvolume(), &pa_context_set_sink_volume_by_index); } bool Sink::isDefault() const { return context()->server()->defaultSink() == this; } void Sink::setDefault(bool enable) { if (!isDefault() && enable) { context()->server()->setDefaultSink(this); } } +pa_channel_position_t Sink::channelNameToPosition(const QString &name) +{ + if (name == QLatin1String("Front Left")) { + return PA_CHANNEL_POSITION_FRONT_LEFT; + } else if (name == QLatin1String("Front Center")) { + return PA_CHANNEL_POSITION_FRONT_CENTER; + } else if (name == QLatin1String("Front Right")) { + return PA_CHANNEL_POSITION_FRONT_RIGHT; + } else if (name == QLatin1String("Side Left")) { + return PA_CHANNEL_POSITION_SIDE_LEFT; + } else if (name == QLatin1String("Side Right")) { + return PA_CHANNEL_POSITION_SIDE_RIGHT; + } else if (name == QLatin1String("Rear Left")) { + return PA_CHANNEL_POSITION_REAR_LEFT; + } else if (name == QLatin1String("Rear Right")) { + return PA_CHANNEL_POSITION_REAR_RIGHT; + } else if (name == QLatin1String("Subwoofer")) { + return PA_CHANNEL_POSITION_SUBWOOFER; + } + + return PA_CHANNEL_POSITION_MONO; +} + +QString Sink::positionToChannelName(pa_channel_position_t position) +{ + switch (position) { + case PA_CHANNEL_POSITION_FRONT_LEFT: + return QStringLiteral("Front Left"); + case PA_CHANNEL_POSITION_FRONT_RIGHT: + return QStringLiteral("Front Right"); + case PA_CHANNEL_POSITION_FRONT_CENTER: + return QStringLiteral("Front Center"); + case PA_CHANNEL_POSITION_SIDE_LEFT: + return QStringLiteral("Side Left"); + case PA_CHANNEL_POSITION_SIDE_RIGHT: + return QStringLiteral("Side Right"); + case PA_CHANNEL_POSITION_REAR_LEFT: + return QStringLiteral("Rear Left"); + case PA_CHANNEL_POSITION_REAR_RIGHT: + return QStringLiteral("Rear Right"); + case PA_CHANNEL_POSITION_SUBWOOFER: + return QStringLiteral("Subwoofer"); + default: + return QStringLiteral("Mono"); + } +} + +void Sink::testChannel(const QString &name) +{ + auto context = CanberraContext::instance()->canberra(); + if (!context) + return; + + char dev[64]; + snprintf(dev, sizeof(dev), "%lu", (unsigned long) m_index); + ca_context_change_device(context, dev); + + QString sound_name = QStringLiteral("audio-channel-") + positionAsString(channelNameToPosition(name)); + ca_proplist *proplist; + ca_proplist_create(&proplist); + + ca_proplist_sets(proplist, CA_PROP_MEDIA_ROLE, "test"); + ca_proplist_sets(proplist, CA_PROP_MEDIA_NAME, name.toLatin1().constData()); + ca_proplist_sets(proplist, CA_PROP_CANBERRA_FORCE_CHANNEL, positionAsString(channelNameToPosition(name)).toLatin1().data()); + ca_proplist_sets(proplist, CA_PROP_CANBERRA_ENABLE, "1"); + + ca_proplist_sets(proplist, CA_PROP_EVENT_ID, sound_name.toLatin1().data()); + if (ca_context_play_full(context, 0, proplist, nullptr, NULL) < 0) { + // Try a different sound name. + ca_proplist_sets(proplist, CA_PROP_EVENT_ID, "audio-test-signal"); + if (ca_context_play_full(context, 0, proplist, nullptr, NULL) < 0) { + // Finaly try this... if this doesn't work, then stuff it. + ca_proplist_sets(proplist, CA_PROP_EVENT_ID, "bell-window-system"); + ca_context_play_full(context, 0, proplist, nullptr, NULL); + } + } + + ca_context_change_device(context, nullptr); + ca_proplist_destroy(proplist); +} + +QString Sink::positionAsString(pa_channel_position_t pos) +{ + switch (pos) { + case PA_CHANNEL_POSITION_FRONT_LEFT: + return QStringLiteral("front-left"); + case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: + return QStringLiteral("front-left-of-center"); + case PA_CHANNEL_POSITION_FRONT_CENTER: + return QStringLiteral("front-center"); + case PA_CHANNEL_POSITION_MONO: + return QStringLiteral("mono"); + case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: + return QStringLiteral("front-right-of-center"); + case PA_CHANNEL_POSITION_FRONT_RIGHT: + return QStringLiteral("front-right"); + case PA_CHANNEL_POSITION_SIDE_LEFT: + return QStringLiteral("side-left"); + case PA_CHANNEL_POSITION_SIDE_RIGHT: + return QStringLiteral("side-right"); + case PA_CHANNEL_POSITION_REAR_LEFT: + return QStringLiteral("rear-left"); + case PA_CHANNEL_POSITION_REAR_CENTER: + return QStringLiteral("rear-center"); + case PA_CHANNEL_POSITION_REAR_RIGHT: + return QStringLiteral("rear-right"); + case PA_CHANNEL_POSITION_SUBWOOFER: + return QStringLiteral("subwoofer"); + default: + break; + } + return QStringLiteral("invalid"); +} } // QPulseAudio diff --git a/src/sink.h b/src/sink.h index f3f2a2c..99a6c74 100644 --- a/src/sink.h +++ b/src/sink.h @@ -1,47 +1,59 @@ /* Copyright 2014-2015 Harald Sitter This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef SINK_H #define SINK_H #include "device.h" +#include +#include namespace QPulseAudio { class Sink : public Device { Q_OBJECT public: explicit Sink(QObject *parent); + virtual ~Sink(); void update(const pa_sink_info *info); void setVolume(qint64 volume) override; void setMuted(bool muted) override; void setActivePortIndex(quint32 port_index) override; void setChannelVolume(int channel, qint64 volume) override; bool isDefault() const override; void setDefault(bool enable) override; + +public slots: + void testChannel(const QString &name); + +private: + pa_channel_position_t channelNameToPosition(const QString &name); + QString positionToChannelName(pa_channel_position_t position); + QString positionAsString(pa_channel_position_t pos); + }; } // QPulseAudio #endif // SINK_H