diff --git a/CMakeLists.txt b/CMakeLists.txt index 628c9f8bd34..e737601d0fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1298,9 +1298,6 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL src/widget/wnumberpos.cpp src/widget/wnumberrate.cpp src/widget/woverview.cpp - src/widget/woverviewhsv.cpp - src/widget/woverviewlmh.cpp - src/widget/woverviewrgb.cpp src/widget/wpixmapstore.cpp src/widget/wpushbutton.cpp src/widget/wraterange.cpp diff --git a/src/preferences/dialog/dlgpreferences.cpp b/src/preferences/dialog/dlgpreferences.cpp index daf46fcea3b..47b96b9fa06 100644 --- a/src/preferences/dialog/dlgpreferences.cpp +++ b/src/preferences/dialog/dlgpreferences.cpp @@ -160,17 +160,11 @@ DlgPreferences::DlgPreferences( // ugly proxy for determining whether this is being instantiated for QML or legacy QWidgets GUI if (pSkinLoader) { - DlgPrefWaveform* pWaveformPage = new DlgPrefWaveform(this, m_pConfig, pLibrary); addPageWidget(PreferencesPage( - pWaveformPage, + new DlgPrefWaveform(this, m_pConfig, pLibrary), new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type)), tr("Waveforms"), "ic_preferences_waveforms.svg"); - connect(pWaveformPage, - &DlgPrefWaveform::reloadUserInterface, - this, - &DlgPreferences::reloadUserInterface, - Qt::DirectConnection); } addPageWidget(PreferencesPage( diff --git a/src/preferences/dialog/dlgprefwaveform.cpp b/src/preferences/dialog/dlgprefwaveform.cpp index 15e0daf6c0b..fbee90e4d4b 100644 --- a/src/preferences/dialog/dlgprefwaveform.cpp +++ b/src/preferences/dialog/dlgprefwaveform.cpp @@ -1,5 +1,6 @@ #include "preferences/dialog/dlgprefwaveform.h" +#include "control/controlobject.h" #include "library/dao/analysisdao.h" #include "library/library.h" #include "moc_dlgprefwaveform.cpp" @@ -21,6 +22,10 @@ DlgPrefWaveform::DlgPrefWaveform( waveformOverviewComboBox->addItem(tr("Filtered")); // "0" waveformOverviewComboBox->addItem(tr("HSV")); // "1" waveformOverviewComboBox->addItem(tr("RGB")); // "2" + m_pTypeControl = std::make_unique( + ConfigKey(QStringLiteral("[Waveform]"), + QStringLiteral("WaveformOverviewType"))); + m_pTypeControl->setReadOnly(); // Populate waveform options. WaveformWidgetFactory* factory = WaveformWidgetFactory::instance(); @@ -205,6 +210,7 @@ void DlgPrefWaveform::slotUpdate() { if (overviewType != waveformOverviewComboBox->currentIndex()) { waveformOverviewComboBox->setCurrentIndex(overviewType); } + slotSetWaveformOverviewType(overviewType); WaveformSettings waveformSettings(m_pConfig); enableWaveformCaching->setChecked(waveformSettings.waveformCachingEnabled()); @@ -299,7 +305,7 @@ void DlgPrefWaveform::updateEnableUntilMark() { void DlgPrefWaveform::slotSetWaveformOverviewType(int index) { m_pConfig->set(ConfigKey("[Waveform]","WaveformOverviewType"), ConfigValue(index)); - emit reloadUserInterface(); + m_pTypeControl->forceSet(index); } void DlgPrefWaveform::slotSetDefaultZoom(int index) { diff --git a/src/preferences/dialog/dlgprefwaveform.h b/src/preferences/dialog/dlgprefwaveform.h index e141c17f996..67a9ff9916b 100644 --- a/src/preferences/dialog/dlgprefwaveform.h +++ b/src/preferences/dialog/dlgprefwaveform.h @@ -6,6 +6,7 @@ #include "preferences/dialog/ui_dlgprefwaveformdlg.h" #include "preferences/usersettings.h" +class ControlObject; class Library; class DlgPrefWaveform : public DlgPreferencePage, public Ui::DlgPrefWaveformDlg { @@ -42,8 +43,6 @@ class DlgPrefWaveform : public DlgPreferencePage, public Ui::DlgPrefWaveformDlg void slotSetUntilMarkShowTime(bool checked); void slotSetUntilMarkAlign(int index); void slotSetUntilMarkTextPointSize(int value); - signals: - void reloadUserInterface(); private: void initWaveformControl(); @@ -51,6 +50,8 @@ class DlgPrefWaveform : public DlgPreferencePage, public Ui::DlgPrefWaveformDlg void notifyRebootNecessary(); void updateEnableUntilMark(); + std::unique_ptr m_pTypeControl; + UserSettingsPointer m_pConfig; std::shared_ptr m_pLibrary; }; diff --git a/src/skin/legacy/legacyskinparser.cpp b/src/skin/legacy/legacyskinparser.cpp index 5ca4c7b8dee..8490b95adf5 100644 --- a/src/skin/legacy/legacyskinparser.cpp +++ b/src/skin/legacy/legacyskinparser.cpp @@ -58,9 +58,7 @@ #include "widget/wnumberdb.h" #include "widget/wnumberpos.h" #include "widget/wnumberrate.h" -#include "widget/woverviewhsv.h" -#include "widget/woverviewlmh.h" -#include "widget/woverviewrgb.h" +#include "widget/woverview.h" #include "widget/wpixmapstore.h" #include "widget/wpushbutton.h" #include "widget/wraterange.h" @@ -984,17 +982,8 @@ QWidget* LegacySkinParser::parseOverview(const QDomElement& node) { return nullptr; } - WOverview* overviewWidget = nullptr; - - // "RGB" = "2", "HSV" = "1" or "Filtered" = "0" (LMH) waveform overview type - int type = m_pConfig->getValue(ConfigKey("[Waveform]","WaveformOverviewType"), 2); - if (type == 0) { - overviewWidget = new WOverviewLMH(group, m_pPlayerManager, m_pConfig, m_pParent); - } else if (type == 1) { - overviewWidget = new WOverviewHSV(group, m_pPlayerManager, m_pConfig, m_pParent); - } else { - overviewWidget = new WOverviewRGB(group, m_pPlayerManager, m_pConfig, m_pParent); - } + WOverview* overviewWidget = + new WOverview(group, m_pPlayerManager, m_pConfig, m_pParent); connect(overviewWidget, &WOverview::trackDropped, diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 794db86a9f4..35be6fe3d22 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -1,9 +1,11 @@ #include "woverview.h" #include +#include #include #include #include +#include #include #include "analyzer/analyzerprogress.h" @@ -13,6 +15,7 @@ #include "moc_woverview.cpp" #include "preferences/colorpalettesettings.h" #include "track/track.h" +#include "util/colorcomponents.h" #include "util/dnd.h" #include "util/duration.h" #include "util/math.h" @@ -29,13 +32,14 @@ WOverview::WOverview( UserSettingsPointer pConfig, QWidget* parent) : WWidget(parent), + m_group(group), + m_pConfig(pConfig), + m_type(-1), m_actualCompletion(0), m_pixmapDone(false), m_waveformPeak(-1.0), m_diffGain(0), m_devicePixelRatio(1.0), - m_group(group), - m_pConfig(pConfig), m_endOfTrack(false), m_bPassthroughEnabled(false), m_pCueMenuPopup(make_parented(pConfig, this)), @@ -44,7 +48,6 @@ WOverview::WOverview( m_bLeftClickDragging(false), m_iPickupPos(0), m_iPlayPos(0), - m_pHoveredMark(nullptr), m_bTimeRulerActive(false), m_orientation(Qt::Horizontal), m_iLabelFontSize(10), @@ -52,26 +55,35 @@ WOverview::WOverview( m_b(0.0), m_analyzerProgress(kAnalyzerProgressUnknown), m_trackLoaded(false), + m_pHoveredMark(nullptr), m_scaleFactor(1.0) { - m_endOfTrackControl = new ControlProxy( - m_group, "end_of_track", this, ControlFlag::NoAssertIfMissing); + m_endOfTrackControl = make_parented( + m_group, QStringLiteral("end_of_track"), this, ControlFlag::NoAssertIfMissing); m_endOfTrackControl->connectValueChanged(this, &WOverview::onEndOfTrackChange); - m_pRateRatioControl = new ControlProxy( - m_group, "rate_ratio", this, ControlFlag::NoAssertIfMissing); + m_pRateRatioControl = make_parented( + m_group, QStringLiteral("rate_ratio"), this, ControlFlag::NoAssertIfMissing); // Needed to recalculate range durations when rate slider is moved without the deck playing m_pRateRatioControl->connectValueChanged( this, &WOverview::onRateRatioChange); - m_trackSampleRateControl = new ControlProxy( - m_group, "track_samplerate", this, ControlFlag::NoAssertIfMissing); - m_trackSamplesControl = new ControlProxy(m_group, "track_samples", this); - m_playpositionControl = new ControlProxy( - m_group, "playposition", this, ControlFlag::NoAssertIfMissing); - m_pPassthroughControl = - new ControlProxy(m_group, "passthrough", this, ControlFlag::NoAssertIfMissing); + m_trackSampleRateControl = make_parented( + m_group, QStringLiteral("track_samplerate"), this, ControlFlag::NoAssertIfMissing); + m_trackSamplesControl = make_parented( + m_group, QStringLiteral("track_samples"), this); + m_playpositionControl = make_parented( + m_group, QStringLiteral("playposition"), this, ControlFlag::NoAssertIfMissing); + m_pPassthroughControl = make_parented( + m_group, QStringLiteral("passthrough"), this, ControlFlag::NoAssertIfMissing); m_pPassthroughControl->connectValueChanged(this, &WOverview::onPassthroughChange); m_bPassthroughEnabled = m_pPassthroughControl->toBool(); - m_pPassthroughLabel = new QLabel(this); + m_pTypeControl = make_parented( + QStringLiteral("[Waveform]"), + QStringLiteral("WaveformOverviewType"), + this); + m_pTypeControl->connectValueChanged(this, &WOverview::slotTypeChanged); + slotTypeChanged(m_pTypeControl->get()); + + m_pPassthroughLabel = make_parented(this); setAcceptDrops(true); @@ -165,7 +177,7 @@ void WOverview::setup(const QDomNode& node, const SkinContext& context) { child = child.nextSibling(); } - DEBUG_ASSERT(m_pPassthroughLabel != nullptr); + DEBUG_ASSERT(m_pPassthroughLabel.get() != nullptr); m_pPassthroughLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); // Shown on the overview waveform when vinyl passthrough is enabled m_pPassthroughLabel->setText(tr("Passthrough")); @@ -388,6 +400,20 @@ void WOverview::onPassthroughChange(double v) { update(); } +void WOverview::slotTypeChanged(double v) { + int type = static_cast(v); + VERIFY_OR_DEBUG_ASSERT(type >= 0 && type <= 2) { + type = 2; + } + if (type == m_type) { + return; + } + + m_type = type; + m_pWaveform.clear(); + slotWaveformSummaryUpdated(); +} + void WOverview::updateCues(const QList &loadedCues) { for (const CuePointer& currentCue : loadedCues) { const WaveformMarkPointer pMark = m_marks.getHotCueMark(currentCue->getHotCue()); @@ -577,7 +603,7 @@ void WOverview::leaveEvent(QEvent* pEvent) { void WOverview::paintEvent(QPaintEvent* pEvent) { Q_UNUSED(pEvent); - ScopedTimer t(u"WOverview::paintEvent"); + ScopedTimer t(QStringLiteral("WOverview::paintEvent")); QPainter painter(this); painter.fillRect(rect(), m_backgroundColor); @@ -1211,6 +1237,283 @@ void WOverview::drawPassthroughOverlay(QPainter* pPainter) { } } +bool WOverview::drawNextPixmapPart() { + ConstWaveformPointer pWaveform = getWaveform(); + if (!pWaveform) { + return false; + } + + const int dataSize = pWaveform->getDataSize(); + const double audioVisualRatio = pWaveform->getAudioVisualRatio(); + const double trackSamples = getTrackSamples(); + if (dataSize <= 0 || audioVisualRatio <= 0 || trackSamples <= 0) { + return false; + } + + if (m_waveformSourceImage.isNull()) { + // Waveform pixmap twice the height of the viewport to be scalable + // by total_gain + // We keep full range waveform data to scale it on paint + m_waveformSourceImage = QImage( + static_cast(trackSamples / audioVisualRatio / 2) + 1, + 2 * 255, + QImage::Format_ARGB32_Premultiplied); + m_waveformSourceImage.fill(QColor(0, 0, 0, 0).value()); + if (dataSize / 2 != m_waveformSourceImage.width()) { + qWarning() << "Track duration has changed since last analysis" + << m_waveformSourceImage.width() << "!=" << dataSize / 2; + } + } + DEBUG_ASSERT(!m_waveformSourceImage.isNull()); + + // Always multiple of 2 + const int waveformCompletion = pWaveform->getCompletion(); + // Test if there is some new to draw (at least of pixel width) + const int completionIncrement = waveformCompletion - m_actualCompletion; + + int visiblePixelIncrement = completionIncrement * length() / dataSize; + if (waveformCompletion < (dataSize - 2) && + (completionIncrement < 2 || visiblePixelIncrement == 0)) { + return false; + } + + const int nextCompletion = m_actualCompletion + completionIncrement; + + // qDebug() << "WOverview::drawNextPixmapPart() - nextCompletion:" + // << nextCompletion + // << "m_actualCompletion:" << m_actualCompletion + // << "waveformCompletion:" << waveformCompletion + // << "completionIncrement:" << completionIncrement; + + QPainter painter(&m_waveformSourceImage); + painter.translate(0.0, static_cast(m_waveformSourceImage.height()) / 2.0); + + if (m_type == 0) { + drawNextPixmapPartLMH(&painter, pWaveform, nextCompletion); + } else if (m_type == 1) { + drawNextPixmapPartHSV(&painter, pWaveform, nextCompletion); + } else { + drawNextPixmapPartRGB(&painter, pWaveform, nextCompletion); + } + + m_waveformImageScaled = QImage(); + m_diffGain = 0; + + // Test if the complete waveform is done + if (m_actualCompletion >= dataSize - 2) { + m_pixmapDone = true; + // qDebug() << "m_waveformPeakRatio" << m_waveformPeak; + } + + return true; +} + +void WOverview::drawNextPixmapPartHSV(QPainter* pPainter, + ConstWaveformPointer pWaveform, + const int nextCompletion) { + DEBUG_ASSERT(!m_waveformSourceImage.isNull()); + ScopedTimer t(QStringLiteral("WOverview::drawNextPixmapPartHSV")); + + // Get HSV of low color. + float h, s, v; + getHsvF(m_signalColors.getLowColor(), &h, &s, &v); + + QColor color; + float lo, hi, total; + + unsigned char maxLow[2] = {0, 0}; + unsigned char maxHigh[2] = {0, 0}; + unsigned char maxMid[2] = {0, 0}; + unsigned char maxAll[2] = {0, 0}; + + int currentCompletion = 0; + for (int currentCompletion = m_actualCompletion; + currentCompletion < nextCompletion; + currentCompletion += 2) { + maxAll[0] = pWaveform->getAll(currentCompletion); + maxAll[1] = pWaveform->getAll(currentCompletion + 1); + if (maxAll[0] || maxAll[1]) { + maxLow[0] = pWaveform->getLow(currentCompletion); + maxLow[1] = pWaveform->getLow(currentCompletion + 1); + maxMid[0] = pWaveform->getMid(currentCompletion); + maxMid[1] = pWaveform->getMid(currentCompletion + 1); + maxHigh[0] = pWaveform->getHigh(currentCompletion); + maxHigh[1] = pWaveform->getHigh(currentCompletion + 1); + + total = (maxLow[0] + maxLow[1] + maxMid[0] + maxMid[1] + + maxHigh[0] + maxHigh[1]) * + 1.2f; + + // Prevent division by zero + if (total > 0) { + // Normalize low and high + // (mid not need, because it not change the color) + lo = (maxLow[0] + maxLow[1]) / total; + hi = (maxHigh[0] + maxHigh[1]) / total; + } else { + lo = hi = 0.0; + } + + // Set color + color.setHsvF(h, 1.0f - hi, 1.0f - lo); + + pPainter->setPen(color); + pPainter->drawLine(QPoint(currentCompletion / 2, -maxAll[0]), + QPoint(currentCompletion / 2, maxAll[1])); + } + } + + // Evaluate waveform ratio peak + for (currentCompletion = m_actualCompletion; + currentCompletion < nextCompletion; + currentCompletion += 2) { + m_waveformPeak = math_max3( + m_waveformPeak, + static_cast(pWaveform->getAll(currentCompletion)), + static_cast(pWaveform->getAll(currentCompletion + 1))); + } + + m_actualCompletion = nextCompletion; +} + +void WOverview::drawNextPixmapPartLMH(QPainter* pPainter, + ConstWaveformPointer pWaveform, + const int nextCompletion) { + DEBUG_ASSERT(!m_waveformSourceImage.isNull()); + ScopedTimer t(QStringLiteral("WOverview::drawNextPixmapPartLMH")); + + QColor lowColor = m_signalColors.getLowColor(); + QPen lowColorPen(QBrush(lowColor), 1); + + QColor midColor = m_signalColors.getMidColor(); + QPen midColorPen(QBrush(midColor), 1); + + QColor highColor = m_signalColors.getHighColor(); + QPen highColorPen(QBrush(highColor), 1); + + int currentCompletion = 0; + for (currentCompletion = m_actualCompletion; + currentCompletion < nextCompletion; + currentCompletion += 2) { + unsigned char lowNeg = pWaveform->getLow(currentCompletion); + unsigned char lowPos = pWaveform->getLow(currentCompletion + 1); + if (lowPos || lowNeg) { + pPainter->setPen(lowColorPen); + pPainter->drawLine(QPoint(currentCompletion / 2, -lowNeg), + QPoint(currentCompletion / 2, lowPos)); + } + } + + for (currentCompletion = m_actualCompletion; + currentCompletion < nextCompletion; + currentCompletion += 2) { + pPainter->setPen(midColorPen); + pPainter->drawLine(QPoint(currentCompletion / 2, + -pWaveform->getMid(currentCompletion)), + QPoint(currentCompletion / 2, + pWaveform->getMid(currentCompletion + 1))); + } + + for (currentCompletion = m_actualCompletion; + currentCompletion < nextCompletion; + currentCompletion += 2) { + pPainter->setPen(highColorPen); + pPainter->drawLine(QPoint(currentCompletion / 2, + -pWaveform->getHigh(currentCompletion)), + QPoint(currentCompletion / 2, + pWaveform->getHigh(currentCompletion + 1))); + } + + // Evaluate waveform ratio peak + + for (currentCompletion = m_actualCompletion; + currentCompletion < nextCompletion; + currentCompletion += 2) { + m_waveformPeak = math_max3( + m_waveformPeak, + static_cast(pWaveform->getAll(currentCompletion)), + static_cast(pWaveform->getAll(currentCompletion + 1))); + } + + m_actualCompletion = nextCompletion; +} + +void WOverview::drawNextPixmapPartRGB(QPainter* pPainter, + ConstWaveformPointer pWaveform, + const int nextCompletion) { + DEBUG_ASSERT(!m_waveformSourceImage.isNull()); + ScopedTimer t(QStringLiteral("WOverview::drawNextPixmapPartRGB")); + + QColor color; + + float lowColor_r, lowColor_g, lowColor_b; + getRgbF(m_signalColors.getRgbLowColor(), &lowColor_r, &lowColor_g, &lowColor_b); + + float midColor_r, midColor_g, midColor_b; + getRgbF(m_signalColors.getRgbMidColor(), &midColor_r, &midColor_g, &midColor_b); + + float highColor_r, highColor_g, highColor_b; + getRgbF(m_signalColors.getRgbHighColor(), &highColor_r, &highColor_g, &highColor_b); + + int currentCompletion = 0; + for (currentCompletion = m_actualCompletion; + currentCompletion < nextCompletion; + currentCompletion += 2) { + unsigned char left = pWaveform->getAll(currentCompletion); + unsigned char right = pWaveform->getAll(currentCompletion + 1); + + // Retrieve "raw" LMH values from waveform + float low = static_cast(pWaveform->getLow(currentCompletion)); + float mid = static_cast(pWaveform->getMid(currentCompletion)); + float high = static_cast(pWaveform->getHigh(currentCompletion)); + + // Do matrix multiplication + float red = low * lowColor_r + mid * midColor_r + high * highColor_r; + float green = low * lowColor_g + mid * midColor_g + high * highColor_g; + float blue = low * lowColor_b + mid * midColor_b + high * highColor_b; + + // Normalize and draw + float max = math_max3(red, green, blue); + if (max > 0.0) { + color.setRgbF(red / max, green / max, blue / max); + pPainter->setPen(color); + pPainter->drawLine(QPointF(currentCompletion / 2, -left), + QPointF(currentCompletion / 2, 0)); + } + + // Retrieve "raw" LMH values from waveform + low = static_cast(pWaveform->getLow(currentCompletion + 1)); + mid = static_cast(pWaveform->getMid(currentCompletion + 1)); + high = static_cast(pWaveform->getHigh(currentCompletion + 1)); + + // Do matrix multiplication + red = low * lowColor_r + mid * midColor_r + high * highColor_r; + green = low * lowColor_g + mid * midColor_g + high * highColor_g; + blue = low * lowColor_b + mid * midColor_b + high * highColor_b; + + // Normalize and draw + max = math_max3(red, green, blue); + if (max > 0.0) { + color.setRgbF(red / max, green / max, blue / max); + pPainter->setPen(color); + pPainter->drawLine(QPointF(currentCompletion / 2, 0), + QPointF(currentCompletion / 2, right)); + } + } + + // Evaluate waveform ratio peak + for (currentCompletion = m_actualCompletion; + currentCompletion < nextCompletion; + currentCompletion += 2) { + m_waveformPeak = math_max3( + m_waveformPeak, + static_cast(pWaveform->getAll(currentCompletion)), + static_cast(pWaveform->getAll(currentCompletion + 1))); + } + + m_actualCompletion = nextCompletion; +} + void WOverview::paintText(const QString& text, QPainter* pPainter) { PainterScope painterScope(pPainter); m_lowColor.setAlphaF(0.5f); @@ -1270,6 +1573,7 @@ void WOverview::resizeEvent(QResizeEvent* pEvent) { void WOverview::dragEnterEvent(QDragEnterEvent* pEvent) { DragAndDropHelper::handleTrackDragEnterEvent(pEvent, m_group, m_pConfig); } + void WOverview::dropEvent(QDropEvent* pEvent) { DragAndDropHelper::handleTrackDropEvent(pEvent, *this, m_group, m_pConfig); } diff --git a/src/widget/woverview.h b/src/widget/woverview.h index 65446d13e96..12613fdd6af 100644 --- a/src/widget/woverview.h +++ b/src/widget/woverview.h @@ -17,13 +17,18 @@ #include "widget/wwidget.h" class PlayerManager; -; class QDomNode; class SkinContext; class WOverview : public WWidget, public TrackDropTarget { Q_OBJECT public: + WOverview( + const QString& group, + PlayerManager* pPlayerManager, + UserSettingsPointer pConfig, + QWidget* parent = nullptr); + void setup(const QDomNode& node, const SkinContext& context); virtual void initWithTrack(TrackPointer pTrack); @@ -39,11 +44,6 @@ class WOverview : public WWidget, public TrackDropTarget { void cloneDeck(const QString& sourceGroup, const QString& targetGroup) override; protected: - WOverview( - const QString& group, - PlayerManager* pPlayerManager, - UserSettingsPointer pConfig, - QWidget* parent = nullptr); void mouseMoveEvent(QMouseEvent* e) override; void mouseReleaseEvent(QMouseEvent* e) override; @@ -54,42 +54,6 @@ class WOverview : public WWidget, public TrackDropTarget { void dragEnterEvent(QDragEnterEvent* event) override; void dropEvent(QDropEvent* event) override; - inline int length() { - return m_orientation == Qt::Horizontal ? width() : height(); - } - - inline int breadth() { - return m_orientation == Qt::Horizontal ? height() : width(); - } - - ConstWaveformPointer getWaveform() const { - return m_pWaveform; - } - - double getTrackSamples() const { - if (m_trackLoaded) { - return m_trackSamplesControl->get(); - } else { - // Ignore the value, because the engine can still have the old track - // during loading - return 0.0; - } - } - - QImage m_waveformSourceImage; - QImage m_waveformImageScaled; - - WaveformSignalColors m_signalColors; - - // Hold the last visual sample processed to generate the pixmap - int m_actualCompletion; - - bool m_pixmapDone; - float m_waveformPeak; - - float m_diffGain; - qreal m_devicePixelRatio; - private slots: void onEndOfTrackChange(double v); @@ -102,10 +66,22 @@ class WOverview : public WWidget, public TrackDropTarget { void slotWaveformSummaryUpdated(); void slotCueMenuPopupAboutToHide(); + void slotTypeChanged(double v); + private: // Append the waveform overview pixmap according to available data // in waveform - virtual bool drawNextPixmapPart() = 0; + bool drawNextPixmapPart(); + void drawNextPixmapPartHSV(QPainter* pPainter, + ConstWaveformPointer pWaveform, + const int nextCompletion); + void drawNextPixmapPartLMH(QPainter* pPainter, + ConstWaveformPointer pWaveform, + const int nextCompletion); + void drawNextPixmapPartRGB(QPainter* pPainter, + ConstWaveformPointer pWaveform, + const int nextCompletion); + void drawEndOfTrackBackground(QPainter* pPainter); void drawAxis(QPainter* pPainter); void drawWaveformPixmap(QPainter* pPainter); @@ -130,20 +106,41 @@ class WOverview : public WWidget, public TrackDropTarget { void updateCues(const QList &loadedCues); + inline int length() { + return m_orientation == Qt::Horizontal ? width() : height(); + } + + inline int breadth() { + return m_orientation == Qt::Horizontal ? height() : width(); + } + + ConstWaveformPointer getWaveform() const { + return m_pWaveform; + } + + double getTrackSamples() const { + if (m_trackLoaded) { + return m_trackSamplesControl->get(); + } else { + // Ignore the value, because the engine can still have the old track + // during loading + return 0.0; + } + } + + // Hold the last visual sample processed to generate the pixmap + const QString m_group; UserSettingsPointer m_pConfig; - ControlProxy* m_endOfTrackControl; + + int m_type; + int m_actualCompletion; + bool m_pixmapDone; + float m_waveformPeak; + float m_diffGain; + qreal m_devicePixelRatio; bool m_endOfTrack; bool m_bPassthroughEnabled; - ControlProxy* m_pRateRatioControl; - ControlProxy* m_trackSampleRateControl; - ControlProxy* m_trackSamplesControl; - ControlProxy* m_playpositionControl; - ControlProxy* m_pPassthroughControl; - - // Current active track - TrackPointer m_pCurrentTrack; - ConstWaveformPointer m_pWaveform; parented_ptr m_pCueMenuPopup; bool m_bShowCueTimes; @@ -155,19 +152,44 @@ class WOverview : public WWidget, public TrackDropTarget { int m_iPickupPos; // position of the overlay shadow int m_iPlayPos; + bool m_bTimeRulerActive; + Qt::Orientation m_orientation; + int m_iLabelFontSize; + // Coefficient value-position linear transposition + double m_a; + double m_b; + + AnalyzerProgress m_analyzerProgress; + bool m_trackLoaded; WaveformMarkPointer m_pHoveredMark; - bool m_bTimeRulerActive; + double m_scaleFactor; + + // Current active track + TrackPointer m_pCurrentTrack; + ConstWaveformPointer m_pWaveform; + + QImage m_waveformSourceImage; + QImage m_waveformImageScaled; + + WaveformSignalColors m_signalColors; + + parented_ptr m_endOfTrackControl; + parented_ptr m_pRateRatioControl; + parented_ptr m_trackSampleRateControl; + parented_ptr m_trackSamplesControl; + parented_ptr m_playpositionControl; + parented_ptr m_pPassthroughControl; + parented_ptr m_pTypeControl; + QPointF m_timeRulerPos; WaveformMarkLabel m_timeRulerPositionLabel; WaveformMarkLabel m_timeRulerDistanceLabel; - Qt::Orientation m_orientation; QPixmap m_backgroundPixmap; QString m_backgroundPixmapPath; QColor m_backgroundColor; - int m_iLabelFontSize; QColor m_labelTextColor; QColor m_labelBackgroundColor; QColor m_axesColor; @@ -177,18 +199,11 @@ class WOverview : public WWidget, public TrackDropTarget { QColor m_playedOverlayColor; QColor m_lowColor; int m_dimBrightThreshold; - QLabel* m_pPassthroughLabel; + parented_ptr m_pPassthroughLabel; WaveformMarkSet m_marks; std::vector m_markRanges; WaveformMarkLabel m_cuePositionLabel; WaveformMarkLabel m_cueTimeDistanceLabel; - // Coefficient value-position linear transposition - double m_a; - double m_b; - - AnalyzerProgress m_analyzerProgress; - bool m_trackLoaded; - double m_scaleFactor; }; diff --git a/src/widget/woverviewhsv.cpp b/src/widget/woverviewhsv.cpp deleted file mode 100644 index 8f20884b1d2..00000000000 --- a/src/widget/woverviewhsv.cpp +++ /dev/null @@ -1,146 +0,0 @@ -#include "widget/woverviewhsv.h" - -#include -#include - -#include "moc_woverviewhsv.cpp" -#include "util/colorcomponents.h" -#include "util/math.h" -#include "util/timer.h" -#include "waveform/waveform.h" - -WOverviewHSV::WOverviewHSV( - const QString& group, - PlayerManager* pPlayerManager, - UserSettingsPointer pConfig, - QWidget* parent) - : WOverview(group, pPlayerManager, pConfig, parent) { -} - -bool WOverviewHSV::drawNextPixmapPart() { - ScopedTimer t(u"WOverviewHSV::drawNextPixmapPart"); - - //qDebug() << "WOverview::drawNextPixmapPart()"; - - int currentCompletion; - - ConstWaveformPointer pWaveform = getWaveform(); - if (!pWaveform) { - return false; - } - - const int dataSize = pWaveform->getDataSize(); - const double audioVisualRatio = pWaveform->getAudioVisualRatio(); - const double trackSamples = getTrackSamples(); - if (dataSize <= 0 || audioVisualRatio <= 0 || trackSamples <= 0) { - return false; - } - - if (m_waveformSourceImage.isNull()) { - // Waveform pixmap twice the height of the viewport to be scalable - // by total_gain - // We keep full range waveform data to scale it on paint - m_waveformSourceImage = QImage( - static_cast(trackSamples / audioVisualRatio / 2) + 1, - 2 * 255, - QImage::Format_ARGB32_Premultiplied); - m_waveformSourceImage.fill(QColor(0, 0, 0, 0).value()); - if (dataSize / 2 != m_waveformSourceImage.width()) { - qWarning() << "Track duration has changed since last analysis" - << m_waveformSourceImage.width() << "!=" << dataSize / 2; - } - } - DEBUG_ASSERT(!m_waveformSourceImage.isNull()); - - // Always multiple of 2 - const int waveformCompletion = pWaveform->getCompletion(); - // Test if there is some new to draw (at least of pixel width) - const int completionIncrement = waveformCompletion - m_actualCompletion; - - int visiblePixelIncrement = completionIncrement * length() / dataSize; - if (waveformCompletion < (dataSize - 2) && - (completionIncrement < 2 || visiblePixelIncrement == 0)) { - return false; - } - - const int nextCompletion = m_actualCompletion + completionIncrement; - - //qDebug() << "WOverview::drawNextPixmapPart() - nextCompletion:" - // << nextCompletion - // << "m_actualCompletion:" << m_actualCompletion - // << "waveformCompletion:" << waveformCompletion - // << "completionIncrement:" << completionIncrement; - - - QPainter painter(&m_waveformSourceImage); - painter.translate(0.0, static_cast(m_waveformSourceImage.height()) / 2.0); - - // Get HSV of low color. - float h, s, v; - getHsvF(m_signalColors.getLowColor(), &h, &s, &v); - - QColor color; - float lo, hi, total; - - unsigned char maxLow[2] = {0, 0}; - unsigned char maxHigh[2] = {0, 0}; - unsigned char maxMid[2] = {0, 0}; - unsigned char maxAll[2] = {0, 0}; - - for (currentCompletion = m_actualCompletion; - currentCompletion < nextCompletion; currentCompletion += 2) { - maxAll[0] = pWaveform->getAll(currentCompletion); - maxAll[1] = pWaveform->getAll(currentCompletion+1); - if (maxAll[0] || maxAll[1]) { - maxLow[0] = pWaveform->getLow(currentCompletion); - maxLow[1] = pWaveform->getLow(currentCompletion+1); - maxMid[0] = pWaveform->getMid(currentCompletion); - maxMid[1] = pWaveform->getMid(currentCompletion+1); - maxHigh[0] = pWaveform->getHigh(currentCompletion); - maxHigh[1] = pWaveform->getHigh(currentCompletion+1); - - total = (maxLow[0] + maxLow[1] + maxMid[0] + maxMid[1] + - maxHigh[0] + maxHigh[1]) * - 1.2f; - - // Prevent division by zero - if (total > 0) { - // Normalize low and high - // (mid not need, because it not change the color) - lo = (maxLow[0] + maxLow[1]) / total; - hi = (maxHigh[0] + maxHigh[1]) / total; - } else { - lo = hi = 0.0; - } - - // Set color - color.setHsvF(h, 1.0f - hi, 1.0f - lo); - - painter.setPen(color); - painter.drawLine(QPoint(currentCompletion / 2, -maxAll[0]), - QPoint(currentCompletion / 2, maxAll[1])); - } - } - - // Evaluate waveform ratio peak - - for (currentCompletion = m_actualCompletion; - currentCompletion < nextCompletion; currentCompletion += 2) { - m_waveformPeak = math_max3( - m_waveformPeak, - static_cast(pWaveform->getAll(currentCompletion)), - static_cast(pWaveform->getAll(currentCompletion + 1))); - } - - m_actualCompletion = nextCompletion; - m_waveformImageScaled = QImage(); - m_diffGain = 0; - - // Test if the complete waveform is done - if (m_actualCompletion >= dataSize - 2) { - m_pixmapDone = true; - //qDebug() << "m_waveformPeakRatio" << m_waveformPeak; - } - - return true; -} diff --git a/src/widget/woverviewhsv.h b/src/widget/woverviewhsv.h deleted file mode 100644 index 3fa9bf7d153..00000000000 --- a/src/widget/woverviewhsv.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "widget/woverview.h" - -class WOverviewHSV : public WOverview { - Q_OBJECT - public: - WOverviewHSV( - const QString& group, - PlayerManager* pPlayerManager, - UserSettingsPointer pConfig, - QWidget* parent = nullptr); - - private: - bool drawNextPixmapPart() override; -}; diff --git a/src/widget/woverviewlmh.cpp b/src/widget/woverviewlmh.cpp deleted file mode 100644 index 72ad03124a5..00000000000 --- a/src/widget/woverviewlmh.cpp +++ /dev/null @@ -1,137 +0,0 @@ -#include "widget/woverviewlmh.h" - -#include -#include -#include - -#include "moc_woverviewlmh.cpp" -#include "util/math.h" -#include "util/timer.h" -#include "waveform/waveform.h" - -WOverviewLMH::WOverviewLMH( - const QString& group, - PlayerManager* pPlayerManager, - UserSettingsPointer pConfig, - QWidget* parent) - : WOverview(group, pPlayerManager, pConfig, parent) { -} - -bool WOverviewLMH::drawNextPixmapPart() { - ScopedTimer t(u"WOverviewLMH::drawNextPixmapPart"); - - //qDebug() << "WOverview::drawNextPixmapPart()"; - - int currentCompletion; - - ConstWaveformPointer pWaveform = getWaveform(); - if (!pWaveform) { - return false; - } - - const int dataSize = pWaveform->getDataSize(); - const double audioVisualRatio = pWaveform->getAudioVisualRatio(); - const double trackSamples = getTrackSamples(); - if (dataSize <= 0 || audioVisualRatio <= 0 || trackSamples <= 0) { - return false; - } - - if (m_waveformSourceImage.isNull()) { - // Waveform pixmap twice the height of the viewport to be scalable - // by total_gain - // We keep full range waveform data to scale it on paint - m_waveformSourceImage = QImage( - static_cast(trackSamples / audioVisualRatio / 2) + 1, - 2 * 255, - QImage::Format_ARGB32_Premultiplied); - m_waveformSourceImage.fill(QColor(0, 0, 0, 0).value()); - if (dataSize / 2 != m_waveformSourceImage.width()) { - qWarning() << "Track duration has changed since last analysis" - << m_waveformSourceImage.width() << "!=" << dataSize / 2; - } - } - DEBUG_ASSERT(!m_waveformSourceImage.isNull()); - - // Always multiple of 2 - const int waveformCompletion = pWaveform->getCompletion(); - // Test if there is some new to draw (at least of pixel width) - const int completionIncrement = waveformCompletion - m_actualCompletion; - - int visiblePixelIncrement = completionIncrement * length() / dataSize; - if (waveformCompletion < (dataSize - 2) && - (completionIncrement < 2 || visiblePixelIncrement == 0)) { - return false; - } - - const int nextCompletion = m_actualCompletion + completionIncrement; - - //qDebug() << "WOverview::drawNextPixmapPart() - nextCompletion:" - // << nextCompletion - // << "m_actualCompletion:" << m_actualCompletion - // << "waveformCompletion:" << waveformCompletion - // << "completionIncrement:" << completionIncrement; - - - QPainter painter(&m_waveformSourceImage); - painter.translate(0.0, static_cast(m_waveformSourceImage.height()) / 2.0); - - QColor lowColor = m_signalColors.getLowColor(); - QPen lowColorPen(QBrush(lowColor), 1); - - QColor midColor = m_signalColors.getMidColor(); - QPen midColorPen(QBrush(midColor), 1); - - QColor highColor = m_signalColors.getHighColor(); - QPen highColorPen(QBrush(highColor), 1); - - for (currentCompletion = m_actualCompletion; - currentCompletion < nextCompletion; currentCompletion += 2) { - unsigned char lowNeg = pWaveform->getLow(currentCompletion); - unsigned char lowPos = pWaveform->getLow(currentCompletion+1); - if (lowPos || lowNeg) { - painter.setPen(lowColorPen); - painter.drawLine(QPoint(currentCompletion / 2, -lowNeg), - QPoint(currentCompletion / 2, lowPos)); - } - } - - for (currentCompletion = m_actualCompletion; - currentCompletion < nextCompletion; currentCompletion += 2) { - painter.setPen(midColorPen); - painter.drawLine(QPoint(currentCompletion / 2, - -pWaveform->getMid(currentCompletion)), - QPoint(currentCompletion / 2, - pWaveform->getMid(currentCompletion+1))); - } - - for (currentCompletion = m_actualCompletion; - currentCompletion < nextCompletion; currentCompletion += 2) { - painter.setPen(highColorPen); - painter.drawLine(QPoint(currentCompletion / 2, - -pWaveform->getHigh(currentCompletion)), - QPoint(currentCompletion / 2, - pWaveform->getHigh(currentCompletion+1))); - } - - // Evaluate waveform ratio peak - - for (currentCompletion = m_actualCompletion; - currentCompletion < nextCompletion; currentCompletion += 2) { - m_waveformPeak = math_max3( - m_waveformPeak, - static_cast(pWaveform->getAll(currentCompletion)), - static_cast(pWaveform->getAll(currentCompletion + 1))); - } - - m_actualCompletion = nextCompletion; - m_waveformImageScaled = QImage(); - m_diffGain = 0; - - // Test if the complete waveform is done - if (m_actualCompletion >= dataSize - 2) { - m_pixmapDone = true; - //qDebug() << "m_waveformPeakRatio" << m_waveformPeak; - } - - return true; -} diff --git a/src/widget/woverviewlmh.h b/src/widget/woverviewlmh.h deleted file mode 100644 index 8bc05fb3c8a..00000000000 --- a/src/widget/woverviewlmh.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "widget/woverview.h" - -class WOverviewLMH : public WOverview { - Q_OBJECT - public: - WOverviewLMH( - const QString& group, - PlayerManager* pPlayerManager, - UserSettingsPointer pConfig, - QWidget* parent = nullptr); - - private: - bool drawNextPixmapPart() override; -}; diff --git a/src/widget/woverviewrgb.cpp b/src/widget/woverviewrgb.cpp deleted file mode 100644 index 5cc8ced168e..00000000000 --- a/src/widget/woverviewrgb.cpp +++ /dev/null @@ -1,152 +0,0 @@ -#include "widget/woverviewrgb.h" - -#include - -#include "moc_woverviewrgb.cpp" -#include "util/colorcomponents.h" -#include "util/math.h" -#include "util/timer.h" -#include "waveform/waveform.h" - -WOverviewRGB::WOverviewRGB( - const QString& group, - PlayerManager* pPlayerManager, - UserSettingsPointer pConfig, - QWidget* parent) - : WOverview(group, pPlayerManager, pConfig, parent) { -} - -bool WOverviewRGB::drawNextPixmapPart() { - ScopedTimer t(u"WOverviewRGB::drawNextPixmapPart"); - - //qDebug() << "WOverview::drawNextPixmapPart()"; - - int currentCompletion; - - ConstWaveformPointer pWaveform = getWaveform(); - if (!pWaveform) { - return false; - } - - const int dataSize = pWaveform->getDataSize(); - const double audioVisualRatio = pWaveform->getAudioVisualRatio(); - const double trackSamples = getTrackSamples(); - if (dataSize <= 0 || audioVisualRatio <= 0 || trackSamples <= 0) { - return false; - } - - if (m_waveformSourceImage.isNull()) { - // Waveform pixmap twice the height of the viewport to be scalable - // by total_gain - // We keep full range waveform data to scale it on paint - m_waveformSourceImage = QImage( - static_cast(trackSamples / audioVisualRatio / 2) + 1, - 2 * 255, - QImage::Format_ARGB32_Premultiplied); - m_waveformSourceImage.fill(QColor(0, 0, 0, 0).value()); - if (dataSize / 2 != m_waveformSourceImage.width()) { - qWarning() << "Track duration has changed since last analysis" - << m_waveformSourceImage.width() << "!=" << dataSize / 2; - } - } - DEBUG_ASSERT(!m_waveformSourceImage.isNull()); - - // Always multiple of 2 - const int waveformCompletion = pWaveform->getCompletion(); - // Test if there is some new to draw (at least of pixel width) - const int completionIncrement = waveformCompletion - m_actualCompletion; - - int visiblePixelIncrement = completionIncrement * length() / dataSize; - if (waveformCompletion < (dataSize - 2) && - (completionIncrement < 2 || visiblePixelIncrement == 0)) { - return false; - } - - const int nextCompletion = m_actualCompletion + completionIncrement; - - //qDebug() << "WOverview::drawNextPixmapPart() - nextCompletion:" - // << nextCompletion - // << "m_actualCompletion:" << m_actualCompletion - // << "waveformCompletion:" << waveformCompletion - // << "completionIncrement:" << completionIncrement; - - QPainter painter(&m_waveformSourceImage); - painter.translate(0.0, static_cast(m_waveformSourceImage.height()) / 2.0); - - QColor color; - - float lowColor_r, lowColor_g, lowColor_b; - getRgbF(m_signalColors.getRgbLowColor(), &lowColor_r, &lowColor_g, &lowColor_b); - - float midColor_r, midColor_g, midColor_b; - getRgbF(m_signalColors.getRgbMidColor(), &midColor_r, &midColor_g, &midColor_b); - - float highColor_r, highColor_g, highColor_b; - getRgbF(m_signalColors.getRgbHighColor(), &highColor_r, &highColor_g, &highColor_b); - - for (currentCompletion = m_actualCompletion; - currentCompletion < nextCompletion; currentCompletion += 2) { - - unsigned char left = pWaveform->getAll(currentCompletion); - unsigned char right = pWaveform->getAll(currentCompletion + 1); - - // Retrieve "raw" LMH values from waveform - float low = static_cast(pWaveform->getLow(currentCompletion)); - float mid = static_cast(pWaveform->getMid(currentCompletion)); - float high = static_cast(pWaveform->getHigh(currentCompletion)); - - // Do matrix multiplication - float red = low * lowColor_r + mid * midColor_r + high * highColor_r; - float green = low * lowColor_g + mid * midColor_g + high * highColor_g; - float blue = low * lowColor_b + mid * midColor_b + high * highColor_b; - - // Normalize and draw - float max = math_max3(red, green, blue); - if (max > 0.0) { - color.setRgbF(red / max, green / max, blue / max); - painter.setPen(color); - painter.drawLine(QPointF(currentCompletion / 2, -left), - QPointF(currentCompletion / 2, 0)); - } - - // Retrieve "raw" LMH values from waveform - low = static_cast(pWaveform->getLow(currentCompletion + 1)); - mid = static_cast(pWaveform->getMid(currentCompletion + 1)); - high = static_cast(pWaveform->getHigh(currentCompletion + 1)); - - // Do matrix multiplication - red = low * lowColor_r + mid * midColor_r + high * highColor_r; - green = low * lowColor_g + mid * midColor_g + high * highColor_g; - blue = low * lowColor_b + mid * midColor_b + high * highColor_b; - - // Normalize and draw - max = math_max3(red, green, blue); - if (max > 0.0) { - color.setRgbF(red / max, green / max, blue / max); - painter.setPen(color); - painter.drawLine(QPointF(currentCompletion / 2, 0), - QPointF(currentCompletion / 2, right)); - } - } - - // Evaluate waveform ratio peak - for (currentCompletion = m_actualCompletion; - currentCompletion < nextCompletion; currentCompletion += 2) { - m_waveformPeak = math_max3( - m_waveformPeak, - static_cast(pWaveform->getAll(currentCompletion)), - static_cast(pWaveform->getAll(currentCompletion + 1))); - } - - m_actualCompletion = nextCompletion; - m_waveformImageScaled = QImage(); - m_diffGain = 0; - - // Test if the complete waveform is done - if (m_actualCompletion >= dataSize - 2) { - m_pixmapDone = true; - //qDebug() << "m_waveformPeakRatio" << m_waveformPeak; - } - - return true; -} diff --git a/src/widget/woverviewrgb.h b/src/widget/woverviewrgb.h deleted file mode 100644 index 000b60be4f1..00000000000 --- a/src/widget/woverviewrgb.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "widget/woverview.h" - -class WOverviewRGB : public WOverview { - Q_OBJECT - public: - WOverviewRGB( - const QString& group, - PlayerManager* pPlayerManager, - UserSettingsPointer pConfig, - QWidget* parent = nullptr); - - private: - bool drawNextPixmapPart() override; -};