diff --git a/src/preferences/dialog/dlgprefwaveform.cpp b/src/preferences/dialog/dlgprefwaveform.cpp index d770f10e1249..723b3ad17dd6 100644 --- a/src/preferences/dialog/dlgprefwaveform.cpp +++ b/src/preferences/dialog/dlgprefwaveform.cpp @@ -217,6 +217,18 @@ DlgPrefWaveform::DlgPrefWaveform( QOverload::of(&QComboBox::currentIndexChanged), this, &DlgPrefWaveform::slotSetUntilMarkTextHeightLimit); + connect(stemReorderLayerOnChangedCheckBox, + &QCheckBox::clicked, + this, + &DlgPrefWaveform::slotStemReorderOnChange); + connect(stemOpacitySpinBox, + &QDoubleSpinBox::valueChanged, + this, + &DlgPrefWaveform::slotStemOpacity); + connect(stemOutlineOpacitySpinBox, + &QDoubleSpinBox::valueChanged, + this, + &DlgPrefWaveform::slotStemOutlineOpacity); setScrollSafeGuardForAllInputWidgets(this); } @@ -306,6 +318,10 @@ void DlgPrefWaveform::slotUpdate() { WaveformWidgetFactory::toUntilMarkTextHeightLimitIndex( factory->getUntilMarkTextHeightLimit())); + stemReorderLayerOnChangedCheckBox->setChecked(factory->isStemReorderOnChange()); + stemOpacitySpinBox->setValue(factory->getStemOpacity()); + stemOutlineOpacitySpinBox->setValue(factory->getStemOutlineOpacity()); + mixxx::OverviewType cfgOverviewType = m_pConfig->getValue(kOverviewTypeCfgKey, mixxx::OverviewType::RGB); // Assumes the combobox index is in sync with the ControlPushButton @@ -674,6 +690,17 @@ void DlgPrefWaveform::slotSetUntilMarkTextHeightLimit(int index) { WaveformWidgetFactory::toUntilMarkTextHeightLimit(index)); } +void DlgPrefWaveform::slotStemOpacity(float value) { + WaveformWidgetFactory::instance()->setStemOpacity(value); +} +void DlgPrefWaveform::slotStemReorderOnChange(bool value) { + WaveformWidgetFactory::instance()->setStemReorderOnChange(value); +} + +void DlgPrefWaveform::slotStemOutlineOpacity(float value) { + WaveformWidgetFactory::instance()->setStemOutlineOpacity(value); +} + void DlgPrefWaveform::calculateCachedWaveformDiskUsage() { AnalysisDao analysisDao(m_pConfig); QSqlDatabase dbConnection = mixxx::DbConnectionPooled(m_pLibrary->dbConnectionPool()); diff --git a/src/preferences/dialog/dlgprefwaveform.h b/src/preferences/dialog/dlgprefwaveform.h index 367dda8b396a..9e020ca71467 100644 --- a/src/preferences/dialog/dlgprefwaveform.h +++ b/src/preferences/dialog/dlgprefwaveform.h @@ -63,6 +63,9 @@ class DlgPrefWaveform : public DlgPreferencePage, public Ui::DlgPrefWaveformDlg void slotSetUntilMarkAlign(int index); void slotSetUntilMarkTextPointSize(int value); void slotSetUntilMarkTextHeightLimit(int index); + void slotStemOpacity(float value); + void slotStemReorderOnChange(bool value); + void slotStemOutlineOpacity(float value); private: void initWaveformControl(); diff --git a/src/preferences/dialog/dlgprefwaveformdlg.ui b/src/preferences/dialog/dlgprefwaveformdlg.ui index 01fc4e8ddef5..4d659eccb50b 100644 --- a/src/preferences/dialog/dlgprefwaveformdlg.ui +++ b/src/preferences/dialog/dlgprefwaveformdlg.ui @@ -589,6 +589,96 @@ Select from different types of displays for the waveform, which differ primarily + + + + Stem + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + stemOpacityMainLabel + + + + + + + + + Channel opacity + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + stemOpacityMainLabel + + + + + + + Channel opacity (outline) + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + stemOpacityMainLabel + + + + + + + + Main stem opacity + + + 2 + + + 0.010000000000000 + + + 1.000000000000000 + + + 0.010000000000000 + + + + + + + Outline stem opacity + + + 2 + + + 0.010000000000000 + + + 1.000000000000000 + + + 0.010000000000000 + + + + + + + Move channel to foreground when volume is adjusted + + + + + + diff --git a/src/waveform/renderers/allshader/waveformrendererstem.cpp b/src/waveform/renderers/allshader/waveformrendererstem.cpp index 73e9e1750968..73bbba64cec1 100644 --- a/src/waveform/renderers/allshader/waveformrendererstem.cpp +++ b/src/waveform/renderers/allshader/waveformrendererstem.cpp @@ -4,6 +4,7 @@ #include #include +#include "control/controlproxy.h" #include "engine/channels/enginedeck.h" #include "engine/engine.h" #include "rendergraph/material/rgbamaterial.h" @@ -13,6 +14,7 @@ #include "util/math.h" #include "waveform/renderers/waveformwidgetrenderer.h" #include "waveform/waveform.h" +#include "waveform/waveformwidgetfactory.h" namespace { #ifdef __SCENEGRAPH__ @@ -34,7 +36,9 @@ WaveformRendererStem::WaveformRendererStem( ::WaveformRendererAbstract::PositionSource type) : WaveformRendererSignalBase(waveformWidget), m_isSlipRenderer(type == ::WaveformRendererAbstract::Slip), - m_splitStemTracks(false) { + m_splitStemTracks(false), + m_outlineOpacity(0.15f), + m_opacity(0.75f) { initForRectangles(0); setUsePreprocess(true); } @@ -46,12 +50,43 @@ bool WaveformRendererStem::init() { for (int stemIdx = 0; stemIdx < mixxx::kMaxSupportedStems; stemIdx++) { QString stemGroup = EngineDeck::getGroupForStem(m_waveformRenderer->getGroup(), stemIdx); m_pStemGain.emplace_back( - std::make_unique(stemGroup, + std::make_unique(stemGroup, QStringLiteral("volume"))); m_pStemMute.emplace_back( - std::make_unique(stemGroup, + std::make_unique(stemGroup, QStringLiteral("mute"))); + auto bringToForeground = [this, stemIdx](double) { + if (!m_reorderOnChange) { + return; + } + m_stackOrder.removeAll(stemIdx); + m_stackOrder.append(stemIdx); + }; + m_pStemGain.back()->connectValueChanged(this, bringToForeground); + m_pStemMute.back()->connectValueChanged(this, bringToForeground); } + + m_stackOrder.resize(mixxx::kMaxSupportedStems); + std::iota(m_stackOrder.begin(), m_stackOrder.end(), 0); + +#ifndef __SCENEGRAPH__ + auto* pWaveformWidgetFactory = WaveformWidgetFactory::instance(); + setReorderOnChange(pWaveformWidgetFactory->isStemReorderOnChange()); + connect(pWaveformWidgetFactory, + &WaveformWidgetFactory::stemReorderOnChangeChanged, + this, + &WaveformRendererStem::setReorderOnChange); + setOutlineOpacity(pWaveformWidgetFactory->getStemOutlineOpacity()); + connect(pWaveformWidgetFactory, + &WaveformWidgetFactory::stemOutlineOpacityChanged, + this, + &WaveformRendererStem::setOutlineOpacity); + setOpacity(pWaveformWidgetFactory->getStemOpacity()); + connect(pWaveformWidgetFactory, + &WaveformWidgetFactory::stemOpacityChanged, + this, + &WaveformRendererStem::setOpacity); +#endif return true; } @@ -152,7 +187,8 @@ bool WaveformRendererStem::preprocessInner() { const double maxSamplingRange = visualIncrementPerPixel / 2.0; for (int visualIdx = 0; visualIdx < stripLength; visualIdx++) { - for (int stemIdx = 0; stemIdx < mixxx::kMaxSupportedStems; stemIdx++) { + int stemLayer = 0; + for (int stemIdx : std::as_const(m_stackOrder)) { // Stem is drawn twice with different opacity level, this allow to // see the maximum signal by transparency for (int layerIdx = 0; layerIdx < 2; layerIdx++) { @@ -160,7 +196,7 @@ bool WaveformRendererStem::preprocessInner() { float color_r = stemColor.redF(), color_g = stemColor.greenF(), color_b = stemColor.blueF(), - color_a = stemColor.alphaF() * (layerIdx ? 0.75f : 0.15f); + color_a = stemColor.alphaF() * (layerIdx ? m_opacity : m_outlineOpacity); const int visualFrameStart = std::lround(xVisualFrame - maxSamplingRange); const int visualFrameStop = std::lround(xVisualFrame + maxSamplingRange); @@ -198,15 +234,16 @@ bool WaveformRendererStem::preprocessInner() { // shadow vertexUpdater.addRectangle( {fVisualIdx - halfStripSize, - stemIdx * stemBreadth + halfBreadth - + stemLayer * stemBreadth + halfBreadth - heightFactor * max}, {fVisualIdx + halfStripSize, m_isSlipRenderer - ? stemIdx * stemBreadth + halfBreadth - : stemIdx * stemBreadth + halfBreadth + + ? stemLayer * stemBreadth + halfBreadth + : stemLayer * stemBreadth + halfBreadth + heightFactor * max}, {color_r, color_g, color_b, color_a}); } + stemLayer++; } xVisualFrame += visualIncrementPerPixel; diff --git a/src/waveform/renderers/allshader/waveformrendererstem.h b/src/waveform/renderers/allshader/waveformrendererstem.h index 9916c2d91115..d9ad7b873ca6 100644 --- a/src/waveform/renderers/allshader/waveformrendererstem.h +++ b/src/waveform/renderers/allshader/waveformrendererstem.h @@ -2,12 +2,12 @@ #include -#include "control/pollingcontrolproxy.h" #include "rendergraph/geometrynode.h" #include "util/class.h" #include "waveform/renderers/allshader/waveformrenderersignalbase.h" class QOpenGLTexture; +class ControlProxy; namespace allshader { class WaveformRendererStem; @@ -37,13 +37,32 @@ class allshader::WaveformRendererStem final void setSplitStemTracks(bool splitStemTracks) { m_splitStemTracks = splitStemTracks; } + void setReorderOnChange(bool value) { + m_reorderOnChange = value; + // Reset the stem layer stack to the natural order + std::iota(m_stackOrder.begin(), m_stackOrder.end(), 0); + } + void setOutlineOpacity(float value) { + m_outlineOpacity = value; + markDirtyMaterial(); + } + void setOpacity(float value) { + m_opacity = value; + markDirtyMaterial(); + } private: bool m_isSlipRenderer; bool m_splitStemTracks; - std::vector> m_pStemGain; - std::vector> m_pStemMute; + bool m_reorderOnChange; + float m_outlineOpacity; + float m_opacity; + + std::vector> m_pStemGain; + std::vector> m_pStemMute; + + QVarLengthArray m_stackOrder; bool preprocessInner(); diff --git a/src/waveform/waveformwidgetfactory.cpp b/src/waveform/waveformwidgetfactory.cpp index b5f367a2f341..630c2d63a436 100644 --- a/src/waveform/waveformwidgetfactory.cpp +++ b/src/waveform/waveformwidgetfactory.cpp @@ -427,6 +427,12 @@ bool WaveformWidgetFactory::setConfig(UserSettingsPointer config) { setUntilMarkTextHeightLimit(toUntilMarkTextHeightLimit( m_config->getValue(ConfigKey("[Waveform]", "UntilMarkTextHeightLimit"), toUntilMarkTextHeightLimitIndex(m_untilMarkTextHeightLimit)))); + setStemReorderOnChange(m_config->getValue( + ConfigKey("[Waveform]", "stem_reorder_on_change"), true)); + setStemOpacity(static_cast( + m_config->getValue(ConfigKey("[Waveform]", "stem_opacity"), 0.75))); + setStemOutlineOpacity(static_cast(m_config->getValue( + ConfigKey("[Waveform]", "stem_outline_opacity"), 0.15))); return true; } @@ -1362,6 +1368,33 @@ void WaveformWidgetFactory::setUntilMarkTextHeightLimit(float value) { emit untilMarkTextHeightLimitChanged(value); } +void WaveformWidgetFactory::setStemReorderOnChange(bool value) { + m_stemReorderOnChange = value; + if (m_config) { + m_config->setValue(ConfigKey("[Waveform]", "stem_reorder_on_change"), + value); + } + emit stemReorderOnChangeChanged(value); +} + +void WaveformWidgetFactory::setStemOutlineOpacity(float value) { + m_stemOutlineOpacity = value; + if (m_config) { + m_config->setValue(ConfigKey("[Waveform]", "stem_outline_opacity"), + static_cast(value)); + } + emit stemOutlineOpacityChanged(value); +} + +void WaveformWidgetFactory::setStemOpacity(float value) { + m_stemOpacity = value; + if (m_config) { + m_config->setValue(ConfigKey("[Waveform]", "stem_opacity"), + static_cast(value)); + } + emit stemOpacityChanged(value); +} + // static Qt::Alignment WaveformWidgetFactory::toUntilMarkAlign(int index) { switch (index) { diff --git a/src/waveform/waveformwidgetfactory.h b/src/waveform/waveformwidgetfactory.h index 05e3ced526b4..2a1e3d63fd90 100644 --- a/src/waveform/waveformwidgetfactory.h +++ b/src/waveform/waveformwidgetfactory.h @@ -156,6 +156,10 @@ class WaveformWidgetFactory : public QObject, void setUntilMarkTextPointSize(int value); void setUntilMarkTextHeightLimit(float value); + void setStemReorderOnChange(bool value); + void setStemOutlineOpacity(float value); + void setStemOpacity(float value); + bool getUntilMarkShowBeats() const { return m_untilMarkShowBeats; } @@ -171,6 +175,15 @@ class WaveformWidgetFactory : public QObject, float getUntilMarkTextHeightLimit() const { return m_untilMarkTextHeightLimit; } + bool isStemReorderOnChange() const { + return m_stemReorderOnChange; + } + float getStemOutlineOpacity() const { + return m_stemOutlineOpacity; + } + float getStemOpacity() const { + return m_stemOpacity; + } static Qt::Alignment toUntilMarkAlign(int index); static int toUntilMarkAlignIndex(Qt::Alignment align); static float toUntilMarkTextHeightLimit(int index); @@ -232,6 +245,10 @@ class WaveformWidgetFactory : public QObject, void untilMarkTextPointSizeChanged(int value); void untilMarkTextHeightLimitChanged(float value); + void stemReorderOnChangeChanged(bool value); + void stemOutlineOpacityChanged(float value); + void stemOpacityChanged(float value); + public slots: void slotSkinLoaded(); @@ -292,6 +309,10 @@ class WaveformWidgetFactory : public QObject, int m_untilMarkTextPointSize; float m_untilMarkTextHeightLimit; + bool m_stemReorderOnChange; + float m_stemOutlineOpacity; + float m_stemOpacity; + bool m_openGlAvailable; bool m_openGlesAvailable; QString m_openGLVersion;