diff options
author | Loïc Collignon <loic.collignon@iot.bzh> | 2018-06-27 17:30:37 +0200 |
---|---|---|
committer | Loïc Collignon <loic.collignon@iot.bzh> | 2018-06-27 17:30:37 +0200 |
commit | 63dc51c35d26c837295ac0ef33c1b8e41353ea35 (patch) | |
tree | 24918fe062c14b29f1c69b2e024cb8d66d045c50 /app | |
parent | d9bb450ee8898cb810027897a32afd3adcb05d9f (diff) |
Merge 'eel' into 'master'
Replace content from 'master' by content from 'eel' as it's the new
version based on 4a on which new development will be done.
Change-Id: I2966af7dcee59701ff3a344487c008d7e65e68ed
Signed-off-by: Loïc Collignon <loic.collignon@iot.bzh>
Diffstat (limited to 'app')
-rw-r--r-- | app/CMakeLists.txt | 57 | ||||
-rw-r--r-- | app/Mixer.qml | 151 | ||||
-rw-r--r-- | app/app.pri | 12 | ||||
-rw-r--r-- | app/app.pro | 18 | ||||
-rw-r--r-- | app/main.cpp | 27 | ||||
-rw-r--r-- | app/mixer.cpp | 91 | ||||
-rw-r--r-- | app/mixer.h | 54 | ||||
-rw-r--r-- | app/paclient.cpp | 329 | ||||
-rw-r--r-- | app/paclient.h | 97 | ||||
-rw-r--r-- | app/pacontrolmodel.cpp | 208 | ||||
-rw-r--r-- | app/pacontrolmodel.h | 92 |
11 files changed, 286 insertions, 850 deletions
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt new file mode 100644 index 0000000..8f8bd1e --- /dev/null +++ b/app/CMakeLists.txt @@ -0,0 +1,57 @@ +########################################################################### +# Copyright 2018 IoT.bzh +# +# author: Loïc Collignon <loic.collignon@iot.bzh> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_CXX_STANDARD 14) + +find_package(Qt5 COMPONENTS Core Gui QuickControls2 WebSockets QuickWidgets REQUIRED) +qt5_add_resources(RESOURCES Mixer.qrc) + +PROJECT_TARGET_ADD(mixer) + +add_executable(mixer + main.cpp + mixer.cpp + ${RESOURCES} +) + +set_target_properties(mixer PROPERTIES + LABELS "EXECUTABLE" + PREFIX "" + COMPILE_FLAGS "${EXTRAS_CFLAGS} -DFOR_AFB_BINDING" + LINK_FLAGS "${BINDINGS_LINK_FLAG}" + LINK_LIBRARIES "${EXTRAS_LIBRARIES}" + OUTPUT_NAME "${TARGET_NAME}" +) + +target_link_libraries(mixer + Qt5::QuickControls2 + Qt5::WebSockets + homescreen + qtWindowmanagerWrapper + json-c + afb-helpers +) + +#add_custom_command(TARGET ${TARGET_NAME} +#PRE_BUILD +#COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/../package/htdocs +#COMMAND cp -rv ${CMAKE_CURRENT_SOURCE_DIR}/../htdocs ${CMAKE_CURRENT_BINARY_DIR}/../package/ +#COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/../package/etc +#COMMAND cp -rv ${CMAKE_CURRENT_SOURCE_DIR}/../etc ${CMAKE_CURRENT_BINARY_DIR}/../package/) diff --git a/app/Mixer.qml b/app/Mixer.qml index 96875e0..8b1ba06 100644 --- a/app/Mixer.qml +++ b/app/Mixer.qml @@ -14,85 +14,94 @@ * limitations under the License. */ +// BUG: ValueChanged event is raised by sliders when you are moving the caret, should be raised only when you release it. +// TODO: Call mixer.setVolume(sliderName, Value) on value change +// TODO: Call mixer.getVolume(sliderName) on load + import QtQuick 2.6 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.0 import AGL.Demo.Controls 1.0 -import PaControlModel 1.0 +import Mixer 1.0 ApplicationWindow { - id: root + id: root + + Mixer { + id: mixer + Component.objectName: { + mixer.open(bindingAddress) + } + onRolesChanged: { + // Remove existing sliders + for(var i = sliders.children.length; i > 0 ; --i) { + console.log("destroying: " + i) + sliders.children[i-1].destroy() + } + + // Add slider for each role + for(var j = 0; j < mixer.roles.length; ++j) { + addSlider(mixer.roles[j]) + } + } - Label { - id: title - font.pixelSize: 48 - text: "Mixer" - anchors.horizontalCenter: parent.horizontalCenter - } + function addSlider(name) { + Qt.createQmlObject(" +import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.0 +RowLayout { + property int value + id: slider_" + name + " + Layout.minimumHeight: 75 + Label { + font.pixelSize: 24 + text: \"" + name+ "\" + Layout.minimumWidth: 150 + } + Label { + id: slider_" + name + "_textvalue + font.pixelSize: 24 + text: \"0 %\" + } + Slider { + id: slider_" + name + "_slider + Layout.fillWidth: true + from: 0 + to: 100 + stepSize: 1 + snapMode: Slider.SnapOnRelease + onValueChanged: { + slider_" + name + "_textvalue.text = value + \" %\" + mixer.setVolume(\"" + name + "\", value) + } + Component.objectName: { + mixer.getVolume(\"" + name + "\") + } + } + }", sliders, "volumeslider") + } - Component { - id: ctldesc - Label { - font.pixelSize: 32 - width: listView.width - wrapMode: Text.WordWrap - property var typeString: {modelType ? "Output" : "Input"} - text: "[" + typeString + " #" + modelCIndex + "]: " + modelDesc - } - } + function deleteChilds(item) { + for(var i = item.children.length; i > 0 ; i--) { + deleteChilds(item.children[i-1]) + } + item.destroy() + } + } - Component { - id: empty - Item { - } - } + Label { + id: title + font.pixelSize: 48 + text: "Mixer" + anchors.horizontalCenter: parent.horizontalCenter + } - ListView { - id: listView - anchors.left: parent.left - anchors.top: title.bottom - anchors.margins: 80 - anchors.fill: parent - model: PaControlModel { objectName: "pacm" } - delegate: ColumnLayout { - width: parent.width - spacing: 40 - Connections { - target: listView.model - onDataChanged: slider.value = volume - } - Loader { - property int modelType: type - property int modelCIndex: cindex - property string modelDesc: name - sourceComponent: (channel == 0) ? ctldesc : empty - } - RowLayout { - Layout.minimumHeight: 75 - Label { - font.pixelSize: 24 - text: cdesc - Layout.minimumWidth: 150 - } - Label { - font.pixelSize: 24 - text: "0 %" - } - Slider { - id: slider - Layout.fillWidth: true - from: 0 - to: 65536 - stepSize: 256 - snapMode: Slider.SnapOnRelease - onValueChanged: volume = value - Component.onCompleted: value = volume - } - Label { - font.pixelSize: 24 - text: "100 %" - } - } - } - } + ColumnLayout { + id: sliders + anchors.margins: 80 + anchors.top: title.bottom + anchors.left: parent.left + anchors.right: parent.right + } } + diff --git a/app/app.pri b/app/app.pri deleted file mode 100644 index 014646f..0000000 --- a/app/app.pri +++ /dev/null @@ -1,12 +0,0 @@ -TEMPLATE = app - -load(configure) -qtCompileTest(libhomescreen) - -config_libhomescreen { - CONFIG += link_pkgconfig - PKGCONFIG += homescreen - DEFINES += HAVE_LIBHOMESCREEN -} - -DESTDIR = $${OUT_PWD}/../package/root/bin diff --git a/app/app.pro b/app/app.pro deleted file mode 100644 index a33fc0d..0000000 --- a/app/app.pro +++ /dev/null @@ -1,18 +0,0 @@ -TARGET = mixer -QT = quickcontrols2 - -HEADERS += \ - pacontrolmodel.h \ - paclient.h - -SOURCES = main.cpp \ - pacontrolmodel.cpp \ - paclient.cpp - -CONFIG += link_pkgconfig -PKGCONFIG += libhomescreen qlibwindowmanager libpulse - -RESOURCES += \ - Mixer.qrc - -include(app.pri) diff --git a/app/main.cpp b/app/main.cpp index bfce498..9c6339f 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2016 The Qt Company Ltd. * Copyright (C) 2016,2017 Konsulko Group + * Copyright (C) 2018 IoT.bzh * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,9 +16,6 @@ * limitations under the License. */ -#include "paclient.h" -#include "pacontrolmodel.h" - #include <QtCore/QDebug> #include <QtCore/QCommandLineParser> #include <QtCore/QUrlQuery> @@ -34,6 +32,7 @@ #include <QQuickWindow> #include <libhomescreen.hpp> #include <qlibwindowmanager.h> +#include "mixer.h" int main(int argc, char *argv[]) { @@ -51,14 +50,7 @@ int main(int argc, char *argv[]) parser.process(app); QStringList positionalArguments = parser.positionalArguments(); - // Fire up PA client QThread - QThread* pat = new QThread; - PaClient* client = new PaClient(); - client->moveToThread(pat); - pat->start(); - - // Register the PA Control Model - qmlRegisterType<PaControlModel>("PaControlModel", 1, 0, "PaControlModel"); + qmlRegisterType<Mixer>("Mixer", 1, 0, "Mixer"); QQmlApplicationEngine engine; if (positionalArguments.length() == 2) { @@ -88,7 +80,7 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } // Create an event callbnewack against an event type. Here a lambda is called when SyncDraw event occurs - qwm->set_event_handler(QLibWindowmanager::Event_SyncDraw, [qwm, myname](json_object *object) { + qwm->set_event_handler(QLibWindowmanager::Event_SyncDraw, [qwm, myname](json_object*) { fprintf(stderr, "Surface got syncDraw!\n"); qwm->endDraw(myname); }); @@ -116,17 +108,6 @@ int main(int argc, char *argv[]) QQuickWindow *window = qobject_cast<QQuickWindow *>(mobjs.first()); QObject::connect(window, SIGNAL(frameSwapped()), qwm, SLOT(slotActivateSurface())); - - PaControlModel *pacm = mobjs.first()->findChild<PaControlModel *>("pacm"); - QObject::connect(client, SIGNAL(controlAdded(int, QString, QString, int, int, const char *, int)), - pacm, SLOT(addOneControl(int, QString, QString, int, int, const char *, int))); - QObject::connect(client, SIGNAL(volumeExternallyChanged(uint32_t, uint32_t, uint32_t, uint32_t)), - pacm, SLOT(changeExternalVolume(uint32_t, uint32_t, uint32_t, uint32_t))); - QObject::connect(pacm, SIGNAL(volumeChanged(uint32_t, uint32_t, uint32_t, uint32_t)), - client, SLOT(setVolume(uint32_t, uint32_t, uint32_t, uint32_t))); - - // Initalize PA client - client->init(); } return app.exec(); } diff --git a/app/mixer.cpp b/app/mixer.cpp new file mode 100644 index 0000000..6614569 --- /dev/null +++ b/app/mixer.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2016 The Qt Company Ltd. + * Copyright (C) 2016,2017 Konsulko Group + * Copyright (C) 2018 IoT.bzh + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <QJsonArray> +#include <QJsonObject> +#include <QtDebug> +#include "mixer.h" + +Mixer::Mixer(QObject* parent) + : QObject(parent) +{ + connect(&m_client, SIGNAL(connected()), this, SLOT(onClientConnected())); +} + +QStringList Mixer::roles() const +{ + return m_roles; +} + +void Mixer::open(const QUrl &url) +{ + m_client.open(url); +} + +void Mixer::onClientConnected() +{ + // Call HAL to populate list + m_client.call("ahl-4a", "get_roles", QJsonValue(), [this](bool r, const QJsonValue& val) { + if (r) + { + m_roles.clear(); + //BUG: should be able to add this, but not handled right now: m_roles.append("playback"); + QJsonArray cards = val.toObject()["response"].toArray(); + foreach (const QJsonValue& card, cards) + { + m_roles.append(card.toString()); + qDebug() << "Mixer::onClientConnected - added this HAL: " << card.toString(); + } + emit rolesChanged(); + } + }); +} + +void Mixer::setVolume(const QString& name, int value) +{ + QJsonObject arg; + arg.insert("action", "volume"); + arg.insert("value", QJsonValue(value)); + m_client.call("ahl-4a", name, arg, [this, name](bool r, const QJsonValue& v) { + if (r && v.isObject()) + { + // TODO: Success, update the slider + } + else + { + // TODO: Failed, reset the slider to previous value + } + }); +} + +void Mixer::getVolume(const QString& name) +{ + QJsonObject arg; + arg.insert("action", "volume"); + arg.insert("value", QJsonValue("+0")); // FIXME: Hack to get volume: ask for a relative change with a delta of zero + m_client.call("ahl-4a", name, arg, [this, name](bool r, const QJsonValue& v) { + if (r && v.isObject()) + { + // TODO: Success, update the slider + } + else + { + // TODO: Failed, what to do ? + } + }); +} diff --git a/app/mixer.h b/app/mixer.h new file mode 100644 index 0000000..a46c8a1 --- /dev/null +++ b/app/mixer.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 The Qt Company Ltd. + * Copyright (C) 2016,2017 Konsulko Group + * Copyright (C) 2018 IoT.bzh + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <QObject> +#include <QString> +#include <QSharedPointer> +#include <QStringList> +#include "qafbwebsocketclient.h" +//#include "volumeslider.h" + +class Mixer + : public QObject +{ + Q_OBJECT + Q_PROPERTY(QStringList roles READ roles NOTIFY rolesChanged) + +private: + +public: + explicit Mixer(QObject* parent = nullptr); + Mixer(const Mixer&) = delete; + + Q_INVOKABLE void open(const QUrl& url); + Q_INVOKABLE QStringList roles() const; + Q_INVOKABLE void setVolume(const QString& name, int value); + Q_INVOKABLE void getVolume(const QString& name); + +signals: + void rolesChanged(); + void volumeChanged(const QString& name, int value); + +private slots: + void onClientConnected(); + +private: + QStringList m_roles; + QAfbWebsocketClient m_client; +}; diff --git a/app/paclient.cpp b/app/paclient.cpp deleted file mode 100644 index bd53cde..0000000 --- a/app/paclient.cpp +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright (C) 2016,2017 Konsulko Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "paclient.h" - -#include <QtCore/QDebug> - -PaClient::PaClient() - : m_init(false), m_ml(nullptr), m_mlapi(nullptr), m_ctx(nullptr) -{ -} - -PaClient::~PaClient() -{ - if (m_init) - close(); -} - -void PaClient::close() -{ - if (!m_init) return; - pa_threaded_mainloop_stop(m_ml); - pa_threaded_mainloop_free(m_ml); - m_init = false; -} - -static void set_sink_volume_cb(pa_context *c, int success, void *data) -{ - Q_UNUSED(data); - - if (!success) - qWarning() << "PaClient: set sink volume: " << - pa_strerror(pa_context_errno(c)); -} - -static void set_source_volume_cb(pa_context *c, int success, void *data) -{ - Q_UNUSED(data); - - if (!success) - qWarning() << "PaClient: set source volume: " << - pa_strerror(pa_context_errno(c)); -} - -void PaClient::setVolume(uint32_t type, uint32_t index, uint32_t channel, uint32_t volume) -{ - pa_operation *o; - pa_context *c = context(); - pa_cvolume *cvolume = NULL; - - if (type == C_SINK) { - cvolume = m_sink_states.value(index); - cvolume->values[channel] = volume; - if (!(o = pa_context_set_sink_volume_by_index(c, index, cvolume, set_sink_volume_cb, NULL))) { - qWarning() << "PaClient: set sink #" << index << - " channel #" << channel << - " volume: " << pa_strerror(pa_context_errno(c)); - return; - } - pa_operation_unref(o); - } else if (type == C_SOURCE) { - cvolume = m_source_states.value(index); - cvolume->values[channel] = volume; - if (!(o = pa_context_set_source_volume_by_index(c, index, cvolume, set_source_volume_cb, NULL))) { - qWarning() << "PaClient: set source #" << index << - " channel #" << channel << - " volume: " << pa_strerror(pa_context_errno(c)); - return; - } - pa_operation_unref(o); - } -} - -void get_source_list_cb(pa_context *c, - const pa_source_info *i, - int eol, - void *data) -{ - int chan; - - PaClient *self = reinterpret_cast<PaClient*>(data); - - if (eol < 0) { - qWarning() << "PaClient: get source list: " << - pa_strerror(pa_context_errno(c)); - - self->close(); - return; - } - - if (!eol) { - self->addOneControlState(C_SOURCE, i->index, &i->volume); - for (chan = 0; chan < i->channel_map.channels; chan++) { - // NOTE: hide input control - if (QString(i->name).endsWith("monitor")) - continue; - - emit self->controlAdded(i->index, QString(i->name), QString(i->description), - C_SOURCE, chan, channel_position_string[i->channel_map.map[chan]], - i->volume.values[chan]); - } - } -} - -void get_sink_list_cb(pa_context *c, - const pa_sink_info *i, - int eol, - void *data) -{ - PaClient *self = reinterpret_cast<PaClient*>(data); - int chan; - - if(eol < 0) { - qWarning() << "PaClient: get sink list: " << - pa_strerror(pa_context_errno(c)); - self->close(); - return; - } - - if(!eol) { - self->addOneControlState(C_SINK, i->index, &i->volume); - for (chan = 0; chan < i->channel_map.channels; chan++) { - emit self->controlAdded(i->index, QString(i->name), QString(i->description), - C_SINK, chan, channel_position_string[i->channel_map.map[chan]], - i->volume.values[chan]); - } - } -} - -void get_sink_info_change_cb(pa_context *c, - const pa_sink_info *i, - int eol, - void *data) -{ - Q_UNUSED(c); - Q_ASSERT(i); - Q_ASSERT(data); - - if (eol) return; - - for (int chan = 0; chan < i->channel_map.channels; chan++) { - PaClient *self = reinterpret_cast<PaClient*>(data); - QHash<int, pa_cvolume *> states = self->sink_states(); - pa_cvolume *cvolume = states.value(i->index); - // Check each channel for volume change - if (cvolume->values[chan] != i->volume.values[chan]) { - // On change, update cache and signal - cvolume->values[chan] = i->volume.values[chan]; - emit self->volumeExternallyChanged(C_SINK, i->index, chan, i->volume.values[chan]); - } - } -} - -void get_source_info_change_cb(pa_context *c, - const pa_source_info *i, - int eol, - void *data) -{ - Q_UNUSED(c); - Q_ASSERT(i); - Q_ASSERT(data); - - if (eol) return; - - for (int chan = 0; chan < i->channel_map.channels; chan++) { - PaClient *self = reinterpret_cast<PaClient*>(data); - QHash<int, pa_cvolume *> states = self->source_states(); - pa_cvolume *cvolume = states.value(i->index); - // Check each channel for volume change - if (cvolume->values[chan] != i->volume.values[chan]) { - // On change, update cache and signal - cvolume->values[chan] = i->volume.values[chan]; - emit self->volumeExternallyChanged(C_SOURCE, i->index, chan, i->volume.values[chan]); - } - } -} - - -void subscribe_cb(pa_context *c, - pa_subscription_event_type_t type, - uint32_t index, - void *data) -{ - pa_operation *o; - - if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) { - qWarning("PaClient: unhandled subscribe event operation"); - return; - } - - switch (type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { - case PA_SUBSCRIPTION_EVENT_SINK: - if (!(o = pa_context_get_sink_info_by_index(c, index, get_sink_info_change_cb, data))) { - qWarning() << "PaClient: get sink info by index: " << - pa_strerror(pa_context_errno(c)); - return; - } - break; - case PA_SUBSCRIPTION_EVENT_SOURCE: - if (!(o = pa_context_get_source_info_by_index(c, index, get_source_info_change_cb, data))) { - qWarning() << "PaClient: get source info by index: " << - pa_strerror(pa_context_errno(c)); - return; - } - break; - default: - qWarning("PaClient: unhandled subscribe event facility"); - } -} - -void context_state_cb(pa_context *c, void *data) -{ - pa_operation *o; - PaClient *self = reinterpret_cast<PaClient*>(data); - - switch (pa_context_get_state(c)) { - case PA_CONTEXT_CONNECTING: - case PA_CONTEXT_AUTHORIZING: - case PA_CONTEXT_SETTING_NAME: - break; - case PA_CONTEXT_READY: - // Fetch the controls of interest - if (!(o = pa_context_get_source_info_list(c, get_source_list_cb, data))) { - qWarning() << "PaClient: get source info list: " << - pa_strerror(pa_context_errno(c)); - return; - } - pa_operation_unref(o); - if (!(o = pa_context_get_sink_info_list(c, &get_sink_list_cb, data))) { - qWarning() << "PaClient: get sink info list: " << - pa_strerror(pa_context_errno(c)); - return; - } - pa_operation_unref(o); - pa_context_set_subscribe_callback(c, subscribe_cb, data); - if (!(o = pa_context_subscribe(c, (pa_subscription_mask_t)(PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE), NULL, NULL))) { - qWarning() << "PaClient: subscribe: " << - pa_strerror(pa_context_errno(c)); - return; - } - break; - case PA_CONTEXT_TERMINATED: - self->close(); - break; - - case PA_CONTEXT_FAILED: - default: - qCritical() << "PaClient: connection failed: " << - pa_strerror(pa_context_errno(c)); - self->close(); - break; - } -} - -void PaClient::init() -{ - m_ml = pa_threaded_mainloop_new(); - if (!m_ml) { - qCritical("PaClient: failed to create mainloop"); - return; - } - - pa_threaded_mainloop_set_name(m_ml, "PaClient mainloop"); - - m_mlapi = pa_threaded_mainloop_get_api(m_ml); - - lock(); - - m_ctx = pa_context_new(m_mlapi, "Mixer"); - if (!m_ctx) { - qCritical("PaClient: failed to create context"); - pa_threaded_mainloop_free(m_ml); - return; - } - pa_context_set_state_callback(m_ctx, context_state_cb, this); - - if (pa_context_connect(m_ctx, 0, (pa_context_flags_t)0, 0) < 0) { - qCritical("PaClient: failed to connect"); - pa_context_unref(m_ctx); - pa_threaded_mainloop_free(m_ml); - return; - } - - if (pa_threaded_mainloop_start(m_ml) != 0) { - qCritical("PaClient: failed to start mainloop"); - pa_context_unref(m_ctx); - pa_threaded_mainloop_free(m_ml); - return; - } - - unlock(); - - m_init = true; -} - -void PaClient::addOneControlState(int type, int index, const pa_cvolume *cvolume) -{ - pa_cvolume *cvolume_new = new pa_cvolume; - cvolume_new->channels = cvolume->channels; - for (int i = 0; i < cvolume->channels; i++) - cvolume_new->values[i] = cvolume->values[i]; - if (type == C_SINK) - m_sink_states.insert(index, cvolume_new); - else if (type == C_SOURCE) - m_source_states.insert(index, cvolume_new); -} - -QHash<int, pa_cvolume *> PaClient::sink_states(void) -{ - return m_sink_states; -} - -QHash<int, pa_cvolume *> PaClient::source_states(void) -{ - return m_source_states; -} diff --git a/app/paclient.h b/app/paclient.h deleted file mode 100644 index 73137f2..0000000 --- a/app/paclient.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2016,2017 Konsulko Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <pulse/pulseaudio.h> - -#include <QtCore/QHash> -#include <QtCore/QObject> - -const char * const channel_position_string[] = -{ - "Mono", - "Front Left", - "Front Right", - "Center", - "Rear Center", - "Rear Left", - "Rear Right", - "LFE", - "Left Center", - "Right Center", - "Side Left", - "Side Right", -}; - -enum control_type -{ - C_SOURCE, - C_SINK -}; - -typedef struct -{ - uint32_t type; - uint32_t index; - pa_cvolume cvolume; -} CState; - -class PaClient : public QObject -{ - Q_OBJECT - public: - PaClient(); - ~PaClient(); - - void init(); - void close(); - - inline pa_context *context() const - { - return m_ctx; - } - - inline void lock() - { - pa_threaded_mainloop_lock(m_ml); - } - - inline void unlock() - { - pa_threaded_mainloop_unlock(m_ml); - } - - void addOneControlState(int type, int index, const pa_cvolume *cvolume); - - QHash<int, pa_cvolume *> sink_states(); - QHash<int, pa_cvolume *> source_states(); - - public slots: - void setVolume(uint32_t type, uint32_t index, uint32_t channel, uint32_t volume); - - signals: - void controlAdded(int cindex, QString name, QString desc, int type, int channel, const char *cdesc, int volume); - void volumeExternallyChanged(uint32_t type, uint32_t cindex, uint32_t channel, uint32_t volume); - - private: - bool m_init; - pa_threaded_mainloop *m_ml; - pa_mainloop_api *m_mlapi; - pa_context *m_ctx; - QHash<int, pa_cvolume *> m_sink_states; - QHash<int, pa_cvolume *> m_source_states; - - public slots: -}; diff --git a/app/pacontrolmodel.cpp b/app/pacontrolmodel.cpp deleted file mode 100644 index 9489052..0000000 --- a/app/pacontrolmodel.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (C) 2016,2017 Konsulko Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "pacontrolmodel.h" - -PaControl::PaControl(const quint32 &cindex, const QString &name, const QString &desc, const quint32 &type, const quint32 &channel, const QString &cdesc, const quint32 &volume) - : m_cindex(cindex), m_name(name), m_desc(desc), m_type(type), m_channel(channel), m_cdesc(cdesc), m_volume(volume) -{ -} - -quint32 PaControl::cindex() const -{ - return m_cindex; -} - -QString PaControl::name() const -{ - QStringList list = m_name.split("."); - - return list.at(1); -} - -QString PaControl::desc() const -{ - return m_desc; -} - -quint32 PaControl::type() const -{ - return m_type; -} - -quint32 PaControl::channel() const -{ - return m_channel; -} - -QString PaControl::cdesc() const -{ - return m_cdesc; -} - - -quint32 PaControl::volume() const -{ - return m_volume; -} - -// FIXME: Not all of these should be editable roles -void PaControl::setCIndex(const QVariant &cindex) -{ - m_cindex = cindex.toUInt(); -} - -void PaControl::setName(const QVariant &name) -{ - m_name = name.toString(); -} - -void PaControl::setDesc(const QVariant &desc) -{ - m_desc = desc.toString(); -} - -void PaControl::setType(const QVariant &type) -{ - m_type = type.toUInt(); -} - -void PaControl::setChannel(const QVariant &channel) -{ - m_channel = channel.toUInt(); -} - -void PaControl::setCDesc(const QVariant &cdesc) -{ - m_cdesc = cdesc.toString(); -} - -void PaControl::setVolume(PaControlModel *pacm, const QVariant &volume) -{ - if (volume != m_volume) { - m_volume = volume.toUInt(); - if (pacm) - emit pacm->volumeChanged(type(), cindex(), channel(), m_volume); - } -} - -PaControlModel::PaControlModel(QObject *parent) - : QAbstractListModel(parent) -{ -} - -void PaControlModel::addControl(const PaControl &control) -{ - beginInsertRows(QModelIndex(), rowCount(), rowCount()); - m_controls << control; - endInsertRows(); -} - -void PaControlModel::addOneControl(int cindex, QString name, QString desc, int type, int channel, const char *cdesc, int volume) -{ - addControl(PaControl(cindex, name, desc, type, channel, cdesc, volume)); -} - -void PaControlModel::changeExternalVolume(uint32_t type, uint32_t cindex, uint32_t channel, uint32_t volume) -{ - QList<PaControl>::iterator i; - int row; - - for (i = m_controls.begin(), row = 0; i < m_controls.end(); ++i, ++row) { - if ((i->type() == type) && - (i->cindex() == cindex) && - (i->channel() == channel)) { - break; - } - } - - i->setVolume(NULL, QVariant(volume)); - QModelIndex qmindex = index(row); - QVector<int> roles; - roles.push_back(VolumeRole); - emit dataChanged(qmindex, qmindex, roles); -} - -int PaControlModel::rowCount(const QModelIndex & parent) const { - Q_UNUSED(parent); - return m_controls.count(); -} - -bool PaControlModel::setData(const QModelIndex &index, const QVariant &value, int role) { - if (index.row() < 0 || index.row() >= m_controls.count()) - return false; - PaControl &control = m_controls[index.row()]; - if (role == CIndexRole) - control.setCIndex(value); - else if (role == NameRole) - control.setName(value); - else if (role == DescRole) - control.setDesc(value); - else if (role == TypeRole) - control.setType(value); - else if (role == ChannelRole) - control.setChannel(value); - else if (role == CDescRole) - control.setCDesc(value); - else if (role == VolumeRole) - control.setVolume(this, value); - QVector<int> roles; - roles.push_back(role); - emit dataChanged(index, index, roles); - return true; -} - -QVariant PaControlModel::data(const QModelIndex & index, int role) const { - if (index.row() < 0 || index.row() >= m_controls.count()) - return QVariant(); - - const PaControl &control = m_controls[index.row()]; - if (role == CIndexRole) - return control.cindex(); - else if (role == NameRole) - return control.name(); - else if (role == DescRole) - return control.desc(); - else if (role == TypeRole) - return control.type(); - else if (role == ChannelRole) - return control.channel(); - else if (role == CDescRole) - return control.cdesc(); - else if (role == VolumeRole) - return control.volume(); - return QVariant(); -} - -Qt::ItemFlags PaControlModel::flags(const QModelIndex &index) const -{ - if (!index.isValid()) - return Qt::ItemIsEnabled; - - return QAbstractListModel::flags(index) | Qt::ItemIsEditable; -} - -QHash<int, QByteArray> PaControlModel::roleNames() const { - QHash<int, QByteArray> roles; - roles[CIndexRole] = "cindex"; - roles[NameRole] = "name"; - roles[DescRole] = "desc"; - roles[TypeRole] = "type"; - roles[ChannelRole] = "channel"; - roles[CDescRole] = "cdesc"; - roles[VolumeRole] = "volume"; - return roles; -} diff --git a/app/pacontrolmodel.h b/app/pacontrolmodel.h deleted file mode 100644 index 81eb70b..0000000 --- a/app/pacontrolmodel.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2016,2017 Konsulko Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <pulse/pulseaudio.h> - -#include <QtCore/QAbstractListModel> -#include <QtCore/QList> - -class PaControlModel; - -class PaControl -{ - public: - PaControl(const quint32 &index, const QString &name, const QString &desc, const quint32 &type, const quint32 &channel, const QString &cdesc, const quint32 &volume); - - quint32 cindex() const; - QString name() const; - QString desc() const; - quint32 type() const; - quint32 channel() const; - QString cdesc() const; - quint32 volume() const; - void setCIndex(const QVariant&); - void setName(const QVariant&); - void setDesc(const QVariant&); - void setType(const QVariant&); - void setChannel(const QVariant&); - void setCDesc(const QVariant&); - void setVolume(PaControlModel *, const QVariant&); - - private: - quint32 m_cindex; - QString m_name; - QString m_desc; - quint32 m_type; - quint32 m_channel; - QString m_cdesc; - quint32 m_volume; -}; - -class PaControlModel : public QAbstractListModel -{ - Q_OBJECT - public: - enum PaControlRoles { - CIndexRole = Qt::UserRole + 1, - NameRole, - DescRole, - TypeRole, - ChannelRole, - CDescRole, - VolumeRole - }; - - PaControlModel(QObject *parent = 0); - - void addControl(const PaControl &control); - - int rowCount(const QModelIndex &parent = QModelIndex()) const; - - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - - bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); - - Qt::ItemFlags flags(const QModelIndex &index) const; - - public slots: - void addOneControl(int cindex, QString name, QString desc, int type, int channel, const char *cdesc, int volume); - void changeExternalVolume(uint32_t type, uint32_t cindex, uint32_t chan, uint32_t volume); - - signals: - void volumeChanged(uint32_t type, uint32_t index, uint32_t channel, uint32_t volume); - - protected: - QHash<int, QByteArray> roleNames() const; - private: - QList<PaControl> m_controls; - pa_context *pa_ctx; -}; |