From 16031cc9d7bad2e8537bf36c857889bf857bcab5 Mon Sep 17 00:00:00 2001 From: m0dB Date: Sun, 11 Sep 2022 18:41:44 +0200 Subject: [PATCH 01/33] converted wvumeter into a qglwidget and rendering it just like wspinny --- src/skin/legacy/legacyskinparser.cpp | 11 +- src/waveform/waveformwidgetfactory.cpp | 13 +- src/waveform/waveformwidgetfactory.h | 4 +- src/widget/wvumeter.cpp | 211 ++++++++++++++++++------- src/widget/wvumeter.h | 22 ++- 5 files changed, 181 insertions(+), 80 deletions(-) diff --git a/src/skin/legacy/legacyskinparser.cpp b/src/skin/legacy/legacyskinparser.cpp index 23b04855836..24b2f6ea23b 100644 --- a/src/skin/legacy/legacyskinparser.cpp +++ b/src/skin/legacy/legacyskinparser.cpp @@ -528,7 +528,15 @@ QList LegacySkinParser::parseNode(const QDomElement& node) { result = wrapWidget(parseStarRating(node)); } else if (nodeName == "VuMeter") { WVuMeter* pVuMeterWidget = parseStandardWidget(node); - WaveformWidgetFactory::instance()->addTimerListener(pVuMeterWidget); + auto* waveformWidgetFactory = WaveformWidgetFactory::instance(); + connect(waveformWidgetFactory, + &WaveformWidgetFactory::renderVuMeters, + pVuMeterWidget, + &WVuMeter::render); + connect(waveformWidgetFactory, + &WaveformWidgetFactory::swapVuMeters, + pVuMeterWidget, + &WVuMeter::swap); result = wrapWidget(pVuMeterWidget); } else if (nodeName == "StatusLight") { result = wrapWidget(parseStandardWidget(node)); @@ -1255,7 +1263,6 @@ QWidget* LegacySkinParser::parseSpinny(const QDomElement& node) { } auto* waveformWidgetFactory = WaveformWidgetFactory::instance(); - if (!waveformWidgetFactory->isOpenGlAvailable() && !waveformWidgetFactory->isOpenGlesAvailable()) { WLabel* dummy = new WLabel(m_pParent); diff --git a/src/waveform/waveformwidgetfactory.cpp b/src/waveform/waveformwidgetfactory.cpp index 0ae08e33aeb..d82cc4758a2 100644 --- a/src/waveform/waveformwidgetfactory.cpp +++ b/src/waveform/waveformwidgetfactory.cpp @@ -353,17 +353,6 @@ void WaveformWidgetFactory::destroyWidgets() { m_waveformWidgetHolders.clear(); } -void WaveformWidgetFactory::addTimerListener(WVuMeter* pWidget) { - // Do not hold the pointer to of timer listeners since they may be deleted. - // We don't activate update() or repaint() directly so listener widgets - // can decide whether to paint or not. - connect(this, - &WaveformWidgetFactory::waveformUpdateTick, - pWidget, - &WVuMeter::maybeUpdate, - Qt::DirectConnection); -} - void WaveformWidgetFactory::slotSkinLoaded() { setWidgetTypeFromConfig(); #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) && defined __WINDOWS__ @@ -673,6 +662,7 @@ void WaveformWidgetFactory::render() { // WSpinnys are also double-buffered QGLWidgets, like all the waveform // renderers. Render all the WSpinny widgets now. emit renderSpinnies(m_vsyncThread); + emit renderVuMeters(m_vsyncThread); // Notify all other waveform-like widgets (e.g. WSpinny's) that they should // update. @@ -729,6 +719,7 @@ void WaveformWidgetFactory::swap() { // WSpinnys are also double-buffered QGLWidgets, like all the waveform // renderers. Swap all the WSpinny widgets now. emit swapSpinnies(); + emit swapVuMeters(); } //qDebug() << "swap end" << m_vsyncThread->elapsed(); m_vsyncThread->vsyncSlotFinished(); diff --git a/src/waveform/waveformwidgetfactory.h b/src/waveform/waveformwidgetfactory.h index 483ea4caad8..328b85ea66c 100644 --- a/src/waveform/waveformwidgetfactory.h +++ b/src/waveform/waveformwidgetfactory.h @@ -122,8 +122,6 @@ class WaveformWidgetFactory : public QObject, public Singleton >* list); void destroyWidgets(); - void addTimerListener(WVuMeter* pWidget); - void startVSync(GuiTick* pGuiTick, VisualsManager* pVisualsManager); void setVSyncType(int vsType); int getVSyncType(); @@ -140,6 +138,8 @@ class WaveformWidgetFactory : public QObject, public Singleton 100) { @@ -83,7 +93,7 @@ void WVuMeter::setPixmapBackground( Paintable::DrawMode mode, double scaleFactor) { m_pPixmapBack = WPixmapStore::getPaintable(source, mode, scaleFactor); - if (m_pPixmapBack.isNull() || m_pPixmapBack->isNull()) { + if (m_pPixmapBack.isNull()) { qDebug() << metaObject()->className() << "Error loading background pixmap:" << source.getPath(); } else if (mode == Paintable::FIXED) { @@ -96,7 +106,7 @@ void WVuMeter::setPixmaps(const PixmapSource& source, Paintable::DrawMode mode, double scaleFactor) { m_pPixmapVu = WPixmapStore::getPaintable(source, mode, scaleFactor); - if (m_pPixmapVu.isNull() || m_pPixmapVu->isNull()) { + if (m_pPixmapVu.isNull()) { qDebug() << "WVuMeter: Error loading vu pixmap" << source.getPath(); } else { m_bHorizontal = bHorizontal; @@ -148,88 +158,175 @@ void WVuMeter::updateState(mixxx::Duration elapsed) { m_dPeakParameter = math_clamp(m_dPeakParameter, 0.0, 1.0); } -void WVuMeter::maybeUpdate() { - if (m_dParameter != m_dLastParameter || m_dPeakParameter != m_dLastPeakParameter) { - repaint(); - } +void WVuMeter::paintEvent(QPaintEvent* e) { + Q_UNUSED(e); } -void WVuMeter::paintEvent(QPaintEvent * /*unused*/) { - ScopedTimer t("WVuMeter::paintEvent"); +void WVuMeter::render(VSyncThread* /* UNUSED vSyncThread */) { + ScopedTimer t("WVuMeter::render"); - QStyleOption option; - option.initFrom(this); - QStylePainter p(this); - p.drawPrimitive(QStyle::PE_Widget, option); + m_bSwapNeeded = false; + + if (m_dParameter == m_dLastParameter && m_dPeakParameter == m_dLastPeakParameter) { + return; + } - if (!m_pPixmapBack.isNull() && !m_pPixmapBack->isNull()) { - // Draw background. DrawMode takes care of whether to stretch or not. - m_pPixmapBack->draw(rect(), &p); + if (!isValid() || !isVisible()) { + return; } - if (!m_pPixmapVu.isNull() && !m_pPixmapVu->isNull()) { + auto* window = windowHandle(); + if (window == nullptr || !window->isExposed()) { + return; + } + + const bool drawWithRects = false; + if (drawWithRects) { + QPainter p(this); + p.fillRect(rect(), QColor(0, 0, 0)); + const double widgetWidth = width(); const double widgetHeight = height(); - const double pixmapWidth = m_pPixmapVu->width(); - const double pixmapHeight = m_pPixmapVu->height(); // Draw (part of) vu if (m_bHorizontal) { const double widgetPosition = math_clamp(widgetWidth * m_dParameter, - 0.0, widgetWidth); + 0.0, + widgetWidth); QRectF targetRect(0, 0, widgetPosition, widgetHeight); - - const double pixmapPosition = math_clamp(pixmapWidth * m_dParameter, - 0.0, pixmapWidth); - QRectF sourceRect(0, 0, pixmapPosition, m_pPixmapVu->height()); - m_pPixmapVu->draw(targetRect, &p, sourceRect); + p.fillRect(targetRect, QColor(0, 255, 0)); if (m_iPeakHoldSize > 0 && m_dPeakParameter > 0.0) { const double widgetPeakPosition = math_clamp( widgetWidth * m_dPeakParameter, 0.0, widgetWidth); const double widgetPeakHoldSize = widgetWidth * - static_cast(m_iPeakHoldSize) / pixmapWidth; - - const double pixmapPeakPosition = math_clamp( - pixmapWidth * m_dPeakParameter, 0.0, pixmapWidth); - const double pixmapPeakHoldSize = m_iPeakHoldSize; + static_cast(m_iPeakHoldSize) / widgetWidth; - targetRect = QRectF(widgetPeakPosition - widgetPeakHoldSize, 0, - widgetPeakHoldSize, widgetHeight); - sourceRect = QRectF(pixmapPeakPosition - pixmapPeakHoldSize, 0, - pixmapPeakHoldSize, pixmapHeight); - m_pPixmapVu->draw(targetRect, &p, sourceRect); + targetRect = QRectF(widgetPeakPosition - widgetPeakHoldSize, + 0, + widgetPeakHoldSize, + widgetHeight); + p.fillRect(targetRect, QColor(0, 255, 0)); } } else { const double widgetPosition = math_clamp(widgetHeight * m_dParameter, - 0.0, widgetHeight); - QRectF targetRect(0, widgetHeight - widgetPosition, - widgetWidth, widgetPosition); - - const double pixmapPosition = math_clamp(pixmapHeight * m_dParameter, - 0.0, pixmapHeight); - QRectF sourceRect(0, pixmapHeight - pixmapPosition, - pixmapWidth, pixmapPosition); - m_pPixmapVu->draw(targetRect, &p, sourceRect); + 0.0, + widgetHeight); + QRectF targetRect(0, widgetHeight - widgetPosition, widgetWidth, widgetPosition); + p.fillRect(targetRect, QColor(0, 255, 0)); if (m_iPeakHoldSize > 0 && m_dPeakParameter > 0.0) { const double widgetPeakPosition = math_clamp( widgetHeight * m_dPeakParameter, 0.0, widgetHeight); const double widgetPeakHoldSize = widgetHeight * - static_cast(m_iPeakHoldSize) / pixmapHeight; + static_cast(m_iPeakHoldSize) / widgetHeight; - const double pixmapPeakPosition = math_clamp( - pixmapHeight * m_dPeakParameter, 0.0, pixmapHeight); - const double pixmapPeakHoldSize = m_iPeakHoldSize; + targetRect = QRectF(0, + widgetHeight - widgetPeakPosition, + widgetWidth, + widgetPeakHoldSize); + p.fillRect(targetRect, QColor(0, 1, 0)); + } + } + } else { + QPainter p(this); + + p.fillRect(rect(), QColor(0, 0, 0)); + if (!m_pPixmapBack.isNull()) { + // Draw background. + QRectF sourceRect(0, 0, m_pPixmapBack->width(), m_pPixmapBack->height()); + m_pPixmapBack->draw(rect(), &p, sourceRect); + } - targetRect = QRectF(0, widgetHeight - widgetPeakPosition, - widgetWidth, widgetPeakHoldSize); - sourceRect = QRectF(0, pixmapHeight - pixmapPeakPosition, - pixmapWidth, pixmapPeakHoldSize); + if (!m_pPixmapVu.isNull()) { + const double widgetWidth = width(); + const double widgetHeight = height(); + const double pixmapWidth = m_pPixmapVu->width(); + const double pixmapHeight = m_pPixmapVu->height(); + + // Draw (part of) vu + if (m_bHorizontal) { + const double widgetPosition = math_clamp(widgetWidth * m_dParameter, + 0.0, + widgetWidth); + QRectF targetRect(0, 0, widgetPosition, widgetHeight); + + const double pixmapPosition = math_clamp(pixmapWidth * m_dParameter, + 0.0, + pixmapWidth); + QRectF sourceRect(0, 0, pixmapPosition, m_pPixmapVu->height()); m_pPixmapVu->draw(targetRect, &p, sourceRect); + + if (m_iPeakHoldSize > 0 && m_dPeakParameter > 0.0) { + const double widgetPeakPosition = math_clamp( + widgetWidth * m_dPeakParameter, 0.0, widgetWidth); + const double widgetPeakHoldSize = widgetWidth * + static_cast(m_iPeakHoldSize) / pixmapWidth; + + const double pixmapPeakPosition = math_clamp( + pixmapWidth * m_dPeakParameter, 0.0, pixmapWidth); + const double pixmapPeakHoldSize = m_iPeakHoldSize; + + targetRect = QRectF(widgetPeakPosition - widgetPeakHoldSize, + 0, + widgetPeakHoldSize, + widgetHeight); + sourceRect = QRectF(pixmapPeakPosition - pixmapPeakHoldSize, + 0, + pixmapPeakHoldSize, + pixmapHeight); + m_pPixmapVu->draw(targetRect, &p, sourceRect); + } + } else { + const double widgetPosition = math_clamp(widgetHeight * m_dParameter, + 0.0, + widgetHeight); + QRectF targetRect(0, widgetHeight - widgetPosition, widgetWidth, widgetPosition); + + const double pixmapPosition = math_clamp(pixmapHeight * m_dParameter, + 0.0, + pixmapHeight); + QRectF sourceRect(0, pixmapHeight - pixmapPosition, pixmapWidth, pixmapPosition); + m_pPixmapVu->draw(targetRect, &p, sourceRect); + + if (m_iPeakHoldSize > 0 && m_dPeakParameter > 0.0) { + const double widgetPeakPosition = math_clamp( + widgetHeight * m_dPeakParameter, 0.0, widgetHeight); + const double widgetPeakHoldSize = widgetHeight * + static_cast(m_iPeakHoldSize) / pixmapHeight; + + const double pixmapPeakPosition = math_clamp( + pixmapHeight * m_dPeakParameter, 0.0, pixmapHeight); + const double pixmapPeakHoldSize = m_iPeakHoldSize; + + targetRect = QRectF(0, + widgetHeight - widgetPeakPosition, + widgetWidth, + widgetPeakHoldSize); + sourceRect = QRectF(0, + pixmapHeight - pixmapPeakPosition, + pixmapWidth, + pixmapPeakHoldSize); + m_pPixmapVu->draw(targetRect, &p, sourceRect); + } } } } m_dLastParameter = m_dParameter; m_dLastPeakParameter = m_dPeakParameter; + m_bSwapNeeded = true; +} + +void WVuMeter::swap() { + if (!isValid() || !isVisible() || !m_bSwapNeeded) { + return; + } + auto* window = windowHandle(); + if (window == nullptr || !window->isExposed()) { + return; + } + if (context() != QGLContext::currentContext()) { + makeCurrent(); + } + swapBuffers(); } diff --git a/src/widget/wvumeter.h b/src/widget/wvumeter.h index 988f0c463e3..5134be6f9f1 100644 --- a/src/widget/wvumeter.h +++ b/src/widget/wvumeter.h @@ -1,18 +1,20 @@ #pragma once +#include +#include +#include #include #include -#include -#include -#include -#include "widget/wwidget.h" -#include "widget/wpixmapstore.h" #include "skin/legacy/skincontext.h" #include "util/performancetimer.h" +#include "widget/wpixmapstore.h" +#include "widget/wwidget.h" -class WVuMeter : public WWidget { - Q_OBJECT +class VSyncThread; + +class WVuMeter : public QGLWidget, public WBaseWidget { + Q_OBJECT public: explicit WVuMeter(QWidget *parent=nullptr); @@ -29,15 +31,19 @@ class WVuMeter : public WWidget { void onConnectedControlChanged(double dParameter, double dValue) override; public slots: - void maybeUpdate(); + void render(VSyncThread* vSyncThread); + void swap(); protected slots: void updateState(mixxx::Duration elapsed); private: void paintEvent(QPaintEvent * /*unused*/) override; + void setPeak(double parameter); + // To indicate that we rendered so we need to swap + bool m_bSwapNeeded{}; // Current parameter and peak parameter. double m_dParameter; double m_dPeakParameter; From ed121623560e06b6e3aad6157da80056cce99c4f Mon Sep 17 00:00:00 2001 From: m0dB Date: Mon, 12 Sep 2022 22:06:15 +0200 Subject: [PATCH 02/33] use parseVuMeter modeled after parseSpinny --- src/skin/legacy/legacyskinparser.cpp | 50 +++++++++++++++++++++------- src/skin/legacy/legacyskinparser.h | 1 + 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/skin/legacy/legacyskinparser.cpp b/src/skin/legacy/legacyskinparser.cpp index 24b2f6ea23b..6bf91114412 100644 --- a/src/skin/legacy/legacyskinparser.cpp +++ b/src/skin/legacy/legacyskinparser.cpp @@ -527,17 +527,7 @@ QList LegacySkinParser::parseNode(const QDomElement& node) { } else if (nodeName == "StarRating") { result = wrapWidget(parseStarRating(node)); } else if (nodeName == "VuMeter") { - WVuMeter* pVuMeterWidget = parseStandardWidget(node); - auto* waveformWidgetFactory = WaveformWidgetFactory::instance(); - connect(waveformWidgetFactory, - &WaveformWidgetFactory::renderVuMeters, - pVuMeterWidget, - &WVuMeter::render); - connect(waveformWidgetFactory, - &WaveformWidgetFactory::swapVuMeters, - pVuMeterWidget, - &WVuMeter::swap); - result = wrapWidget(pVuMeterWidget); + result = wrapWidget(parseVuMeter(node)); } else if (nodeName == "StatusLight") { result = wrapWidget(parseStandardWidget(node)); } else if (nodeName == "Display") { @@ -1253,7 +1243,6 @@ QWidget* LegacySkinParser::parseRecordingDuration(const QDomElement& node) { p->installEventFilter(m_pControllerManager->getControllerLearningEventFilter()); return p; } - QWidget* LegacySkinParser::parseSpinny(const QDomElement& node) { if (CmdlineArgs::Instance().getSafeMode()) { WLabel* dummy = new WLabel(m_pParent); @@ -1295,6 +1284,43 @@ QWidget* LegacySkinParser::parseSpinny(const QDomElement& node) { return spinny; } +QWidget* LegacySkinParser::parseVuMeter(const QDomElement& node) { + if (CmdlineArgs::Instance().getSafeMode()) { + WLabel* dummy = new WLabel(m_pParent); + //: Shown when Mixxx is running in safe mode. + dummy->setText(tr("Safe Mode Enabled")); + return dummy; + } + + auto* waveformWidgetFactory = WaveformWidgetFactory::instance(); + if (!waveformWidgetFactory->isOpenGlAvailable() && + !waveformWidgetFactory->isOpenGlesAvailable()) { + WLabel* dummy = new WLabel(m_pParent); + //: Shown when Spinny can not be displayed. Please keep \n unchanged + dummy->setText(tr("No OpenGL\nsupport.")); + return dummy; + } + WVuMeter* vuMeter = new WVuMeter(m_pParent); + commonWidgetSetup(node, vuMeter); + + connect(waveformWidgetFactory, + &WaveformWidgetFactory::renderVuMeters, + vuMeter, + &WVuMeter::render); + connect(waveformWidgetFactory, + &WaveformWidgetFactory::swapVuMeters, + vuMeter, + &WVuMeter::swap); + + vuMeter->setup(node, *m_pContext); + vuMeter->installEventFilter(m_pKeyboard); + vuMeter->installEventFilter( + m_pControllerManager->getControllerLearningEventFilter()); + vuMeter->Init(); + + return vuMeter; +} + QWidget* LegacySkinParser::parseSearchBox(const QDomElement& node) { // TODO(XXX): Currently this is the only opportunity to initialize // the static configuration settings of the widget. The settings diff --git a/src/skin/legacy/legacyskinparser.h b/src/skin/legacy/legacyskinparser.h index 69fc1393651..347cc3a82e6 100644 --- a/src/skin/legacy/legacyskinparser.h +++ b/src/skin/legacy/legacyskinparser.h @@ -110,6 +110,7 @@ class LegacySkinParser : public QObject, public SkinParser { QWidget* parseVisual(const QDomElement& node); QWidget* parseOverview(const QDomElement& node); QWidget* parseSpinny(const QDomElement& node); + QWidget* parseVuMeter(const QDomElement& node); // Library widgets. QWidget* parseTableView(const QDomElement& node); From 28dca424bbd414c15b552f5035cfce371c15ef9f Mon Sep 17 00:00:00 2001 From: m0dB Date: Mon, 12 Sep 2022 22:09:56 +0200 Subject: [PATCH 03/33] make sure we have renderered at least once before skipping rendering when the parameters dont change --- src/widget/wvumeter.cpp | 219 ++++++++++++++++++---------------------- src/widget/wvumeter.h | 5 +- 2 files changed, 103 insertions(+), 121 deletions(-) diff --git a/src/widget/wvumeter.cpp b/src/widget/wvumeter.cpp index 4a9c02b99f0..ce65722942f 100644 --- a/src/widget/wvumeter.cpp +++ b/src/widget/wvumeter.cpp @@ -21,11 +21,12 @@ WVuMeter::WVuMeter(QWidget* parent) : QGLWidget(parent, SharedGLContext::getWidget()), WBaseWidget(this), + m_bHasRendered(false), m_bSwapNeeded(false), m_dParameter(0), m_dPeakParameter(0), - m_dLastParameter(-1), // -1 to force a first render - m_dLastPeakParameter(-1), + m_dLastParameter(0), + m_dLastPeakParameter(0), m_iPixmapLength(0), m_bHorizontal(false), m_iPeakHoldSize(0), @@ -93,6 +94,7 @@ void WVuMeter::setPixmapBackground( Paintable::DrawMode mode, double scaleFactor) { m_pPixmapBack = WPixmapStore::getPaintable(source, mode, scaleFactor); + if (m_pPixmapBack.isNull()) { qDebug() << metaObject()->className() << "Error loading background pixmap:" << source.getPath(); @@ -158,6 +160,10 @@ void WVuMeter::updateState(mixxx::Duration elapsed) { m_dPeakParameter = math_clamp(m_dPeakParameter, 0.0, 1.0); } +void WVuMeter::initializeGL() { + glClearColor(0.0, 0.0, 0.0, 1.0); +} + void WVuMeter::paintEvent(QPaintEvent* e) { Q_UNUSED(e); } @@ -165,9 +171,8 @@ void WVuMeter::paintEvent(QPaintEvent* e) { void WVuMeter::render(VSyncThread* /* UNUSED vSyncThread */) { ScopedTimer t("WVuMeter::render"); - m_bSwapNeeded = false; - - if (m_dParameter == m_dLastParameter && m_dPeakParameter == m_dLastPeakParameter) { + if (m_bHasRendered && m_dParameter == m_dLastParameter && + m_dPeakParameter == m_dLastPeakParameter) { return; } @@ -180,140 +185,113 @@ void WVuMeter::render(VSyncThread* /* UNUSED vSyncThread */) { return; } - const bool drawWithRects = false; - if (drawWithRects) { - QPainter p(this); - p.fillRect(rect(), QColor(0, 0, 0)); + QPainter p(this); - const double widgetWidth = width(); - const double widgetHeight = height(); + // draw a black background, in case the image contains transparency + p.fillRect(rect(), QColor(0, 0, 0)); - // Draw (part of) vu - if (m_bHorizontal) { - const double widgetPosition = math_clamp(widgetWidth * m_dParameter, - 0.0, - widgetWidth); + if (!m_pPixmapBack.isNull()) { + // Draw background. + QRectF sourceRect(0, 0, m_pPixmapBack->width(), m_pPixmapBack->height()); + m_pPixmapBack->draw(rect(), &p, sourceRect); + } + + const double widgetWidth = width(); + const double widgetHeight = height(); + const double pixmapWidth = m_pPixmapVu.isNull() ? 0 : m_pPixmapVu->width(); + const double pixmapHeight = m_pPixmapVu.isNull() ? 0 : m_pPixmapVu->height(); + + // Draw (part of) vu + if (m_bHorizontal) { + { + const double widgetPosition = math_clamp(widgetWidth * m_dParameter, 0.0, widgetWidth); QRectF targetRect(0, 0, widgetPosition, widgetHeight); - p.fillRect(targetRect, QColor(0, 255, 0)); - - if (m_iPeakHoldSize > 0 && m_dPeakParameter > 0.0) { - const double widgetPeakPosition = math_clamp( - widgetWidth * m_dPeakParameter, 0.0, widgetWidth); - const double widgetPeakHoldSize = widgetWidth * - static_cast(m_iPeakHoldSize) / widgetWidth; - - targetRect = QRectF(widgetPeakPosition - widgetPeakHoldSize, - 0, - widgetPeakHoldSize, - widgetHeight); + + if (!m_pPixmapVu.isNull()) { + const double pixmapPosition = math_clamp( + pixmapWidth * m_dParameter, 0.0, pixmapWidth); + QRectF sourceRect(0, 0, pixmapPosition, pixmapHeight); + m_pPixmapVu->draw(targetRect, &p, sourceRect); + } else { + // fallback to green rectangle p.fillRect(targetRect, QColor(0, 255, 0)); } - } else { - const double widgetPosition = math_clamp(widgetHeight * m_dParameter, - 0.0, + } + + if (m_iPeakHoldSize > 0 && m_dPeakParameter > 0.0) { + const double widgetPeakPosition = math_clamp( + widgetWidth * m_dPeakParameter, 0.0, widgetWidth); + const double pixmapPeakHoldSize = static_cast(m_iPeakHoldSize); + const double widgetPeakHoldSize = widgetWidth * pixmapPeakHoldSize / pixmapHeight; + + QRectF targetRect(widgetPeakPosition - widgetPeakHoldSize, + 0, + widgetPeakHoldSize, widgetHeight); - QRectF targetRect(0, widgetHeight - widgetPosition, widgetWidth, widgetPosition); - p.fillRect(targetRect, QColor(0, 255, 0)); - - if (m_iPeakHoldSize > 0 && m_dPeakParameter > 0.0) { - const double widgetPeakPosition = math_clamp( - widgetHeight * m_dPeakParameter, 0.0, widgetHeight); - const double widgetPeakHoldSize = widgetHeight * - static_cast(m_iPeakHoldSize) / widgetHeight; - - targetRect = QRectF(0, - widgetHeight - widgetPeakPosition, - widgetWidth, - widgetPeakHoldSize); - p.fillRect(targetRect, QColor(0, 1, 0)); + + if (!m_pPixmapVu.isNull()) { + const double pixmapPeakPosition = math_clamp( + pixmapWidth * m_dPeakParameter, 0.0, pixmapWidth); + + QRectF sourceRect = + QRectF(pixmapPeakPosition - pixmapPeakHoldSize, + 0, + pixmapPeakHoldSize, + pixmapHeight); + m_pPixmapVu->draw(targetRect, &p, sourceRect); + } else { + // fallback to green rectangle + p.fillRect(targetRect, QColor(0, 255, 0)); } } } else { - QPainter p(this); + // vertical + { + const double widgetPosition = + math_clamp(widgetHeight * m_dParameter, 0.0, widgetHeight); + QRectF targetRect(0, widgetHeight - widgetPosition, widgetWidth, widgetPosition); - p.fillRect(rect(), QColor(0, 0, 0)); - if (!m_pPixmapBack.isNull()) { - // Draw background. - QRectF sourceRect(0, 0, m_pPixmapBack->width(), m_pPixmapBack->height()); - m_pPixmapBack->draw(rect(), &p, sourceRect); + if (!m_pPixmapVu.isNull()) { + const double pixmapPosition = math_clamp( + pixmapHeight * m_dParameter, 0.0, pixmapHeight); + QRectF sourceRect(0, pixmapHeight - pixmapPosition, pixmapWidth, pixmapPosition); + m_pPixmapVu->draw(targetRect, &p, sourceRect); + } else { + // fallback to green rectangle + p.fillRect(targetRect, QColor(0, 255, 0)); + } } - if (!m_pPixmapVu.isNull()) { - const double widgetWidth = width(); - const double widgetHeight = height(); - const double pixmapWidth = m_pPixmapVu->width(); - const double pixmapHeight = m_pPixmapVu->height(); - - // Draw (part of) vu - if (m_bHorizontal) { - const double widgetPosition = math_clamp(widgetWidth * m_dParameter, - 0.0, - widgetWidth); - QRectF targetRect(0, 0, widgetPosition, widgetHeight); - - const double pixmapPosition = math_clamp(pixmapWidth * m_dParameter, - 0.0, - pixmapWidth); - QRectF sourceRect(0, 0, pixmapPosition, m_pPixmapVu->height()); + if (m_iPeakHoldSize > 0 && m_dPeakParameter > 0.0) { + const double widgetPeakPosition = math_clamp( + widgetHeight * m_dPeakParameter, 0.0, widgetHeight); + const double pixmapPeakHoldSize = static_cast(m_iPeakHoldSize); + const double widgetPeakHoldSize = widgetHeight * pixmapPeakHoldSize / pixmapHeight; + + QRectF targetRect(0, + widgetHeight - widgetPeakPosition, + widgetWidth, + widgetPeakHoldSize); + + if (!m_pPixmapVu.isNull()) { + const double pixmapPeakPosition = math_clamp( + pixmapHeight * m_dPeakParameter, 0.0, pixmapHeight); + + QRectF sourceRect = QRectF(0, + pixmapHeight - pixmapPeakPosition, + pixmapWidth, + pixmapPeakHoldSize); m_pPixmapVu->draw(targetRect, &p, sourceRect); - - if (m_iPeakHoldSize > 0 && m_dPeakParameter > 0.0) { - const double widgetPeakPosition = math_clamp( - widgetWidth * m_dPeakParameter, 0.0, widgetWidth); - const double widgetPeakHoldSize = widgetWidth * - static_cast(m_iPeakHoldSize) / pixmapWidth; - - const double pixmapPeakPosition = math_clamp( - pixmapWidth * m_dPeakParameter, 0.0, pixmapWidth); - const double pixmapPeakHoldSize = m_iPeakHoldSize; - - targetRect = QRectF(widgetPeakPosition - widgetPeakHoldSize, - 0, - widgetPeakHoldSize, - widgetHeight); - sourceRect = QRectF(pixmapPeakPosition - pixmapPeakHoldSize, - 0, - pixmapPeakHoldSize, - pixmapHeight); - m_pPixmapVu->draw(targetRect, &p, sourceRect); - } } else { - const double widgetPosition = math_clamp(widgetHeight * m_dParameter, - 0.0, - widgetHeight); - QRectF targetRect(0, widgetHeight - widgetPosition, widgetWidth, widgetPosition); - - const double pixmapPosition = math_clamp(pixmapHeight * m_dParameter, - 0.0, - pixmapHeight); - QRectF sourceRect(0, pixmapHeight - pixmapPosition, pixmapWidth, pixmapPosition); - m_pPixmapVu->draw(targetRect, &p, sourceRect); - - if (m_iPeakHoldSize > 0 && m_dPeakParameter > 0.0) { - const double widgetPeakPosition = math_clamp( - widgetHeight * m_dPeakParameter, 0.0, widgetHeight); - const double widgetPeakHoldSize = widgetHeight * - static_cast(m_iPeakHoldSize) / pixmapHeight; - - const double pixmapPeakPosition = math_clamp( - pixmapHeight * m_dPeakParameter, 0.0, pixmapHeight); - const double pixmapPeakHoldSize = m_iPeakHoldSize; - - targetRect = QRectF(0, - widgetHeight - widgetPeakPosition, - widgetWidth, - widgetPeakHoldSize); - sourceRect = QRectF(0, - pixmapHeight - pixmapPeakPosition, - pixmapWidth, - pixmapPeakHoldSize); - m_pPixmapVu->draw(targetRect, &p, sourceRect); - } + // fallback to green rectangle + p.fillRect(targetRect, QColor(0, 255, 0)); } } } + m_dLastParameter = m_dParameter; m_dLastPeakParameter = m_dPeakParameter; + m_bHasRendered = true; m_bSwapNeeded = true; } @@ -329,4 +307,5 @@ void WVuMeter::swap() { makeCurrent(); } swapBuffers(); + m_bSwapNeeded = false; } diff --git a/src/widget/wvumeter.h b/src/widget/wvumeter.h index 5134be6f9f1..9023f610d18 100644 --- a/src/widget/wvumeter.h +++ b/src/widget/wvumeter.h @@ -38,12 +38,15 @@ class WVuMeter : public QGLWidget, public WBaseWidget { void updateState(mixxx::Duration elapsed); private: + void initializeGL() override; void paintEvent(QPaintEvent * /*unused*/) override; void setPeak(double parameter); + // To make sure we render at least once even when we have no signal + bool m_bHasRendered; // To indicate that we rendered so we need to swap - bool m_bSwapNeeded{}; + bool m_bSwapNeeded; // Current parameter and peak parameter. double m_dParameter; double m_dPeakParameter; From 6ab425e205a68ab30c3b2dafe1996050095ece28 Mon Sep 17 00:00:00 2001 From: m0dB Date: Mon, 12 Sep 2022 22:40:47 +0200 Subject: [PATCH 04/33] undo accidental formatting --- src/widget/wvumeter.cpp | 3 +-- src/widget/wvumeter.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/widget/wvumeter.cpp b/src/widget/wvumeter.cpp index ce65722942f..25c754c5304 100644 --- a/src/widget/wvumeter.cpp +++ b/src/widget/wvumeter.cpp @@ -34,12 +34,12 @@ WVuMeter::WVuMeter(QWidget* parent) m_iPeakHoldTime(0), m_iPeakFallTime(0), m_dPeakHoldCountdownMs(0) { - m_timer.start(); setAttribute(Qt::WA_NoSystemBackground); setAttribute(Qt::WA_OpaquePaintEvent); setAutoFillBackground(false); setAutoBufferSwap(false); + m_timer.start(); } void WVuMeter::setup(const QDomNode& node, const SkinContext& context) { @@ -94,7 +94,6 @@ void WVuMeter::setPixmapBackground( Paintable::DrawMode mode, double scaleFactor) { m_pPixmapBack = WPixmapStore::getPaintable(source, mode, scaleFactor); - if (m_pPixmapBack.isNull()) { qDebug() << metaObject()->className() << "Error loading background pixmap:" << source.getPath(); diff --git a/src/widget/wvumeter.h b/src/widget/wvumeter.h index 9023f610d18..c1eacc1c7de 100644 --- a/src/widget/wvumeter.h +++ b/src/widget/wvumeter.h @@ -40,7 +40,6 @@ class WVuMeter : public QGLWidget, public WBaseWidget { private: void initializeGL() override; void paintEvent(QPaintEvent * /*unused*/) override; - void setPeak(double parameter); // To make sure we render at least once even when we have no signal From 7dd6cd3ca0bce95db59c0fd204f084e227141d72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Tue, 13 Sep 2022 08:42:32 +0200 Subject: [PATCH 05/33] Shade: Put all VU meters in a fixed size parent widget --- res/skins/Shade/auxiliary.xml | 28 +++--- res/skins/Shade/microphone.xml | 28 +++--- res/skins/Shade/mixer_panel.xml | 153 +++++++++++++++++-------------- res/skins/Shade/preview_deck.xml | 30 +++--- res/skins/Shade/sampler.xml | 18 ++++ 5 files changed, 153 insertions(+), 104 deletions(-) diff --git a/res/skins/Shade/auxiliary.xml b/res/skins/Shade/auxiliary.xml index 521a9258232..432c1a17097 100644 --- a/res/skins/Shade/auxiliary.xml +++ b/res/skins/Shade/auxiliary.xml @@ -24,19 +24,23 @@ Visual - Volume level display ********************************************** --> - - auxiliary_VuMeter - skin:/style/volume_display_microphone_over.png - skin:/style/volume_display_microphone.png + 99,14 - 5 - 500 - 50 - 2 - - [Auxiliary],VuMeter - - + + + auxiliary_VuMeter + skin:/style/volume_display_microphone_over.png + skin:/style/volume_display_microphone.png + 5 + 500 + 50 + 2 + + [Auxiliary],VuMeter + + + + auxiliary_pregain diff --git a/res/skins/Shade/microphone.xml b/res/skins/Shade/microphone.xml index 5c1606433fc..c6a66887fda 100644 --- a/res/skins/Shade/microphone.xml +++ b/res/skins/Shade/microphone.xml @@ -25,19 +25,23 @@ Visual - Volume level display ********************************************** --> - - microphone_VuMeter - skin:/style/volume_display_microphone_over.png - skin:/style/volume_display_microphone.png + 82,14 - 5 - 500 - 50 - 2 - - [Microphone],VuMeter - - + + + microphone_VuMeter + skin:/style/volume_display_microphone_over.png + skin:/style/volume_display_microphone.png + 5 + 500 + 50 + 2 + + [Microphone],VuMeter + + + + - - channel_VuMeter - skin:/style/volume_display_over.png - skin:/style/volume_display.png + 107,76 - false - 5 - 500 - 50 - 2 - - [Channel1],VuMeter - - - - channel_VuMeter - skin:/style/volume_display_over.png - skin:/style/volume_display.png + + + channel_VuMeter + skin:/style/volume_display_over.png + skin:/style/volume_display.png + false + 5 + 500 + 50 + 2 + + [Channel1],VuMeter + + + + + 143,76 - false - 5 - 500 - 50 - 2 - - [Channel2],VuMeter - - - - - master_VuMeterL - skin:/style/volume_display_master_over.png - skin:/style/volume_display_master.png + + + channel_VuMeter + skin:/style/volume_display_over.png + skin:/style/volume_display.png + false + 5 + 500 + 50 + 2 + + [Channel2],VuMeter + + + + + 122,76 - 5 - 500 - 50 - 2 - - [Master],VuMeterL - - - - master_VuMeterR - skin:/style/volume_display_master_over.png - skin:/style/volume_display_master.png + + + master_VuMeterL + skin:/style/volume_display_master_over.png + skin:/style/volume_display_master.png + 5 + 500 + 50 + 2 + + [Master],VuMeterL + + + + + 128,76 - 5 - 500 - 50 - 2 - - [Master],VuMeterR - - - + + + master_VuMeterR + skin:/style/volume_display_master_over.png + skin:/style/volume_display_master.png + 5 + 500 + 50 + 2 + + [Master],VuMeterR + + + + 99,14 + 5f,35f auxiliary_VuMeter diff --git a/res/skins/Shade/microphone.xml b/res/skins/Shade/microphone.xml index c6a66887fda..1e572cef83e 100644 --- a/res/skins/Shade/microphone.xml +++ b/res/skins/Shade/microphone.xml @@ -27,6 +27,7 @@ --> 82,14 + 5f,35f microphone_VuMeter diff --git a/res/skins/Shade/mixer_panel.xml b/res/skins/Shade/mixer_panel.xml index 7de679cf29a..37343e38add 100644 --- a/res/skins/Shade/mixer_panel.xml +++ b/res/skins/Shade/mixer_panel.xml @@ -506,13 +506,11 @@ 113,28 + 26f,1f - 113,28 audio_latency_usage - 26,1 - 26,1 audio_latency/audio_latency_usage.png 26 500 @@ -799,6 +797,7 @@ --> 107,76 + 5f,81f channel_VuMeter @@ -817,6 +816,7 @@ 143,76 + 5f,81f channel_VuMeter @@ -835,6 +835,7 @@ 122,76 + 5f,81f master_VuMeterL @@ -852,6 +853,7 @@ 128,76 + 5f,81f master_VuMeterR From ab5443ef9f9ab74b131a4e1936b731c0a33d2703 Mon Sep 17 00:00:00 2001 From: m0dB <79429057+m0dB@users.noreply.github.com> Date: Thu, 15 Sep 2022 10:46:08 +0200 Subject: [PATCH 14/33] Update src/widget/wvumeter.cpp Co-authored-by: ronso0 --- src/widget/wvumeter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/widget/wvumeter.cpp b/src/widget/wvumeter.cpp index c87eda2c061..50609c10056 100644 --- a/src/widget/wvumeter.cpp +++ b/src/widget/wvumeter.cpp @@ -226,7 +226,8 @@ void WVuMeter::render(VSyncThread* /* UNUSED vSyncThread */) { } } - if (m_iPeakHoldSize > 0 && m_dPeakParameter > 0.0) { + if (m_iPeakHoldSize > 0 && m_dPeakParameter > 0.0 && + m_dPeakParameter > m_dParameter) { const double widgetPeakPosition = math_clamp( widgetWidth * m_dPeakParameter, 0.0, widgetWidth); const double pixmapPeakHoldSize = static_cast(m_iPeakHoldSize); From f94cc2a89b19a50f656b459d3acb512559562310 Mon Sep 17 00:00:00 2001 From: m0dB <79429057+m0dB@users.noreply.github.com> Date: Wed, 21 Sep 2022 17:47:26 +0200 Subject: [PATCH 15/33] Update src/skin/legacy/legacyskinparser.cpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Daniel Schürmann --- src/skin/legacy/legacyskinparser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/skin/legacy/legacyskinparser.cpp b/src/skin/legacy/legacyskinparser.cpp index 6bf91114412..8809b9eb85e 100644 --- a/src/skin/legacy/legacyskinparser.cpp +++ b/src/skin/legacy/legacyskinparser.cpp @@ -1296,7 +1296,7 @@ QWidget* LegacySkinParser::parseVuMeter(const QDomElement& node) { if (!waveformWidgetFactory->isOpenGlAvailable() && !waveformWidgetFactory->isOpenGlesAvailable()) { WLabel* dummy = new WLabel(m_pParent); - //: Shown when Spinny can not be displayed. Please keep \n unchanged + //: Shown when VuMeter can not be displayed. Please keep \n unchanged dummy->setText(tr("No OpenGL\nsupport.")); return dummy; } From 80717402d3f70126d7102e8e2689ebbe991f02b8 Mon Sep 17 00:00:00 2001 From: m0dB maarten de boer Date: Fri, 23 Sep 2022 02:20:56 +0200 Subject: [PATCH 16/33] postpone deletion of vumeters, solves segfault on finalize but would still crash on skin change (commented out) --- CMakeLists.txt | 1 + src/mixxx.cpp | 27 ++++++++++++++++++++++++ src/widget/vumeterpool.cpp | 43 ++++++++++++++++++++++++++++++++++++++ src/widget/vumeterpool.h | 21 +++++++++++++++++++ src/widget/wvumeter.cpp | 12 +++++++++-- src/widget/wvumeter.h | 1 + 6 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 src/widget/vumeterpool.cpp create mode 100644 src/widget/vumeterpool.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 33b704b69f3..2e7256b8e89 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -928,6 +928,7 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL src/widget/controlwidgetconnection.cpp src/widget/hexspinbox.cpp src/widget/paintable.cpp + src/widget/vumeterpool.cpp src/widget/wanalysislibrarytableview.cpp src/widget/wbasewidget.cpp src/widget/wbattery.cpp diff --git a/src/mixxx.cpp b/src/mixxx.cpp index 0863356eb03..bbb4a091593 100644 --- a/src/mixxx.cpp +++ b/src/mixxx.cpp @@ -82,6 +82,7 @@ #include "waveform/sharedglcontext.h" #include "waveform/visualsmanager.h" #include "waveform/waveformwidgetfactory.h" +#include "widget/vumeterpool.h" #include "widget/wmainmenubar.h" #ifdef __VINYLCONTROL__ @@ -491,6 +492,8 @@ void MixxxMainWindow::initialize(QApplication* pApp, const CmdlineArgs& args) { WaveformWidgetFactory::instance()->setConfig(pConfig); WaveformWidgetFactory::instance()->startVSync(m_pGuiTick, m_pVisualsManager); + VuMeterPool::createInstance(); + launchProgress(52); connect(this, @@ -753,6 +756,18 @@ void MixxxMainWindow::finalize() { // GUI depends on KeyboardEventFilter, PlayerManager, Library qDebug() << t.elapsed(false).debugMillisWithUnit() << "deleting skin"; + + // We are about to destroy all widgets, but we want to keep the WVuMeters + // alive; we would expect that after their destruction no more signal/slot + // connections would be activated for them, but for some reason (Qt bug?) + // this still happens when we delete the pTrackCollectionManager below. + // (Note: does not occur on macOS nor with recent Qt. It does occur with Qt + // 5.12.8 on linux). The following call changes the parent of all WVuMeters + // to the pool, so when their original parent is destroyed (indirectly when + // pSkin is destroyed), they aren't. They will eventually be destroyed when + // we destroy the VuMeterPool below. + VuMeterPool::instance()->adopt(); + m_pWidgetParent = nullptr; QPointer pSkin(centralWidget()); setCentralWidget(nullptr); @@ -860,6 +875,10 @@ void MixxxMainWindow::finalize() { qDebug() << t.elapsed(false).debugMillisWithUnit() << "detaching all track collections"; delete m_pTrackCollectionManager; + // We can now safely destroy the WVuMeters, by destroying the VuMeterPool that adopted + // them. + VuMeterPool::destroy(); + qDebug() << t.elapsed(false).debugMillisWithUnit() << "closing database connection(s)"; m_pDbConnectionPool->destroyThreadLocalConnection(); m_pDbConnectionPool.reset(); // should drop the last reference @@ -1546,6 +1565,9 @@ void MixxxMainWindow::rebootMixxxView() { // supports since the controls from the previous skin will be left over. m_pMenuBar->onNewSkinAboutToLoad(); + // postpone deletion of vumeters, see MixxxMainWindow::finalize() for detail + VuMeterPool::instance()->adopt(); + if (m_pWidgetParent) { m_pWidgetParent->hide(); WaveformWidgetFactory::instance()->destroyWidgets(); @@ -1605,6 +1627,11 @@ void MixxxMainWindow::rebootMixxxView() { qDebug() << "rebootMixxxView DONE"; emit skinLoaded(); + + // TODO @m0dB + // if we destroy the vu meters now, they will still be called after their destruction, resulting in a + // crash.... + // VuMeterPool::instance()->destroyAdopted(); } bool MixxxMainWindow::eventFilter(QObject* obj, QEvent* event) { diff --git a/src/widget/vumeterpool.cpp b/src/widget/vumeterpool.cpp new file mode 100644 index 00000000000..9e9dfd51048 --- /dev/null +++ b/src/widget/vumeterpool.cpp @@ -0,0 +1,43 @@ +#include "widget/vumeterpool.h" + +#include + +#include "widget/wvumeter.h" + +VuMeterPool::VuMeterPool() { +} + +VuMeterPool::~VuMeterPool() { + destroyAdopted(); + assert(m_pVuMeters.isEmpty()); +} + +void VuMeterPool::destroyAdopted() { + auto it = m_pVuMeters.begin(); + while (it != m_pVuMeters.end()) { + auto pVuMeter = *it; + if (pVuMeter->parent()) { + it++; + } else { + it = m_pVuMeters.erase(it); + delete pVuMeter; + } + } +} + +void VuMeterPool::add(WVuMeter* pVuMeter) { + m_pVuMeters.append(pVuMeter); +} + +void VuMeterPool::remove(WVuMeter* pVuMeter) { + m_pVuMeters.removeOne(pVuMeter); +} + +void VuMeterPool::adopt() { + // Remove the parent of the vumeters,, but we keep their pointers, + // effectively "adopting" them. + for (auto pVuMeter : m_pVuMeters) { + pVuMeter->disconnect(); + pVuMeter->setParent(Q_NULLPTR); + } +} diff --git a/src/widget/vumeterpool.h b/src/widget/vumeterpool.h new file mode 100644 index 00000000000..42fc1499546 --- /dev/null +++ b/src/widget/vumeterpool.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#include "util/singleton.h" + +class WVuMeter; + +class VuMeterPool : public Singleton { + QList m_pVuMeters; + + public: + VuMeterPool(); + ~VuMeterPool(); + + void add(WVuMeter* pVuMeter); + void remove(WVuMeter* pVuMeter); + void adopt(); + void destroyAdopted(); +}; diff --git a/src/widget/wvumeter.cpp b/src/widget/wvumeter.cpp index 50609c10056..0765d0d2a38 100644 --- a/src/widget/wvumeter.cpp +++ b/src/widget/wvumeter.cpp @@ -12,6 +12,7 @@ #include "util/widgethelper.h" #include "waveform/sharedglcontext.h" #include "waveform/vsyncthread.h" +#include "widget/vumeterpool.h" #include "widget/wpixmapstore.h" #define DEFAULT_FALLTIME 20 @@ -41,6 +42,12 @@ WVuMeter::WVuMeter(QWidget* parent) setAutoFillBackground(false); setAutoBufferSwap(false); m_timer.start(); + + VuMeterPool::instance()->add(this); +} + +WVuMeter::~WVuMeter() { + VuMeterPool::instance()->remove(this); } void WVuMeter::setup(const QDomNode& node, const SkinContext& context) { @@ -185,7 +192,7 @@ void WVuMeter::render(VSyncThread* /* UNUSED vSyncThread */) { return; } - if (!isValid() || !isVisible()) { + if (!isValid() || !isVisible() || QWidget::window()->windowState() == Qt::WindowMinimized) { return; } @@ -306,7 +313,8 @@ void WVuMeter::render(VSyncThread* /* UNUSED vSyncThread */) { } void WVuMeter::swap() { - if (!isValid() || !isVisible() || !m_bSwapNeeded) { + if (!m_bSwapNeeded || !isValid() || !isVisible() || + QWidget::window()->windowState() == Qt::WindowMinimized) { return; } auto* window = windowHandle(); diff --git a/src/widget/wvumeter.h b/src/widget/wvumeter.h index 3bbfa0b312e..4afaaf6dcf6 100644 --- a/src/widget/wvumeter.h +++ b/src/widget/wvumeter.h @@ -17,6 +17,7 @@ class WVuMeter : public QGLWidget, public WBaseWidget { Q_OBJECT public: explicit WVuMeter(QWidget *parent=nullptr); + ~WVuMeter(); void setup(const QDomNode& node, const SkinContext& context); void setPixmapBackground( From f3f4ff881177349bd981257bb20714cb5092d252 Mon Sep 17 00:00:00 2001 From: m0dB maarten de boer Date: Fri, 23 Sep 2022 09:29:24 +0200 Subject: [PATCH 17/33] Revert "postpone deletion of vumeters, solves segfault on finalize but would still crash on skin change (commented out)" This reverts commit 80717402d3f70126d7102e8e2689ebbe991f02b8. --- CMakeLists.txt | 1 - src/mixxx.cpp | 27 ------------------------ src/widget/vumeterpool.cpp | 43 -------------------------------------- src/widget/vumeterpool.h | 21 ------------------- src/widget/wvumeter.cpp | 12 ++--------- src/widget/wvumeter.h | 1 - 6 files changed, 2 insertions(+), 103 deletions(-) delete mode 100644 src/widget/vumeterpool.cpp delete mode 100644 src/widget/vumeterpool.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e7256b8e89..33b704b69f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -928,7 +928,6 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL src/widget/controlwidgetconnection.cpp src/widget/hexspinbox.cpp src/widget/paintable.cpp - src/widget/vumeterpool.cpp src/widget/wanalysislibrarytableview.cpp src/widget/wbasewidget.cpp src/widget/wbattery.cpp diff --git a/src/mixxx.cpp b/src/mixxx.cpp index bbb4a091593..0863356eb03 100644 --- a/src/mixxx.cpp +++ b/src/mixxx.cpp @@ -82,7 +82,6 @@ #include "waveform/sharedglcontext.h" #include "waveform/visualsmanager.h" #include "waveform/waveformwidgetfactory.h" -#include "widget/vumeterpool.h" #include "widget/wmainmenubar.h" #ifdef __VINYLCONTROL__ @@ -492,8 +491,6 @@ void MixxxMainWindow::initialize(QApplication* pApp, const CmdlineArgs& args) { WaveformWidgetFactory::instance()->setConfig(pConfig); WaveformWidgetFactory::instance()->startVSync(m_pGuiTick, m_pVisualsManager); - VuMeterPool::createInstance(); - launchProgress(52); connect(this, @@ -756,18 +753,6 @@ void MixxxMainWindow::finalize() { // GUI depends on KeyboardEventFilter, PlayerManager, Library qDebug() << t.elapsed(false).debugMillisWithUnit() << "deleting skin"; - - // We are about to destroy all widgets, but we want to keep the WVuMeters - // alive; we would expect that after their destruction no more signal/slot - // connections would be activated for them, but for some reason (Qt bug?) - // this still happens when we delete the pTrackCollectionManager below. - // (Note: does not occur on macOS nor with recent Qt. It does occur with Qt - // 5.12.8 on linux). The following call changes the parent of all WVuMeters - // to the pool, so when their original parent is destroyed (indirectly when - // pSkin is destroyed), they aren't. They will eventually be destroyed when - // we destroy the VuMeterPool below. - VuMeterPool::instance()->adopt(); - m_pWidgetParent = nullptr; QPointer pSkin(centralWidget()); setCentralWidget(nullptr); @@ -875,10 +860,6 @@ void MixxxMainWindow::finalize() { qDebug() << t.elapsed(false).debugMillisWithUnit() << "detaching all track collections"; delete m_pTrackCollectionManager; - // We can now safely destroy the WVuMeters, by destroying the VuMeterPool that adopted - // them. - VuMeterPool::destroy(); - qDebug() << t.elapsed(false).debugMillisWithUnit() << "closing database connection(s)"; m_pDbConnectionPool->destroyThreadLocalConnection(); m_pDbConnectionPool.reset(); // should drop the last reference @@ -1565,9 +1546,6 @@ void MixxxMainWindow::rebootMixxxView() { // supports since the controls from the previous skin will be left over. m_pMenuBar->onNewSkinAboutToLoad(); - // postpone deletion of vumeters, see MixxxMainWindow::finalize() for detail - VuMeterPool::instance()->adopt(); - if (m_pWidgetParent) { m_pWidgetParent->hide(); WaveformWidgetFactory::instance()->destroyWidgets(); @@ -1627,11 +1605,6 @@ void MixxxMainWindow::rebootMixxxView() { qDebug() << "rebootMixxxView DONE"; emit skinLoaded(); - - // TODO @m0dB - // if we destroy the vu meters now, they will still be called after their destruction, resulting in a - // crash.... - // VuMeterPool::instance()->destroyAdopted(); } bool MixxxMainWindow::eventFilter(QObject* obj, QEvent* event) { diff --git a/src/widget/vumeterpool.cpp b/src/widget/vumeterpool.cpp deleted file mode 100644 index 9e9dfd51048..00000000000 --- a/src/widget/vumeterpool.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "widget/vumeterpool.h" - -#include - -#include "widget/wvumeter.h" - -VuMeterPool::VuMeterPool() { -} - -VuMeterPool::~VuMeterPool() { - destroyAdopted(); - assert(m_pVuMeters.isEmpty()); -} - -void VuMeterPool::destroyAdopted() { - auto it = m_pVuMeters.begin(); - while (it != m_pVuMeters.end()) { - auto pVuMeter = *it; - if (pVuMeter->parent()) { - it++; - } else { - it = m_pVuMeters.erase(it); - delete pVuMeter; - } - } -} - -void VuMeterPool::add(WVuMeter* pVuMeter) { - m_pVuMeters.append(pVuMeter); -} - -void VuMeterPool::remove(WVuMeter* pVuMeter) { - m_pVuMeters.removeOne(pVuMeter); -} - -void VuMeterPool::adopt() { - // Remove the parent of the vumeters,, but we keep their pointers, - // effectively "adopting" them. - for (auto pVuMeter : m_pVuMeters) { - pVuMeter->disconnect(); - pVuMeter->setParent(Q_NULLPTR); - } -} diff --git a/src/widget/vumeterpool.h b/src/widget/vumeterpool.h deleted file mode 100644 index 42fc1499546..00000000000 --- a/src/widget/vumeterpool.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include - -#include "util/singleton.h" - -class WVuMeter; - -class VuMeterPool : public Singleton { - QList m_pVuMeters; - - public: - VuMeterPool(); - ~VuMeterPool(); - - void add(WVuMeter* pVuMeter); - void remove(WVuMeter* pVuMeter); - void adopt(); - void destroyAdopted(); -}; diff --git a/src/widget/wvumeter.cpp b/src/widget/wvumeter.cpp index 0765d0d2a38..50609c10056 100644 --- a/src/widget/wvumeter.cpp +++ b/src/widget/wvumeter.cpp @@ -12,7 +12,6 @@ #include "util/widgethelper.h" #include "waveform/sharedglcontext.h" #include "waveform/vsyncthread.h" -#include "widget/vumeterpool.h" #include "widget/wpixmapstore.h" #define DEFAULT_FALLTIME 20 @@ -42,12 +41,6 @@ WVuMeter::WVuMeter(QWidget* parent) setAutoFillBackground(false); setAutoBufferSwap(false); m_timer.start(); - - VuMeterPool::instance()->add(this); -} - -WVuMeter::~WVuMeter() { - VuMeterPool::instance()->remove(this); } void WVuMeter::setup(const QDomNode& node, const SkinContext& context) { @@ -192,7 +185,7 @@ void WVuMeter::render(VSyncThread* /* UNUSED vSyncThread */) { return; } - if (!isValid() || !isVisible() || QWidget::window()->windowState() == Qt::WindowMinimized) { + if (!isValid() || !isVisible()) { return; } @@ -313,8 +306,7 @@ void WVuMeter::render(VSyncThread* /* UNUSED vSyncThread */) { } void WVuMeter::swap() { - if (!m_bSwapNeeded || !isValid() || !isVisible() || - QWidget::window()->windowState() == Qt::WindowMinimized) { + if (!isValid() || !isVisible() || !m_bSwapNeeded) { return; } auto* window = windowHandle(); diff --git a/src/widget/wvumeter.h b/src/widget/wvumeter.h index 4afaaf6dcf6..3bbfa0b312e 100644 --- a/src/widget/wvumeter.h +++ b/src/widget/wvumeter.h @@ -17,7 +17,6 @@ class WVuMeter : public QGLWidget, public WBaseWidget { Q_OBJECT public: explicit WVuMeter(QWidget *parent=nullptr); - ~WVuMeter(); void setup(const QDomNode& node, const SkinContext& context); void setPixmapBackground( From 6bab4d6d2e1d5a230f5f37dfe864e7943f2456ac Mon Sep 17 00:00:00 2001 From: m0dB Date: Fri, 23 Sep 2022 15:01:10 +0200 Subject: [PATCH 18/33] renamed QGLWidget derived vu-meter to WVuMeterGL --- CMakeLists.txt | 2 +- src/skin/legacy/legacyskinparser.cpp | 8 ++--- src/waveform/waveformwidgetfactory.cpp | 2 +- src/widget/{wvumeter.cpp => wvumetergl.cpp} | 33 +++++++++++---------- src/widget/{wvumeter.h => wvumetergl.h} | 4 +-- 5 files changed, 25 insertions(+), 24 deletions(-) rename src/widget/{wvumeter.cpp => wvumetergl.cpp} (92%) rename src/widget/{wvumeter.h => wvumetergl.h} (94%) diff --git a/CMakeLists.txt b/CMakeLists.txt index e39d72cc133..86cdb42bec7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1001,7 +1001,7 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL src/widget/wtracktableviewheader.cpp src/widget/wtracktext.cpp src/widget/wtrackwidgetgroup.cpp - src/widget/wvumeter.cpp + src/widget/wvumetergl.cpp src/widget/wwaveformviewer.cpp src/widget/wwidget.cpp src/widget/wwidgetgroup.cpp diff --git a/src/skin/legacy/legacyskinparser.cpp b/src/skin/legacy/legacyskinparser.cpp index 8809b9eb85e..c2ad874a4eb 100644 --- a/src/skin/legacy/legacyskinparser.cpp +++ b/src/skin/legacy/legacyskinparser.cpp @@ -78,7 +78,7 @@ #include "widget/wtrackproperty.h" #include "widget/wtracktext.h" #include "widget/wtrackwidgetgroup.h" -#include "widget/wvumeter.h" +#include "widget/wvumetergl.h" #include "widget/wwaveformviewer.h" #include "widget/wwidget.h" #include "widget/wwidgetgroup.h" @@ -1300,17 +1300,17 @@ QWidget* LegacySkinParser::parseVuMeter(const QDomElement& node) { dummy->setText(tr("No OpenGL\nsupport.")); return dummy; } - WVuMeter* vuMeter = new WVuMeter(m_pParent); + WVuMeterGL* vuMeter = new WVuMeterGL(m_pParent); commonWidgetSetup(node, vuMeter); connect(waveformWidgetFactory, &WaveformWidgetFactory::renderVuMeters, vuMeter, - &WVuMeter::render); + &WVuMeterGL::render); connect(waveformWidgetFactory, &WaveformWidgetFactory::swapVuMeters, vuMeter, - &WVuMeter::swap); + &WVuMeterGL::swap); vuMeter->setup(node, *m_pContext); vuMeter->installEventFilter(m_pKeyboard); diff --git a/src/waveform/waveformwidgetfactory.cpp b/src/waveform/waveformwidgetfactory.cpp index d82cc4758a2..d5c99258895 100644 --- a/src/waveform/waveformwidgetfactory.cpp +++ b/src/waveform/waveformwidgetfactory.cpp @@ -35,7 +35,7 @@ #include "waveform/widgets/rgbwaveformwidget.h" #include "waveform/widgets/softwarewaveformwidget.h" #include "waveform/widgets/waveformwidgetabstract.h" -#include "widget/wvumeter.h" +#include "widget/wvumetergl.h" #include "widget/wwaveformviewer.h" namespace { diff --git a/src/widget/wvumeter.cpp b/src/widget/wvumetergl.cpp similarity index 92% rename from src/widget/wvumeter.cpp rename to src/widget/wvumetergl.cpp index 50609c10056..59d957d479b 100644 --- a/src/widget/wvumeter.cpp +++ b/src/widget/wvumetergl.cpp @@ -1,4 +1,4 @@ -#include "widget/wvumeter.h" +#include "widget/wvumetergl.h" #include #include @@ -6,7 +6,7 @@ #include #include -#include "moc_wvumeter.cpp" +#include "moc_wvumetergl.cpp" #include "util/math.h" #include "util/timer.h" #include "util/widgethelper.h" @@ -19,7 +19,7 @@ #define DEFAULT_HOLDTIME 400 #define DEFAULT_HOLDSIZE 5 -WVuMeter::WVuMeter(QWidget* parent) +WVuMeterGL::WVuMeterGL(QWidget* parent) : QGLWidget(parent, SharedGLContext::getWidget()), WBaseWidget(this), m_bHasRendered(false), @@ -40,10 +40,11 @@ WVuMeter::WVuMeter(QWidget* parent) setAutoFillBackground(false); setAutoBufferSwap(false); + m_timer.start(); } -void WVuMeter::setup(const QDomNode& node, const SkinContext& context) { +void WVuMeterGL::setup(const QDomNode& node, const SkinContext& context) { // Set pixmaps bool bHorizontal = false; (void)context.hasNodeSelectBool(node, "Horizontal", &bHorizontal); @@ -90,7 +91,7 @@ void WVuMeter::setup(const QDomNode& node, const SkinContext& context) { setFocusPolicy(Qt::NoFocus); } -void WVuMeter::setPixmapBackground( +void WVuMeterGL::setPixmapBackground( const PixmapSource& source, Paintable::DrawMode mode, double scaleFactor) { @@ -103,13 +104,13 @@ void WVuMeter::setPixmapBackground( } } -void WVuMeter::setPixmaps(const PixmapSource& source, +void WVuMeterGL::setPixmaps(const PixmapSource& source, bool bHorizontal, Paintable::DrawMode mode, double scaleFactor) { m_pPixmapVu = WPixmapStore::getPaintable(source, mode, scaleFactor); if (m_pPixmapVu.isNull()) { - qDebug() << "WVuMeter: Error loading vu pixmap" << source.getPath(); + qDebug() << "WVuMeterGL: Error loading vu pixmap" << source.getPath(); } else { m_bHorizontal = bHorizontal; if (m_bHorizontal) { @@ -120,7 +121,7 @@ void WVuMeter::setPixmaps(const PixmapSource& source, } } -void WVuMeter::onConnectedControlChanged(double dParameter, double dValue) { +void WVuMeterGL::onConnectedControlChanged(double dParameter, double dValue) { Q_UNUSED(dValue); m_dParameter = math_clamp(dParameter, 0.0, 1.0); @@ -134,14 +135,14 @@ void WVuMeter::onConnectedControlChanged(double dParameter, double dValue) { updateState(m_timer.restart()); } -void WVuMeter::setPeak(double parameter) { +void WVuMeterGL::setPeak(double parameter) { if (parameter > m_dPeakParameter) { m_dPeakParameter = parameter; m_dPeakHoldCountdownMs = m_iPeakHoldTime; } } -void WVuMeter::updateState(mixxx::Duration elapsed) { +void WVuMeterGL::updateState(mixxx::Duration elapsed) { double msecsElapsed = elapsed.toDoubleMillis(); // If we're holding at a peak then don't update anything m_dPeakHoldCountdownMs -= msecsElapsed; @@ -160,7 +161,7 @@ void WVuMeter::updateState(mixxx::Duration elapsed) { m_dPeakParameter = math_clamp(m_dPeakParameter, 0.0, 1.0); } -void WVuMeter::paintEvent(QPaintEvent* e) { +void WVuMeterGL::paintEvent(QPaintEvent* e) { Q_UNUSED(e); // Force a rerender when render is called from the vsync thread, e.g. to // git rid artifacts after hiding and showing the mixer or incomplete @@ -168,17 +169,17 @@ void WVuMeter::paintEvent(QPaintEvent* e) { m_bHasRendered = false; } -void WVuMeter::showEvent(QShowEvent* e) { +void WVuMeterGL::showEvent(QShowEvent* e) { Q_UNUSED(e); // Find the base color recursively in parent widget. m_qBgColor = mixxx::widgethelper::findBaseColor(this); } -void WVuMeter::render(VSyncThread* /* UNUSED vSyncThread */) { +void WVuMeterGL::render(VSyncThread* /* UNUSED vSyncThread */) { // TODO (@m0dB) consider using timing information from the vSyncThread, - // instead of having an m_timer in each WVuMeter instance. + // instead of having an m_timer in each WVuMeterGL instance. - ScopedTimer t("WVuMeter::render"); + ScopedTimer t("WVuMeterGL::render"); if (m_bHasRendered && m_dParameter == m_dLastParameter && m_dPeakParameter == m_dLastPeakParameter) { @@ -305,7 +306,7 @@ void WVuMeter::render(VSyncThread* /* UNUSED vSyncThread */) { m_bSwapNeeded = true; } -void WVuMeter::swap() { +void WVuMeterGL::swap() { if (!isValid() || !isVisible() || !m_bSwapNeeded) { return; } diff --git a/src/widget/wvumeter.h b/src/widget/wvumetergl.h similarity index 94% rename from src/widget/wvumeter.h rename to src/widget/wvumetergl.h index 3bbfa0b312e..07fca8d35cd 100644 --- a/src/widget/wvumeter.h +++ b/src/widget/wvumetergl.h @@ -13,10 +13,10 @@ class VSyncThread; -class WVuMeter : public QGLWidget, public WBaseWidget { +class WVuMeterGL : public QGLWidget, public WBaseWidget { Q_OBJECT public: - explicit WVuMeter(QWidget *parent=nullptr); + explicit WVuMeterGL(QWidget* parent = nullptr); void setup(const QDomNode& node, const SkinContext& context); void setPixmapBackground( From 0a94d094791628b4fba97069dfe74e9a08a09edf Mon Sep 17 00:00:00 2001 From: m0dB Date: Fri, 23 Sep 2022 15:07:34 +0200 Subject: [PATCH 19/33] added original wvumeter --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 86cdb42bec7..5187dfad6ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1001,6 +1001,7 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL src/widget/wtracktableviewheader.cpp src/widget/wtracktext.cpp src/widget/wtrackwidgetgroup.cpp + src/widget/wvumeter.cpp src/widget/wvumetergl.cpp src/widget/wwaveformviewer.cpp src/widget/wwidget.cpp From bc38a88da8fe11ce0a3a5ded1f8d88d76d947e8a Mon Sep 17 00:00:00 2001 From: m0dB Date: Fri, 23 Sep 2022 15:14:07 +0200 Subject: [PATCH 20/33] added original wvumeter from 2.3 --- src/widget/wvumeter.cpp | 246 ++++++++++++++++++++++++++++++++++++++++ src/widget/wvumeter.h | 70 ++++++++++++ 2 files changed, 316 insertions(+) create mode 100644 src/widget/wvumeter.cpp create mode 100644 src/widget/wvumeter.h diff --git a/src/widget/wvumeter.cpp b/src/widget/wvumeter.cpp new file mode 100644 index 00000000000..13f29074a24 --- /dev/null +++ b/src/widget/wvumeter.cpp @@ -0,0 +1,246 @@ +#include "widget/wvumeter.h" + +#include +#include +#include +#include +#include + +#include "moc_wvumeter.cpp" +#include "util/math.h" +#include "util/timer.h" +#include "widget/wpixmapstore.h" + +#define DEFAULT_FALLTIME 20 +#define DEFAULT_FALLSTEP 1 +#define DEFAULT_HOLDTIME 400 +#define DEFAULT_HOLDSIZE 5 + +WVuMeter::WVuMeter(QWidget* parent) + : WWidget(parent), + m_dParameter(0), + m_dPeakParameter(0), + m_dLastParameter(0), + m_dLastPeakParameter(0), + m_iPixmapLength(0), + m_bHorizontal(false), + m_iPeakHoldSize(0), + m_iPeakFallStep(0), + m_iPeakHoldTime(0), + m_iPeakFallTime(0), + m_dPeakHoldCountdownMs(0) { + m_timer.start(); +} + +void WVuMeter::setup(const QDomNode& node, const SkinContext& context) { + // Set pixmaps + bool bHorizontal = false; + (void)context.hasNodeSelectBool(node, "Horizontal", &bHorizontal); + + // Set background pixmap if available + QDomElement backPathNode = context.selectElement(node, "PathBack"); + if (!backPathNode.isNull()) { + // The implicit default in <1.12.0 was FIXED so we keep it for backwards + // compatibility. + setPixmapBackground( + context.getPixmapSource(backPathNode), + context.selectScaleMode(backPathNode, Paintable::FIXED), + context.getScaleFactor()); + } + + QDomElement vuNode = context.selectElement(node, "PathVu"); + // The implicit default in <1.12.0 was FIXED so we keep it for backwards + // compatibility. + setPixmaps(context.getPixmapSource(vuNode), + bHorizontal, + context.selectScaleMode(vuNode, Paintable::FIXED), + context.getScaleFactor()); + + m_iPeakHoldSize = context.selectInt(node, "PeakHoldSize"); + if (m_iPeakHoldSize < 0 || m_iPeakHoldSize > 100) { + m_iPeakHoldSize = DEFAULT_HOLDSIZE; + } + + m_iPeakFallStep = context.selectInt(node, "PeakFallStep"); + if (m_iPeakFallStep < 1 || m_iPeakFallStep > 1000) { + m_iPeakFallStep = DEFAULT_FALLSTEP; + } + + m_iPeakHoldTime = context.selectInt(node, "PeakHoldTime"); + if (m_iPeakHoldTime < 1 || m_iPeakHoldTime > 3000) { + m_iPeakHoldTime = DEFAULT_HOLDTIME; + } + + m_iPeakFallTime = context.selectInt(node, "PeakFallTime"); + if (m_iPeakFallTime < 1 || m_iPeakFallTime > 1000) { + m_iPeakFallTime = DEFAULT_FALLTIME; + } + + setFocusPolicy(Qt::NoFocus); +} + +void WVuMeter::setPixmapBackground( + const PixmapSource& source, + Paintable::DrawMode mode, + double scaleFactor) { + m_pPixmapBack = WPixmapStore::getPaintable(source, mode, scaleFactor); + if (m_pPixmapBack.isNull() || m_pPixmapBack->isNull()) { + qDebug() << metaObject()->className() + << "Error loading background pixmap:" << source.getPath(); + } else if (mode == Paintable::FIXED) { + setFixedSize(m_pPixmapBack->size()); + } +} + +void WVuMeter::setPixmaps(const PixmapSource& source, + bool bHorizontal, + Paintable::DrawMode mode, + double scaleFactor) { + m_pPixmapVu = WPixmapStore::getPaintable(source, mode, scaleFactor); + if (m_pPixmapVu.isNull() || m_pPixmapVu->isNull()) { + qDebug() << "WVuMeter: Error loading vu pixmap" << source.getPath(); + } else { + m_bHorizontal = bHorizontal; + if (m_bHorizontal) { + m_iPixmapLength = m_pPixmapVu->width(); + } else { + m_iPixmapLength = m_pPixmapVu->height(); + } + } +} + +void WVuMeter::onConnectedControlChanged(double dParameter, double dValue) { + Q_UNUSED(dValue); + m_dParameter = math_clamp(dParameter, 0.0, 1.0); + + if (dParameter > 0.0) { + setPeak(dParameter); + } else { + // A 0.0 value is very unlikely except when the VU Meter is disabled + m_dPeakParameter = 0; + } + + updateState(m_timer.restart()); +} + +void WVuMeter::setPeak(double parameter) { + if (parameter > m_dPeakParameter) { + m_dPeakParameter = parameter; + m_dPeakHoldCountdownMs = m_iPeakHoldTime; + } +} + +void WVuMeter::updateState(mixxx::Duration elapsed) { + double msecsElapsed = elapsed.toDoubleMillis(); + // If we're holding at a peak then don't update anything + m_dPeakHoldCountdownMs -= msecsElapsed; + if (m_dPeakHoldCountdownMs > 0) { + return; + } else { + m_dPeakHoldCountdownMs = 0; + } + + // Otherwise, decrement the peak position by the fall step size times the + // milliseconds elapsed over the fall time multiplier. The peak will fall + // FallStep times (out of 128 steps) every FallTime milliseconds. + m_dPeakParameter -= static_cast(m_iPeakFallStep) * + msecsElapsed / + static_cast(m_iPeakFallTime * m_iPixmapLength); + m_dPeakParameter = math_clamp(m_dPeakParameter, 0.0, 1.0); +} + +void WVuMeter::maybeUpdate() { + if (m_dParameter != m_dLastParameter || m_dPeakParameter != m_dLastPeakParameter) { + repaint(); + } +} + +void WVuMeter::paintEvent(QPaintEvent* /*unused*/) { + ScopedTimer t("WVuMeter::paintEvent"); + + QStyleOption option; + option.initFrom(this); + QStylePainter p(this); + p.drawPrimitive(QStyle::PE_Widget, option); + + if (!m_pPixmapBack.isNull() && !m_pPixmapBack->isNull()) { + // Draw background. DrawMode takes care of whether to stretch or not. + m_pPixmapBack->draw(rect(), &p); + } + + if (!m_pPixmapVu.isNull() && !m_pPixmapVu->isNull()) { + const double widgetWidth = width(); + const double widgetHeight = height(); + const double pixmapWidth = m_pPixmapVu->width(); + const double pixmapHeight = m_pPixmapVu->height(); + + // Draw (part of) vu + if (m_bHorizontal) { + const double widgetPosition = math_clamp(widgetWidth * m_dParameter, + 0.0, + widgetWidth); + QRectF targetRect(0, 0, widgetPosition, widgetHeight); + + const double pixmapPosition = math_clamp(pixmapWidth * m_dParameter, + 0.0, + pixmapWidth); + QRectF sourceRect(0, 0, pixmapPosition, m_pPixmapVu->height()); + m_pPixmapVu->draw(targetRect, &p, sourceRect); + + if (m_iPeakHoldSize > 0 && m_dPeakParameter > 0.0) { + const double widgetPeakPosition = math_clamp( + widgetWidth * m_dPeakParameter, 0.0, widgetWidth); + const double widgetPeakHoldSize = widgetWidth * + static_cast(m_iPeakHoldSize) / pixmapWidth; + + const double pixmapPeakPosition = math_clamp( + pixmapWidth * m_dPeakParameter, 0.0, pixmapWidth); + const double pixmapPeakHoldSize = m_iPeakHoldSize; + + targetRect = QRectF(widgetPeakPosition - widgetPeakHoldSize, + 0, + widgetPeakHoldSize, + widgetHeight); + sourceRect = QRectF(pixmapPeakPosition - pixmapPeakHoldSize, + 0, + pixmapPeakHoldSize, + pixmapHeight); + m_pPixmapVu->draw(targetRect, &p, sourceRect); + } + } else { + const double widgetPosition = math_clamp(widgetHeight * m_dParameter, + 0.0, + widgetHeight); + QRectF targetRect(0, widgetHeight - widgetPosition, widgetWidth, widgetPosition); + + const double pixmapPosition = math_clamp(pixmapHeight * m_dParameter, + 0.0, + pixmapHeight); + QRectF sourceRect(0, pixmapHeight - pixmapPosition, pixmapWidth, pixmapPosition); + m_pPixmapVu->draw(targetRect, &p, sourceRect); + + if (m_iPeakHoldSize > 0 && m_dPeakParameter > 0.0) { + const double widgetPeakPosition = math_clamp( + widgetHeight * m_dPeakParameter, 0.0, widgetHeight); + const double widgetPeakHoldSize = widgetHeight * + static_cast(m_iPeakHoldSize) / pixmapHeight; + + const double pixmapPeakPosition = math_clamp( + pixmapHeight * m_dPeakParameter, 0.0, pixmapHeight); + const double pixmapPeakHoldSize = m_iPeakHoldSize; + + targetRect = QRectF(0, + widgetHeight - widgetPeakPosition, + widgetWidth, + widgetPeakHoldSize); + sourceRect = QRectF(0, + pixmapHeight - pixmapPeakPosition, + pixmapWidth, + pixmapPeakHoldSize); + m_pPixmapVu->draw(targetRect, &p, sourceRect); + } + } + } + m_dLastParameter = m_dParameter; + m_dLastPeakParameter = m_dPeakParameter; +} diff --git a/src/widget/wvumeter.h b/src/widget/wvumeter.h new file mode 100644 index 00000000000..ee7911145f8 --- /dev/null +++ b/src/widget/wvumeter.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "skin/legacy/skincontext.h" +#include "util/performancetimer.h" +#include "widget/wpixmapstore.h" +#include "widget/wwidget.h" + +class WVuMeter : public WWidget { + Q_OBJECT + public: + explicit WVuMeter(QWidget* parent = nullptr); + + void setup(const QDomNode& node, const SkinContext& context); + void setPixmapBackground( + const PixmapSource& source, + Paintable::DrawMode mode, + double scaleFactor); + void setPixmaps( + const PixmapSource& source, + bool bHorizontal, + Paintable::DrawMode mode, + double scaleFactor); + void onConnectedControlChanged(double dParameter, double dValue) override; + + public slots: + void maybeUpdate(); + + protected slots: + void updateState(mixxx::Duration elapsed); + + private: + void paintEvent(QPaintEvent* /*unused*/) override; + void setPeak(double parameter); + + // Current parameter and peak parameter. + double m_dParameter; + double m_dPeakParameter; + + // The last parameter and peak parameter values at the time of + // rendering. Used to check whether the widget state has changed since the + // last render in maybeUpdate. + double m_dLastParameter; + double m_dLastPeakParameter; + + // Length of the VU-meter pixmap along the relevant axis. + int m_iPixmapLength; + + // Associated pixmaps + PaintablePointer m_pPixmapBack; + PaintablePointer m_pPixmapVu; + + // True if it's a horizontal vu meter + bool m_bHorizontal; + + int m_iPeakHoldSize; + int m_iPeakFallStep; + int m_iPeakHoldTime; + int m_iPeakFallTime; + + // The peak hold time remaining in milliseconds. + double m_dPeakHoldCountdownMs; + + PerformanceTimer m_timer; +}; From a721a09b19b0f7a6b60ecbb7dd594180009e15cc Mon Sep 17 00:00:00 2001 From: m0dB Date: Fri, 23 Sep 2022 15:36:03 +0200 Subject: [PATCH 21/33] handle both WVuMeter and WVuMeterGL is WaveformWidgetFactory --- src/waveform/waveformwidgetfactory.cpp | 18 ++++++++++++++++++ src/waveform/waveformwidgetfactory.h | 2 ++ 2 files changed, 20 insertions(+) diff --git a/src/waveform/waveformwidgetfactory.cpp b/src/waveform/waveformwidgetfactory.cpp index d5c99258895..f63df192043 100644 --- a/src/waveform/waveformwidgetfactory.cpp +++ b/src/waveform/waveformwidgetfactory.cpp @@ -35,6 +35,7 @@ #include "waveform/widgets/rgbwaveformwidget.h" #include "waveform/widgets/softwarewaveformwidget.h" #include "waveform/widgets/waveformwidgetabstract.h" +#include "widget/wvumeter.h" #include "widget/wvumetergl.h" #include "widget/wwaveformviewer.h" @@ -353,6 +354,19 @@ void WaveformWidgetFactory::destroyWidgets() { m_waveformWidgetHolders.clear(); } +void WaveformWidgetFactory::addTimerListener(WVuMeter* pWidget) { + // Note that we are either using WVuMeter or WVuMeterGL. WVuMeterGLs are connected to renderVuMeters and swapVuMeters instead. + + // Do not hold the pointer to of timer listeners since they may be deleted. + // We don't activate update() or repaint() directly so listener widgets + // can decide whether to paint or not. + connect(this, + &WaveformWidgetFactory::waveformUpdateTick, + pWidget, + &WVuMeter::maybeUpdate, + Qt::DirectConnection); +} + void WaveformWidgetFactory::slotSkinLoaded() { setWidgetTypeFromConfig(); #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) && defined __WINDOWS__ @@ -662,6 +676,8 @@ void WaveformWidgetFactory::render() { // WSpinnys are also double-buffered QGLWidgets, like all the waveform // renderers. Render all the WSpinny widgets now. emit renderSpinnies(m_vsyncThread); + // Same for WVuMeterGL. Note that we are either using WVuMeter or WVuMeterGL. + // If we are using WVuMeter, this does nothing emit renderVuMeters(m_vsyncThread); // Notify all other waveform-like widgets (e.g. WSpinny's) that they should @@ -719,6 +735,8 @@ void WaveformWidgetFactory::swap() { // WSpinnys are also double-buffered QGLWidgets, like all the waveform // renderers. Swap all the WSpinny widgets now. emit swapSpinnies(); + // Same for WVuMeterGL. Note that we are either using WVuMeter or WVuMeterGL + // If we are using WVuMeter, this does nothing emit swapVuMeters(); } //qDebug() << "swap end" << m_vsyncThread->elapsed(); diff --git a/src/waveform/waveformwidgetfactory.h b/src/waveform/waveformwidgetfactory.h index 328b85ea66c..1fcbd703e96 100644 --- a/src/waveform/waveformwidgetfactory.h +++ b/src/waveform/waveformwidgetfactory.h @@ -122,6 +122,8 @@ class WaveformWidgetFactory : public QObject, public Singleton >* list); void destroyWidgets(); + void addTimerListener(WVuMeter* pWidget); + void startVSync(GuiTick* pGuiTick, VisualsManager* pVisualsManager); void setVSyncType(int vsType); int getVSyncType(); From dc8a7c89739e24259a311dd938f3175d05334f87 Mon Sep 17 00:00:00 2001 From: m0dB Date: Fri, 23 Sep 2022 15:51:59 +0200 Subject: [PATCH 22/33] not interesting in repaint or update calls --- src/widget/wvumetergl.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/widget/wvumetergl.cpp b/src/widget/wvumetergl.cpp index 59d957d479b..47ef422c5fd 100644 --- a/src/widget/wvumetergl.cpp +++ b/src/widget/wvumetergl.cpp @@ -41,6 +41,9 @@ WVuMeterGL::WVuMeterGL(QWidget* parent) setAutoFillBackground(false); setAutoBufferSwap(false); + // Not interested in repaint or update calls, as we draw from the vsync thread + setUpdatesEnabled(false); + m_timer.start(); } From a56245097ba38c1bd51fd7e2b92370bee789aaa4 Mon Sep 17 00:00:00 2001 From: m0dB Date: Fri, 23 Sep 2022 20:53:19 +0200 Subject: [PATCH 23/33] added command line argument to choose to use vumetergl or vumeter standard, with default determined by OS and Qt version --- src/main.cpp | 13 ++++-- src/skin/legacy/legacyskinparser.cpp | 35 +++++++++------- src/util/cmdlineargs.cpp | 55 ++++++++++++++++++++++---- src/util/cmdlineargs.h | 4 ++ src/waveform/waveformwidgetfactory.cpp | 19 +++++++-- src/waveform/waveformwidgetfactory.h | 4 +- 6 files changed, 99 insertions(+), 31 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 1393a3fde0f..b3a661d8221 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include "errordialoghandler.h" #include "mixxx.h" @@ -67,9 +69,14 @@ int main(int argc, char * argv[]) { // Construct a list of strings based on the command line arguments CmdlineArgs& args = CmdlineArgs::Instance(); - if (!args.Parse(argc, argv)) { - args.printUsage(); - return kParseCmdlineArgsErrorExitCode; + try { + if (!args.Parse(argc, argv)) { + args.printUsage(); + return kParseCmdlineArgsErrorExitCode; + } + } catch (const std::exception& e) { + fprintf(stdout, "Error parsing arguments: %s\n", e.what()); + return -1; } // If you change this here, you also need to change it in diff --git a/src/skin/legacy/legacyskinparser.cpp b/src/skin/legacy/legacyskinparser.cpp index c2ad874a4eb..0e7049ff750 100644 --- a/src/skin/legacy/legacyskinparser.cpp +++ b/src/skin/legacy/legacyskinparser.cpp @@ -78,6 +78,7 @@ #include "widget/wtrackproperty.h" #include "widget/wtracktext.h" #include "widget/wtrackwidgetgroup.h" +#include "widget/wvumeter.h" #include "widget/wvumetergl.h" #include "widget/wwaveformviewer.h" #include "widget/wwidget.h" @@ -1243,6 +1244,7 @@ QWidget* LegacySkinParser::parseRecordingDuration(const QDomElement& node) { p->installEventFilter(m_pControllerManager->getControllerLearningEventFilter()); return p; } + QWidget* LegacySkinParser::parseSpinny(const QDomElement& node) { if (CmdlineArgs::Instance().getSafeMode()) { WLabel* dummy = new WLabel(m_pParent); @@ -1252,6 +1254,7 @@ QWidget* LegacySkinParser::parseSpinny(const QDomElement& node) { } auto* waveformWidgetFactory = WaveformWidgetFactory::instance(); + if (!waveformWidgetFactory->isOpenGlAvailable() && !waveformWidgetFactory->isOpenGlesAvailable()) { WLabel* dummy = new WLabel(m_pParent); @@ -1285,6 +1288,15 @@ QWidget* LegacySkinParser::parseSpinny(const QDomElement& node) { } QWidget* LegacySkinParser::parseVuMeter(const QDomElement& node) { + if (!CmdlineArgs::Instance().getUseVuMeterGL()) { + // Standard WVuMeter + WVuMeter* pVuMeterWidget = parseStandardWidget(node); + WaveformWidgetFactory::instance()->addVuMeter(pVuMeterWidget); + return pVuMeterWidget; + } + + // QGLWidger derived WVuMeterGL + if (CmdlineArgs::Instance().getSafeMode()) { WLabel* dummy = new WLabel(m_pParent); //: Shown when Mixxx is running in safe mode. @@ -1300,25 +1312,18 @@ QWidget* LegacySkinParser::parseVuMeter(const QDomElement& node) { dummy->setText(tr("No OpenGL\nsupport.")); return dummy; } - WVuMeterGL* vuMeter = new WVuMeterGL(m_pParent); - commonWidgetSetup(node, vuMeter); + WVuMeterGL* pVuMeterWidget = new WVuMeterGL(m_pParent); + commonWidgetSetup(node, pVuMeterWidget); - connect(waveformWidgetFactory, - &WaveformWidgetFactory::renderVuMeters, - vuMeter, - &WVuMeterGL::render); - connect(waveformWidgetFactory, - &WaveformWidgetFactory::swapVuMeters, - vuMeter, - &WVuMeterGL::swap); + waveformWidgetFactory->addVuMeter(pVuMeterWidget); - vuMeter->setup(node, *m_pContext); - vuMeter->installEventFilter(m_pKeyboard); - vuMeter->installEventFilter( + pVuMeterWidget->setup(node, *m_pContext); + pVuMeterWidget->installEventFilter(m_pKeyboard); + pVuMeterWidget->installEventFilter( m_pControllerManager->getControllerLearningEventFilter()); - vuMeter->Init(); + pVuMeterWidget->Init(); - return vuMeter; + return pVuMeterWidget; } QWidget* LegacySkinParser::parseSearchBox(const QDomElement& node) { diff --git a/src/util/cmdlineargs.cpp b/src/util/cmdlineargs.cpp index 36ecb2f0185..05dd5370964 100644 --- a/src/util/cmdlineargs.cpp +++ b/src/util/cmdlineargs.cpp @@ -9,11 +9,27 @@ #include "sources/soundsourceproxy.h" #include "util/versionstore.h" +namespace { +bool useVuMeterGLDefault() { + // The QGLWidget derived WVuMeterGL provided better performance, + // particularly on macOS, but on linux with older Qt versions + // (tested with 5.12.3) we have segfaults as Qt seems to still + // be accessing the QGLWidget after deletion, when switching + // skins or on exit. +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 3) || defined(__APPLE__) + return true; +#else + return false; +#endif +} +} // namespace + CmdlineArgs::CmdlineArgs() : m_startInFullscreen(false), // Initialize vars m_midiDebug(false), m_developer(false), m_safeMode(false), + m_useVuMeterGL(useVuMeterGLDefault()), m_debugAssertBreak(false), m_settingsPathSet(false), m_logLevel(mixxx::kLogLevelDefault), @@ -97,14 +113,28 @@ warnings and errors to the console unless this is set properly.\n", stdout); when a critical error occurs unless this is set properly.\n", stdout); } i++; - } else if (QString::fromLocal8Bit(argv[i]).contains("--midiDebug", Qt::CaseInsensitive) || - QString::fromLocal8Bit(argv[i]).contains("--controllerDebug", Qt::CaseInsensitive)) { + } else if (argv[i] == QString("--useVuMeterGL")) { + if (i + 1 == argc) { + throw std::runtime_error("Missing argument after --useVuMeterGL"); + } + QString qs(argv[i + 1]); + if (qs != "yes" && qs != "no") { + throw std::runtime_error("Expected yes or no after --useVuMeterGL"); + } + m_useVuMeterGL = (qs == "yes"); + } else if (QString::fromLocal8Bit(argv[i]).contains( + "--midiDebug", Qt::CaseInsensitive) || + QString::fromLocal8Bit(argv[i]).contains( + "--controllerDebug", Qt::CaseInsensitive)) { m_midiDebug = true; - } else if (QString::fromLocal8Bit(argv[i]).contains("--developer", Qt::CaseInsensitive)) { + } else if (QString::fromLocal8Bit(argv[i]).contains( + "--developer", Qt::CaseInsensitive)) { m_developer = true; - } else if (QString::fromLocal8Bit(argv[i]).contains("--safeMode", Qt::CaseInsensitive)) { + } else if (QString::fromLocal8Bit(argv[i]).contains( + "--safeMode", Qt::CaseInsensitive)) { m_safeMode = true; - } else if (QString::fromLocal8Bit(argv[i]).contains("--debugAssertBreak", Qt::CaseInsensitive)) { + } else if (QString::fromLocal8Bit(argv[i]).contains( + "--debugAssertBreak", Qt::CaseInsensitive)) { m_debugAssertBreak = true; } else if (i > 0) { // Don't try to load the program name to a deck @@ -151,6 +181,14 @@ void CmdlineArgs::printUsage() { --safeMode Enables safe-mode. Disables OpenGL waveforms,\n\ and spinning vinyl widgets. Try this option if\n\ Mixxx is crashing on startup.\n\ +\n", + stdout); + fprintf(stdout, + "\ +--useVuMeterGL yes|no Use OpenGL VuMeter instead of standard.\n\ + Default is: %s\n", + (m_useVuMeterGL ? "yes" : "no")); + fputs("\ \n\ --locale LOCALE Use a custom locale for loading translations\n\ (e.g 'fr')\n\ @@ -169,14 +207,15 @@ void CmdlineArgs::printUsage() { defined at --logLevel above.\n\ \n" #ifdef MIXXX_BUILD_DEBUG -"\ + "\ --debugAssertBreak Breaks (SIGINT) Mixxx, if a DEBUG_ASSERT\n\ evaluates to false. Under a debugger you can\n\ continue afterwards.\ \n" #endif -"\ --h, --help Display this help message and exit", stdout); + "\ +-h, --help Display this help message and exit", + stdout); fputs("\n\n(For more information, see " MIXXX_MANUAL_COMMANDLINEOPTIONS_URL ")\n", stdout); } diff --git a/src/util/cmdlineargs.h b/src/util/cmdlineargs.h index 1aa10034983..f98de23d163 100644 --- a/src/util/cmdlineargs.h +++ b/src/util/cmdlineargs.h @@ -28,6 +28,9 @@ class CmdlineArgs final { bool getMidiDebug() const { return m_midiDebug; } bool getDeveloper() const { return m_developer; } bool getSafeMode() const { return m_safeMode; } + bool getUseVuMeterGL() const { + return m_useVuMeterGL; + } bool getDebugAssertBreak() const { return m_debugAssertBreak; } bool getSettingsPathSet() const { return m_settingsPathSet; } mixxx::LogLevel getLogLevel() const { return m_logLevel; } @@ -47,6 +50,7 @@ class CmdlineArgs final { bool m_midiDebug; bool m_developer; // Developer Mode bool m_safeMode; + bool m_useVuMeterGL; bool m_debugAssertBreak; bool m_settingsPathSet; // has --settingsPath been set on command line ? mixxx::LogLevel m_logLevel; // Level of stderr logging message verbosity diff --git a/src/waveform/waveformwidgetfactory.cpp b/src/waveform/waveformwidgetfactory.cpp index f63df192043..41df8c514e4 100644 --- a/src/waveform/waveformwidgetfactory.cpp +++ b/src/waveform/waveformwidgetfactory.cpp @@ -354,19 +354,30 @@ void WaveformWidgetFactory::destroyWidgets() { m_waveformWidgetHolders.clear(); } -void WaveformWidgetFactory::addTimerListener(WVuMeter* pWidget) { - // Note that we are either using WVuMeter or WVuMeterGL. WVuMeterGLs are connected to renderVuMeters and swapVuMeters instead. - +void WaveformWidgetFactory::addVuMeter(WVuMeter* pVuMeter) { // Do not hold the pointer to of timer listeners since they may be deleted. // We don't activate update() or repaint() directly so listener widgets // can decide whether to paint or not. connect(this, &WaveformWidgetFactory::waveformUpdateTick, - pWidget, + pVuMeter, &WVuMeter::maybeUpdate, Qt::DirectConnection); } +void WaveformWidgetFactory::addVuMeter(WVuMeterGL* pVuMeter) { + // WVuMeterGLs to be rendered and swapped from the vsync thread + + connect(this, + &WaveformWidgetFactory::renderVuMeters, + pVuMeter, + &WVuMeterGL::render); + connect(this, + &WaveformWidgetFactory::swapVuMeters, + pVuMeter, + &WVuMeterGL::swap); +} + void WaveformWidgetFactory::slotSkinLoaded() { setWidgetTypeFromConfig(); #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) && defined __WINDOWS__ diff --git a/src/waveform/waveformwidgetfactory.h b/src/waveform/waveformwidgetfactory.h index 1fcbd703e96..53961480a02 100644 --- a/src/waveform/waveformwidgetfactory.h +++ b/src/waveform/waveformwidgetfactory.h @@ -12,6 +12,7 @@ #include "waveform/widgets/waveformwidgettype.h" class WVuMeter; +class WVuMeterGL; class WWaveformViewer; class WaveformWidgetAbstract; class VSyncThread; @@ -122,7 +123,8 @@ class WaveformWidgetFactory : public QObject, public Singleton >* list); void destroyWidgets(); - void addTimerListener(WVuMeter* pWidget); + void addVuMeter(WVuMeter* pWidget); + void addVuMeter(WVuMeterGL* pWidget); void startVSync(GuiTick* pGuiTick, VisualsManager* pVisualsManager); void setVSyncType(int vsType); From 066bc576313dc2b3cbdd8a2b1c6f646d91e70821 Mon Sep 17 00:00:00 2001 From: m0dB Date: Fri, 23 Sep 2022 21:00:00 +0200 Subject: [PATCH 24/33] formatting --- src/widget/wvumetergl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widget/wvumetergl.h b/src/widget/wvumetergl.h index 07fca8d35cd..4458e9c431e 100644 --- a/src/widget/wvumetergl.h +++ b/src/widget/wvumetergl.h @@ -38,7 +38,7 @@ class WVuMeterGL : public QGLWidget, public WBaseWidget { void updateState(mixxx::Duration elapsed); private: - void paintEvent(QPaintEvent * /*unused*/) override; + void paintEvent(QPaintEvent* /*unused*/) override; void showEvent(QShowEvent* /*unused*/) override; void setPeak(double parameter); From ea1efd5aeea9df0f0677112a8fd562bd90598a6b Mon Sep 17 00:00:00 2001 From: m0dB <79429057+m0dB@users.noreply.github.com> Date: Sun, 25 Sep 2022 11:52:23 +0200 Subject: [PATCH 25/33] Update src/waveform/waveformwidgetfactory.cpp Co-authored-by: ronso0 --- src/waveform/waveformwidgetfactory.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/waveform/waveformwidgetfactory.cpp b/src/waveform/waveformwidgetfactory.cpp index 41df8c514e4..7d404b09b50 100644 --- a/src/waveform/waveformwidgetfactory.cpp +++ b/src/waveform/waveformwidgetfactory.cpp @@ -367,7 +367,6 @@ void WaveformWidgetFactory::addVuMeter(WVuMeter* pVuMeter) { void WaveformWidgetFactory::addVuMeter(WVuMeterGL* pVuMeter) { // WVuMeterGLs to be rendered and swapped from the vsync thread - connect(this, &WaveformWidgetFactory::renderVuMeters, pVuMeter, From 3394ebe7d4ff4dea6b1a4eb47e87bb294f7c5af1 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Sat, 24 Sep 2022 22:24:19 +0200 Subject: [PATCH 26/33] WVuMeter/WVuMeterGL: remove unused includes --- src/widget/wvumeter.cpp | 3 --- src/widget/wvumeter.h | 6 ------ src/widget/wvumetergl.cpp | 6 ------ src/widget/wvumetergl.h | 4 ---- 4 files changed, 19 deletions(-) diff --git a/src/widget/wvumeter.cpp b/src/widget/wvumeter.cpp index 13f29074a24..c6a991b7322 100644 --- a/src/widget/wvumeter.cpp +++ b/src/widget/wvumeter.cpp @@ -1,10 +1,7 @@ #include "widget/wvumeter.h" -#include -#include #include #include -#include #include "moc_wvumeter.cpp" #include "util/math.h" diff --git a/src/widget/wvumeter.h b/src/widget/wvumeter.h index ee7911145f8..df16bf482e8 100644 --- a/src/widget/wvumeter.h +++ b/src/widget/wvumeter.h @@ -1,11 +1,5 @@ #pragma once -#include -#include -#include -#include -#include - #include "skin/legacy/skincontext.h" #include "util/performancetimer.h" #include "widget/wpixmapstore.h" diff --git a/src/widget/wvumetergl.cpp b/src/widget/wvumetergl.cpp index 47ef422c5fd..1748ad3b551 100644 --- a/src/widget/wvumetergl.cpp +++ b/src/widget/wvumetergl.cpp @@ -1,11 +1,5 @@ #include "widget/wvumetergl.h" -#include -#include -#include -#include -#include - #include "moc_wvumetergl.cpp" #include "util/math.h" #include "util/timer.h" diff --git a/src/widget/wvumetergl.h b/src/widget/wvumetergl.h index 4458e9c431e..877bc15dcb7 100644 --- a/src/widget/wvumetergl.h +++ b/src/widget/wvumetergl.h @@ -1,10 +1,6 @@ #pragma once -#include #include -#include -#include -#include #include "skin/legacy/skincontext.h" #include "util/performancetimer.h" From 3f8dfd237e3f07c7b04fdfdaaf35a5f0b834e030 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Sat, 24 Sep 2022 22:30:35 +0200 Subject: [PATCH 27/33] Shade: remove redundant VU meter in sampler --- res/skins/Shade/sampler.xml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/res/skins/Shade/sampler.xml b/res/skins/Shade/sampler.xml index 18fdb7d1a8b..0cd290d5816 100644 --- a/res/skins/Shade/sampler.xml +++ b/res/skins/Shade/sampler.xml @@ -402,20 +402,6 @@ - - sampler_VuMeter - skin:/style/volume_display_sampler_over.png - skin:/style/volume_display_sampler.png - 3,24 - false - 5 - 500 - 50 - 2 - - ,VuMeter - - From 208ddb3e3dc81019e119de59430fa3f127829d31 Mon Sep 17 00:00:00 2001 From: m0dB Date: Sun, 25 Sep 2022 12:14:41 +0200 Subject: [PATCH 28/33] typo in comment --- src/skin/legacy/legacyskinparser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/skin/legacy/legacyskinparser.cpp b/src/skin/legacy/legacyskinparser.cpp index 0e7049ff750..65b791d7b2d 100644 --- a/src/skin/legacy/legacyskinparser.cpp +++ b/src/skin/legacy/legacyskinparser.cpp @@ -1295,7 +1295,7 @@ QWidget* LegacySkinParser::parseVuMeter(const QDomElement& node) { return pVuMeterWidget; } - // QGLWidger derived WVuMeterGL + // QGLWidget derived WVuMeterGL if (CmdlineArgs::Instance().getSafeMode()) { WLabel* dummy = new WLabel(m_pParent); From 44aa76ad058844d9386e2812fe804d4ea09442c9 Mon Sep 17 00:00:00 2001 From: m0dB Date: Sun, 25 Sep 2022 12:25:56 +0200 Subject: [PATCH 29/33] bringing command line parsing of --useVuMeterGL in line with parsing of other arguments --- src/main.cpp | 11 +++-------- src/util/cmdlineargs.cpp | 9 +++------ 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index b3a661d8221..13264af1995 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -69,14 +69,9 @@ int main(int argc, char * argv[]) { // Construct a list of strings based on the command line arguments CmdlineArgs& args = CmdlineArgs::Instance(); - try { - if (!args.Parse(argc, argv)) { - args.printUsage(); - return kParseCmdlineArgsErrorExitCode; - } - } catch (const std::exception& e) { - fprintf(stdout, "Error parsing arguments: %s\n", e.what()); - return -1; + if (!args.Parse(argc, argv)) { + args.printUsage(); + return kParseCmdlineArgsErrorExitCode; } // If you change this here, you also need to change it in diff --git a/src/util/cmdlineargs.cpp b/src/util/cmdlineargs.cpp index 05dd5370964..ebe4b5e85cc 100644 --- a/src/util/cmdlineargs.cpp +++ b/src/util/cmdlineargs.cpp @@ -114,12 +114,9 @@ when a critical error occurs unless this is set properly.\n", stdout); } i++; } else if (argv[i] == QString("--useVuMeterGL")) { - if (i + 1 == argc) { - throw std::runtime_error("Missing argument after --useVuMeterGL"); - } - QString qs(argv[i + 1]); + QString qs(i + 1 < argc ? argv[i + 1] : ""); if (qs != "yes" && qs != "no") { - throw std::runtime_error("Expected yes or no after --useVuMeterGL"); + fputs("\nExpected yes or no after --useVuMeterGL\n", stdout); } m_useVuMeterGL = (qs == "yes"); } else if (QString::fromLocal8Bit(argv[i]).contains( @@ -186,7 +183,7 @@ void CmdlineArgs::printUsage() { fprintf(stdout, "\ --useVuMeterGL yes|no Use OpenGL VuMeter instead of standard.\n\ - Default is: %s\n", + Default for your configuration is: %s\n", (m_useVuMeterGL ? "yes" : "no")); fputs("\ \n\ From 828b90d74290a9fd2203b984cc59177e0243e880 Mon Sep 17 00:00:00 2001 From: m0dB Date: Sun, 25 Sep 2022 13:00:27 +0200 Subject: [PATCH 30/33] use single timer (from vsyncthread) for all vumetergl updateState --- src/waveform/vsyncthread.cpp | 9 +++++++-- src/waveform/vsyncthread.h | 3 ++- src/widget/wvumetergl.cpp | 11 +++-------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/waveform/vsyncthread.cpp b/src/waveform/vsyncthread.cpp index 2105d9221a2..ebb54e9bf18 100644 --- a/src/waveform/vsyncthread.cpp +++ b/src/waveform/vsyncthread.cpp @@ -49,7 +49,7 @@ void VSyncThread::run() { emit vsyncSwap(); // swaps the new waveform to front m_semaVsyncSlot.acquire(); - m_timer.restart(); + m_sinceLastSwap = m_timer.restart(); m_waitToSwapMicros = 1000; usleep(1000); } else { // if (m_vSyncMode == ST_TIMER) { @@ -75,7 +75,8 @@ void VSyncThread::run() { m_semaVsyncSlot.acquire(); // <- Assume we are VSynced here -> - int lastSwapTime = static_cast(m_timer.restart().toIntegerMicros()); + m_sinceLastSwap = m_timer.restart(); + int lastSwapTime = static_cast(m_sinceLastSwap.toIntegerMicros()); if (remainingForSwap < 0) { // Our swapping call was already delayed // The real swap might happens on the following VSync, depending on driver settings @@ -162,3 +163,7 @@ void VSyncThread::getAvailableVSyncTypes(QList >* pList) { } } } + +mixxx::Duration VSyncThread::sinceLastSwap() const { + return m_sinceLastSwap; +} diff --git a/src/waveform/vsyncthread.h b/src/waveform/vsyncthread.h index f7310aee56d..4632fe0e79f 100644 --- a/src/waveform/vsyncthread.h +++ b/src/waveform/vsyncthread.h @@ -37,7 +37,7 @@ class VSyncThread : public QThread { void getAvailableVSyncTypes(QList >* list); void setupSync(QGLWidget* glw, int index); void waitUntilSwap(QGLWidget* glw); - + mixxx::Duration sinceLastSwap() const; signals: void vsyncRender(); void vsyncSwap(); @@ -55,4 +55,5 @@ class VSyncThread : public QThread { QSemaphore m_semaVsyncSlot; double m_displayFrameRate; int m_vSyncPerRendering; + mixxx::Duration m_sinceLastSwap; }; diff --git a/src/widget/wvumetergl.cpp b/src/widget/wvumetergl.cpp index 1748ad3b551..09f2a29ca56 100644 --- a/src/widget/wvumetergl.cpp +++ b/src/widget/wvumetergl.cpp @@ -37,8 +37,6 @@ WVuMeterGL::WVuMeterGL(QWidget* parent) // Not interested in repaint or update calls, as we draw from the vsync thread setUpdatesEnabled(false); - - m_timer.start(); } void WVuMeterGL::setup(const QDomNode& node, const SkinContext& context) { @@ -128,8 +126,6 @@ void WVuMeterGL::onConnectedControlChanged(double dParameter, double dValue) { // A 0.0 value is very unlikely except when the VU Meter is disabled m_dPeakParameter = 0; } - - updateState(m_timer.restart()); } void WVuMeterGL::setPeak(double parameter) { @@ -172,12 +168,11 @@ void WVuMeterGL::showEvent(QShowEvent* e) { m_qBgColor = mixxx::widgethelper::findBaseColor(this); } -void WVuMeterGL::render(VSyncThread* /* UNUSED vSyncThread */) { - // TODO (@m0dB) consider using timing information from the vSyncThread, - // instead of having an m_timer in each WVuMeterGL instance. - +void WVuMeterGL::render(VSyncThread* vSyncThread) { ScopedTimer t("WVuMeterGL::render"); + updateState(vSyncThread->sinceLastSwap()); + if (m_bHasRendered && m_dParameter == m_dLastParameter && m_dPeakParameter == m_dLastPeakParameter) { return; From 4a1140e8eb09234faa9d19fbe497d8d780bb7e28 Mon Sep 17 00:00:00 2001 From: m0dB <79429057+m0dB@users.noreply.github.com> Date: Sun, 25 Sep 2022 18:45:59 +0200 Subject: [PATCH 31/33] removed unused member var m_timer and include correct header --- src/widget/wvumetergl.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/widget/wvumetergl.h b/src/widget/wvumetergl.h index 877bc15dcb7..43437489d80 100644 --- a/src/widget/wvumetergl.h +++ b/src/widget/wvumetergl.h @@ -3,7 +3,7 @@ #include #include "skin/legacy/skincontext.h" -#include "util/performancetimer.h" +#include "util/duration.h" #include "widget/wpixmapstore.h" #include "widget/wwidget.h" @@ -70,7 +70,5 @@ class WVuMeterGL : public QGLWidget, public WBaseWidget { // The peak hold time remaining in milliseconds. double m_dPeakHoldCountdownMs; - PerformanceTimer m_timer; - QColor m_qBgColor; }; From 785fd06e12def2baf54b88ec0b28e7451fb8a2d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Thu, 29 Sep 2022 14:22:56 +0200 Subject: [PATCH 32/33] use horizontal in VU-Meter wrapper widgets --- res/skins/Shade/auxiliary.xml | 1 + res/skins/Shade/microphone.xml | 1 + res/skins/Shade/mixer_panel.xml | 21 +++++++++++++++++++++ res/skins/Shade/preview_deck.xml | 1 + res/skins/Shade/sampler.xml | 1 + 5 files changed, 25 insertions(+) diff --git a/res/skins/Shade/auxiliary.xml b/res/skins/Shade/auxiliary.xml index c4fa6017d92..7a5e3be3c52 100644 --- a/res/skins/Shade/auxiliary.xml +++ b/res/skins/Shade/auxiliary.xml @@ -27,6 +27,7 @@ 99,14 5f,35f + horizontal auxiliary_VuMeter diff --git a/res/skins/Shade/microphone.xml b/res/skins/Shade/microphone.xml index 1e572cef83e..3a51a598b74 100644 --- a/res/skins/Shade/microphone.xml +++ b/res/skins/Shade/microphone.xml @@ -28,6 +28,7 @@ 82,14 5f,35f + horizontal microphone_VuMeter diff --git a/res/skins/Shade/mixer_panel.xml b/res/skins/Shade/mixer_panel.xml index 37343e38add..377b5d818f1 100644 --- a/res/skins/Shade/mixer_panel.xml +++ b/res/skins/Shade/mixer_panel.xml @@ -507,6 +507,7 @@ 113,28 26f,1f + horizontal @@ -798,6 +799,7 @@ 107,76 5f,81f + horizontal channel_VuMeter @@ -808,6 +810,10 @@ 500 50 2 + 1 + vinyl_spinny_background.png + vinyl_spinny_foreground.png + vinyl_spinny_foreground_ghost.png [Channel1],VuMeter @@ -817,6 +823,7 @@ 143,76 5f,81f + horizontal channel_VuMeter @@ -827,6 +834,10 @@ 500 50 2 + 1 + vinyl_spinny_background.png + vinyl_spinny_foreground.png + vinyl_spinny_foreground_ghost.png [Channel2],VuMeter @@ -836,6 +847,7 @@ 122,76 5f,81f + horizontal master_VuMeterL @@ -845,6 +857,10 @@ 500 50 2 + 1 + vinyl_spinny_background.png + vinyl_spinny_foreground.png + vinyl_spinny_foreground_ghost.png [Master],VuMeterL @@ -854,6 +870,7 @@ 128,76 5f,81f + horizontal master_VuMeterR @@ -863,6 +880,10 @@ 500 50 2 + 1 + vinyl_spinny_background.png + vinyl_spinny_foreground.png + vinyl_spinny_foreground_ghost.png [Master],VuMeterR diff --git a/res/skins/Shade/preview_deck.xml b/res/skins/Shade/preview_deck.xml index 9a0d5280461..0449fe8a114 100644 --- a/res/skins/Shade/preview_deck.xml +++ b/res/skins/Shade/preview_deck.xml @@ -218,6 +218,7 @@ 2,11 5f,41f + horizontal sampler_VuMeter diff --git a/res/skins/Shade/sampler.xml b/res/skins/Shade/sampler.xml index 0cd290d5816..2244543a5af 100644 --- a/res/skins/Shade/sampler.xml +++ b/res/skins/Shade/sampler.xml @@ -386,6 +386,7 @@ 3,24 + horizontal sampler_VuMeter From c9af1e05b1a41aef568f76990650158e36bc2505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Thu, 29 Sep 2022 14:59:50 +0200 Subject: [PATCH 33/33] Fix sefgault cause by a concurrent creation of platform windows. --- src/skin/legacy/legacyskinparser.cpp | 38 +++++++++++++++++++--------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/skin/legacy/legacyskinparser.cpp b/src/skin/legacy/legacyskinparser.cpp index 65b791d7b2d..f794b2b41eb 100644 --- a/src/skin/legacy/legacyskinparser.cpp +++ b/src/skin/legacy/legacyskinparser.cpp @@ -1269,22 +1269,29 @@ QWidget* LegacySkinParser::parseSpinny(const QDomElement& node) { SKIN_WARNING(node, *m_pContext) << "No player found for group:" << group; return nullptr; } - WSpinny* spinny = new WSpinny(m_pParent, group, m_pConfig, m_pVCManager, pPlayer); - commonWidgetSetup(node, spinny); + // Note: For some reasons we need to create the widget without a parent to avoid to + // create two platform windows in QWidget::create() for this widget. + // This happens, because the QWidget::create() of a parent() will populate all children + // with platform windows q_createNativeChildrenAndSetParent() while another window is already + // under construction. The ID for the first window is not cleared and leads to a segfault + // during on shutdown + WSpinny* pSpinny = new WSpinny(nullptr, group, m_pConfig, m_pVCManager, pPlayer); + pSpinny->setParent(m_pParent); + commonWidgetSetup(node, pSpinny); connect(waveformWidgetFactory, &WaveformWidgetFactory::renderSpinnies, - spinny, + pSpinny, &WSpinny::render); - connect(waveformWidgetFactory, &WaveformWidgetFactory::swapSpinnies, spinny, &WSpinny::swap); - connect(spinny, &WSpinny::trackDropped, m_pPlayerManager, &PlayerManager::slotLoadToPlayer); - connect(spinny, &WSpinny::cloneDeck, m_pPlayerManager, &PlayerManager::slotCloneDeck); + connect(waveformWidgetFactory, &WaveformWidgetFactory::swapSpinnies, pSpinny, &WSpinny::swap); + connect(pSpinny, &WSpinny::trackDropped, m_pPlayerManager, &PlayerManager::slotLoadToPlayer); + connect(pSpinny, &WSpinny::cloneDeck, m_pPlayerManager, &PlayerManager::slotCloneDeck); - spinny->setup(node, *m_pContext); - spinny->installEventFilter(m_pKeyboard); - spinny->installEventFilter(m_pControllerManager->getControllerLearningEventFilter()); - spinny->Init(); - return spinny; + pSpinny->setup(node, *m_pContext); + pSpinny->installEventFilter(m_pKeyboard); + pSpinny->installEventFilter(m_pControllerManager->getControllerLearningEventFilter()); + pSpinny->Init(); + return pSpinny; } QWidget* LegacySkinParser::parseVuMeter(const QDomElement& node) { @@ -1312,7 +1319,14 @@ QWidget* LegacySkinParser::parseVuMeter(const QDomElement& node) { dummy->setText(tr("No OpenGL\nsupport.")); return dummy; } - WVuMeterGL* pVuMeterWidget = new WVuMeterGL(m_pParent); + // Note: For some reasons we need to create the widget without a parent to avoid to + // create two platform windows in QWidget::create() for this widget. + // This happens, because the QWidget::create() of a parent() will populate all children + // with platform windows q_createNativeChildrenAndSetParent() while another window is already + // under construction. The ID for the first window is not cleared and leads to a segfault + // during on shutdown + WVuMeterGL* pVuMeterWidget = new WVuMeterGL(); + pVuMeterWidget->setParent(m_pParent); commonWidgetSetup(node, pVuMeterWidget); waveformWidgetFactory->addVuMeter(pVuMeterWidget);