diff --git a/CMakeLists.txt b/CMakeLists.txt
index 89b5fbaf84f..d7f9811acd9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1219,6 +1219,7 @@ add_library(
src/library/missing_hidden/hiddentablemodel.cpp
src/library/missing_hidden/missingtablemodel.cpp
src/library/mixxxlibraryfeature.cpp
+ src/library/overviewcache.cpp
src/library/parser.cpp
src/library/parsercsv.cpp
src/library/parserm3u.cpp
@@ -1248,6 +1249,7 @@ add_library(
src/library/tabledelegates/keydelegate.cpp
src/library/tabledelegates/locationdelegate.cpp
src/library/tabledelegates/multilineeditdelegate.cpp
+ src/library/tabledelegates/overviewdelegate.cpp
src/library/tabledelegates/previewbuttondelegate.cpp
src/library/tabledelegates/stardelegate.cpp
src/library/tabledelegates/stareditor.cpp
@@ -1470,11 +1472,13 @@ add_library(
src/util/workerthreadscheduler.cpp
src/util/xml.cpp
src/waveform/guitick.cpp
+ src/waveform/overviewtype.cpp
src/waveform/renderers/glwaveformrenderbackground.cpp
src/waveform/renderers/glvsynctestrenderer.cpp
src/waveform/renderers/waveformmark.cpp
src/waveform/renderers/waveformmarkrange.cpp
src/waveform/renderers/waveformmarkset.cpp
+ src/waveform/renderers/waveformoverviewrenderer.cpp
src/waveform/renderers/waveformrenderbackground.cpp
src/waveform/renderers/waveformrenderbeat.cpp
src/waveform/renderers/waveformrendererabstract.cpp
diff --git a/res/skins/Deere/library.xml b/res/skins/Deere/library.xml
index 1f08cc11345..235578a2796 100644
--- a/res/skins/Deere/library.xml
+++ b/res/skins/Deere/library.xml
@@ -72,6 +72,10 @@
false
0.175
+
+
+
diff --git a/res/skins/Deere/preview_deck.xml b/res/skins/Deere/preview_deck.xml
index 1806cb669f5..7437022b816 100644
--- a/res/skins/Deere/preview_deck.xml
+++ b/res/skins/Deere/preview_deck.xml
@@ -100,7 +100,7 @@
- #FF8000
+
#00FF00
false
diff --git a/res/skins/Deere/sampler_controls_row.xml b/res/skins/Deere/sampler_controls_row.xml
index a56c178839a..62e5b8d6a88 100644
--- a/res/skins/Deere/sampler_controls_row.xml
+++ b/res/skins/Deere/sampler_controls_row.xml
@@ -42,7 +42,7 @@
- #FF8000
+
#00FF00
false
diff --git a/res/skins/Deere/skin.xml b/res/skins/Deere/skin.xml
index 74f7ad973c8..0cbd46f1bc9 100644
--- a/res/skins/Deere/skin.xml
+++ b/res/skins/Deere/skin.xml
@@ -135,6 +135,9 @@
22,-1
f,me
+
+ #FF8000
+
+
+
diff --git a/res/skins/Shade/deck_overview.xml b/res/skins/Shade/deck_overview.xml
index 5d7ad987fc0..044a455346e 100644
--- a/res/skins/Shade/deck_overview.xml
+++ b/res/skins/Shade/deck_overview.xml
@@ -9,7 +9,7 @@
- #191F24
+
#00FF00
#EA0000
diff --git a/res/skins/Shade/preview_deck.xml b/res/skins/Shade/preview_deck.xml
index aa132122d68..c260cdc88ad 100644
--- a/res/skins/Shade/preview_deck.xml
+++ b/res/skins/Shade/preview_deck.xml
@@ -171,7 +171,7 @@
- #191F24
+
#00FF00
false
diff --git a/res/skins/Shade/sampler.xml b/res/skins/Shade/sampler.xml
index ec147651d11..09a06ffccfb 100644
--- a/res/skins/Shade/sampler.xml
+++ b/res/skins/Shade/sampler.xml
@@ -176,7 +176,7 @@
- #191F24
+
#00FF00
false
diff --git a/res/skins/Shade/skin.xml b/res/skins/Shade/skin.xml
index 57584106cfd..a87cae5b918 100644
--- a/res/skins/Shade/skin.xml
+++ b/res/skins/Shade/skin.xml
@@ -273,6 +273,17 @@
vertical
+
+
+ #191F24
+ #55F764
+
+ #8d98a3
+
+
+
- #55F764
-
- #8d98a3
-
-
Overview1
diff --git a/res/skins/Tango/decks/preview_deck.xml b/res/skins/Tango/decks/preview_deck.xml
index f31cd98321f..9d156a3a429 100644
--- a/res/skins/Tango/decks/preview_deck.xml
+++ b/res/skins/Tango/decks/preview_deck.xml
@@ -114,7 +114,7 @@ Variables:
1me,34f
#151515
- #bababa
+
#FF4300
#00FF00
diff --git a/res/skins/Tango/library.xml b/res/skins/Tango/library.xml
index 090bea24728..3add726203b 100644
--- a/res/skins/Tango/library.xml
+++ b/res/skins/Tango/library.xml
@@ -118,6 +118,10 @@ Description:
#eece33
false
0.2
+
+
+
diff --git a/res/skins/Tango/mic_aux_sampler/sampler.xml b/res/skins/Tango/mic_aux_sampler/sampler.xml
index 305776cdbf8..83ff7c842d3 100644
--- a/res/skins/Tango/mic_aux_sampler/sampler.xml
+++ b/res/skins/Tango/mic_aux_sampler/sampler.xml
@@ -200,7 +200,7 @@ Variables:
me,min
#151515
- #bababa
+
#FF4300
false
diff --git a/res/skins/Tango/skin.xml b/res/skins/Tango/skin.xml
index 052b60090d3..d2cd9c342c0 100644
--- a/res/skins/Tango/skin.xml
+++ b/res/skins/Tango/skin.xml
@@ -182,6 +182,8 @@
#19260B
purple
green
+
+ #bababa
empty pixmap, add to ignore list";
+ m_tracksWithoutOverview.insert(res.trackId);
+ }
+ m_currentlyLoading.remove(res.trackId);
+
+ emit overviewReady(res.requester, res.trackId, !pixmap.isNull());
+}
diff --git a/src/library/overviewcache.h b/src/library/overviewcache.h
new file mode 100644
index 00000000000..a052f211e42
--- /dev/null
+++ b/src/library/overviewcache.h
@@ -0,0 +1,80 @@
+#pragma once
+
+#include
+
+#include "analyzer/analyzerprogress.h"
+#include "preferences/usersettings.h"
+#include "track/track.h"
+#include "util/db/dbconnectionpool.h"
+#include "util/singleton.h"
+#include "waveform/overviewtype.h"
+
+class WaveformSignalColors;
+
+class OverviewCache : public QObject, public Singleton {
+ Q_OBJECT
+ public:
+ void onTrackSummaryChanged(TrackId);
+
+ QPixmap requestCachedOverview(
+ const mixxx::OverviewType type,
+ const TrackId trackId,
+ const QObject* pRequester,
+ const QSize desiredSize);
+ QPixmap requestUncachedOverview(
+ const mixxx::OverviewType type,
+ const WaveformSignalColors& signalColors,
+ const TrackId trackId,
+ const QObject* pRequester,
+ const QSize desiredSize);
+
+ struct FutureResult {
+ FutureResult()
+ : requester(nullptr) {
+ }
+
+ TrackId trackId;
+ mixxx::OverviewType type;
+ QImage image;
+ QSize resizedToSize;
+ const QObject* requester;
+ };
+
+ public slots:
+ void onNormalizeOrVisualGainChanged();
+ void overviewPrepared();
+ void onTrackAnalysisProgress(TrackId trackId, AnalyzerProgress analyzerProgress);
+
+ signals:
+ void overviewReady(
+ const QObject* pRequester,
+ const TrackId trackId,
+ bool pixmapValid);
+
+ void overviewChanged(TrackId);
+
+ protected:
+ OverviewCache(UserSettingsPointer pConfig,
+ mixxx::DbConnectionPoolPtr m_pDbConnectionPool);
+ virtual ~OverviewCache() override = default;
+ friend class Singleton;
+
+ static FutureResult prepareOverview(
+ UserSettingsPointer pConfig,
+ mixxx::DbConnectionPoolPtr pDbConnectionPool,
+ const mixxx::OverviewType type,
+ const WaveformSignalColors& signalColors,
+ const TrackId trackId,
+ const QObject* pRequester,
+ const QSize desiredSize);
+
+ private:
+ UserSettingsPointer m_pConfig;
+ mixxx::DbConnectionPoolPtr m_pDbConnectionPool;
+
+ QSet m_currentlyLoading;
+ QSet m_tracksWithoutOverview;
+ QMultiHash m_cacheKeysByTrackId;
+ bool m_clearingCache;
+ bool m_stopClearing;
+};
diff --git a/src/library/tabledelegates/overviewdelegate.cpp b/src/library/tabledelegates/overviewdelegate.cpp
new file mode 100644
index 00000000000..ec6f93fb689
--- /dev/null
+++ b/src/library/tabledelegates/overviewdelegate.cpp
@@ -0,0 +1,172 @@
+#include "library/tabledelegates/overviewdelegate.h"
+
+#include
+
+#include "control/controlproxy.h"
+#include "library/dao/trackdao.h"
+#include "library/overviewcache.h"
+#include "library/trackmodel.h"
+#include "moc_overviewdelegate.cpp"
+#include "util/logger.h"
+#include "util/make_const_iterator.h"
+#include "widget/wlibrary.h"
+
+namespace {
+
+const mixxx::Logger kLogger("OverviewDelegate");
+
+inline TrackModel* asTrackModel(
+ QTableView* pTableView) {
+ auto* pTrackModel =
+ dynamic_cast(pTableView->model());
+ DEBUG_ASSERT(pTrackModel);
+ return pTrackModel;
+}
+
+inline WLibrary* findLibraryWidgetParent(QWidget* pWidget) {
+ while (pWidget) {
+ WLibrary* pLibrary = qobject_cast(pWidget);
+ if (pLibrary) {
+ return pLibrary;
+ }
+
+ pWidget = pWidget->parentWidget();
+ }
+
+ return nullptr;
+}
+
+} // anonymous namespace
+
+OverviewDelegate::OverviewDelegate(QTableView* pTableView)
+ : TableItemDelegate(pTableView),
+ m_pTrackModel(asTrackModel(pTableView)),
+ m_pCache(OverviewCache::instance()),
+ m_type(mixxx::OverviewType::RGB),
+ m_inhibitLazyLoading(false) {
+ WLibrary* pLibrary = findLibraryWidgetParent(pTableView);
+ if (pLibrary) {
+ m_signalColors = pLibrary->getOverviewSignalColors();
+ }
+
+ connect(m_pCache,
+ &OverviewCache::overviewReady,
+ this,
+ &OverviewDelegate::slotOverviewReady);
+
+ connect(m_pCache,
+ &OverviewCache::overviewChanged,
+ this,
+ &OverviewDelegate::slotOverviewChanged);
+
+ m_pTypeControl = make_parented(
+ QStringLiteral("[Waveform]"),
+ QStringLiteral("WaveformOverviewType"),
+ this);
+ m_pTypeControl->connectValueChanged(this, &OverviewDelegate::slotTypeControlChanged);
+ slotTypeControlChanged(m_pTypeControl->get());
+}
+
+void OverviewDelegate::slotTypeControlChanged(double v) {
+ // Assert that v is in enum range to prevent UB.
+ DEBUG_ASSERT(v >= 0 && v < QMetaEnum::fromType().keyCount());
+ mixxx::OverviewType type = static_cast(static_cast(v));
+ if (type == m_type) {
+ return;
+ }
+
+ m_type = type;
+ // Instantly update visible overviews so we get a live preview
+ // when changing the type in the preferences.
+ m_pTableView->update();
+}
+
+void OverviewDelegate::emitOverviewRowsChanged(QSet&& trackIds) {
+ if (trackIds.isEmpty()) {
+ return;
+ }
+
+ QList rows;
+ for (auto id : trackIds) {
+ const QList idRows = m_pTrackModel->getTrackRows(id);
+ rows.append(idRows);
+ }
+ if (rows.isEmpty()) {
+ // For some reason this can happen during startup
+ return;
+ }
+ // Sort in ascending order...
+ std::sort(rows.begin(), rows.end());
+ // ...and then deduplicate...
+ constErase(
+ &rows,
+ make_const_iterator(
+ rows, std::unique(rows.begin(), rows.end())),
+ rows.constEnd());
+ // ...before emitting the signal.
+ DEBUG_ASSERT(!rows.isEmpty());
+ emit overviewRowsChanged(std::move(rows));
+}
+
+void OverviewDelegate::slotInhibitLazyLoading(bool inhibitLazyLoading) {
+ m_inhibitLazyLoading = inhibitLazyLoading;
+ if (m_inhibitLazyLoading || m_cacheMissIds.isEmpty()) {
+ return;
+ }
+ // If we can request non-cache covers now, request updates
+ // for all rows that were cache misses since the last time.
+ // Reset the member variable before mutating the aggregated
+ // rows list (-> implicit sharing) and emitting a signal that
+ // in turn may trigger new signals for CoverArtDelegate!
+ QSet staleIds = std::move(m_cacheMissIds);
+ DEBUG_ASSERT(m_cacheMissIds.isEmpty());
+ emitOverviewRowsChanged(std::move(staleIds));
+}
+
+/// Maybe request repaint via dataChanged() by BaseTrackTableModel
+void OverviewDelegate::slotOverviewReady(const QObject* pRequester,
+ const TrackId trackId,
+ bool pixmapValid) {
+ // kLogger.info() << "slotOverviewReady()" << trackId << "pixmap valid:" << pixmapValid;
+
+ if (pRequester == this && pixmapValid) {
+ emitOverviewRowsChanged(QSet{trackId});
+ }
+}
+
+/// Maybe request repaint via dataChanged() by BaseTrackTableModel
+void OverviewDelegate::slotOverviewChanged(const TrackId trackId) {
+ // kLogger.info() << "slotOverviewChanged()" << trackId;
+ emitOverviewRowsChanged(QSet{trackId});
+}
+
+void OverviewDelegate::paintItem(QPainter* painter,
+ const QStyleOptionViewItem& option,
+ const QModelIndex& index) const {
+ const TrackId trackId(m_pTrackModel->getTrackId(index));
+ const double scaleFactor = m_pTableView->devicePixelRatioF();
+ QPixmap pixmap = m_pCache->requestCachedOverview(m_type,
+ trackId,
+ this,
+ option.rect.size() * scaleFactor);
+ if (pixmap.isNull()) {
+ // Cache miss
+ if (m_inhibitLazyLoading) {
+ // We are requesting cache-only covers and got a cache
+ // miss. Record this row so that when we switch to requesting
+ // non-cache we can request an update.
+ m_cacheMissIds.insert(trackId);
+ } else {
+ pixmap = m_pCache->requestUncachedOverview(m_type,
+ m_signalColors,
+ trackId,
+ this,
+ option.rect.size() * scaleFactor);
+ }
+ paintItemBackground(painter, option, index);
+ } else {
+ // We have a cached pixmap, paint it
+ pixmap.setDevicePixelRatio(scaleFactor);
+ painter->drawPixmap(option.rect, pixmap);
+ }
+}
diff --git a/src/library/tabledelegates/overviewdelegate.h b/src/library/tabledelegates/overviewdelegate.h
new file mode 100644
index 00000000000..219ccba1d75
--- /dev/null
+++ b/src/library/tabledelegates/overviewdelegate.h
@@ -0,0 +1,62 @@
+#pragma once
+
+#include
+#include
+#include
+
+#include "library/tabledelegates/tableitemdelegate.h"
+#include "track/trackid.h"
+#include "util/parented_ptr.h"
+#include "waveform/overviewtype.h"
+#include "waveform/renderers/waveformsignalcolors.h"
+
+class ControlProxy;
+class OverviewCache;
+class TrackModel;
+
+class OverviewDelegate : public TableItemDelegate {
+ Q_OBJECT
+
+ public:
+ explicit OverviewDelegate(QTableView* parent);
+ ~OverviewDelegate() override = default;
+
+ void paintItem(QPainter* painter,
+ const QStyleOptionViewItem& option,
+ const QModelIndex& index) const final;
+
+ signals:
+ void overviewRowsChanged(const QList& rows);
+
+ public slots:
+ // Advise the delegate to temporarily inhibit lazy loading
+ // of overview images and to only display those images
+ // that have already been cached.
+ //
+ // It is useful to handle cases when the user scroll down
+ // very fast or when they hold an arrow key. In this case
+ // it is NOT desirable to start multiple expensive file
+ // system operations for loading and scaling images that
+ // are not even displayed after scrolling beyond them.
+ void slotInhibitLazyLoading(bool inhibitLazyLoading);
+
+ private slots:
+ void slotTypeControlChanged(double v);
+ void slotOverviewReady(const QObject* pRequester,
+ const TrackId trackId,
+ bool pixmapValid);
+ void slotOverviewChanged(const TrackId trackId);
+
+ protected:
+ TrackModel* const m_pTrackModel;
+
+ private:
+ void emitOverviewRowsChanged(QSet&& trackIds);
+ OverviewCache* const m_pCache;
+ mixxx::OverviewType m_type;
+ bool m_inhibitLazyLoading;
+ parented_ptr m_pTypeControl;
+ WaveformSignalColors m_signalColors;
+
+ mutable QSet m_cacheMissIds;
+};
diff --git a/src/mixxxmainwindow.cpp b/src/mixxxmainwindow.cpp
index 3d0edcef530..e703a3a016b 100644
--- a/src/mixxxmainwindow.cpp
+++ b/src/mixxxmainwindow.cpp
@@ -38,6 +38,7 @@
#ifdef __ENGINEPRIME__
#include "library/export/libraryexporter.h"
#endif
+#include "library/overviewcache.h"
#include "library/trackcollectionmanager.h"
#include "mixer/playerinfo.h"
#include "mixer/playermanager.h"
@@ -274,6 +275,18 @@ void MixxxMainWindow::initialize() {
WaveformWidgetFactory::createInstance(); // takes a long time
WaveformWidgetFactory::instance()->setConfig(m_pCoreServices->getSettings());
WaveformWidgetFactory::instance()->startVSync(m_pGuiTick, m_pVisualsManager);
+ // Connect OverviewCache so we can clear and re-render overviews in the library
+ // when "OverviewNormalized" or "VisualGain_0" (all) have been changed in the
+ // preferences.
+ auto* pOverviewCache = OverviewCache::instance();
+ connect(WaveformWidgetFactory::instance(),
+ &WaveformWidgetFactory::overallVisualGainChanged,
+ pOverviewCache,
+ &OverviewCache::onNormalizeOrVisualGainChanged);
+ connect(WaveformWidgetFactory::instance(),
+ &WaveformWidgetFactory::overviewNormalizeChanged,
+ pOverviewCache,
+ &OverviewCache::onNormalizeOrVisualGainChanged);
connect(this,
&MixxxMainWindow::skinLoaded,
diff --git a/src/preferences/dialog/dlgprefwaveform.cpp b/src/preferences/dialog/dlgprefwaveform.cpp
index 3e4c50ea460..67b1b65c71f 100644
--- a/src/preferences/dialog/dlgprefwaveform.cpp
+++ b/src/preferences/dialog/dlgprefwaveform.cpp
@@ -8,9 +8,9 @@
#include "moc_dlgprefwaveform.cpp"
#include "preferences/waveformsettings.h"
#include "util/db/dbconnectionpooled.h"
+#include "waveform/overviewtype.h"
#include "waveform/renderers/waveformwidgetrenderer.h"
#include "waveform/waveformwidgetfactory.h"
-#include "widget/woverview.h"
namespace {
const ConfigKey kOverviewTypeCfgKey(QStringLiteral("[Waveform]"),
@@ -28,20 +28,20 @@ DlgPrefWaveform::DlgPrefWaveform(
// Waveform overview init
waveformOverviewComboBox->addItem(
- tr("Filtered"), QVariant::fromValue(WOverview::Type::Filtered));
- waveformOverviewComboBox->addItem(tr("HSV"), QVariant::fromValue(WOverview::Type::HSV));
- waveformOverviewComboBox->addItem(tr("RGB"), QVariant::fromValue(WOverview::Type::RGB));
+ tr("Filtered"), QVariant::fromValue(mixxx::OverviewType::Filtered));
+ waveformOverviewComboBox->addItem(tr("HSV"), QVariant::fromValue(mixxx::OverviewType::HSV));
+ waveformOverviewComboBox->addItem(tr("RGB"), QVariant::fromValue(mixxx::OverviewType::RGB));
m_pTypeControl = std::make_unique(kOverviewTypeCfgKey);
- m_pTypeControl->setStates(QMetaEnum::fromType().keyCount());
+ m_pTypeControl->setStates(QMetaEnum::fromType().keyCount());
m_pTypeControl->setReadOnly();
// Update the control with the config value
- WOverview::Type overviewType =
- m_pConfig->getValue(kOverviewTypeCfgKey, WOverview::Type::RGB);
+ mixxx::OverviewType overviewType =
+ m_pConfig->getValue(kOverviewTypeCfgKey, mixxx::OverviewType::RGB);
int cfgTypeIndex = waveformOverviewComboBox->findData(QVariant::fromValue(overviewType));
if (cfgTypeIndex == -1) {
// Invalid config value, set default type RGB and write it to config
waveformOverviewComboBox->setCurrentIndex(
- waveformOverviewComboBox->findData(QVariant::fromValue(WOverview::Type::RGB)));
+ waveformOverviewComboBox->findData(QVariant::fromValue(mixxx::OverviewType::RGB)));
m_pConfig->setValue(kOverviewTypeCfgKey, cfgTypeIndex);
} else {
waveformOverviewComboBox->setCurrentIndex(cfgTypeIndex);
@@ -304,10 +304,10 @@ void DlgPrefWaveform::slotUpdate() {
WaveformWidgetFactory::toUntilMarkTextHeightLimitIndex(
factory->getUntilMarkTextHeightLimit()));
- WOverview::Type cfgOverviewType =
- m_pConfig->getValue(kOverviewTypeCfgKey, WOverview::Type::RGB);
+ mixxx::OverviewType cfgOverviewType =
+ m_pConfig->getValue(kOverviewTypeCfgKey, mixxx::OverviewType::RGB);
// Assumes the combobox index is in sync with the ControlPushButton
- if (cfgOverviewType != waveformOverviewComboBox->currentData().value()) {
+ if (cfgOverviewType != waveformOverviewComboBox->currentData().value()) {
int cfgOverviewTypeIndex =
waveformOverviewComboBox->findData(QVariant::fromValue(cfgOverviewType));
waveformOverviewComboBox->setCurrentIndex(cfgOverviewTypeIndex);
@@ -368,7 +368,7 @@ void DlgPrefWaveform::slotResetToDefaults() {
// RGB overview.
waveformOverviewComboBox->setCurrentIndex(
- waveformOverviewComboBox->findData(QVariant::fromValue(WOverview::Type::RGB)));
+ waveformOverviewComboBox->findData(QVariant::fromValue(mixxx::OverviewType::RGB)));
// Don't normalize overview.
normalizeOverviewCheckBox->setChecked(false);
@@ -551,8 +551,8 @@ void DlgPrefWaveform::updateEnableUntilMark() {
void DlgPrefWaveform::slotSetWaveformOverviewType() {
// Apply immediately
QVariant comboboxData = waveformOverviewComboBox->currentData();
- DEBUG_ASSERT(comboboxData.canConvert());
- auto type = comboboxData.value();
+ DEBUG_ASSERT(comboboxData.canConvert());
+ auto type = comboboxData.value();
m_pConfig->setValue(kOverviewTypeCfgKey, type);
m_pTypeControl->forceSet(static_cast(type));
}
diff --git a/src/skin/legacy/legacyskinparser.cpp b/src/skin/legacy/legacyskinparser.cpp
index a509f8c0fb9..6b52f1c0f7e 100644
--- a/src/skin/legacy/legacyskinparser.cpp
+++ b/src/skin/legacy/legacyskinparser.cpp
@@ -1049,6 +1049,13 @@ QWidget* LegacySkinParser::parseOverview(const QDomElement& node) {
&PlayerManager::slotLoadLocationToPlayerMaybePlay);
connect(overviewWidget, &WOverview::cloneDeck,
m_pPlayerManager, &PlayerManager::slotCloneDeck);
+ // This signal stems from AnalysisFeature, and the overview can now
+ // render the analysis progress like when listening to Playermanager's
+ // analysis progress signal when loading a track.
+ connect(m_pLibrary,
+ &Library::onTrackAnalyzerProgress,
+ overviewWidget,
+ &WOverview::onTrackAnalyzerProgress);
commonWidgetSetup(node, overviewWidget);
overviewWidget->setup(node, *m_pContext);
diff --git a/src/waveform/overviewtype.cpp b/src/waveform/overviewtype.cpp
new file mode 100644
index 00000000000..5077ce71b68
--- /dev/null
+++ b/src/waveform/overviewtype.cpp
@@ -0,0 +1,4 @@
+// just a stub so the MOC file can be included somewhere
+#include "overviewtype.h"
+
+#include "moc_overviewtype.cpp"
diff --git a/src/waveform/overviewtype.h b/src/waveform/overviewtype.h
new file mode 100644
index 00000000000..811c9a3a2a1
--- /dev/null
+++ b/src/waveform/overviewtype.h
@@ -0,0 +1,16 @@
+#pragma once
+
+// required for Qt-Macros
+#include
+
+namespace mixxx {
+Q_NAMESPACE
+
+enum class OverviewType {
+ Filtered,
+ HSV,
+ RGB,
+};
+Q_ENUM_NS(OverviewType);
+
+} // namespace mixxx
diff --git a/src/waveform/renderers/waveformoverviewrenderer.cpp b/src/waveform/renderers/waveformoverviewrenderer.cpp
new file mode 100644
index 00000000000..362c1691cfa
--- /dev/null
+++ b/src/waveform/renderers/waveformoverviewrenderer.cpp
@@ -0,0 +1,328 @@
+#include "waveformoverviewrenderer.h"
+
+#include
+
+#include "util/colorcomponents.h"
+#include "util/math.h"
+#include "waveform/renderers/waveformsignalcolors.h"
+#include "waveform/waveformwidgetfactory.h"
+
+QImage WaveformOverviewRenderer::render(ConstWaveformPointer pWaveform,
+ mixxx::OverviewType type,
+ const WaveformSignalColors& signalColors,
+ bool mono) {
+ const int dataSize = pWaveform->getDataSize();
+ if (dataSize <= 0) {
+ return QImage();
+ }
+
+ QImage image(dataSize / 2, 2 * 255, QImage::Format_ARGB32_Premultiplied);
+ image.fill(QColor(0, 0, 0, 0).value());
+
+ QPainter painter(&image);
+ painter.translate(0.0, static_cast(image.height()) / 2.0);
+
+ if (type == mixxx::OverviewType::HSV) {
+ drawWaveformPartHSV(&painter,
+ pWaveform,
+ nullptr,
+ dataSize,
+ signalColors,
+ mono);
+ } else if (type == mixxx::OverviewType::Filtered) {
+ drawWaveformPartLMH(&painter,
+ pWaveform,
+ nullptr,
+ dataSize,
+ signalColors,
+ mono);
+ } else {
+ drawWaveformPartRGB(&painter,
+ pWaveform,
+ nullptr,
+ dataSize,
+ signalColors,
+ mono);
+ }
+
+ // Evaluate waveform ratio peak
+ float peak = 1;
+ for (int i = 0; i < dataSize; i += 2) {
+ peak = math_max3(
+ peak,
+ static_cast(pWaveform->getAll(i)),
+ static_cast(pWaveform->getAll(i + 1)));
+ }
+ // Normalize
+ WaveformWidgetFactory* widgetFactory = WaveformWidgetFactory::instance();
+ float diffGain = 0;
+ bool normalize = widgetFactory->isOverviewNormalized();
+ if (normalize && peak > 1) {
+ diffGain = 255 - peak - 1;
+ } else {
+ const auto visualGain = static_cast(
+ widgetFactory->getVisualGain(WaveformWidgetFactory::All));
+ diffGain = 255.0f - (255.0f / visualGain);
+ }
+
+ const int topLeft = static_cast(mono ? diffGain * 2 : diffGain);
+ const QRect sourceRect(0,
+ topLeft,
+ image.width(),
+ image.height() -
+ 2 * static_cast(diffGain));
+ QImage croppedImage = image.copy(sourceRect);
+ // Copy image, otherwise QPainter crashes when we alter it.
+ QImage normImage = croppedImage.scaled(image.size(),
+ Qt::IgnoreAspectRatio,
+ Qt::SmoothTransformation);
+
+ return normImage;
+}
+
+void WaveformOverviewRenderer::drawWaveformPartRGB(
+ QPainter* pPainter,
+ ConstWaveformPointer pWaveform,
+ int* start,
+ int end,
+ const WaveformSignalColors& signalColors,
+ bool mono) {
+ int startVal = 0;
+ if (start) {
+ startVal = *start;
+ }
+
+ const QColor lowColor = signalColors.getRgbLowColor();
+ const QColor midColor = signalColors.getRgbMidColor();
+ const QColor highColor = signalColors.getRgbHighColor();
+ QColor color;
+
+ float lowColor_r = 0, lowColor_g = 0, lowColor_b = 0,
+ midColor_r = 0, midColor_g = 0, midColor_b = 0,
+ highColor_r = 0, highColor_g = 0, highColor_b = 0,
+ all = 0, low = 0, mid = 0, high = 0,
+ red = 0, green = 0, blue = 0, max = 0;
+
+ getRgbF(lowColor, &lowColor_r, &lowColor_g, &lowColor_b);
+ getRgbF(midColor, &midColor_r, &midColor_g, &midColor_b);
+ getRgbF(highColor, &highColor_r, &highColor_g, &highColor_b);
+
+ if (mono) {
+ // Mono means we're going to paint from bottom to top with l+r.
+ const qreal dy = pPainter->deviceTransform().dy();
+ pPainter->resetTransform();
+ // shift y0 to bottom
+ pPainter->translate(0, 2 * dy);
+ // flip y-axis
+ pPainter->scale(1, -1);
+ for (int i = startVal, x = startVal / 2; i < end; i += 2, ++x) {
+ // Left
+ all = pWaveform->getAll(i) + pWaveform->getAll(i + 1);
+ low = pWaveform->getLow(i) + pWaveform->getLow(i + 1);
+ mid = pWaveform->getMid(i) + pWaveform->getMid(i + 1);
+ high = pWaveform->getHigh(i) + pWaveform->getHigh(i + 1);
+
+ 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
+ max = math_max3(red, green, blue);
+ // Draw
+ if (max > 0.0) {
+ color.setRgbF(static_cast(low / max),
+ static_cast(mid / max),
+ static_cast(high / max));
+ pPainter->setPen(color);
+ pPainter->drawLine(x, static_cast(all), x, 0);
+ }
+ }
+ } else { // stereo
+ for (int i = startVal, x = startVal / 2; i < end; i += 2, ++x) {
+ // Left
+ all = pWaveform->getAll(i);
+ low = pWaveform->getLow(i);
+ mid = pWaveform->getMid(i);
+ high = pWaveform->getHigh(i);
+
+ 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
+ max = math_max3(red, green, blue);
+ // Draw
+ if (max > 0.0) {
+ color.setRgbF(static_cast(low / max),
+ static_cast(mid / max),
+ static_cast(high / max));
+ pPainter->setPen(color);
+ pPainter->drawLine(x, static_cast(-all), x, 0);
+ }
+
+ // Right
+ all = pWaveform->getAll(i + 1);
+ low = pWaveform->getLow(i + 1);
+ mid = pWaveform->getMid(i + 1);
+ high = pWaveform->getHigh(i + 1);
+
+ 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;
+
+ max = math_max3(red, green, blue);
+
+ if (max > 0.0) {
+ color.setRgbF(static_cast(low / max),
+ static_cast(mid / max),
+ static_cast(high / max));
+ pPainter->setPen(color);
+ pPainter->drawLine(x, 0, x, static_cast(all));
+ }
+ }
+ }
+
+ if (start) {
+ *start = end;
+ }
+}
+
+void WaveformOverviewRenderer::drawWaveformPartLMH(
+ QPainter* pPainter,
+ ConstWaveformPointer pWaveform,
+ int* start,
+ int end,
+ const WaveformSignalColors& signalColors,
+ bool mono) {
+ const QColor lowColor = signalColors.getLowColor();
+ const QColor midColor = signalColors.getMidColor();
+ const QColor highColor = signalColors.getHighColor();
+ int startVal = 0;
+ if (start) {
+ startVal = *start;
+ }
+
+ if (mono) {
+ // Mono means we're going to paint from bottom to top with l+r.
+ const qreal dy = pPainter->deviceTransform().dy();
+ pPainter->resetTransform();
+ // shift y0 to bottom
+ pPainter->translate(0, 2 * dy);
+ // flip y-axis
+ pPainter->scale(1, -1);
+
+ for (int i = startVal, x = startVal / 2; i < end; i += 2, ++x) {
+ x = i / 2;
+ pPainter->setPen(lowColor);
+ pPainter->drawLine(QPoint(x, 0),
+ QPoint(x, pWaveform->getLow(i) + pWaveform->getLow(i + 1)));
+
+ pPainter->setPen(midColor);
+ pPainter->drawLine(QPoint(x, 0),
+ QPoint(x, pWaveform->getMid(i) + pWaveform->getMid(i + 1)));
+
+ pPainter->setPen(highColor);
+ pPainter->drawLine(QPoint(x, 0),
+ QPoint(x, pWaveform->getHigh(i) + pWaveform->getHigh(i + 1)));
+ }
+ } else { // stereo
+ for (int i = startVal, x = startVal / 2; i < end; i += 2, ++x) {
+ x = i / 2;
+ pPainter->setPen(lowColor);
+ pPainter->drawLine(QPoint(x, -pWaveform->getLow(i)),
+ QPoint(x, pWaveform->getLow(i + 1)));
+
+ pPainter->setPen(midColor);
+ pPainter->drawLine(QPoint(x, -pWaveform->getMid(i)),
+ QPoint(x, pWaveform->getMid(i + 1)));
+
+ pPainter->setPen(highColor);
+ pPainter->drawLine(QPoint(x, -pWaveform->getHigh(i)),
+ QPoint(x, pWaveform->getHigh(i + 1)));
+ }
+ }
+
+ if (start) {
+ *start = end;
+ }
+}
+
+void WaveformOverviewRenderer::drawWaveformPartHSV(
+ QPainter* pPainter,
+ ConstWaveformPointer pWaveform,
+ int* start,
+ int end,
+ const WaveformSignalColors& signalColors,
+ bool mono) {
+ int startVal = 0;
+ if (start) {
+ startVal = *start;
+ }
+
+ float h = 0, s = 0, v = 0, lo = 0, hi = 0, total = 0;
+ // Get HSV of low color.
+ const QColor lowColor = signalColors.getLowColor();
+ getHsvF(lowColor, &h, &s, &v);
+ QColor color;
+
+ unsigned char low[2] = {0, 0};
+ unsigned char high[2] = {0, 0};
+ unsigned char mid[2] = {0, 0};
+ unsigned char all[2] = {0, 0};
+
+ if (mono) {
+ // Mono means we're going to paint from bottom to top with l+r.
+ const qreal dy = pPainter->deviceTransform().dy();
+ pPainter->resetTransform();
+ // shift y0 to bottom
+ pPainter->translate(0, 2 * dy);
+ // flip y-axis
+ pPainter->scale(1, -1);
+ }
+
+ for (int i = startVal, x = startVal / 2; i < end; i += 2, ++x) {
+ x = i / 2;
+ all[0] = pWaveform->getAll(i);
+ all[1] = pWaveform->getAll(i + 1);
+
+ if (!all[0] && !all[1]) {
+ continue;
+ }
+
+ low[0] = pWaveform->getLow(i);
+ low[1] = pWaveform->getLow(i + 1);
+ mid[0] = pWaveform->getMid(i);
+ mid[1] = pWaveform->getMid(i + 1);
+ high[0] = pWaveform->getHigh(i);
+ high[1] = pWaveform->getHigh(i + 1);
+
+ total = (low[0] + low[1] + mid[0] + mid[1] +
+ high[0] + high[1]) *
+ 1.2f;
+
+ // Prevent division by zero
+ if (total > 0) {
+ // Normalize low and high
+ // (mid not need, because it not change the color)
+ lo = (low[0] + low[1]) / total;
+ hi = (high[0] + high[1]) / total;
+ } else {
+ lo = hi = 0.0;
+ }
+
+ // Set color
+ color.setHsvF(h, 1.0f - hi, 1.0f - lo);
+
+ if (mono) {
+ pPainter->setPen(color);
+ pPainter->drawLine(QPoint(i / 2, 0),
+ QPoint(i / 2, all[0] + all[1]));
+ } else {
+ pPainter->setPen(color);
+ pPainter->drawLine(QPoint(i / 2, -all[0]),
+ QPoint(i / 2, all[1]));
+ }
+ }
+
+ if (start) {
+ *start = end;
+ }
+}
diff --git a/src/waveform/renderers/waveformoverviewrenderer.h b/src/waveform/renderers/waveformoverviewrenderer.h
new file mode 100644
index 00000000000..825e4f2a341
--- /dev/null
+++ b/src/waveform/renderers/waveformoverviewrenderer.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#include
+
+#include "waveform/overviewtype.h"
+#include "waveform/waveform.h"
+
+class QPainter;
+class WaveformSignalColors;
+
+class WaveformOverviewRenderer {
+ public:
+ static QImage render(ConstWaveformPointer pWaveform,
+ mixxx::OverviewType type,
+ const WaveformSignalColors& signalColors,
+ bool mono = false);
+ /// These paint methods allow "mono" rendering (mono-mixdown, bottom-aligned).
+ /// Note: Don't use with WOverview, it's not adjusted yet! It does some
+ /// additional scaling for normalization which will atm cut off the bottom part.
+ static void drawWaveformPartRGB(
+ QPainter* pPainter,
+ ConstWaveformPointer pWaveform,
+ int* start,
+ int end,
+ const WaveformSignalColors& signalColors,
+ bool mono = false);
+ static void drawWaveformPartLMH(
+ QPainter* pPainter,
+ ConstWaveformPointer pWaveform,
+ int* start,
+ int end,
+ const WaveformSignalColors& signalColors,
+ bool mono = false);
+ static void drawWaveformPartHSV(
+ QPainter* pPainter,
+ ConstWaveformPointer pWaveform,
+ int* start,
+ int end,
+ const WaveformSignalColors& signalColors,
+ bool mono = false);
+};
diff --git a/src/widget/wlibrary.cpp b/src/widget/wlibrary.cpp
index 8787b3f0487..dcc0390dde3 100644
--- a/src/widget/wlibrary.cpp
+++ b/src/widget/wlibrary.cpp
@@ -30,6 +30,8 @@ void WLibrary::setup(const QDomNode& node, const SkinContext& context) {
kDefaultTrackTableBackgroundColorOpacity),
kMinTrackTableBackgroundColorOpacity,
kMaxTrackTableBackgroundColorOpacity);
+
+ m_overviewSignalColors.setup(node, context);
}
bool WLibrary::registerView(const QString& name, QWidget* pView) {
diff --git a/src/widget/wlibrary.h b/src/widget/wlibrary.h
index a017e9d823d..ebce2f45fa6 100644
--- a/src/widget/wlibrary.h
+++ b/src/widget/wlibrary.h
@@ -8,6 +8,7 @@
#include "library/libraryview.h"
#include "skin/legacy/skincontext.h"
#include "util/compatibility/qmutex.h"
+#include "waveform/renderers/waveformsignalcolors.h"
#include "widget/wbasewidget.h"
class LibraryView;
@@ -55,6 +56,10 @@ class WLibrary : public QStackedWidget, public WBaseWidget {
return m_bShowButtonText;
}
+ WaveformSignalColors getOverviewSignalColors() const {
+ return m_overviewSignalColors;
+ }
+
signals:
FocusWidget setLibraryFocus(FocusWidget newFocus);
@@ -77,4 +82,5 @@ class WLibrary : public QStackedWidget, public WBaseWidget {
QMap m_viewMap;
double m_trackTableBackgroundColorOpacity;
bool m_bShowButtonText;
+ WaveformSignalColors m_overviewSignalColors;
};
diff --git a/src/widget/wlibrarytableview.h b/src/widget/wlibrarytableview.h
index ad060872f2f..fb915d2ce10 100644
--- a/src/widget/wlibrarytableview.h
+++ b/src/widget/wlibrarytableview.h
@@ -66,7 +66,7 @@ class WLibraryTableView : public QTableView, public virtual LibraryView {
#endif
bool play = false);
void trackSelected(TrackPointer pTrack);
- void onlyCachedCoverArt(bool);
+ void onlyCachedCoversAndOverviews(bool);
void scrollValueChanged(int);
FocusWidget setLibraryFocus(FocusWidget newFocus);
diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp
index 21824313e0d..e7682dda88b 100644
--- a/src/widget/woverview.cpp
+++ b/src/widget/woverview.cpp
@@ -21,6 +21,7 @@
#include "util/math.h"
#include "util/painterscope.h"
#include "util/timer.h"
+#include "waveform/renderers/waveformoverviewrenderer.h"
#include "waveform/waveform.h"
#include "waveform/waveformwidgetfactory.h"
#include "widget/controlwidgetconnection.h"
@@ -34,7 +35,7 @@ WOverview::WOverview(
: WWidget(parent),
m_group(group),
m_pConfig(pConfig),
- m_type(Type::RGB),
+ m_type(mixxx::OverviewType::RGB),
m_actualCompletion(0),
m_pixmapDone(false),
m_waveformPeak(-1.0),
@@ -427,8 +428,8 @@ void WOverview::onPassthroughChange(double v) {
void WOverview::slotTypeControlChanged(double v) {
// Assert that v is in enum range to prevent UB.
- DEBUG_ASSERT(v >= 0 && v < QMetaEnum::fromType().keyCount());
- Type type = static_cast(static_cast(v));
+ DEBUG_ASSERT(v >= 0 && v < QMetaEnum::fromType().keyCount());
+ mixxx::OverviewType type = static_cast(static_cast(v));
if (type == m_type) {
return;
}
@@ -701,8 +702,8 @@ void WOverview::drawAxis(QPainter* pPainter) {
}
void WOverview::drawWaveformPixmap(QPainter* pPainter) {
- WaveformWidgetFactory* widgetFactory = WaveformWidgetFactory::instance();
if (!m_waveformSourceImage.isNull()) {
+ WaveformWidgetFactory* widgetFactory = WaveformWidgetFactory::instance();
PainterScope painterScope(pPainter);
float diffGain;
bool normalize = widgetFactory->isOverviewNormalized();
@@ -1368,83 +1369,8 @@ bool WOverview::drawNextPixmapPart() {
QPainter painter(&m_waveformSourceImage);
painter.translate(0.0, static_cast(m_waveformSourceImage.height()) / 2.0);
- if (m_type == Type::Filtered) {
- drawNextPixmapPartLMH(&painter, pWaveform, nextCompletion);
- } else if (m_type == Type::HSV) {
- drawNextPixmapPartHSV(&painter, pWaveform, nextCompletion);
- } else { // Type::RGB:
- 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;
+ for (int currentCompletion = m_actualCompletion;
currentCompletion < nextCompletion;
currentCompletion += 2) {
m_waveformPeak = math_max3(
@@ -1453,145 +1379,41 @@ void WOverview::drawNextPixmapPartHSV(QPainter* pPainter,
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));
- }
+ if (m_type == mixxx::OverviewType::Filtered) {
+ ScopedTimer t(QStringLiteral("WOverview::drawNextPixmapPartLMH"));
+ WaveformOverviewRenderer::drawWaveformPartLMH(
+ &painter,
+ pWaveform,
+ &m_actualCompletion,
+ nextCompletion,
+ m_signalColors);
+ } else if (m_type == mixxx::OverviewType::HSV) {
+ ScopedTimer t(QStringLiteral("WOverview::drawNextPixmapPartHSV"));
+ WaveformOverviewRenderer::drawWaveformPartHSV(
+ &painter,
+ pWaveform,
+ &m_actualCompletion,
+ nextCompletion,
+ m_signalColors);
+ } else { // mixxx::OverviewType::RGB:
+ ScopedTimer t(QStringLiteral("WOverview::drawNextPixmapPartRGB"));
+ WaveformOverviewRenderer::drawWaveformPartRGB(
+ &painter,
+ pWaveform,
+ &m_actualCompletion,
+ nextCompletion,
+ m_signalColors);
}
- 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));
- }
- }
+ m_waveformImageScaled = QImage();
+ m_diffGain = 0;
- // 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)));
+ // Test if the complete waveform is done
+ if (m_actualCompletion >= dataSize - 2) {
+ m_pixmapDone = true;
}
- m_actualCompletion = nextCompletion;
+ return true;
}
void WOverview::paintText(const QString& text, QPainter* pPainter) {
diff --git a/src/widget/woverview.h b/src/widget/woverview.h
index 242802d057f..b2587eedd9c 100644
--- a/src/widget/woverview.h
+++ b/src/widget/woverview.h
@@ -8,6 +8,7 @@
#include "track/track_decl.h"
#include "track/trackid.h"
#include "util/parented_ptr.h"
+#include "waveform/overviewtype.h"
#include "waveform/renderers/waveformmarkrange.h"
#include "waveform/renderers/waveformmarkset.h"
#include "waveform/renderers/waveformsignalcolors.h"
@@ -32,13 +33,6 @@ class WOverview : public WWidget, public TrackDropTarget {
void setup(const QDomNode& node, const SkinContext& context);
virtual void initWithTrack(TrackPointer pTrack);
- enum class Type {
- Filtered,
- HSV,
- RGB,
- };
- Q_ENUM(Type);
-
public slots:
void onConnectedControlChanged(double dParameter, double dValue) override;
void slotTrackLoaded(TrackPointer pTrack);
@@ -143,7 +137,7 @@ class WOverview : public WWidget, public TrackDropTarget {
const QString m_group;
UserSettingsPointer m_pConfig;
- Type m_type;
+ mixxx::OverviewType m_type;
int m_actualCompletion;
bool m_pixmapDone;
float m_waveformPeak;
diff --git a/src/widget/wtracktableview.cpp b/src/widget/wtracktableview.cpp
index c5241fea0e5..aad6d94c936 100644
--- a/src/widget/wtracktableview.cpp
+++ b/src/widget/wtracktableview.cpp
@@ -89,7 +89,7 @@ void WTrackTableView::enableCachedOnly() {
if (!m_loadCachedOnly) {
// don't try to load and search covers, drawing only
// covers which are already in the QPixmapCache.
- emit onlyCachedCoverArt(true);
+ emit onlyCachedCoversAndOverviews(true);
m_loadCachedOnly = true;
}
m_lastUserAction = mixxx::Time::elapsed();
@@ -153,7 +153,7 @@ void WTrackTableView::slotGuiTick50ms(double /*unused*/) {
// This allows CoverArtDelegate to request that we load covers from disk
// (as opposed to only serving them from cache).
- emit onlyCachedCoverArt(false);
+ emit onlyCachedCoversAndOverviews(false);
m_loadCachedOnly = false;
}
}