Volume
This commit is contained in:
parent
94ce8617d6
commit
4bf830763f
4 changed files with 285 additions and 3 deletions
|
|
@ -5,10 +5,13 @@
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
#include <QFontMetrics>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
#include <QHBoxLayout>
|
||||||
#include <QSplitter>
|
#include <QSplitter>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
#include <QScrollArea>
|
||||||
|
|
||||||
#include <QtNodes/GraphicsViewStyle>
|
#include <QtNodes/GraphicsViewStyle>
|
||||||
#include <QtNodes/NodeStyle>
|
#include <QtNodes/NodeStyle>
|
||||||
|
|
@ -74,9 +77,28 @@ GraphEditorWidget::GraphEditorWidget(Potato::PipeWireController *controller, QWi
|
||||||
|
|
||||||
m_meter = new AudioLevelMeter(meterPanel);
|
m_meter = new AudioLevelMeter(meterPanel);
|
||||||
meterLayout->addWidget(m_meter, 1);
|
meterLayout->addWidget(m_meter, 1);
|
||||||
|
|
||||||
|
auto *nodesLabel = new QLabel(QString("NODE METERS"), meterPanel);
|
||||||
|
nodesLabel->setStyleSheet("color: #8c94a5; font-weight: 700; font-size: 10px; letter-spacing: 1px; border: none;");
|
||||||
|
nodesLabel->setAlignment(Qt::AlignLeft);
|
||||||
|
meterLayout->addWidget(nodesLabel);
|
||||||
|
|
||||||
|
m_meterScroll = new QScrollArea(meterPanel);
|
||||||
|
m_meterScroll->setFrameShape(QFrame::NoFrame);
|
||||||
|
m_meterScroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
m_meterScroll->setWidgetResizable(true);
|
||||||
|
|
||||||
|
m_meterList = new QWidget(m_meterScroll);
|
||||||
|
m_meterListLayout = new QVBoxLayout(m_meterList);
|
||||||
|
m_meterListLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
m_meterListLayout->setSpacing(10);
|
||||||
|
m_meterList->setLayout(m_meterListLayout);
|
||||||
|
m_meterScroll->setWidget(m_meterList);
|
||||||
|
meterLayout->addWidget(m_meterScroll, 2);
|
||||||
|
|
||||||
meterLayout->addStretch();
|
meterLayout->addStretch();
|
||||||
|
|
||||||
meterPanel->setFixedWidth(160);
|
meterPanel->setFixedWidth(320);
|
||||||
splitter->addWidget(meterPanel);
|
splitter->addWidget(meterPanel);
|
||||||
splitter->setStretchFactor(0, 1);
|
splitter->setStretchFactor(0, 1);
|
||||||
splitter->setStretchFactor(1, 0);
|
splitter->setStretchFactor(1, 0);
|
||||||
|
|
@ -175,6 +197,9 @@ GraphEditorWidget::GraphEditorWidget(Potato::PipeWireController *controller, QWi
|
||||||
|
|
||||||
static bool isAudioEndpoint(const Potato::NodeInfo &node)
|
static bool isAudioEndpoint(const Potato::NodeInfo &node)
|
||||||
{
|
{
|
||||||
|
if (node.name.startsWith("Potato-Meter") || node.name.startsWith("Potato-Node-Meter")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return node.mediaClass == Potato::MediaClass::AudioSink
|
return node.mediaClass == Potato::MediaClass::AudioSink
|
||||||
|| node.mediaClass == Potato::MediaClass::AudioSource
|
|| node.mediaClass == Potato::MediaClass::AudioSource
|
||||||
|| node.mediaClass == Potato::MediaClass::AudioDuplex
|
|| node.mediaClass == Potato::MediaClass::AudioDuplex
|
||||||
|
|
@ -187,6 +212,7 @@ void GraphEditorWidget::syncGraph()
|
||||||
for (const auto &node : nodes) {
|
for (const auto &node : nodes) {
|
||||||
if (isAudioEndpoint(node)) {
|
if (isAudioEndpoint(node)) {
|
||||||
m_model->addPipeWireNode(node);
|
m_model->addPipeWireNode(node);
|
||||||
|
refreshNodeMeter(node.id, node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -215,6 +241,7 @@ void GraphEditorWidget::onNodeAdded(const Potato::NodeInfo &node)
|
||||||
{
|
{
|
||||||
if (isAudioEndpoint(node)) {
|
if (isAudioEndpoint(node)) {
|
||||||
m_model->addPipeWireNode(node);
|
m_model->addPipeWireNode(node);
|
||||||
|
refreshNodeMeter(node.id, node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -227,11 +254,21 @@ void GraphEditorWidget::onNodeChanged(const Potato::NodeInfo &node)
|
||||||
if (!m_model->updatePipeWireNode(node)) {
|
if (!m_model->updatePipeWireNode(node)) {
|
||||||
m_model->addPipeWireNode(node);
|
m_model->addPipeWireNode(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshNodeMeter(node.id, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphEditorWidget::onNodeRemoved(uint32_t nodeId)
|
void GraphEditorWidget::onNodeRemoved(uint32_t nodeId)
|
||||||
{
|
{
|
||||||
m_model->removePipeWireNode(nodeId);
|
m_model->removePipeWireNode(nodeId);
|
||||||
|
|
||||||
|
m_controller->removeNodeMeter(nodeId);
|
||||||
|
|
||||||
|
if (m_nodeMeterRows.contains(nodeId)) {
|
||||||
|
QWidget *row = m_nodeMeterRows.take(nodeId);
|
||||||
|
m_nodeMeters.remove(nodeId);
|
||||||
|
row->deleteLater();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphEditorWidget::onLinkAdded(const Potato::LinkInfo &link)
|
void GraphEditorWidget::onLinkAdded(const Potato::LinkInfo &link)
|
||||||
|
|
@ -345,4 +382,58 @@ void GraphEditorWidget::updateMeter()
|
||||||
|
|
||||||
const float peak = m_controller->meterPeak();
|
const float peak = m_controller->meterPeak();
|
||||||
m_meter->setLevel(peak);
|
m_meter->setLevel(peak);
|
||||||
|
|
||||||
|
for (auto it = m_nodeMeters.begin(); it != m_nodeMeters.end(); ++it) {
|
||||||
|
const uint32_t nodeId = static_cast<uint32_t>(it.key());
|
||||||
|
const float nodePeak = m_controller->nodeMeterPeak(nodeId);
|
||||||
|
it.value()->setLevel(nodePeak);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphEditorWidget::refreshNodeMeter(uint32_t nodeId, const Potato::NodeInfo &node)
|
||||||
|
{
|
||||||
|
if (m_nodeMeterRows.contains(nodeId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!node.name.isEmpty()) {
|
||||||
|
bool captureSink = node.mediaClass == Potato::MediaClass::AudioSink
|
||||||
|
|| node.mediaClass == Potato::MediaClass::AudioDuplex;
|
||||||
|
if (!captureSink) {
|
||||||
|
for (const auto &port : node.outputPorts) {
|
||||||
|
if (port.name.contains("monitor", Qt::CaseInsensitive)) {
|
||||||
|
captureSink = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_controller->ensureNodeMeter(nodeId, node.name, captureSink);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *row = new QWidget(m_meterList);
|
||||||
|
auto *rowLayout = new QHBoxLayout(row);
|
||||||
|
rowLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
rowLayout->setSpacing(8);
|
||||||
|
|
||||||
|
const QString title = node.description.isEmpty() ? node.name : node.description;
|
||||||
|
auto *label = new QLabel(title, row);
|
||||||
|
label->setStyleSheet("color: #c7cfdd; font-size: 11px; border: none;");
|
||||||
|
label->setToolTip(title);
|
||||||
|
const int labelWidth = 250;
|
||||||
|
label->setFixedWidth(labelWidth);
|
||||||
|
QFontMetrics metrics(label->font());
|
||||||
|
label->setText(metrics.elidedText(title, Qt::ElideRight, labelWidth));
|
||||||
|
|
||||||
|
auto *meter = new AudioLevelMeter(row);
|
||||||
|
meter->setMinimumHeight(70);
|
||||||
|
meter->setFixedWidth(26);
|
||||||
|
|
||||||
|
rowLayout->addWidget(label, 1);
|
||||||
|
rowLayout->addWidget(meter, 0, Qt::AlignRight);
|
||||||
|
row->setLayout(rowLayout);
|
||||||
|
|
||||||
|
m_meterListLayout->addWidget(row);
|
||||||
|
|
||||||
|
m_nodeMeters.insert(nodeId, meter);
|
||||||
|
m_nodeMeterRows.insert(nodeId, row);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@
|
||||||
|
|
||||||
class AudioLevelMeter;
|
class AudioLevelMeter;
|
||||||
class QTimer;
|
class QTimer;
|
||||||
|
class QScrollArea;
|
||||||
|
class QVBoxLayout;
|
||||||
|
|
||||||
class GraphEditorWidget : public QWidget
|
class GraphEditorWidget : public QWidget
|
||||||
{
|
{
|
||||||
|
|
@ -30,6 +32,7 @@ private slots:
|
||||||
void onConnectionCreated(QtNodes::ConnectionId const connectionId);
|
void onConnectionCreated(QtNodes::ConnectionId const connectionId);
|
||||||
void onConnectionDeleted(QtNodes::ConnectionId const connectionId);
|
void onConnectionDeleted(QtNodes::ConnectionId const connectionId);
|
||||||
void updateMeter();
|
void updateMeter();
|
||||||
|
void refreshNodeMeter(uint32_t nodeId, const Potato::NodeInfo &node);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void syncGraph();
|
void syncGraph();
|
||||||
|
|
@ -47,4 +50,9 @@ private:
|
||||||
QMap<uint32_t, QString> m_linkIdToConnection;
|
QMap<uint32_t, QString> m_linkIdToConnection;
|
||||||
AudioLevelMeter *m_meter = nullptr;
|
AudioLevelMeter *m_meter = nullptr;
|
||||||
QTimer *m_meterTimer = nullptr;
|
QTimer *m_meterTimer = nullptr;
|
||||||
|
QScrollArea *m_meterScroll = nullptr;
|
||||||
|
QWidget *m_meterList = nullptr;
|
||||||
|
QVBoxLayout *m_meterListLayout = nullptr;
|
||||||
|
QMap<uint32_t, AudioLevelMeter*> m_nodeMeters;
|
||||||
|
QMap<uint32_t, QWidget*> m_nodeMeterRows;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QMutexLocker>
|
#include <QMutexLocker>
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
|
#include <QString>
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
@ -159,6 +160,58 @@ static const struct pw_stream_events meter_events = []() {
|
||||||
return events;
|
return events;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
|
struct NodeMeter {
|
||||||
|
uint32_t nodeId;
|
||||||
|
QString targetName;
|
||||||
|
pw_stream *stream = nullptr;
|
||||||
|
std::atomic<float> peak{0.0f};
|
||||||
|
};
|
||||||
|
|
||||||
|
static void nodeMeterProcess(void *data)
|
||||||
|
{
|
||||||
|
auto *meter = static_cast<NodeMeter*>(data);
|
||||||
|
if (!meter || !meter->stream) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pw_buffer *buf = pw_stream_dequeue_buffer(meter->stream);
|
||||||
|
if (!buf || !buf->buffer || buf->buffer->n_datas == 0) {
|
||||||
|
if (buf) {
|
||||||
|
pw_stream_queue_buffer(meter->stream, buf);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct spa_buffer *spaBuf = buf->buffer;
|
||||||
|
struct spa_data *data0 = &spaBuf->datas[0];
|
||||||
|
if (!data0->data || !data0->chunk) {
|
||||||
|
pw_stream_queue_buffer(meter->stream, buf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t size = data0->chunk->size;
|
||||||
|
const float *samples = static_cast<const float*>(data0->data);
|
||||||
|
const uint32_t count = size / sizeof(float);
|
||||||
|
|
||||||
|
float peak = 0.0f;
|
||||||
|
for (uint32_t i = 0; i < count; ++i) {
|
||||||
|
const float value = std::fabs(samples[i]);
|
||||||
|
if (value > peak) {
|
||||||
|
peak = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
meter->peak.store(peak, std::memory_order_relaxed);
|
||||||
|
pw_stream_queue_buffer(meter->stream, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct pw_stream_events node_meter_events = []() {
|
||||||
|
struct pw_stream_events events{};
|
||||||
|
events.version = PW_VERSION_STREAM_EVENTS;
|
||||||
|
events.process = nodeMeterProcess;
|
||||||
|
return events;
|
||||||
|
}();
|
||||||
|
|
||||||
PipeWireController::PipeWireController(QObject *parent)
|
PipeWireController::PipeWireController(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
{
|
{
|
||||||
|
|
@ -256,6 +309,18 @@ void PipeWireController::shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
teardownMeterStream();
|
teardownMeterStream();
|
||||||
|
|
||||||
|
{
|
||||||
|
QMutexLocker lock(&m_meterMutex);
|
||||||
|
for (auto it = m_nodeMeters.begin(); it != m_nodeMeters.end(); ++it) {
|
||||||
|
NodeMeter *meter = it.value();
|
||||||
|
if (meter && meter->stream) {
|
||||||
|
pw_stream_destroy(meter->stream);
|
||||||
|
}
|
||||||
|
delete meter;
|
||||||
|
}
|
||||||
|
m_nodeMeters.clear();
|
||||||
|
}
|
||||||
|
|
||||||
if (m_core) {
|
if (m_core) {
|
||||||
pw_core_disconnect(m_core);
|
pw_core_disconnect(m_core);
|
||||||
|
|
@ -310,6 +375,115 @@ float PipeWireController::meterPeak() const
|
||||||
return m_meterPeak.load(std::memory_order_relaxed);
|
return m_meterPeak.load(std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float PipeWireController::nodeMeterPeak(uint32_t nodeId) const
|
||||||
|
{
|
||||||
|
QMutexLocker lock(&m_meterMutex);
|
||||||
|
if (!m_nodeMeters.contains(nodeId)) {
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeMeter *meter = m_nodeMeters.value(nodeId);
|
||||||
|
if (!meter) {
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return meter->peak.load(std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PipeWireController::ensureNodeMeter(uint32_t nodeId, const QString &targetName, bool captureSink)
|
||||||
|
{
|
||||||
|
if (!m_threadLoop || !m_core) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
QMutexLocker lock(&m_meterMutex);
|
||||||
|
if (m_nodeMeters.contains(nodeId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *meter = new NodeMeter;
|
||||||
|
meter->nodeId = nodeId;
|
||||||
|
meter->targetName = targetName;
|
||||||
|
|
||||||
|
const QByteArray targetNameBytes = meter->targetName.toUtf8();
|
||||||
|
struct pw_properties *props = pw_properties_new(
|
||||||
|
PW_KEY_MEDIA_TYPE, "Audio",
|
||||||
|
PW_KEY_MEDIA_CATEGORY, "Capture",
|
||||||
|
PW_KEY_MEDIA_CLASS, "Stream/Input/Audio",
|
||||||
|
PW_KEY_TARGET_OBJECT, targetNameBytes.constData(),
|
||||||
|
PW_KEY_STREAM_MONITOR, "true",
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
if (captureSink) {
|
||||||
|
pw_properties_set(props, PW_KEY_STREAM_CAPTURE_SINK, "true");
|
||||||
|
}
|
||||||
|
|
||||||
|
lock();
|
||||||
|
|
||||||
|
meter->stream = pw_stream_new_simple(
|
||||||
|
pw_thread_loop_get_loop(m_threadLoop),
|
||||||
|
"Potato-Node-Meter",
|
||||||
|
props,
|
||||||
|
&node_meter_events,
|
||||||
|
meter);
|
||||||
|
|
||||||
|
if (!meter->stream) {
|
||||||
|
pw_properties_free(props);
|
||||||
|
unlock();
|
||||||
|
delete meter;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t buffer[512];
|
||||||
|
spa_pod_builder builder = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
||||||
|
|
||||||
|
spa_audio_info_raw info{};
|
||||||
|
info.format = SPA_AUDIO_FORMAT_F32;
|
||||||
|
info.rate = 48000;
|
||||||
|
info.channels = 2;
|
||||||
|
info.position[0] = SPA_AUDIO_CHANNEL_FL;
|
||||||
|
info.position[1] = SPA_AUDIO_CHANNEL_FR;
|
||||||
|
|
||||||
|
const struct spa_pod *params[1];
|
||||||
|
params[0] = spa_format_audio_raw_build(&builder, SPA_PARAM_EnumFormat, &info);
|
||||||
|
|
||||||
|
const int res = pw_stream_connect(
|
||||||
|
meter->stream,
|
||||||
|
PW_DIRECTION_INPUT,
|
||||||
|
PW_ID_ANY,
|
||||||
|
static_cast<pw_stream_flags>(PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS),
|
||||||
|
params,
|
||||||
|
1);
|
||||||
|
|
||||||
|
if (res != 0) {
|
||||||
|
pw_stream_destroy(meter->stream);
|
||||||
|
unlock();
|
||||||
|
delete meter;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock();
|
||||||
|
|
||||||
|
QMutexLocker lock(&m_meterMutex);
|
||||||
|
m_nodeMeters.insert(nodeId, meter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PipeWireController::removeNodeMeter(uint32_t nodeId)
|
||||||
|
{
|
||||||
|
QMutexLocker lock(&m_meterMutex);
|
||||||
|
if (!m_nodeMeters.contains(nodeId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeMeter *meter = m_nodeMeters.take(nodeId);
|
||||||
|
if (meter && meter->stream) {
|
||||||
|
pw_stream_destroy(meter->stream);
|
||||||
|
}
|
||||||
|
delete meter;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t PipeWireController::createLink(uint32_t outputNodeId, uint32_t outputPortId,
|
uint32_t PipeWireController::createLink(uint32_t outputNodeId, uint32_t outputPortId,
|
||||||
uint32_t inputNodeId, uint32_t inputPortId)
|
uint32_t inputNodeId, uint32_t inputPortId)
|
||||||
{
|
{
|
||||||
|
|
@ -611,7 +785,9 @@ bool PipeWireController::setupMeterStream()
|
||||||
struct pw_properties *props = pw_properties_new(
|
struct pw_properties *props = pw_properties_new(
|
||||||
PW_KEY_MEDIA_TYPE, "Audio",
|
PW_KEY_MEDIA_TYPE, "Audio",
|
||||||
PW_KEY_MEDIA_CATEGORY, "Capture",
|
PW_KEY_MEDIA_CATEGORY, "Capture",
|
||||||
PW_KEY_MEDIA_CLASS, "Audio/Source",
|
PW_KEY_MEDIA_CLASS, "Stream/Input/Audio",
|
||||||
|
PW_KEY_STREAM_CAPTURE_SINK, "true",
|
||||||
|
PW_KEY_STREAM_MONITOR, "true",
|
||||||
nullptr);
|
nullptr);
|
||||||
|
|
||||||
m_meterStream = pw_stream_new_simple(
|
m_meterStream = pw_stream_new_simple(
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,10 @@ struct pw_registry;
|
||||||
struct pw_stream;
|
struct pw_stream;
|
||||||
struct spa_hook;
|
struct spa_hook;
|
||||||
struct spa_dict;
|
struct spa_dict;
|
||||||
|
|
||||||
namespace Potato {
|
namespace Potato {
|
||||||
|
|
||||||
|
struct NodeMeter;
|
||||||
|
|
||||||
class PipeWireController : public QObject
|
class PipeWireController : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
@ -34,6 +35,9 @@ public:
|
||||||
NodeInfo nodeById(uint32_t id) const;
|
NodeInfo nodeById(uint32_t id) const;
|
||||||
QVector<LinkInfo> links() const;
|
QVector<LinkInfo> links() const;
|
||||||
float meterPeak() const;
|
float meterPeak() const;
|
||||||
|
float nodeMeterPeak(uint32_t nodeId) const;
|
||||||
|
void ensureNodeMeter(uint32_t nodeId, const QString &targetName, bool captureSink);
|
||||||
|
void removeNodeMeter(uint32_t nodeId);
|
||||||
|
|
||||||
uint32_t createLink(uint32_t outputNodeId, uint32_t outputPortId,
|
uint32_t createLink(uint32_t outputNodeId, uint32_t outputPortId,
|
||||||
uint32_t inputNodeId, uint32_t inputPortId);
|
uint32_t inputNodeId, uint32_t inputPortId);
|
||||||
|
|
@ -89,6 +93,9 @@ private:
|
||||||
QAtomicInteger<bool> m_connected{false};
|
QAtomicInteger<bool> m_connected{false};
|
||||||
QAtomicInteger<bool> m_initialized{false};
|
QAtomicInteger<bool> m_initialized{false};
|
||||||
std::atomic<float> m_meterPeak{0.0f};
|
std::atomic<float> m_meterPeak{0.0f};
|
||||||
|
|
||||||
|
mutable QMutex m_meterMutex;
|
||||||
|
QMap<uint32_t, NodeMeter*> m_nodeMeters;
|
||||||
|
|
||||||
uint32_t m_nodeIdCounter = 0;
|
uint32_t m_nodeIdCounter = 0;
|
||||||
uint32_t m_linkIdCounter = 0;
|
uint32_t m_linkIdCounter = 0;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue