From 5d2d4f77223133d1bed3488c461ccfd87af19bd2 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Wed, 5 Jun 2024 16:39:27 +0200 Subject: [PATCH 001/201] Preferences Effects: add Hide/Unhide buttons to Effects tab --- src/preferences/dialog/dlgprefeffects.cpp | 72 ++++++++++++++- src/preferences/dialog/dlgprefeffects.h | 4 +- src/preferences/dialog/dlgprefeffectsdlg.ui | 94 +++++++++++++++++++- src/preferences/effectmanifesttablemodel.cpp | 6 +- src/preferences/effectmanifesttablemodel.h | 10 ++- 5 files changed, 173 insertions(+), 13 deletions(-) diff --git a/src/preferences/dialog/dlgprefeffects.cpp b/src/preferences/dialog/dlgprefeffects.cpp index bf057f6fe16..12916498b1e 100644 --- a/src/preferences/dialog/dlgprefeffects.cpp +++ b/src/preferences/dialog/dlgprefeffects.cpp @@ -1,6 +1,7 @@ #include "preferences/dialog/dlgprefeffects.h" #include +#include #include "effects/backends/effectmanifest.h" #include "effects/backends/effectsbackend.h" @@ -36,6 +37,17 @@ DlgPrefEffects::DlgPrefEffects(QWidget* pParent, hiddenEffectsTableView->setModel(m_pHiddenEffectsModel); setupManifestTableView(hiddenEffectsTableView); + updateHideUnhideButtons(); + // TODO Use only one button, set text/icon depending on focused list view? + connect(hideButton, + &QPushButton::clicked, + this, + &DlgPrefEffects::slotHideUnhideEffect); + connect(unhideButton, + &QPushButton::clicked, + this, + &DlgPrefEffects::slotHideUnhideEffect); + setupChainListView(chainListView); setupChainListView(quickEffectListView); @@ -112,10 +124,11 @@ void DlgPrefEffects::slotUpdate() { hiddenEffects.removeAll(pManifest); } m_pHiddenEffectsModel->setList(hiddenEffects); + updateHideUnhideButtons(); // No chain preset is selected when the preferences are opened clearChainInfo(); - updateButtons(0); + updateChainPresetButtons(0); loadChainPresetLists(); @@ -162,7 +175,7 @@ void DlgPrefEffects::clearChainInfo() { } } -void DlgPrefEffects::updateButtons(int selectedIndices) { +void DlgPrefEffects::updateChainPresetButtons(int selectedIndices) { // Allow Delete and Export of multiple presets chainPresetDeleteButton->setEnabled(selectedIndices > 0); chainPresetExportButton->setEnabled(selectedIndices > 0); @@ -170,6 +183,17 @@ void DlgPrefEffects::updateButtons(int selectedIndices) { chainPresetRenameButton->setEnabled(selectedIndices == 1); } +void DlgPrefEffects::updateHideUnhideButtons(const QModelIndex& selected) { + if (!selected.isValid() || m_pFocusedEffectList == nullptr) { + hideButton->setEnabled(false); + unhideButton->setEnabled(false); + return; + } + bool enableHide = m_pFocusedEffectList == visibleEffectsTableView; + hideButton->setEnabled(enableHide); + unhideButton->setEnabled(!enableHide); +} + void DlgPrefEffects::loadChainPresetLists() { QStringList chainPresetNames; for (const auto& pChainPreset : m_pChainPresetManager->getPresetsSorted()) { @@ -201,6 +225,7 @@ void DlgPrefEffects::effectsTableItemSelected(const QModelIndex& selected) { // in eventFilter() if (!selected.isValid()) { clearEffectInfo(); + updateHideUnhideButtons(); return; } const auto* pModel = static_cast(selected.model()); @@ -217,6 +242,47 @@ void DlgPrefEffects::effectsTableItemSelected(const QModelIndex& selected) { effectDescription->setText(pManifest->description()); effectVersion->setText(pManifest->version()); effectType->setText(EffectsBackend::translatedBackendName(pManifest->backendType())); + updateHideUnhideButtons(selected); +} + +void DlgPrefEffects::slotHideUnhideEffect() { + auto* pSourceList = m_pFocusedEffectList; + auto* pTargetList = unfocusedEffectList(); + VERIFY_OR_DEBUG_ASSERT(pSourceList && pTargetList) { + return; + } + auto* pSelectionModel = pSourceList->selectionModel(); + if (!pSelectionModel || pSelectionModel->selectedRows().size() != 1) { + return; + } + auto* pSourceModel = static_cast(pSourceList->model()); + VERIFY_OR_DEBUG_ASSERT(pSourceModel) { + return; + } + auto* pTargetModel = static_cast(pTargetList->model()); + VERIFY_OR_DEBUG_ASSERT(pTargetModel) { + return; + } + QModelIndex selIdx = pSelectionModel->selectedRows().first(); + EffectManifestPointer pManifest = pSourceModel->getList().at(selIdx.row()); + VERIFY_OR_DEBUG_ASSERT(pManifest) { + return; + } + + QMimeData* mimeData = new QMimeData; + mimeData->setText(pManifest->uniqueId()); + // Append the selected effect to the target list + if (!pTargetModel->dropMimeData(mimeData)) { + return; + } + // Note the added item so we can remove it if necessary + QModelIndex pMovedEffect = pTargetModel->index(pTargetModel->rowCount() - 1, 0); + DEBUG_ASSERT(pMovedEffect.isValid()); + + if (!pSourceModel->removeRows(selIdx.row(), 1, selIdx.parent())) { + // If removing failed, undo add to target list + pTargetModel->removeRows(pMovedEffect.row(), 1, pMovedEffect.parent()); + } } void DlgPrefEffects::slotChainPresetSelectionChanged(const QItemSelection& selected) { @@ -227,7 +293,7 @@ void DlgPrefEffects::slotChainPresetSelectionChanged(const QItemSelection& selec auto* pSelModel = m_pFocusedChainList->selectionModel(); auto selIndices = pSelModel->selectedIndexes(); - updateButtons(selIndices.count()); + updateChainPresetButtons(selIndices.count()); // Clear the info box and return if the index is invalid, e.g. after clearCurrentIndex() // in eventFilter() diff --git a/src/preferences/dialog/dlgprefeffects.h b/src/preferences/dialog/dlgprefeffects.h index bddf0d1d478..379e3bdec45 100644 --- a/src/preferences/dialog/dlgprefeffects.h +++ b/src/preferences/dialog/dlgprefeffects.h @@ -23,6 +23,7 @@ class DlgPrefEffects : public DlgPreferencePage, public Ui::DlgPrefEffectsDlg { private slots: void effectsTableItemSelected(const QModelIndex& selected); + void slotHideUnhideEffect(); void slotChainPresetSelectionChanged(const QItemSelection& selected); void slotImportPreset(); void slotExportPreset(); @@ -35,7 +36,8 @@ class DlgPrefEffects : public DlgPreferencePage, public Ui::DlgPrefEffectsDlg { void clearEffectInfo(); void clearChainInfo(); - void updateButtons(int selectedIndices); + void updateChainPresetButtons(int selectedIndices); + void updateHideUnhideButtons(const QModelIndex& selected = QModelIndex()); void loadChainPresetLists(); void saveChainPresetLists(); diff --git a/src/preferences/dialog/dlgprefeffectsdlg.ui b/src/preferences/dialog/dlgprefeffectsdlg.ui index a59bf462a10..6f4201b91c0 100644 --- a/src/preferences/dialog/dlgprefeffectsdlg.ui +++ b/src/preferences/dialog/dlgprefeffectsdlg.ui @@ -31,6 +31,7 @@ 0 + @@ -230,6 +231,7 @@ + @@ -243,7 +245,7 @@ - + Drag and drop to rearrange lists and show or hide effects. @@ -266,7 +268,7 @@ - + @@ -282,13 +284,95 @@ + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + 20 + 60 + + + + + + + + + + + + + 0 + 0 + + + + + 20 + 40 + + + + + + + + + + + + + 0 + 0 + + + + + 20 + 40 + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + - + + @@ -469,6 +553,7 @@ + @@ -498,12 +583,15 @@ + chainListView quickEffectListView visibleEffectsTableView + hideButton + unhideButton hiddenEffectsTableView chainPresetImportButton chainPresetExportButton diff --git a/src/preferences/effectmanifesttablemodel.cpp b/src/preferences/effectmanifesttablemodel.cpp index 9b730e5c714..d4eceb54bd2 100644 --- a/src/preferences/effectmanifesttablemodel.cpp +++ b/src/preferences/effectmanifesttablemodel.cpp @@ -125,9 +125,11 @@ bool EffectManifestTableModel::dropMimeData(const QMimeData* data, return false; } if (row == -1) { - row = parent.row(); + if (parent.isValid()) { + row = parent.row(); + } // Dropping onto an empty model or dropping past the end of a model - if (parent.row() == -1) { + if (row == -1) { row = m_manifests.size(); } } diff --git a/src/preferences/effectmanifesttablemodel.h b/src/preferences/effectmanifesttablemodel.h index 7a097ebc2d5..8b53af711eb 100644 --- a/src/preferences/effectmanifesttablemodel.h +++ b/src/preferences/effectmanifesttablemodel.h @@ -33,12 +33,14 @@ class EffectManifestTableModel : public QAbstractTableModel { // These functions are required for drag and drop. Qt::ItemFlags flags(const QModelIndex& index) const override; QMimeData* mimeData(const QModelIndexList& indexes) const override; + // Set defaults so we can call it with mime data only for inserting + // an effect at the end bool dropMimeData( const QMimeData* data, - Qt::DropAction action, - int row, - int column, - const QModelIndex& parent) override; + Qt::DropAction action = Qt::MoveAction, + int row = -1, + int column = -1, + const QModelIndex& parent = QModelIndex()) override; QStringList mimeTypes() const override; Qt::DropActions supportedDropActions() const override; bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override; From 5c311b14cac138dd1a3212e865d28d6e1dddb8f0 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Sun, 23 Jun 2024 13:00:26 +0200 Subject: [PATCH 002/201] Track: add swapHotcues() --- src/track/cue.cpp | 11 +++++++++++ src/track/cue.h | 3 ++- src/track/track.cpp | 39 +++++++++++++++++++++++++++++++++++++++ src/track/track.h | 3 ++- 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/track/cue.cpp b/src/track/cue.cpp index 28fe9cca19c..6b678b6746d 100644 --- a/src/track/cue.cpp +++ b/src/track/cue.cpp @@ -221,6 +221,17 @@ mixxx::audio::FrameDiff_t Cue::getLengthFrames() const { return m_endPosition - m_startPosition; } +void Cue::setHotCue(int n) { + VERIFY_OR_DEBUG_ASSERT(n >= mixxx::kFirstHotCueIndex) { + return; + } + const auto lock = lockMutex(&m_mutex); + if (m_iHotCue == n) { + return; + } + m_iHotCue = n; +} + int Cue::getHotCue() const { const auto lock = lockMutex(&m_mutex); return m_iHotCue; diff --git a/src/track/cue.h b/src/track/cue.h index c0474e76ae9..92eed026443 100644 --- a/src/track/cue.h +++ b/src/track/cue.h @@ -74,6 +74,7 @@ class Cue : public QObject { mixxx::audio::FrameDiff_t getLengthFrames() const; + void setHotCue(int n); int getHotCue() const; QString getLabel() const; @@ -104,7 +105,7 @@ class Cue : public QObject { mixxx::CueType m_type; mixxx::audio::FramePos m_startPosition; mixxx::audio::FramePos m_endPosition; - const int m_iHotCue; + int m_iHotCue; QString m_label; mixxx::RgbColor m_color; diff --git a/src/track/track.cpp b/src/track/track.cpp index ff2c42d79dd..1f7177f3335 100644 --- a/src/track/track.cpp +++ b/src/track/track.cpp @@ -1062,6 +1062,21 @@ CuePointer Track::findCueById(DbId id) const { return CuePointer(); } +CuePointer Track::findHotcueByIndex(int idx) const { + auto locked = lockMutex(&m_qMutex); + auto cueIt = std::find_if( + m_cuePoints.begin(), + m_cuePoints.end(), + [idx](const CuePointer& pCue) { + return pCue && pCue->getHotCue() == idx; + }); + if (cueIt != m_cuePoints.end()) { + return *cueIt; + } else { + return {}; + } +} + void Track::removeCue(const CuePointer& pCue) { if (!pCue) { return; @@ -1103,6 +1118,30 @@ void Track::removeCuesOfType(mixxx::CueType type) { } } +void Track::swapHotcues(int a, int b) { + VERIFY_OR_DEBUG_ASSERT(a != b) { + qWarning() << "Track::swapHotcues rejected," << a << "==" << b; + return; + } + VERIFY_OR_DEBUG_ASSERT(a != Cue::kNoHotCue || b != Cue::kNoHotCue) { + qWarning() << "Track::swapHotcues rejected, both a and b are kNoHotCue"; + return; + } + auto locked = lockMutex(&m_qMutex); + CuePointer pCueA = findHotcueByIndex(a); + CuePointer pCueB = findHotcueByIndex(b); + if (!pCueA && !pCueB) { + return; + } + if (pCueA) { + pCueA->setHotCue(b); + } + if (pCueB) { + pCueB->setHotCue(a); + } + emit cuesUpdated(); +} + void Track::setCuePoints(const QList& cuePoints) { // While this method could be called from any thread, // associated Cue objects should always live on the diff --git a/src/track/track.h b/src/track/track.h index f053b6b2166..2f7b67a3029 100644 --- a/src/track/track.h +++ b/src/track/track.h @@ -322,6 +322,7 @@ class Track : public QObject { } CuePointer findCueByType(mixxx::CueType type) const; // NOTE: Cannot be used for hotcues. CuePointer findCueById(DbId id) const; + CuePointer findHotcueByIndex(int idx) const; void removeCue(const CuePointer& pCue); void removeCuesOfType(mixxx::CueType); QList getCuePoints() const { @@ -329,7 +330,7 @@ class Track : public QObject { // lock thread-unsafe copy constructors of QList return m_cuePoints; } - + void swapHotcues(int a, int b); void setCuePoints(const QList& cuePoints); #ifdef __STEM__ From 98bb751f15f6b4270405c2d9ad74a51d80142eea Mon Sep 17 00:00:00 2001 From: ronso0 Date: Sun, 23 Jun 2024 16:00:35 +0200 Subject: [PATCH 003/201] CueControl: add `hotcue_X_swap` control, value is 1-based target index --- src/engine/controls/cuecontrol.cpp | 36 ++++++++++++++++++++++++++++++ src/engine/controls/cuecontrol.h | 4 ++++ 2 files changed, 40 insertions(+) diff --git a/src/engine/controls/cuecontrol.cpp b/src/engine/controls/cuecontrol.cpp index f99a7816036..9d41fb67783 100644 --- a/src/engine/controls/cuecontrol.cpp +++ b/src/engine/controls/cuecontrol.cpp @@ -394,6 +394,11 @@ void CueControl::connectControls() { this, &CueControl::hotcueClear, Qt::DirectConnection); + connect(pControl, + &HotcueControl::hotcueSwap, + this, + &CueControl::hotcueSwap, + Qt::DirectConnection); } } @@ -1218,6 +1223,26 @@ void CueControl::hotcueClear(HotcueControl* pControl, double value) { setHotcueFocusIndex(Cue::kNoHotCue); } +void CueControl::hotcueSwap(HotcueControl* pControl, double v) { + // 1-based GUI/human index to 0-based internal index + int newCuenum = static_cast(v) - 1; + if (newCuenum < mixxx::kFirstHotCueIndex || newCuenum >= m_iNumHotCues) { + return; + } + + auto lock = lockMutex(&m_trackMutex); + if (!m_pLoadedTrack) { + return; + } + + CuePointer pCue = pControl->getCue(); + if (!pCue) { + return; + } + + m_pLoadedTrack->swapHotcues(pCue->getHotCue(), newCuenum); +} + void CueControl::hotcuePositionChanged( HotcueControl* pControl, double value) { auto lock = lockMutex(&m_trackMutex); @@ -2585,6 +2610,13 @@ HotcueControl::HotcueControl(const QString& group, int hotcueIndex) &HotcueControl::slotHotcueClear, Qt::DirectConnection); + m_hotcueSwap = std::make_unique(keyForControl(QStringLiteral("swap"))); + connect(m_hotcueSwap.get(), + &ControlObject::valueChanged, + this, + &HotcueControl::slotHotcueSwap, + Qt::DirectConnection); + m_previewingType.setValue(mixxx::CueType::Invalid); m_previewingPosition.setValue(mixxx::audio::kInvalidFramePos); } @@ -2643,6 +2675,10 @@ void HotcueControl::slotHotcueClear(double v) { emit hotcueClear(this, v); } +void HotcueControl::slotHotcueSwap(double v) { + emit hotcueSwap(this, v); +} + void HotcueControl::slotHotcuePositionChanged(double newPosition) { emit hotcuePositionChanged(this, newPosition); } diff --git a/src/engine/controls/cuecontrol.h b/src/engine/controls/cuecontrol.h index c3bb82b0ec8..7eb177e2cf3 100644 --- a/src/engine/controls/cuecontrol.h +++ b/src/engine/controls/cuecontrol.h @@ -136,6 +136,7 @@ class HotcueControl : public QObject { void slotHotcueActivateLoop(double v); void slotHotcueActivatePreview(double v); void slotHotcueClear(double v); + void slotHotcueSwap(double v); void slotHotcueEndPositionChanged(double newPosition); void slotHotcuePositionChanged(double newPosition); void slotHotcueColorChangeRequest(double newColor); @@ -150,6 +151,7 @@ class HotcueControl : public QObject { void hotcueActivate(HotcueControl* pHotcue, double v, HotcueSetMode mode); void hotcueActivatePreview(HotcueControl* pHotcue, double v); void hotcueClear(HotcueControl* pHotcue, double v); + void hotcueSwap(HotcueControl* pHotcue, double v); void hotcuePositionChanged(HotcueControl* pHotcue, double newPosition); void hotcueEndPositionChanged(HotcueControl* pHotcue, double newEndPosition); void hotcuePlay(double v); @@ -181,6 +183,7 @@ class HotcueControl : public QObject { std::unique_ptr m_hotcueActivateLoop; std::unique_ptr m_hotcueActivatePreview; std::unique_ptr m_hotcueClear; + std::unique_ptr m_hotcueSwap; ControlValueAtomic m_previewingType; ControlValueAtomic m_previewingPosition; @@ -228,6 +231,7 @@ class CueControl : public EngineControl { void hotcueActivatePreview(HotcueControl* pControl, double v); void updateCurrentlyPreviewingIndex(int hotcueIndex); void hotcueClear(HotcueControl* pControl, double v); + void hotcueSwap(HotcueControl* pHotcue, double v); void hotcuePositionChanged(HotcueControl* pControl, double newPosition); void hotcueEndPositionChanged(HotcueControl* pControl, double newEndPosition); From d939c77a24bf29618cda7ff0d63070de01538a14 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Thu, 3 Oct 2024 15:46:22 +0200 Subject: [PATCH 004/201] DbId: add QDataStream operators <> --- src/util/db/dbid.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/util/db/dbid.h b/src/util/db/dbid.h index a001459df21..6aa57810f6f 100644 --- a/src/util/db/dbid.h +++ b/src/util/db/dbid.h @@ -88,6 +88,18 @@ class DbId { return debug << dbId.m_value; } + friend QDataStream& operator<<(QDataStream& out, const DbId& dbId) { + // explicit cast as recommended by Qt docs + return out << static_cast(dbId.m_value); + } + + friend QDataStream& operator>>(QDataStream& in, DbId& dbId) { + quint32 v; + in >> v; + dbId.m_value = v; + return in; + } + friend qhash_seed_t qHash( const DbId& dbId, qhash_seed_t seed = 0) { From 1c446877931759de66c491df78314800ddc781eb Mon Sep 17 00:00:00 2001 From: ronso0 Date: Thu, 10 Oct 2024 14:25:56 +0200 Subject: [PATCH 005/201] Overview: simply handling of QMouseEvent position --- src/widget/woverview.cpp | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index babd35ad401..60e9ed6b8a0 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -474,17 +474,9 @@ void WOverview::receiveCuesUpdated() { void WOverview::mouseMoveEvent(QMouseEvent* e) { if (m_bLeftClickDragging) { if (m_orientation == Qt::Horizontal) { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - m_iPickupPos = math_clamp(static_cast(e->position().x()), 0, width() - 1); -#else - m_iPickupPos = math_clamp(e->x(), 0, width() - 1); -#endif + m_iPickupPos = math_clamp(e->pos().x(), 0, width() - 1); } else { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - m_iPickupPos = math_clamp(static_cast(e->position().y()), 0, height() - 1); -#else - m_iPickupPos = math_clamp(e->y(), 0, height() - 1); -#endif + m_iPickupPos = math_clamp(e->pos().y(), 0, height() - 1); } } @@ -541,17 +533,9 @@ void WOverview::mousePressEvent(QMouseEvent* e) { } if (e->button() == Qt::LeftButton) { if (m_orientation == Qt::Horizontal) { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - m_iPickupPos = math_clamp(static_cast(e->position().x()), 0, width() - 1); -#else - m_iPickupPos = math_clamp(e->x(), 0, width() - 1); -#endif + m_iPickupPos = math_clamp(e->pos().x(), 0, width() - 1); } else { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - m_iPickupPos = math_clamp(static_cast(e->position().y()), 0, height() - 1); -#else - m_iPickupPos = math_clamp(e->y(), 0, height() - 1); -#endif + m_iPickupPos = math_clamp(e->pos().y(), 0, height() - 1); } if (m_pHoveredMark != nullptr) { From f5dd6579a715f1792184fd90705f505f894766b1 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Thu, 10 Oct 2024 14:28:16 +0200 Subject: [PATCH 006/201] Overview: abort play pos dragging when Passthrough is enabled --- src/widget/woverview.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 60e9ed6b8a0..9d008293bd4 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -407,11 +407,16 @@ void WOverview::onRateRatioChange(double v) { void WOverview::onPassthroughChange(double v) { m_bPassthroughEnabled = static_cast(v); - if (!m_bPassthroughEnabled) { + if (m_bPassthroughEnabled) { + // Abort play position dragging + m_bLeftClickDragging = false; + m_bTimeRulerActive = false; + m_iPickupPos = m_iPlayPos; + } else { slotWaveformSummaryUpdated(); } - // Always call this to trigger a repaint even if not track is loaded + // Always call this to trigger a repaint even if no track is loaded update(); } From 3f3bfdb4a0b6dbf9d2d830bf7ab322db67fea805 Mon Sep 17 00:00:00 2001 From: Sergey <5637569+fonsargo@users.noreply.github.com> Date: Wed, 6 Nov 2024 22:24:38 +0100 Subject: [PATCH 007/201] Fix talkover ducking bug: apply ducking after apllying effects --- src/engine/enginemixer.cpp | 30 ++++++++++++++++++++++++------ src/engine/enginemixer.h | 11 ++++------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/engine/enginemixer.cpp b/src/engine/enginemixer.cpp index e13691cbd90..c88ab29b2f2 100644 --- a/src/engine/enginemixer.cpp +++ b/src/engine/enginemixer.cpp @@ -40,6 +40,7 @@ EngineMixer::EngineMixer( m_boothGainOld(0.0), m_headphoneMainGainOld(0.0), m_headphoneGainOld(1.0), + m_duckingGainOld(1.0), m_balleftOld(1.0), m_balrightOld(1.0), m_numMicsConfigured(0), @@ -530,12 +531,8 @@ void EngineMixer::process(const int iBufferSize) { // Make the mix for each crossfader orientation output bus. // m_mainGain takes care of applying the attenuation from - // channel volume faders, crossfader, and talkover ducking. - // Talkover is mixed in later according to the configured MicMonitorMode - m_mainGain.setGains(crossfaderLeftGain, - 1.0f, - crossfaderRightGain, - m_pTalkoverDucking->getGain(iFrames)); + // channel volume faders and crossfader. + m_mainGain.setGains(crossfaderLeftGain, 1.0f, crossfaderRightGain); for (int o = EngineChannel::LEFT; o <= EngineChannel::RIGHT; o++) { ChannelMixer::applyEffectsInPlaceAndMixChannels(m_mainGain, @@ -608,6 +605,13 @@ void EngineMixer::process(const int iBufferSize) { // buffers within the same callback. applyMainEffects(iBufferSize); + // Apply talkover ducking gain after applying effects in order to + // avoid ducking neutralization by some effects (e.g. compressor or + // AGC) + CSAMPLE_GAIN duckingGain = m_pTalkoverDucking->getGain(iFrames); + SampleUtil::applyRampingGain(m_main.data(), m_duckingGainOld, duckingGain, iBufferSize); + m_duckingGainOld = duckingGain; + if (headphoneEnabled) { processHeadphones(mainMixGainInHeadphones, iBufferSize); } @@ -648,6 +652,13 @@ void EngineMixer::process(const int iBufferSize) { // process main effects here before mixing in talkover. applyMainEffects(iBufferSize); + // Apply talkover ducking gain after applying effects in order to + // avoid ducking neutralization by some effects (e.g. compressor or + // AGC) + CSAMPLE_GAIN duckingGain = m_pTalkoverDucking->getGain(iFrames); + SampleUtil::applyRampingGain(m_main.data(), m_duckingGainOld, duckingGain, iBufferSize); + m_duckingGainOld = duckingGain; + if (headphoneEnabled) { processHeadphones(mainMixGainInHeadphones, iBufferSize); } @@ -706,6 +717,13 @@ void EngineMixer::process(const int iBufferSize) { // as what is heard on the main & booth outputs. applyMainEffects(iBufferSize); + // Apply talkover ducking gain after applying effects in order to + // avoid ducking neutralization by some effects (e.g. compressor or + // AGC) + CSAMPLE_GAIN duckingGain = m_pTalkoverDucking->getGain(iFrames); + SampleUtil::applyRampingGain(m_main.data(), m_duckingGainOld, duckingGain, iBufferSize); + m_duckingGainOld = duckingGain; + if (headphoneEnabled) { processHeadphones(mainMixGainInHeadphones, iBufferSize); } diff --git a/src/engine/enginemixer.h b/src/engine/enginemixer.h index 359c186d78a..f60d8a26d04 100644 --- a/src/engine/enginemixer.h +++ b/src/engine/enginemixer.h @@ -155,8 +155,7 @@ class EngineMixer : public QObject, public AudioSource { OrientationVolumeGainCalculator() : m_dLeftGain(1.0), m_dCenterGain(1.0), - m_dRightGain(1.0), - m_dTalkoverDuckingGain(1.0) { + m_dRightGain(1.0) { } inline CSAMPLE_GAIN getGain(ChannelInfo* pChannelInfo) const override { @@ -167,24 +166,21 @@ class EngineMixer : public QObject, public AudioSource { m_dLeftGain, m_dCenterGain, m_dRightGain); - return channelVolume * orientationGain * m_dTalkoverDuckingGain; + return channelVolume * orientationGain; } inline void setGains(CSAMPLE_GAIN leftGain, CSAMPLE_GAIN centerGain, - CSAMPLE_GAIN rightGain, - CSAMPLE_GAIN talkoverDuckingGain) { + CSAMPLE_GAIN rightGain) { m_dLeftGain = leftGain; m_dCenterGain = centerGain; m_dRightGain = rightGain; - m_dTalkoverDuckingGain = talkoverDuckingGain; } private: CSAMPLE_GAIN m_dLeftGain; CSAMPLE_GAIN m_dCenterGain; CSAMPLE_GAIN m_dRightGain; - CSAMPLE_GAIN m_dTalkoverDuckingGain; }; enum class MicMonitorMode { @@ -329,6 +325,7 @@ class EngineMixer : public QObject, public AudioSource { CSAMPLE_GAIN m_boothGainOld; CSAMPLE_GAIN m_headphoneMainGainOld; CSAMPLE_GAIN m_headphoneGainOld; + CSAMPLE_GAIN m_duckingGainOld; CSAMPLE_GAIN m_balleftOld; CSAMPLE_GAIN m_balrightOld; std::atomic m_numMicsConfigured; From 35d2857747e635c884312f2d206cba915915209f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 29 Dec 2024 18:14:08 +0100 Subject: [PATCH 008/201] Update CHANGELOG.md --- CHANGELOG.md | 175 +++++++- res/linux/org.mixxx.Mixxx.metainfo.xml | 546 ++++++++++++++++++++++++- 2 files changed, 696 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78ffa1dc478..15139887ae8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,33 +2,195 @@ ## [2.6.0](https://github.com/mixxxdj/mixxx/milestone/44) (Unreleased) +### Engine + +* fix: sync rate using the current BPM instead of the file one + [#13671](https://github.com/mixxxdj/mixxx/pull/13671) + [#12738](https://github.com/mixxxdj/mixxx/issues/12738) +* fix: prevent null CO access when cloning sampler or preview [#13740](https://github.com/mixxxdj/mixxx/pull/13740) +* Tooltips: fix cue mode setting location [#14045](https://github.com/mixxxdj/mixxx/pull/14045) + +### Experimental Features + +* SoundManagerIOS: Remove unsupported/redundant options [#13487](https://github.com/mixxxdj/mixxx/pull/13487) +* ControllerRenderingEngine: Disable BGRA when targeting Wasm [#13502](https://github.com/mixxxdj/mixxx/pull/13502) +* BaseTrackTableModel: Disable inline track editing on iOS [#13494](https://github.com/mixxxdj/mixxx/pull/13494) +* set QQuickStyle to "basic" [#13696](https://github.com/mixxxdj/mixxx/pull/13696) + [#13600](https://github.com/mixxxdj/mixxx/issues/13600) +* fix: trigger QML waveform slot at init [#13736](https://github.com/mixxxdj/mixxx/pull/13736) + +### Preferences + +* (fix) Sound preferences: don't set m_settingsModified in update slots [#13450](https://github.com/mixxxdj/mixxx/pull/13450) +* Track Search Preferences: Fix accidental use of wrong preference controls [#13592](https://github.com/mixxxdj/mixxx/pull/13592) +* (fix) Pref Mixer: fix crossader graph [#13848](https://github.com/mixxxdj/mixxx/pull/13848) +* Make extended controller information available for device selection [#13896](https://github.com/mixxxdj/mixxx/pull/13896) + +### Skins + +* LegacySkinParser: Short-circuit if template fails to open [#13488](https://github.com/mixxxdj/mixxx/pull/13488) +* Update waveforms_container.xml [#13501](https://github.com/mixxxdj/mixxx/pull/13501) + +### Library + +* feat: static color coding for key column [#13390](https://github.com/mixxxdj/mixxx/pull/13390) +* fix: Key text is elided from left, should be right [#13475](https://github.com/mixxxdj/mixxx/pull/13475) +* Add Key Color Palettes [#13497](https://github.com/mixxxdj/mixxx/pull/13497) +* Fix BPM and Bitrate columns were wider than normal [#13571](https://github.com/mixxxdj/mixxx/pull/13571) +* Track Info dialogs: move metadata buttons below color picker [#13632](https://github.com/mixxxdj/mixxx/pull/13632) +* CmdlineArgs: Add `--rescan-library` for rescanning on startup [#13661](https://github.com/mixxxdj/mixxx/pull/13661) +* Track menu, purge: allow to hide further success popups in the current session [#13807](https://github.com/mixxxdj/mixxx/pull/13807) + +### Effects + +* Compressor effect: Adjust Makeup Time constant calculation [#13261](https://github.com/mixxxdj/mixxx/pull/13261) +[#13237](https://github.com/mixxxdj/mixxx/issues/13237) +* fix: prevent quickFX model out of bound [#13668](https://github.com/mixxxdj/mixxx/pull/13668) + ### Waveforms * Simplify waveform combobox in preferences [#13220](https://github.com/mixxxdj/mixxx/issues/13220) [#6428](https://github.com/mixxxdj/mixxx/issues/6428) [#13226](https://github.com/mixxxdj/mixxx/issues/13226) +* Disable textured waveforms when using OpenGL ES + [#13381](https://github.com/mixxxdj/mixxx/pull/13381) + [#13380](https://github.com/mixxxdj/mixxx/issues/13380) +* ControllerRenderingEngine: Patch out unavailable APIs when using GL ES [#13382](https://github.com/mixxxdj/mixxx/pull/13382) +* fix: invalid slip render marker [#13422](https://github.com/mixxxdj/mixxx/pull/13422) +* Add minute markers on horizontal waveform overview + [#13401](https://github.com/mixxxdj/mixxx/pull/13401) + [#5843](https://github.com/mixxxdj/mixxx/issues/5843) + [#13648](https://github.com/mixxxdj/mixxx/pull/13648) +* Fix high details waveforms wrapping around after visual index 65K [#13491](https://github.com/mixxxdj/mixxx/pull/13491) +* Fix: support for new WaveformData struct in shaders + [#13474](https://github.com/mixxxdj/mixxx/pull/13474) + [#13472](https://github.com/mixxxdj/mixxx/issues/13472) +* Overview waveform: draw minute markers on top of played overlay [#13489](https://github.com/mixxxdj/mixxx/pull/13489) +* fix: remove scaleSignal in waveform analyzer [#13416](https://github.com/mixxxdj/mixxx/pull/13416) +* feat: improve screen rendering framework [#13737](https://github.com/mixxxdj/mixxx/pull/13737) +* fix: prevent double free on DigitsRenderer [#13859](https://github.com/mixxxdj/mixxx/pull/13859) +* fix: waveform overview seeking + [#13947](https://github.com/mixxxdj/mixxx/pull/13947) + [#13946](https://github.com/mixxxdj/mixxx/issues/13946) +* rendergraph: add rendergraph library [#14007](https://github.com/mixxxdj/mixxx/pull/14007) +* mark rendering improvements [#13969](https://github.com/mixxxdj/mixxx/pull/13969) ### STEM support * Add simple support for STEM files [#13044](https://github.com/mixxxdj/mixxx/pull/13044) -* Multithreaded Rubberband [#13143](https://github.com/mixxxdj/mixxx/pull/13143) +* Multithreaded Rubberband + [#13143](https://github.com/mixxxdj/mixxx/pull/13143) + [#13649](https://github.com/mixxxdj/mixxx/pull/13649) +* Add support for stem in the engine [#13070](https://github.com/mixxxdj/mixxx/pull/13070) +* Add analyser support for stem [#13106](https://github.com/mixxxdj/mixxx/pull/13106) +* Add stem controls [#13086](https://github.com/mixxxdj/mixxx/pull/13086) +* Add quick effect support on stem [#13123](https://github.com/mixxxdj/mixxx/pull/13123) +* Add stem files to the taglib lookup table [#13612](https://github.com/mixxxdj/mixxx/pull/13612) +* fix: exclude stem samples for QML waveform [#13655](https://github.com/mixxxdj/mixxx/pull/13655) +* Non-floating stem controls for LateNight [#13537](https://github.com/mixxxdj/mixxx/pull/13537) +* (fix) make "stem_group,mute" a powerwindow button + [#13751](https://github.com/mixxxdj/mixxx/pull/13751) + [#13749](https://github.com/mixxxdj/mixxx/issues/13749) +* feat: add advanced stem loading COs [#13268](https://github.com/mixxxdj/mixxx/pull/13268) +* Fix build with -DSTEM=OFF [#13948](https://github.com/mixxxdj/mixxx/pull/13948) +* Stem control test fix [#13960](https://github.com/mixxxdj/mixxx/pull/13960) +* Solves problem with special characters in path to stems [#13784](https://github.com/mixxxdj/mixxx/pull/13784) ### Controller Backend * Add screen renderer to support controllers with a screen [#11407](https://github.com/mixxxdj/mixxx/pull/11407) [#13334](https://github.com/mixxxdj/mixxx/pull/13334) +* Deprecate `lodash.mixxx.js`, and `script.deepMerge` [#13460](https://github.com/mixxxdj/mixxx/pull/13460) +* Don't return in JogWheelBasic on deck absent in option [#13425](https://github.com/mixxxdj/mixxx/pull/13425) +* Refactor: modernize softtakeover code [#13553](https://github.com/mixxxdj/mixxx/pull/13553) +* document `ScriptConnection` readonly properties & slight cleanup [#13630](https://github.com/mixxxdj/mixxx/pull/13630) +* Modernize Hid/Bulk Lists [#13622](https://github.com/mixxxdj/mixxx/pull/13622) +* Prevent deadlock with BULK transfer and reduce log noise [#13735](https://github.com/mixxxdj/mixxx/pull/13735) +* feat: add file and color controller setting types [#13669](https://github.com/mixxxdj/mixxx/pull/13669) +* Controllers: allow to enable MIDI Through Port in non-developer sessions [#13909](https://github.com/mixxxdj/mixxx/pull/13909) +* Expose convertCharset convenience function to controllers [#13935](https://github.com/mixxxdj/mixxx/pull/13935) ### Auto-DJ * Add AutoDJ xfader recenter option (default off) [#13303](https://github.com/mixxxdj/mixxx/pull/13303) [#11571](https://github.com/mixxxdj/mixxx/issues/11571) +* Auto DJ: Add context menu action for enabling/disabling the Auto DJ [#13593](https://github.com/mixxxdj/mixxx/pull/13593) +* Auto DJ Cross fader center [#13628](https://github.com/mixxxdj/mixxx/pull/13628) + +### Target support + +* DlgPrefSound: Add missing ifdefs for building without Rubberband [#13577](https://github.com/mixxxdj/mixxx/pull/13577) +* Update Linux-GitHub runner to Ubuntu 24.04.01 LTS + [#13781](https://github.com/mixxxdj/mixxx/pull/13781) + [#13880](https://github.com/mixxxdj/mixxx/pull/13880) +* Add missing qt6-declarative-private-dev and qt6-base-private-dev package [#13904](https://github.com/mixxxdj/mixxx/pull/13904) ### Misc Refactorings * Refactor/shrink modernize scopedtimer [#13258](https://github.com/mixxxdj/mixxx/pull/13258) +* Improve use of parented_ptr [#13411](https://github.com/mixxxdj/mixxx/pull/13411) +* Pre-allocate memory in basetrackcache to avoid multiple reallocations [#13368](https://github.com/mixxxdj/mixxx/pull/13368) +* Bump actions/checkout from 4.1.6 to 4.1.7 [#13386](https://github.com/mixxxdj/mixxx/pull/13386) +* Bump actions/checkout from 4.1.7 to 4.2.0 [#13713](https://github.com/mixxxdj/mixxx/pull/13713) +* Bump actions/checkout from 4.2.0 to 4.2.1 [#13726](https://github.com/mixxxdj/mixxx/pull/13726) +* Bump actions/checkout from 4.2.1 to 4.2.2 [#13810](https://github.com/mixxxdj/mixxx/pull/13810) +* Bump azure/trusted-signing-action from 0.3.20 to 0.4.0 [#13500](https://github.com/mixxxdj/mixxx/pull/13500) +* Bump azure/trusted-signing-action from 0.4.0 to 0.5.0 [#13809](https://github.com/mixxxdj/mixxx/pull/13809) +* Bump actions/upload-artifact from 4.3.4 to 4.3.5 [#13539](https://github.com/mixxxdj/mixxx/pull/13539) +* Bump actions/upload-artifact from 4.3.5 to 4.3.6 [#13562](https://github.com/mixxxdj/mixxx/pull/13562) +* Bump actions/upload-artifact from 4.3.6 to 4.4.0 [#13621](https://github.com/mixxxdj/mixxx/pull/13621) +* Bump actions/upload-artifact from 4.4.0 to 4.4.1 [#13725](https://github.com/mixxxdj/mixxx/pull/13725) +* Bump actions/upload-artifact from 4.4.1 to 4.4.3 [#13765](https://github.com/mixxxdj/mixxx/pull/13765) +* Bump coverallsapp/github-action from 2.3.0 to 2.3.1 [#13766](https://github.com/mixxxdj/mixxx/pull/13766) +* Bump coverallsapp/github-action from 2.3.1 to 2.3.3 [#13793](https://github.com/mixxxdj/mixxx/pull/13793) +* Bump coverallsapp/github-action from 2.3.3 to 2.3.4 [#13811](https://github.com/mixxxdj/mixxx/pull/13811) +* chore: update the donate button label [#13353](https://github.com/mixxxdj/mixxx/pull/13353) +* WPixmapStore: Change getPixmapNoCache to std::unique_ptr and further optimizations [#13369](https://github.com/mixxxdj/mixxx/pull/13369) +* Removed unused setSVG and hash functionality from pixmapsource [#13423](https://github.com/mixxxdj/mixxx/pull/13423) +* remove FAQ from Readme.md [#13453](https://github.com/mixxxdj/mixxx/pull/13453) +* [#13452](https://github.com/mixxxdj/mixxx/pull/13452) +* Paintable cleanup [#13435](https://github.com/mixxxdj/mixxx/pull/13435) +* Made Paintable::DrawMode an enum class [#13424](https://github.com/mixxxdj/mixxx/pull/13424) +* hash clean up [#13458](https://github.com/mixxxdj/mixxx/pull/13458) +* clang-format: Indent Objective-C blocks with 4 spaces [#13503](https://github.com/mixxxdj/mixxx/pull/13503) +* fix(basetracktablemodel): Fix `-Wimplicit-fallthrough` warning on GCC 14.1.1 [#13505](https://github.com/mixxxdj/mixxx/pull/13505) +* Refactor fix trivial cpp coreguideline violations [#13552](https://github.com/mixxxdj/mixxx/pull/13552) +* Refactor `EngineMixer` [#13568](https://github.com/mixxxdj/mixxx/pull/13568) +* more `ControlDoublePrivate` optimization [#13581](https://github.com/mixxxdj/mixxx/pull/13581) +* Modernize `ControlValueAtomic` [#13574](https://github.com/mixxxdj/mixxx/pull/13574) +* Optimize control code [#13354](https://github.com/mixxxdj/mixxx/pull/13354) +* Fix some minor code issue [#13586](https://github.com/mixxxdj/mixxx/pull/13586) +* Static initialization order fix [#13594](https://github.com/mixxxdj/mixxx/pull/13594) +* Remove referenceholder [#13240](https://github.com/mixxxdj/mixxx/pull/13240) +* chore: add note about ConfigKey naming convention [#13658](https://github.com/mixxxdj/mixxx/pull/13658) +* refactor: split out `AutoFileReloader` from `QmlAutoReload` + [#13607](https://github.com/mixxxdj/mixxx/pull/13607) + [#13756](https://github.com/mixxxdj/mixxx/pull/13756) + [#13755](https://github.com/mixxxdj/mixxx/issues/13755) +* Fix Clazy v1.12 errors in main [#13770](https://github.com/mixxxdj/mixxx/pull/13770) +* Code cleanup in SidebarModel and WLibrarySidebar [#13816](https://github.com/mixxxdj/mixxx/pull/13816) +* Refactor: `MovingInterquartileMean` [#13730](https://github.com/mixxxdj/mixxx/pull/13730) +* Improved comments in enginecontrol and use of std::size_t for bufferSize across the codebase [#13819](https://github.com/mixxxdj/mixxx/pull/13819) +* refactor: use higher-level `std::span` based logic [#13654](https://github.com/mixxxdj/mixxx/pull/13654) +* tsan fix pll vars data race [#13873](https://github.com/mixxxdj/mixxx/pull/13873) +* use atomic to fix tsan detected data race condition of blink value in control indicator [#13875](https://github.com/mixxxdj/mixxx/pull/13875) +* Fix undefined behaviour of infinity() [#13884](https://github.com/mixxxdj/mixxx/pull/13884) +* use atomic for m_bWakeScheduler, protect m_bQuit with mutex [#13898](https://github.com/mixxxdj/mixxx/pull/13898) +* Refactor `ValueTransformer` and `WBaseWidget` [#13853](https://github.com/mixxxdj/mixxx/pull/13853) +* avoid data race on m_pStream [#13899](https://github.com/mixxxdj/mixxx/pull/13899) +* Cleanup and deprecate more `util/` classes + [#13687](https://github.com/mixxxdj/mixxx/pull/13687) + [#13968](https://github.com/mixxxdj/mixxx/pull/13968) + [#13965](https://github.com/mixxxdj/mixxx/issues/13965) +* ci(pre-commit): Add cmake-lint hook [#13932](https://github.com/mixxxdj/mixxx/pull/13932) +* refactor: remove samplew_autogen.h + [#13988](https://github.com/mixxxdj/mixxx/pull/13988) + [#14005](https://github.com/mixxxdj/mixxx/pull/14005) +* fix clang-tidy complain [#14029](https://github.com/mixxxdj/mixxx/pull/14029) +* ci(dependabot): Open PRs against 2.5 branch instead of main [#14060](https://github.com/mixxxdj/mixxx/pull/14060) ## [2.5.0](https://github.com/mixxxdj/mixxx/issues?q=milestone%3A2.5.0) (2024-12-24) @@ -114,9 +276,6 @@ [#13930](https://github.com/mixxxdj/mixxx/pull/13930) * Require a minimum movement before initiating the drag&drop of tracks [#12903](https://github.com/mixxxdj/mixxx/pull/12903) * Add type toggle to cue popup [#13215](https://github.com/mixxxdj/mixxx/pull/13215) -* Effect Meta Knob: draws arc from default meta position - [#12638](https://github.com/mixxxdj/mixxx/pull/12638) - [#12634](https://github.com/mixxxdj/mixxx/issues/12634) * Handle not supported files when dragging to waveforms and spinnies [#13206](https://github.com/mixxxdj/mixxx/issues/13206) * Tooltips: Improve `rate_up/down` description regarding pitch vs. speed [#12590](https://github.com/mixxxdj/mixxx/pull/12590) @@ -281,7 +440,8 @@ ### Controller Backend * Send sysex to all handlers [#12827](https://github.com/mixxxdj/mixxx/pull/12827) -* Speed up midi sysex receive [#12843](https://github.com/mixxxdj/mixxx/pull/12843) +* Speed up midi sysex receive + [#12843](https://github.com/mixxxdj/mixxx/pull/12843) * Add control for showing a deck's track menu [#10825](https://github.com/mixxxdj/mixxx/pull/10825) * Removed old examples HID keyboard and HID trackpad [#12977](https://github.com/mixxxdj/mixxx/pull/12977) * Reduce log noise with HID device @@ -303,8 +463,6 @@ * Controller IO table: Fix display text for Action/control delegate [#13188](https://github.com/mixxxdj/mixxx/pull/13188) * Drop lodash dependency in ComponentJS [#12779](https://github.com/mixxxdj/mixxx/pull/12779) * Support for bulk devices on Windows and Mac [#13008](https://github.com/mixxxdj/mixxx/pull/13008) -* Drop lodash dependency in ComponentJS - [#12779](https://github.com/mixxxdj/mixxx/pull/12779) * Fix pending reference to the old mapping after selecting 'No mapping' [#13907](https://github.com/mixxxdj/mixxx/pull/13907) * Fix crash with GoToItem when no app windows has the focus [#13657](https://github.com/mixxxdj/mixxx/pull/13657) @@ -447,9 +605,6 @@ [#13248](https://github.com/mixxxdj/mixxx/issues/13248) * Recording: with empty config, save default split size immediately [#13304](https://github.com/mixxxdj/mixxx/pull/13304) -* Allow to drop files with supported MIME type regardless off the file extensions - [#13209](https://github.com/mixxxdj/mixxx/pull/13209) - [#13204](https://github.com/mixxxdj/mixxx/issues/13204) * Add support for Ubuntu Oracular Oriole and remove Lunar Lobster [#13348](https://github.com/mixxxdj/mixxx/pull/13348) * Recordbox: Fix string decoding issues diff --git a/res/linux/org.mixxx.Mixxx.metainfo.xml b/res/linux/org.mixxx.Mixxx.metainfo.xml index 82ae9978ab6..470e5bbb069 100644 --- a/res/linux/org.mixxx.Mixxx.metainfo.xml +++ b/res/linux/org.mixxx.Mixxx.metainfo.xml @@ -96,8 +96,135 @@ Do not edit it manually. --> - + +

+ Engine +

+
    +
  • + fix: sync rate using the current BPM instead of the file one + #13671 + #12738 +
  • +
  • + fix: prevent null CO access when cloning sampler or preview + #13740 +
  • +
  • + Tooltips: fix cue mode setting location + #14045 +
  • +
+

+ Experimental Features +

+
    +
  • + SoundManagerIOS: Remove unsupported/redundant options + #13487 +
  • +
  • + ControllerRenderingEngine: Disable BGRA when targeting Wasm + #13502 +
  • +
  • + BaseTrackTableModel: Disable inline track editing on iOS + #13494 +
  • +
  • + set QQuickStyle to "basic" + #13696 + #13600 +
  • +
  • + fix: trigger QML waveform slot at init + #13736 +
  • +
+

+ Preferences +

+
    +
  • + (fix) Sound preferences: don't set m_settingsModified in update slots + #13450 +
  • +
  • + Track Search Preferences: Fix accidental use of wrong preference controls + #13592 +
  • +
  • + (fix) Pref Mixer: fix crossader graph + #13848 +
  • +
  • + Make extended controller information available for device selection + #13896 +
  • +
+

+ Skins +

+
    +
  • + LegacySkinParser: Short-circuit if template fails to open + #13488 +
  • +
  • + Update waveforms_container.xml + #13501 +
  • +
+

+ Library +

+
    +
  • + feat: static color coding for key column + #13390 +
  • +
  • + fix: Key text is elided from left, should be right + #13475 +
  • +
  • + Add Key Color Palettes + #13497 +
  • +
  • + Fix BPM and Bitrate columns were wider than normal + #13571 +
  • +
  • + Track Info dialogs: move metadata buttons below color picker + #13632 +
  • +
  • + CmdlineArgs: Add + --rescan-library + for rescanning on startup + #13661 +
  • +
  • + Track menu, purge: allow to hide further success popups in the current session + #13807 +
  • +
+

+ Effects +

+
    +
  • + Compressor effect: Adjust Makeup Time constant calculation + #13261 + #13237 +
  • +
  • + fix: prevent quickFX model out of bound + #13668 +
  • +

Waveforms

@@ -108,6 +235,63 @@ #6428 #13226 +
  • + Disable textured waveforms when using OpenGL ES + #13381 + #13380 +
  • +
  • + ControllerRenderingEngine: Patch out unavailable APIs when using GL ES + #13382 +
  • +
  • + fix: invalid slip render marker + #13422 +
  • +
  • + Add minute markers on horizontal waveform overview + #13401 + #5843 + #13648 +
  • +
  • + Fix high details waveforms wrapping around after visual index 65K + #13491 +
  • +
  • + Fix: support for new WaveformData struct in shaders + #13474 + #13472 +
  • +
  • + Overview waveform: draw minute markers on top of played overlay + #13489 +
  • +
  • + fix: remove scaleSignal in waveform analyzer + #13416 +
  • +
  • + feat: improve screen rendering framework + #13737 +
  • +
  • + fix: prevent double free on DigitsRenderer + #13859 +
  • +
  • + fix: waveform overview seeking + #13947 + #13946 +
  • +
  • + rendergraph: add rendergraph library + #14007 +
  • +
  • + mark rendering improvements + #13969 +
  • STEM support @@ -120,6 +304,56 @@

  • Multithreaded Rubberband #13143 + #13649 +
  • +
  • + Add support for stem in the engine + #13070 +
  • +
  • + Add analyser support for stem + #13106 +
  • +
  • + Add stem controls + #13086 +
  • +
  • + Add quick effect support on stem + #13123 +
  • +
  • + Add stem files to the taglib lookup table + #13612 +
  • +
  • + fix: exclude stem samples for QML waveform + #13655 +
  • +
  • + Non-floating stem controls for LateNight + #13537 +
  • +
  • + (fix) make "stem_group,mute" a powerwindow button + #13751 + #13749 +
  • +
  • + feat: add advanced stem loading COs + #13268 +
  • +
  • + Fix build with -DSTEM=OFF + #13948 +
  • +
  • + Stem control test fix + #13960 +
  • +
  • + Solves problem with special characters in path to stems + #13784
  • @@ -131,6 +365,47 @@ #11407 #13334 +

  • + Deprecate + lodash.mixxx.js + , and + script.deepMerge + #13460 +
  • +
  • + Don't return in JogWheelBasic on deck absent in option + #13425 +
  • +
  • + Refactor: modernize softtakeover code + #13553 +
  • +
  • + document + ScriptConnection + readonly properties & slight cleanup + #13630 +
  • +
  • + Modernize Hid/Bulk Lists + #13622 +
  • +
  • + Prevent deadlock with BULK transfer and reduce log noise + #13735 +
  • +
  • + feat: add file and color controller setting types + #13669 +
  • +
  • + Controllers: allow to enable MIDI Through Port in non-developer sessions + #13909 +
  • +
  • + Expose convertCharset convenience function to controllers + #13935 +
  • Auto-DJ @@ -141,6 +416,32 @@ #13303 #11571 +

  • + Auto DJ: Add context menu action for enabling/disabling the Auto DJ + #13593 +
  • +
  • + Auto DJ Cross fader center + #13628 +
  • + +

    + Target support +

    +
      +
    • + DlgPrefSound: Add missing ifdefs for building without Rubberband + #13577 +
    • +
    • + Update Linux-GitHub runner to Ubuntu 24.04.01 LTS + #13781 + #13880 +
    • +
    • + Add missing qt6-declarative-private-dev and qt6-base-private-dev package + #13904 +

    Misc Refactorings @@ -150,6 +451,235 @@ Refactor/shrink modernize scopedtimer #13258 +

  • + Improve use of parented_ptr + #13411 +
  • +
  • + Pre-allocate memory in basetrackcache to avoid multiple reallocations + #13368 +
  • +
  • + Bump actions/checkout from 4.1.6 to 4.1.7 + #13386 +
  • +
  • + Bump actions/checkout from 4.1.7 to 4.2.0 + #13713 +
  • +
  • + Bump actions/checkout from 4.2.0 to 4.2.1 + #13726 +
  • +
  • + Bump actions/checkout from 4.2.1 to 4.2.2 + #13810 +
  • +
  • + Bump azure/trusted-signing-action from 0.3.20 to 0.4.0 + #13500 +
  • +
  • + Bump azure/trusted-signing-action from 0.4.0 to 0.5.0 + #13809 +
  • +
  • + Bump actions/upload-artifact from 4.3.4 to 4.3.5 + #13539 +
  • +
  • + Bump actions/upload-artifact from 4.3.5 to 4.3.6 + #13562 +
  • +
  • + Bump actions/upload-artifact from 4.3.6 to 4.4.0 + #13621 +
  • +
  • + Bump actions/upload-artifact from 4.4.0 to 4.4.1 + #13725 +
  • +
  • + Bump actions/upload-artifact from 4.4.1 to 4.4.3 + #13765 +
  • +
  • + Bump coverallsapp/github-action from 2.3.0 to 2.3.1 + #13766 +
  • +
  • + Bump coverallsapp/github-action from 2.3.1 to 2.3.3 + #13793 +
  • +
  • + Bump coverallsapp/github-action from 2.3.3 to 2.3.4 + #13811 +
  • +
  • + chore: update the donate button label + #13353 +
  • +
  • + WPixmapStore: Change getPixmapNoCache to std::unique_ptr and further optimizations + #13369 +
  • +
  • + Removed unused setSVG and hash functionality from pixmapsource + #13423 +
  • +
  • + remove FAQ from Readme.md + #13453 +
  • +
  • + #13452 +
  • +
  • + Paintable cleanup + #13435 +
  • +
  • + Made Paintable::DrawMode an enum class + #13424 +
  • +
  • + hash clean up + #13458 +
  • +
  • + clang-format: Indent Objective-C blocks with 4 spaces + #13503 +
  • +
  • + fix(basetracktablemodel): Fix + -Wimplicit-fallthrough + warning on GCC 14.1.1 + #13505 +
  • +
  • + Refactor fix trivial cpp coreguideline violations + #13552 +
  • +
  • + Refactor + EngineMixer + #13568 +
  • +
  • + more + ControlDoublePrivate + optimization + #13581 +
  • +
  • + Modernize + ControlValueAtomic + #13574 +
  • +
  • + Optimize control code + #13354 +
  • +
  • + Fix some minor code issue + #13586 +
  • +
  • + Static initialization order fix + #13594 +
  • +
  • + Remove referenceholder + #13240 +
  • +
  • + chore: add note about ConfigKey naming convention + #13658 +
  • +
  • + refactor: split out + AutoFileReloader + from + QmlAutoReload + #13607 + #13756 + #13755 +
  • +
  • + Fix Clazy v1.12 errors in main + #13770 +
  • +
  • + Code cleanup in SidebarModel and WLibrarySidebar + #13816 +
  • +
  • + Refactor: + MovingInterquartileMean + #13730 +
  • +
  • + Improved comments in enginecontrol and use of std::size_t for bufferSize across the codebase + #13819 +
  • +
  • + refactor: use higher-level + std::span + based logic + #13654 +
  • +
  • + tsan fix pll vars data race + #13873 +
  • +
  • + use atomic to fix tsan detected data race condition of blink value in control indicator + #13875 +
  • +
  • + Fix undefined behaviour of infinity() + #13884 +
  • +
  • + use atomic for m_bWakeScheduler, protect m_bQuit with mutex + #13898 +
  • +
  • + Refactor + ValueTransformer + and + WBaseWidget + #13853 +
  • +
  • + avoid data race on m_pStream + #13899 +
  • +
  • + Cleanup and deprecate more + util/ + classes + #13687 + #13968 + #13965 +
  • +
  • + ci(pre-commit): Add cmake-lint hook + #13932 +
  • +
  • + refactor: remove samplew_autogen.h + #13988 + #14005 +
  • +
  • + fix clang-tidy complain + #14029 +
  • +
  • + ci(dependabot): Open PRs against 2.5 branch instead of main + #14060 +
  • @@ -309,11 +839,6 @@ Add type toggle to cue popup #13215 -
  • - Effect Meta Knob: draws arc from default meta position - #12638 - #12634 -
  • Handle not supported files when dragging to waveforms and spinnies #13206 @@ -730,10 +1255,6 @@ Support for bulk devices on Windows and Mac #13008
  • -
  • - Drop lodash dependency in ComponentJS - #12779 -
  • Fix pending reference to the old mapping after selecting 'No mapping' #13907 @@ -1016,11 +1537,6 @@ Recording: with empty config, save default split size immediately #13304
  • -
  • - Allow to drop files with supported MIME type regardless off the file extensions - #13209 - #13204 -
  • Add support for Ubuntu Oracular Oriole and remove Lunar Lobster #13348 From 6b13db3560a4d82c38eb435d6d185a411afcb1fc Mon Sep 17 00:00:00 2001 From: ronso0 Date: Sun, 23 Jun 2024 13:01:53 +0200 Subject: [PATCH 009/201] WHotcueButton: allow drag-and-drop to swap hotcues --- .../LateNight/controls/button_hotcue.xml | 1 + res/skins/LateNight/skin.xml | 4 + src/skin/legacy/tooltips.cpp | 5 +- src/widget/whotcuebutton.cpp | 117 ++++++++++++++++-- src/widget/whotcuebutton.h | 39 +++++- src/widget/wpushbutton.cpp | 9 +- src/widget/wpushbutton.h | 2 + 7 files changed, 164 insertions(+), 13 deletions(-) diff --git a/res/skins/LateNight/controls/button_hotcue.xml b/res/skins/LateNight/controls/button_hotcue.xml index 0e499d5715c..952b85b37e5 100644 --- a/res/skins/LateNight/controls/button_hotcue.xml +++ b/res/skins/LateNight/controls/button_hotcue.xml @@ -17,6 +17,7 @@ 3 + 0 skins:LateNight//buttons/btn__square.svg diff --git a/res/skins/LateNight/skin.xml b/res/skins/LateNight/skin.xml index a553f5500b4..0c3c80fe9d6 100644 --- a/res/skins/LateNight/skin.xml +++ b/res/skins/LateNight/skin.xml @@ -163,6 +163,10 @@ 127 105 105 + + 1 #999 #999 diff --git a/src/skin/legacy/tooltips.cpp b/src/skin/legacy/tooltips.cpp index bc033a15ffc..1a99acf87d5 100644 --- a/src/skin/legacy/tooltips.cpp +++ b/src/skin/legacy/tooltips.cpp @@ -694,7 +694,10 @@ void Tooltips::addStandardTooltips() { << QString("%1 + %2: %3") .arg(rightClick, shift, - tr("Delete selected hotcue.")); + tr("Delete selected hotcue.")) + << tr("Drag this button onto another Hotcue button to move it " + "there (change its index). If the other hotcue is set, " + "the two are swapped."); // Status displays and toggle buttons add("toggle_recording") diff --git a/src/widget/whotcuebutton.cpp b/src/widget/whotcuebutton.cpp index 0e91f1812ad..9eb0ba292ab 100644 --- a/src/widget/whotcuebutton.cpp +++ b/src/widget/whotcuebutton.cpp @@ -1,15 +1,22 @@ #include "widget/whotcuebutton.h" +#include +#include +#include +#include +#include #include #include "mixer/playerinfo.h" #include "moc_whotcuebutton.cpp" #include "track/track.h" +#include "util/dnd.h" #include "widget/controlwidgetconnection.h" namespace { constexpr int kDefaultDimBrightThreshold = 127; -} // namespace +const QString kDragDataType = QStringLiteral("hotcueDragInfo"); +} // anonymous namespace WHotcueButton::WHotcueButton(const QString& group, QWidget* pParent) : WPushButton(pParent), @@ -21,6 +28,7 @@ WHotcueButton::WHotcueButton(const QString& group, QWidget* pParent) m_bCueColorDimmed(false), m_bCueColorIsLight(false), m_bCueColorIsDark(false) { + setAcceptDrops(true); } void WHotcueButton::setup(const QDomNode& node, const SkinContext& context) { @@ -46,6 +54,15 @@ void WHotcueButton::setup(const QDomNode& node, const SkinContext& context) { m_hoverCueColor = context.selectBool(node, QStringLiteral("Hover"), false); + // For dnd/swapping hotcues we use the rendered widget pixmap as dnd cursor. + // Unfortnately the margin that constraints the bg color is not considered, + // so we shrink the rect by custom margins. + okay = false; + int dndMargin = context.selectInt(node, QStringLiteral("DndRectMargin"), &okay); + if (okay && dndMargin > 0) { + m_dndRectMargins = QMargins(dndMargin, dndMargin, dndMargin, dndMargin); + } + m_pCueMenuPopup = make_parented(context.getConfig(), this); ColorPaletteSettings colorPaletteSettings(context.getConfig()); auto colorPalette = colorPaletteSettings.getHotcueColorPalette(); @@ -90,8 +107,8 @@ void WHotcueButton::setup(const QDomNode& node, const SkinContext& context) { } } -void WHotcueButton::mousePressEvent(QMouseEvent* e) { - const bool rightClick = e->button() == Qt::RightButton; +void WHotcueButton::mousePressEvent(QMouseEvent* pEvent) { + const bool rightClick = pEvent->button() == Qt::RightButton; if (rightClick) { if (isPressed()) { // Discard right clicks when already left clicked. @@ -117,7 +134,7 @@ void WHotcueButton::mousePressEvent(QMouseEvent* e) { if (!pHotCue) { return; } - if (e->modifiers().testFlag(Qt::ShiftModifier)) { + if (pEvent->modifiers().testFlag(Qt::ShiftModifier)) { pTrack->removeCue(pHotCue); return; } @@ -129,16 +146,100 @@ void WHotcueButton::mousePressEvent(QMouseEvent* e) { } // Pass all other press events to the base class. - WPushButton::mousePressEvent(e); + WPushButton::mousePressEvent(pEvent); } -void WHotcueButton::mouseReleaseEvent(QMouseEvent* e) { - const bool rightClick = e->button() == Qt::RightButton; +void WHotcueButton::mouseReleaseEvent(QMouseEvent* pEvent) { + const bool rightClick = pEvent->button() == Qt::RightButton; if (rightClick) { // Don't handle stray release events return; } - WPushButton::mouseReleaseEvent(e); + WPushButton::mouseReleaseEvent(pEvent); +} + +void WHotcueButton::mouseMoveEvent(QMouseEvent* pEvent) { + TrackPointer pTrack = PlayerInfo::instance().getTrackInfo(m_group); + if (!pTrack) { + return; + } + + // Maybe set up a QDrag for swapping hotcues. + // Only allow moving set hotcues to empty or set slots. + // Note that Track::swapHotcues() allows both directions. + if (m_hotcue == Cue::kNoHotCue) { + return; + } + + if (DragAndDropHelper::mouseMoveInitiatesDrag(pEvent)) { + const TrackId id = pTrack->getId(); + VERIFY_OR_DEBUG_ASSERT(id.isValid()) { + return; + } + QDrag* pDrag = new QDrag(this); + HotcueDragInfo dragData(id, m_hotcue); + auto mimeData = std::make_unique(); + mimeData->setData(kDragDataType, dragData.toByteArray()); + pDrag->setMimeData(mimeData.release()); + + // Use the currently rendered button as dnd cursor + // (incl. hover and pressed style). + // Note: for some reason, both grab() and render() render with sharp corners, + // ie. qss 'border-radius' is not applied to the drag image. + const QPixmap currLook = grab(rect().marginsRemoved(m_dndRectMargins)); + pDrag->setDragCursor(currLook, Qt::MoveAction); + + m_dragging = true; + pDrag->exec(); + m_dragging = false; + + // Release this button afterwards. + // This prevents both the preview and the pressed state from getting stuck. + QEvent leaveEv(QEvent::Leave); + QApplication::sendEvent(this, &leaveEv); + } +} + +void WHotcueButton::dragEnterEvent(QDragEnterEvent* pEvent) { + if (pEvent->source() == this) { + pEvent->ignore(); + return; + } + TrackPointer pTrack = PlayerInfo::instance().getTrackInfo(m_group); + if (!pTrack) { + return; + } + QByteArray mimeDataBytes = pEvent->mimeData()->data(kDragDataType); + if (mimeDataBytes.isEmpty()) { + return; + } + HotcueDragInfo dragData = HotcueDragInfo::fromByteArray(mimeDataBytes); + if (dragData.isValid() && + dragData.trackId == pTrack->getId() && + dragData.hotcue != m_hotcue) { + pEvent->acceptProposedAction(); + } +} + +void WHotcueButton::dropEvent(QDropEvent* pEvent) { + if (pEvent->source() == this) { + pEvent->ignore(); + return; + } + TrackPointer pTrack = PlayerInfo::instance().getTrackInfo(m_group); + if (!pTrack) { + return; + } + QByteArray mimeDataBytes = pEvent->mimeData()->data(kDragDataType); + if (mimeDataBytes.isEmpty()) { + return; + } + HotcueDragInfo dragData = HotcueDragInfo::fromByteArray(mimeDataBytes); + if (dragData.isValid() && + dragData.trackId == pTrack->getId() && + dragData.hotcue != m_hotcue) { + pTrack->swapHotcues(dragData.hotcue, m_hotcue); + } } ConfigKey WHotcueButton::createConfigKey(const QString& name) { diff --git a/src/widget/whotcuebutton.h b/src/widget/whotcuebutton.h index 48dd3395c74..6867ef68126 100644 --- a/src/widget/whotcuebutton.h +++ b/src/widget/whotcuebutton.h @@ -2,12 +2,42 @@ #include +#include "track/trackid.h" #include "util/parented_ptr.h" #include "widget/wcuemenupopup.h" #include "widget/wpushbutton.h" class WHotcueButton : public WPushButton { Q_OBJECT + + struct HotcueDragInfo { + HotcueDragInfo(TrackId id, int cue) + : trackId(id), + hotcue(cue) {}; + + static HotcueDragInfo fromByteArray(const QByteArray& bytes) { + QDataStream stream(bytes); + TrackId trackId; + int hotcue; + stream >> trackId >> hotcue; + return HotcueDragInfo(trackId, hotcue); + }; + + QByteArray toByteArray() { + QByteArray bytes; + QDataStream dataStream(&bytes, QIODevice::WriteOnly); + dataStream << trackId << hotcue; + return bytes; + }; + + bool isValid() { + return trackId.isValid() && hotcue != Cue::kNoHotCue; + } + + TrackId trackId = TrackId(); + int hotcue = Cue::kNoHotCue; + }; + public: WHotcueButton(const QString& group, QWidget* pParent); @@ -25,8 +55,12 @@ class WHotcueButton : public WPushButton { Q_PROPERTY(QString type MEMBER m_type); protected: - void mousePressEvent(QMouseEvent* e) override; - void mouseReleaseEvent(QMouseEvent* e) override; + void mousePressEvent(QMouseEvent* pEvent) override; + void mouseReleaseEvent(QMouseEvent* pEvent) override; + void mouseMoveEvent(QMouseEvent* pEvent) override; + void dragEnterEvent(QDragEnterEvent* pEvent) override; + void dropEvent(QDropEvent* pEvent) override; + void restyleAndRepaint() override; private slots: @@ -48,4 +82,5 @@ class WHotcueButton : public WPushButton { bool m_bCueColorIsLight; bool m_bCueColorIsDark; QString m_type; + QMargins m_dndRectMargins; }; diff --git a/src/widget/wpushbutton.cpp b/src/widget/wpushbutton.cpp index e6e54ccfbe2..d60117fe312 100644 --- a/src/widget/wpushbutton.cpp +++ b/src/widget/wpushbutton.cpp @@ -237,6 +237,7 @@ void WPushButton::setup(const QDomNode& node, const SkinContext& context) { void WPushButton::setStates(int iStates) { m_bHovered = false; m_bPressed = false; + m_dragging = false; m_iNoStates = iStates; m_elideMode = Qt::ElideNone; m_activeTouchButton = Qt::NoButton; @@ -440,7 +441,9 @@ bool WPushButton::event(QEvent* e) { m_bHovered = true; restyleAndRepaint(); } else if (e->type() == QEvent::Leave) { - if (m_bPressed) { + // Leave might occur sporadically while dragging (swapping) a WHotcueButton. + // Don't release in that case. + if (m_bPressed && !m_dragging) { // A Leave event is send instead of a mouseReleaseEvent() // fake it to get not stuck in pressed state QMouseEvent mouseEvent = QMouseEvent( @@ -473,6 +476,8 @@ void WPushButton::focusOutEvent(QFocusEvent* e) { } void WPushButton::mouseReleaseEvent(QMouseEvent * e) { + // Note. when changing any of these actions, also take care of + // WHotcueButton::release() const bool leftClick = e->button() == Qt::LeftButton; const bool rightClick = e->button() == Qt::RightButton; @@ -499,7 +504,7 @@ void WPushButton::mouseReleaseEvent(QMouseEvent * e) { if (rightClick) { // This is the secondary clickButton function, - // due the leak of visual feedback we do not allow a toggle + // due the lack of visual feedback we do not allow a toggle // function m_bPressed = false; if (m_rightButtonMode == ControlPushButton::PUSH diff --git a/src/widget/wpushbutton.h b/src/widget/wpushbutton.h index a1970cf02ae..b05ab585057 100644 --- a/src/widget/wpushbutton.h +++ b/src/widget/wpushbutton.h @@ -97,6 +97,8 @@ class WPushButton : public WWidget { bool m_bPressed; // True, if the button is pointer is above button bool m_bHovered; + // Set true by WHotcueButton while it's being dragged + bool m_dragging; // Array of associated pixmaps int m_iNoStates; From 4b906320027f842e81318f3c7594fee34d4a4446 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Tue, 1 Oct 2024 23:39:02 +0200 Subject: [PATCH 010/201] WHotcueButton: drag with Shift for preview-less swapping --- src/skin/legacy/tooltips.cpp | 4 +++- src/widget/whotcuebutton.cpp | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/skin/legacy/tooltips.cpp b/src/skin/legacy/tooltips.cpp index 1a99acf87d5..db42ebc1d84 100644 --- a/src/skin/legacy/tooltips.cpp +++ b/src/skin/legacy/tooltips.cpp @@ -697,7 +697,9 @@ void Tooltips::addStandardTooltips() { tr("Delete selected hotcue.")) << tr("Drag this button onto another Hotcue button to move it " "there (change its index). If the other hotcue is set, " - "the two are swapped."); + "the two are swapped.") + << tr("Dragging with Shift key pressed will not start previewing " + "the hotcue"); // Status displays and toggle buttons add("toggle_recording") diff --git a/src/widget/whotcuebutton.cpp b/src/widget/whotcuebutton.cpp index 9eb0ba292ab..4b34fbb62be 100644 --- a/src/widget/whotcuebutton.cpp +++ b/src/widget/whotcuebutton.cpp @@ -146,7 +146,11 @@ void WHotcueButton::mousePressEvent(QMouseEvent* pEvent) { } // Pass all other press events to the base class. - WPushButton::mousePressEvent(pEvent); + // Except when Shift is pressed which is used to swap hotcues without + // starting the preview. + if (!pEvent->modifiers().testFlag(Qt::ShiftModifier)) { + WPushButton::mousePressEvent(pEvent); + } } void WHotcueButton::mouseReleaseEvent(QMouseEvent* pEvent) { From d52e36dd9bf87fc08bec30d88ffff4548fbc5efc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Thu, 2 Jan 2025 10:40:18 +0100 Subject: [PATCH 011/201] Silence warning introduced in #13339 It leads to failing tests when running mixxx-test directly --- src/engine/controls/bpmcontrol.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/engine/controls/bpmcontrol.cpp b/src/engine/controls/bpmcontrol.cpp index 55f071fab3b..48a9ef48f1f 100644 --- a/src/engine/controls/bpmcontrol.cpp +++ b/src/engine/controls/bpmcontrol.cpp @@ -1133,7 +1133,6 @@ void BpmControl::trackBeatsUpdated(mixxx::BeatsPointer pBeats) { frameInfo().trackEndPosition) : mixxx::Bpm()); } - qWarning() << "BpmControl::trackBeatsUpdated"; m_pBeats = pBeats; updateLocalBpm(); resetSyncAdjustment(); From 6d63fbfc307eb5de100988cad6c0b5e85f4a7af5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Fri, 3 Jan 2025 10:34:48 +0100 Subject: [PATCH 012/201] Move Experimental Features block --- CHANGELOG.md | 18 ++++----- res/linux/org.mixxx.Mixxx.metainfo.xml | 54 +++++++++++++------------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15139887ae8..503de4dc4c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,15 +10,6 @@ * fix: prevent null CO access when cloning sampler or preview [#13740](https://github.com/mixxxdj/mixxx/pull/13740) * Tooltips: fix cue mode setting location [#14045](https://github.com/mixxxdj/mixxx/pull/14045) -### Experimental Features - -* SoundManagerIOS: Remove unsupported/redundant options [#13487](https://github.com/mixxxdj/mixxx/pull/13487) -* ControllerRenderingEngine: Disable BGRA when targeting Wasm [#13502](https://github.com/mixxxdj/mixxx/pull/13502) -* BaseTrackTableModel: Disable inline track editing on iOS [#13494](https://github.com/mixxxdj/mixxx/pull/13494) -* set QQuickStyle to "basic" [#13696](https://github.com/mixxxdj/mixxx/pull/13696) - [#13600](https://github.com/mixxxdj/mixxx/issues/13600) -* fix: trigger QML waveform slot at init [#13736](https://github.com/mixxxdj/mixxx/pull/13736) - ### Preferences * (fix) Sound preferences: don't set m_settingsModified in update slots [#13450](https://github.com/mixxxdj/mixxx/pull/13450) @@ -120,6 +111,15 @@ * Auto DJ: Add context menu action for enabling/disabling the Auto DJ [#13593](https://github.com/mixxxdj/mixxx/pull/13593) * Auto DJ Cross fader center [#13628](https://github.com/mixxxdj/mixxx/pull/13628) +### Experimental Features + +* SoundManagerIOS: Remove unsupported/redundant options [#13487](https://github.com/mixxxdj/mixxx/pull/13487) +* ControllerRenderingEngine: Disable BGRA when targeting Wasm [#13502](https://github.com/mixxxdj/mixxx/pull/13502) +* BaseTrackTableModel: Disable inline track editing on iOS [#13494](https://github.com/mixxxdj/mixxx/pull/13494) +* set QQuickStyle to "basic" [#13696](https://github.com/mixxxdj/mixxx/pull/13696) + [#13600](https://github.com/mixxxdj/mixxx/issues/13600) +* fix: trigger QML waveform slot at init [#13736](https://github.com/mixxxdj/mixxx/pull/13736) + ### Target support * DlgPrefSound: Add missing ifdefs for building without Rubberband [#13577](https://github.com/mixxxdj/mixxx/pull/13577) diff --git a/res/linux/org.mixxx.Mixxx.metainfo.xml b/res/linux/org.mixxx.Mixxx.metainfo.xml index 470e5bbb069..e4a63dcdbc0 100644 --- a/res/linux/org.mixxx.Mixxx.metainfo.xml +++ b/res/linux/org.mixxx.Mixxx.metainfo.xml @@ -96,7 +96,7 @@ Do not edit it manually. --> - +

    Engine @@ -116,32 +116,6 @@ #14045

  • -

    - Experimental Features -

    -
      -
    • - SoundManagerIOS: Remove unsupported/redundant options - #13487 -
    • -
    • - ControllerRenderingEngine: Disable BGRA when targeting Wasm - #13502 -
    • -
    • - BaseTrackTableModel: Disable inline track editing on iOS - #13494 -
    • -
    • - set QQuickStyle to "basic" - #13696 - #13600 -
    • -
    • - fix: trigger QML waveform slot at init - #13736 -
    • -

    Preferences

    @@ -425,6 +399,32 @@ #13628 +

    + Experimental Features +

    +
      +
    • + SoundManagerIOS: Remove unsupported/redundant options + #13487 +
    • +
    • + ControllerRenderingEngine: Disable BGRA when targeting Wasm + #13502 +
    • +
    • + BaseTrackTableModel: Disable inline track editing on iOS + #13494 +
    • +
    • + set QQuickStyle to "basic" + #13696 + #13600 +
    • +
    • + fix: trigger QML waveform slot at init + #13736 +
    • +

    Target support

    From f090ed02bde5cedf19882906eef2293977e1cf91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Fri, 3 Jan 2025 10:44:29 +0100 Subject: [PATCH 013/201] Add recently merged PRs --- CHANGELOG.md | 6 ++++++ res/linux/org.mixxx.Mixxx.metainfo.xml | 11 ++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 503de4dc4c8..9054e280084 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -185,12 +185,17 @@ [#13687](https://github.com/mixxxdj/mixxx/pull/13687) [#13968](https://github.com/mixxxdj/mixxx/pull/13968) [#13965](https://github.com/mixxxdj/mixxx/issues/13965) + [#14107](https://github.com/mixxxdj/mixxx/pull/14107) + [#14095](https://github.com/mixxxdj/mixxx/issues/14095) + [#14087](https://github.com/mixxxdj/mixxx/pull/14087) + [#14086](https://github.com/mixxxdj/mixxx/issues/14086) * ci(pre-commit): Add cmake-lint hook [#13932](https://github.com/mixxxdj/mixxx/pull/13932) * refactor: remove samplew_autogen.h [#13988](https://github.com/mixxxdj/mixxx/pull/13988) [#14005](https://github.com/mixxxdj/mixxx/pull/14005) * fix clang-tidy complain [#14029](https://github.com/mixxxdj/mixxx/pull/14029) * ci(dependabot): Open PRs against 2.5 branch instead of main [#14060](https://github.com/mixxxdj/mixxx/pull/14060) +* Happy New Year 2025! [#14098](https://github.com/mixxxdj/mixxx/pull/14098) ## [2.5.0](https://github.com/mixxxdj/mixxx/issues?q=milestone%3A2.5.0) (2024-12-24) @@ -322,6 +327,7 @@ * Add backend for Audio Unit (AU) plugins on macOS [#12112](https://github.com/mixxxdj/mixxx/pull/12112) [#13938](https://github.com/mixxxdj/mixxx/pull/13938) + [#13887](https://github.com/mixxxdj/mixxx/pull/13887) * Effect Meta knob: Draw arc from default meta position [#12638](https://github.com/mixxxdj/mixxx/pull/12638) [#12634](https://github.com/mixxxdj/mixxx/issues/12634) diff --git a/res/linux/org.mixxx.Mixxx.metainfo.xml b/res/linux/org.mixxx.Mixxx.metainfo.xml index e4a63dcdbc0..76ffe6df25c 100644 --- a/res/linux/org.mixxx.Mixxx.metainfo.xml +++ b/res/linux/org.mixxx.Mixxx.metainfo.xml @@ -96,7 +96,7 @@ Do not edit it manually. --> - +

    Engine @@ -662,6 +662,10 @@ #13687 #13968 #13965 + #14107 + #14095 + #14087 + #14086

  • ci(pre-commit): Add cmake-lint hook @@ -680,6 +684,10 @@ ci(dependabot): Open PRs against 2.5 branch instead of main #14060
  • +
  • + Happy New Year 2025! + #14098 +
  • @@ -952,6 +960,7 @@ Add backend for Audio Unit (AU) plugins on macOS #12112 #13938 + #13887
  • Effect Meta knob: Draw arc from default meta position From 63db023b4c9b710ae524cdf256be528c0c9101e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Fri, 3 Jan 2025 10:51:10 +0100 Subject: [PATCH 014/201] Introduce a changelog entry for 2.5.1 --- CHANGELOG.md | 14 ++++++++++++ res/linux/org.mixxx.Mixxx.metainfo.xml | 31 ++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8ca5d3ab87..270cfdc7d1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## [2.5.1](https://github.com/mixxxdj/mixxx/milestone/45) (unreleased) + +### Controller Mappings + +* Numark NS6II: Add new controller mapping [#11075](https://github.com/mixxxdj/mixxx/pull/11075) +* Hercules Inpulse 300: Updated mapping [#14051](https://github.com/mixxxdj/mixxx/pull/14051) + +### Fixes + +* Deere (64 samplers): Bring back library in regular view + [#14101](https://github.com/mixxxdj/mixxx/pull/14101) + [#14097](https://github.com/mixxxdj/mixxx/issues/14097) +* Enable R3 time-stretching with Rubberband 4.0.0 API version numbers [#14100](https://github.com/mixxxdj/mixxx/pull/14100) + ## [2.5.0](https://github.com/mixxxdj/mixxx/issues?q=milestone%3A2.5.0) (2024-12-24) ### Modernized Platform: Update to Qt6 diff --git a/res/linux/org.mixxx.Mixxx.metainfo.xml b/res/linux/org.mixxx.Mixxx.metainfo.xml index c27c115eeef..2093fbef310 100644 --- a/res/linux/org.mixxx.Mixxx.metainfo.xml +++ b/res/linux/org.mixxx.Mixxx.metainfo.xml @@ -96,6 +96,37 @@ Do not edit it manually. --> + + +

    + Controller Mappings +

    +
      +
    • + Numark NS6II: Add new controller mapping + #11075 +
    • +
    • + Hercules Inpulse 300: Updated mapping + #14051 +
    • +
    +

    + Fixes +

    +
      +
    • + Deere (64 samplers): Bring back library in regular view + #14101 + #14097 +
    • +
    • + Enable R3 time-stretching with Rubberband 4.0.0 API version numbers + #14100 +
    • +
    +
    +

    From 7fd7855a47edbb1103d9f950968c1c61dc224e35 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Fri, 3 Jan 2025 17:20:02 +0100 Subject: [PATCH 015/201] (fix) Tracks: allow copy also with locked track model --- src/widget/wtracktableview.cpp | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/widget/wtracktableview.cpp b/src/widget/wtracktableview.cpp index 4c937c817eb..8e32da76799 100644 --- a/src/widget/wtracktableview.cpp +++ b/src/widget/wtracktableview.cpp @@ -1116,23 +1116,30 @@ void WTrackTableView::keyPressEvent(QKeyEvent* event) { break; } TrackModel* pTrackModel = getTrackModel(); - if (pTrackModel && !pTrackModel->isLocked()) { - if (event->matches(QKeySequence::Delete) || event->key() == Qt::Key_Backspace) { - removeSelectedTracks(); - return; - } - if (event->matches(QKeySequence::Cut)) { - cutSelectedTracks(); - return; + if (pTrackModel) { + if (!pTrackModel->isLocked()) { + if (event->matches(QKeySequence::Delete) || event->key() == Qt::Key_Backspace) { + removeSelectedTracks(); + return; + } + if (event->matches(QKeySequence::Cut)) { + cutSelectedTracks(); + return; + } + if (event->matches(QKeySequence::Paste)) { + pasteTracks(currentIndex()); + return; + } + if (event->key() == Qt::Key_Escape) { + clearSelection(); + setCurrentIndex(QModelIndex()); + } } + if (event->matches(QKeySequence::Copy)) { copySelectedTracks(); return; } - if (event->matches(QKeySequence::Paste)) { - pasteTracks(currentIndex()); - return; - } if (event->modifiers().testFlag(Qt::AltModifier) && (event->key() == Qt::Key_Up || event->key() == Qt::Key_Down || From 25fcc410ded34fdef006334ce25bb3f1e197272b Mon Sep 17 00:00:00 2001 From: ronso0 Date: Fri, 3 Jan 2025 17:20:38 +0100 Subject: [PATCH 016/201] Tracks: re-enable Ctrl+C copy the cell content --- src/widget/wtracktableview.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/widget/wtracktableview.cpp b/src/widget/wtracktableview.cpp index 8e32da76799..2bbdf69d4da 100644 --- a/src/widget/wtracktableview.cpp +++ b/src/widget/wtracktableview.cpp @@ -1151,9 +1151,14 @@ void WTrackTableView::keyPressEvent(QKeyEvent* event) { moveSelectedTracks(event); return; } - if (event->key() == Qt::Key_Escape) { - clearSelection(); - setCurrentIndex(QModelIndex()); + if (event->modifiers().testFlag(Qt::ControlModifier) && + event->modifiers().testFlag(Qt::ShiftModifier) && + event->key() == Qt::Key_C) { + // copy the cell content as native QKeySequence::Copy would + QKeyEvent ke = + QKeyEvent{QEvent::KeyPress, Qt::Key_C, Qt::ControlModifier}; + QTableView::keyPressEvent(&ke); + return; } } QTableView::keyPressEvent(event); From e5f36555c30f9b6982911275575bbdaba5f897c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Fri, 3 Jan 2025 19:57:07 +0100 Subject: [PATCH 017/201] Add --no-show-signature to not clutter GIT_COMMIT_DATE with signature infos --- cmake/modules/GitInfo.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/GitInfo.cmake b/cmake/modules/GitInfo.cmake index 0d3729fd052..ab2c5e755ee 100644 --- a/cmake/modules/GitInfo.cmake +++ b/cmake/modules/GitInfo.cmake @@ -54,7 +54,7 @@ endif() # Get the current commit date if(NOT GIT_COMMIT_DATE) execute_process( - COMMAND git show --quiet --format=%cI --date=short + COMMAND git show --quiet --format=%cI --date=short --no-show-signature WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE GIT_COMMIT_DATE OUTPUT_STRIP_TRAILING_WHITESPACE From 355eb9ce880349ff94759f731bb7ae747bf6677c Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Mon, 30 Dec 2024 12:11:32 +0100 Subject: [PATCH 018/201] fix: Qt6.9 QString stricter .arg matching (Hopefully) fixes #14071 --- src/engine/sidechain/enginerecord.cpp | 6 +++--- src/recording/recordingmanager.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/engine/sidechain/enginerecord.cpp b/src/engine/sidechain/enginerecord.cpp index 11c10223da8..585a31ec053 100644 --- a/src/engine/sidechain/enginerecord.cpp +++ b/src/engine/sidechain/enginerecord.cpp @@ -224,9 +224,9 @@ void EngineRecord::process(const CSAMPLE* pBuffer, const int iBufferSize) { } QString EngineRecord::getRecordedDurationStr() { - return QString("%1:%2") - .arg(m_recordedDuration / 60, 2, 'f', 0, '0') // minutes - .arg(m_recordedDuration % 60, 2, 'f', 0, '0'); // seconds + return QStringLiteral("%1:%2") + .arg(m_recordedDuration / 60, 2, 10, QChar('0')) // minutes + .arg(m_recordedDuration % 60, 2, 10, QChar('0')); // seconds } void EngineRecord::writeCueLine() { diff --git a/src/recording/recordingmanager.cpp b/src/recording/recordingmanager.cpp index 759a370be87..1483ace9cb5 100644 --- a/src/recording/recordingmanager.cpp +++ b/src/recording/recordingmanager.cpp @@ -209,9 +209,9 @@ void RecordingManager::slotDurationRecorded(quint64 duration) { // Copy from the implementation in enginerecord.cpp QString RecordingManager::getRecordedDurationStr(unsigned int duration) { - return QString("%1:%2") - .arg(duration / 60, 2, 'f', 0, '0') // minutes - .arg(duration % 60, 2, 'f', 0, '0'); // seconds + return QStringLiteral("%1:%2") + .arg(duration / 60, 2, 10, QChar('0')) // minutes + .arg(duration % 60, 2, 10, QChar('0')); // seconds } // Only called when recording is active. From 2c2dda97818b4a16af411ae6f1001b9b786fd4d2 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Wed, 8 Jan 2025 14:31:21 +0100 Subject: [PATCH 019/201] remove obsolete woverview files (again) probably reintroduced during merge of 2.5 --- src/widget/woverviewhsv.cpp | 146 ---------------------------------- src/widget/woverviewlmh.cpp | 137 -------------------------------- src/widget/woverviewrgb.cpp | 152 ------------------------------------ 3 files changed, 435 deletions(-) delete mode 100644 src/widget/woverviewhsv.cpp delete mode 100644 src/widget/woverviewlmh.cpp delete mode 100644 src/widget/woverviewrgb.cpp diff --git a/src/widget/woverviewhsv.cpp b/src/widget/woverviewhsv.cpp deleted file mode 100644 index c818bcf7094..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(QStringLiteral("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/woverviewlmh.cpp b/src/widget/woverviewlmh.cpp deleted file mode 100644 index 30f2f4aad01..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(QStringLiteral("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/woverviewrgb.cpp b/src/widget/woverviewrgb.cpp deleted file mode 100644 index e9e03145377..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(QStringLiteral("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; -} From db4d822d6ace3243a8763edb19701a3f9646de6c Mon Sep 17 00:00:00 2001 From: ronso0 Date: Wed, 8 Jan 2025 14:53:58 +0100 Subject: [PATCH 020/201] WOverview: remove unused coefficients --- src/widget/woverview.cpp | 17 ++--------------- src/widget/woverview.h | 9 ++++----- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 21824313e0d..d7936bd39eb 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -51,8 +51,7 @@ WOverview::WOverview( m_bTimeRulerActive(false), m_orientation(Qt::Horizontal), m_iLabelFontSize(10), - m_a(1.0), - m_b(0.0), + m_maxPixelPos(1.0), m_analyzerProgress(kAnalyzerProgressUnknown), m_trackLoaded(false), m_pHoveredMark(nullptr), @@ -1634,19 +1633,7 @@ double WOverview::samplePositionToSeconds(double sample) { void WOverview::resizeEvent(QResizeEvent* pEvent) { Q_UNUSED(pEvent); - // Play-position potmeters range from 0 to 1 but they allow out-of-range - // sets. This is to give VC access to the pre-roll area. - constexpr double kMaxPlayposRange = 1.0; - constexpr double kMinPlayposRange = 0.0; - - // Values of zero and one in normalized space. - const double zero = (0.0 - kMinPlayposRange) / (kMaxPlayposRange - kMinPlayposRange); - const double one = (1.0 - kMinPlayposRange) / (kMaxPlayposRange - kMinPlayposRange); - - // These coefficients convert between widget space and normalized value - // space. - m_a = (length() - 1) / (one - zero); - m_b = zero * m_a; + m_maxPixelPos = length() - 1; m_devicePixelRatio = devicePixelRatioF(); diff --git a/src/widget/woverview.h b/src/widget/woverview.h index 242802d057f..bb4c15b49db 100644 --- a/src/widget/woverview.h +++ b/src/widget/woverview.h @@ -108,10 +108,10 @@ class WOverview : public WWidget, public TrackDropTarget { void paintText(const QString& text, QPainter* pPainter); double samplePositionToSeconds(double sample); inline int valueToPosition(double value) const { - return static_cast(m_a * value - m_b); + return static_cast(m_maxPixelPos * value); } inline double positionToValue(int position) const { - return (static_cast(position) + m_b) / m_a; + return static_cast(position) / m_maxPixelPos; } void updateCues(const QList &loadedCues); @@ -166,9 +166,8 @@ class WOverview : public WWidget, public TrackDropTarget { Qt::Orientation m_orientation; int m_iLabelFontSize; - // Coefficient value-position linear transposition - double m_a; - double m_b; + // Coefficient for linear value <-> position transposition + double m_maxPixelPos; AnalyzerProgress m_analyzerProgress; bool m_trackLoaded; From bb34577abbf2e24cbaf98209279ec2c3d5c42230 Mon Sep 17 00:00:00 2001 From: Joerg Date: Wed, 8 Jan 2025 20:42:41 +0100 Subject: [PATCH 021/201] Fix wrong access to ENV var MIXXX_VCPKG_ROOT instead of CMake setting MIXXX_VCPKG_ROOT --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 20d7d8d632b..c445844ffb9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,12 +78,12 @@ if(DEFINED ENV{MIXXX_VCPKG_ROOT} AND NOT DEFINED MIXXX_VCPKG_ROOT) endif() if(DEFINED MIXXX_VCPKG_ROOT) - if(EXISTS "$ENV{MIXXX_VCPKG_ROOT}/overlay/ports" OR NOT EXISTS "$ENV{MIXXX_VCPKG_ROOT}/ports") + if(EXISTS "${MIXXX_VCPKG_ROOT}/overlay/ports" OR NOT EXISTS "${MIXXX_VCPKG_ROOT}/ports") # MIXXX_VCPKG_ROOT points to our vcpkg environment # and we configure the CMAKE_TOOLCHAIN_FILE and overlays accordingly - message(STATUS "Using MIXXX_VCPKG_ROOT: $ENV{MIXXX_VCPKG_ROOT}") + message(STATUS "Using MIXXX_VCPKG_ROOT: ${MIXXX_VCPKG_ROOT}") else() - message(STATUS "MIXXX_VCPKG_ROOT not correct (missing $ENV{MIXXX_VCPKG_ROOT}/overlay/ports)") + message(STATUS "MIXXX_VCPKG_ROOT not correct (missing ${MIXXX_VCPKG_ROOT}/overlay/ports)") FATAL_ERROR_MISSING_ENV() endif() From 36dde30caa5f860483d29d43b4bb61ed1d77ed06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Wed, 8 Jan 2025 23:47:39 +0100 Subject: [PATCH 022/201] Debian: recommend qt6-translations-l10n --- CMakeLists.txt | 5 +++++ packaging/debian/control.in | 1 + 2 files changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 20d7d8d632b..127758d12d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3831,6 +3831,11 @@ set(CPACK_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") set(CPACK_DEBIAN_PACKAGE_SECTION "sound") set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") +if(QT6) + set(CPACK_DEBIAN_PACKAGE_RECOMMENDS "qt6-translations-l10n") +else() + set(CPACK_DEBIAN_PACKAGE_RECOMMENDS "qttranslations5-l10n") +endif() set(CPACK_DEBIAN_PACKAGE_SUGGESTS "pdf-viewer, pulseaudio-utils") set(CPACK_DEBIAN_PACKAGE_REPLACES "mixxx-data") if(QT6) diff --git a/packaging/debian/control.in b/packaging/debian/control.in index a2600ce2f10..f7e70d9ad0d 100644 --- a/packaging/debian/control.in +++ b/packaging/debian/control.in @@ -65,6 +65,7 @@ Package: mixxx Section: @CPACK_DEBIAN_PACKAGE_SECTION@ Architecture: linux-any Depends: ${shlibs:Depends}, ${misc:Depends}, @CPACK_DEBIAN_PACKAGE_DEPENDS@ +Recommends: @CPACK_DEBIAN_PACKAGE_RECOMMENDS@ Suggests: @CPACK_DEBIAN_PACKAGE_SUGGESTS@ Replaces: mixxx-data Description: @CPACK_DEBIAN_PACKAGE_DESCRIPTION_MERGED@ From 2e2fb33c565990d0e3be5808365f273932566040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Wed, 8 Jan 2025 23:57:14 +0100 Subject: [PATCH 023/201] Welcome Plucky Puffin; Good bye Mantic Minotaur --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 20d7d8d632b..fc248d88614 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3864,7 +3864,7 @@ if (NOT CPACK_DEBIAN_PACKAGE_RELEASE) set(CPACK_DEBIAN_PACKAGE_RELEASE 1) endif() -set(CPACK_DEBIAN_DISTRIBUTION_RELEASES jammy mantic noble oracular) +set(CPACK_DEBIAN_DISTRIBUTION_RELEASES jammy noble oracular plucky) set(CPACK_DEBIAN_SOURCE_DIR ${CMAKE_SOURCE_DIR}) set(CPACK_DEBIAN_UPLOAD_PPA_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/packaging/CPackDebUploadPPA.cmake") set(CPACK_DEBIAN_INSTALL_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/packaging/CPackDebInstall.cmake") From 6cd0a01dd027e3090128e0f985612dc0bd5e3d3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Thu, 9 Jan 2025 17:16:35 +0100 Subject: [PATCH 024/201] Add Keywords to org.mixxx.Mixxx.desktop taken from the dfsg release --- res/linux/org.mixxx.Mixxx.desktop | 1 + 1 file changed, 1 insertion(+) diff --git a/res/linux/org.mixxx.Mixxx.desktop b/res/linux/org.mixxx.Mixxx.desktop index 4218ed78544..6559027553b 100644 --- a/res/linux/org.mixxx.Mixxx.desktop +++ b/res/linux/org.mixxx.Mixxx.desktop @@ -9,6 +9,7 @@ Comment=A digital DJ interface Comment[de]=Ein digitales DJ-System Comment[fr]=Une interface numérique pour DJ Exec=sh -c "pasuspender -- mixxx || mixxx" +Keywords=dj;music;alsa;jack:realtime;standalone; Terminal=false Icon=mixxx Type=Application From 96453d56fd86d70c7ea0d0de0d04d5a971ea0e4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Thu, 9 Jan 2025 17:54:32 +0100 Subject: [PATCH 025/201] Add translations to org.mixxx.Mixxx.desktop for > 15 % translated languages --- res/linux/org.mixxx.Mixxx.desktop | 41 ++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/res/linux/org.mixxx.Mixxx.desktop b/res/linux/org.mixxx.Mixxx.desktop index 6559027553b..0d63ef8ff75 100644 --- a/res/linux/org.mixxx.Mixxx.desktop +++ b/res/linux/org.mixxx.Mixxx.desktop @@ -1,13 +1,52 @@ [Desktop Entry] Version=1.0 Name=Mixxx -Name[de]=Mixxx GenericName=Digital DJ interface GenericName[de]=Digitales DJ-System GenericName[fr]=Interface numérique pour DJ +GenericName[nl]=Digitaal DJ-systeem +GenericName[it]=Sistema DJ digitale +GenericName[es]=Sistema DJ digital +GenericName[cz]=Digitální DJ systém +GenericName[ru]=Цифровая диджейская система +GenericName[uk]=Цифрова діджейська система +GenericName[sl]=Digitalni DJ sistem +GenericName[pl]=Cyfrowy system DJ-ski +GenericName[zh]=数字 DJ 系统 +GenericName[sv]=Digitalt DJ-system +GenericName[pt]=Sistema de DJ digital +GenericName[ro]=Sistem DJ digital +GenericName[ja]=デジタルDJシステム +GenericName[hu]=Digitális DJ rendszer +GenericName[tr]=Dijital DJ sistemi +GenericName[gr]=Ψηφιακό σύστημα DJ +GenericName[fi]=Digitaalinen DJ-järjestelmä +GenericName[ko]=디지털 DJ 시스템 +GenericName[nb]=Digitalt DJ-system +GenericName[bg]=Цифрова DJ система Comment=A digital DJ interface Comment[de]=Ein digitales DJ-System Comment[fr]=Une interface numérique pour DJ +Comment[nl]=Een digitaal DJ-systeem +Comment[it]=Un sistema DJ digitale +Comment[es]=Un sistema de DJ digital +Comment[cz]=Digitální DJ systém +Comment[ru]=Цифровая диджейская система +Comment[uk]=Цифрова діджейська система +Comment[sl]=Digitalni DJ sistem +Comment[pl]=Cyfrowy system DJ-ski +Comment[zh]=数字 DJ 系统 +Comment[sv]=Ett digitalt DJ-system +Comment[pt]=Um sistema de DJ digital +Comment[ro]=Un sistem DJ digital +Comment[ja]=デジタルDJシステム +Comment[hu]=Digitális DJ rendszer +Comment[tr]=Dijital bir DJ sistemi +Comment[gr]=Ένα ψηφιακό σύστημα DJ +Comment[fi]=Digitaalinen DJ-järjestelmä +Comment[ko]=디지털 DJ 시스템 +Comment[nb]=Et digitalt DJ-system +Comment[bg]=Цифрова DJ система Exec=sh -c "pasuspender -- mixxx || mixxx" Keywords=dj;music;alsa;jack:realtime;standalone; Terminal=false From 6fc3426e6cbe7ca9144c16dc5ce8a7e16998767d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 12 Jan 2025 23:18:14 +0100 Subject: [PATCH 026/201] Add missing p prefix to pTexture --- .../renderers/allshader/waveformrendermark.cpp | 10 +++++----- .../renderers/allshader/waveformrendermark.h | 2 +- src/widget/wspinnyglsl.cpp | 10 +++++----- src/widget/wspinnyglsl.h | 2 +- src/widget/wvumeterglsl.cpp | 14 +++++++------- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index a60d668ab97..397792e1802 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -97,7 +97,7 @@ void allshader::WaveformRenderMark::initializeGL() { } void allshader::WaveformRenderMark::drawTexture( - const QMatrix4x4& matrix, float x, float y, QOpenGLTexture* texture) { + const QMatrix4x4& matrix, float x, float y, QOpenGLTexture* pTexture) { const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); const float texx1 = 0.f; const float texy1 = 0.f; @@ -105,9 +105,9 @@ void allshader::WaveformRenderMark::drawTexture( const float texy2 = 1.f; const float posx1 = x; - const float posx2 = x + static_cast(texture->width() / devicePixelRatio); + const float posx2 = x + static_cast(pTexture->width() / devicePixelRatio); const float posy1 = y; - const float posy2 = y + static_cast(texture->height() / devicePixelRatio); + const float posy2 = y + static_cast(pTexture->height() / devicePixelRatio); const float posarray[] = {posx1, posy1, posx2, posy1, posx1, posy2, posx2, posy2}; const float texarray[] = {texx1, texy1, texx2, texy1, texx1, texy2, texx2, texy2}; @@ -130,11 +130,11 @@ void allshader::WaveformRenderMark::drawTexture( m_textureShader.setUniformValue(textureLocation, 0); - texture->bind(); + pTexture->bind(); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - texture->release(); + pTexture->release(); m_textureShader.disableAttributeArray(positionLocation); m_textureShader.disableAttributeArray(texcoordLocation); diff --git a/src/waveform/renderers/allshader/waveformrendermark.h b/src/waveform/renderers/allshader/waveformrendermark.h index 0937cfbbfa1..5f4b812388a 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.h +++ b/src/waveform/renderers/allshader/waveformrendermark.h @@ -51,7 +51,7 @@ class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, QPointF p3); void drawMark(const QMatrix4x4& matrix, const QRectF& rect, QColor color); - void drawTexture(const QMatrix4x4& matrix, float x, float y, QOpenGLTexture* texture); + void drawTexture(const QMatrix4x4& matrix, float x, float y, QOpenGLTexture* pTexture); void updateUntilMark(double playPosition, double markerPosition); void drawUntilMark(const QMatrix4x4& matrix, float x); float getMaxHeightForText(float proportion) const; diff --git a/src/widget/wspinnyglsl.cpp b/src/widget/wspinnyglsl.cpp index 8ffed866e7b..bda968e4d3a 100644 --- a/src/widget/wspinnyglsl.cpp +++ b/src/widget/wspinnyglsl.cpp @@ -171,14 +171,14 @@ void WSpinnyGLSL::initializeGL() { m_vinylQualityShader.init(); } -void WSpinnyGLSL::drawTexture(QOpenGLTexture* texture) { +void WSpinnyGLSL::drawTexture(QOpenGLTexture* pTexture) { const float texx1 = 0.f; const float texy1 = 1.0; const float texx2 = 1.f; const float texy2 = 0.f; - const float tw = texture->width(); - const float th = texture->height(); + const float tw = pTexture->width(); + const float th = pTexture->height(); // fill centered const float posx2 = tw >= th ? 1.f : tw / th; @@ -197,11 +197,11 @@ void WSpinnyGLSL::drawTexture(QOpenGLTexture* texture) { m_textureShader.setAttributeArray( texcoordLocation, GL_FLOAT, texarray.data(), 2); - texture->bind(); + pTexture->bind(); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - texture->release(); + pTexture->release(); } void WSpinnyGLSL::drawVinylQuality() { diff --git a/src/widget/wspinnyglsl.h b/src/widget/wspinnyglsl.h index 2c418a7b867..d6441b52025 100644 --- a/src/widget/wspinnyglsl.h +++ b/src/widget/wspinnyglsl.h @@ -26,7 +26,7 @@ class WSpinnyGLSL : public WSpinnyBase, private QOpenGLFunctions { void initializeGL() override; void paintGL() override; void resizeGL(int w, int h) override; - void drawTexture(QOpenGLTexture* texture); + void drawTexture(QOpenGLTexture* pTexture); void cleanupGL(); void updateTextures(); diff --git a/src/widget/wvumeterglsl.cpp b/src/widget/wvumeterglsl.cpp index 39da264ecc9..e28da41334e 100644 --- a/src/widget/wvumeterglsl.cpp +++ b/src/widget/wvumeterglsl.cpp @@ -150,15 +150,15 @@ void WVuMeterGLSL::cleanupGL() { doneCurrent(); } -void WVuMeterGLSL::drawTexture(QOpenGLTexture* texture, +void WVuMeterGLSL::drawTexture(QOpenGLTexture* pTexture, const QRectF& targetRect, const QRectF& sourceRect) { - const float texx1 = static_cast(sourceRect.x() / texture->width()); - const float texy1 = static_cast(sourceRect.y() / texture->height()); + const float texx1 = static_cast(sourceRect.x() / pTexture->width()); + const float texy1 = static_cast(sourceRect.y() / pTexture->height()); const float texx2 = static_cast( - (sourceRect.x() + sourceRect.width()) / texture->width()); + (sourceRect.x() + sourceRect.width()) / pTexture->width()); const float texy2 = static_cast( - (sourceRect.y() + sourceRect.height()) / texture->height()); + (sourceRect.y() + sourceRect.height()) / pTexture->height()); const float posx1 = static_cast(targetRect.x()); const float posy1 = static_cast(targetRect.y()); @@ -176,9 +176,9 @@ void WVuMeterGLSL::drawTexture(QOpenGLTexture* texture, m_textureShader.setAttributeArray( texcoordLocation, GL_FLOAT, texarray.data(), 2); - texture->bind(); + pTexture->bind(); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - texture->release(); + pTexture->release(); } From 38995fd9e4bbd6863713955e0c9f1e4c900e900f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 18:45:07 +0000 Subject: [PATCH 027/201] Bump azure/trusted-signing-action from 0.5.0 to 0.5.1 Bumps [azure/trusted-signing-action](https://github.com/azure/trusted-signing-action) from 0.5.0 to 0.5.1. - [Release notes](https://github.com/azure/trusted-signing-action/releases) - [Commits](https://github.com/azure/trusted-signing-action/compare/v0.5.0...v0.5.1) --- updated-dependencies: - dependency-name: azure/trusted-signing-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 63e2af1c792..138091e29e4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -315,7 +315,7 @@ jobs: env: AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} if: runner.os == 'Windows' && env.AZURE_TENANT_ID - uses: azure/trusted-signing-action@v0.5.0 + uses: azure/trusted-signing-action@v0.5.1 with: azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} @@ -379,7 +379,7 @@ jobs: env: AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} if: runner.os == 'Windows' && env.AZURE_TENANT_ID - uses: azure/trusted-signing-action@v0.5.0 + uses: azure/trusted-signing-action@v0.5.1 with: azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} From c0a9abdf55083a1cca57a24e5ac6821dfccdaa42 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 18:45:07 +0000 Subject: [PATCH 028/201] Bump actions/upload-artifact from 4.3.3 to 4.6.0 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.3 to 4.6.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.3.3...v4.6.0) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 2 +- .github/workflows/pre-commit.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 63e2af1c792..8571ba0d8ac 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -481,7 +481,7 @@ jobs: - name: "Upload GitHub Actions artifacts" if: matrix.artifacts_path != null - uses: actions/upload-artifact@v4.3.4 + uses: actions/upload-artifact@v4.6.0 with: name: ${{ matrix.artifacts_name }} path: ${{ matrix.artifacts_path }} diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index d0344e0af78..c953c6d1b79 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -69,14 +69,14 @@ jobs: - name: "Upload patch artifact" if: failure() && env.UPLOAD_PATCH_FILE != null - uses: actions/upload-artifact@v4.3.3 + uses: actions/upload-artifact@v4.6.0 with: name: ${{ env.UPLOAD_PATCH_FILE }} path: ${{ env.UPLOAD_PATCH_FILE }} - name: "Upload pre-commit.log" if: failure() && env.UPLOAD_PATCH_FILE == null - uses: actions/upload-artifact@v4.3.3 + uses: actions/upload-artifact@v4.6.0 with: name: pre-commit.log path: /github/home/.cache/pre-commit/pre-commit.log From 10fd15eb7b0068a56dba935117cc16f665045e56 Mon Sep 17 00:00:00 2001 From: Jan Holthuis Date: Mon, 13 Jan 2025 23:25:42 +0100 Subject: [PATCH 029/201] ci(pre-commit): Fix failing codespell hook in desktop file The codespell hook is failing due to translations. This was introduced in PR #14153. This commits excludes the file from the codespell check. --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 179b192e628..477182717b2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -64,7 +64,7 @@ repos: "\\W(?:m_p*(?=[A-Z])|m_(?=\\w)|pp*(?=[A-Z])|k(?=[A-Z])|s_(?=\\w))", --write-changes, ] - exclude: ^(packaging/wix/LICENSE.rtf.in|src/dialog/dlgabout\.cpp|.*\.(?:pot?|(? Date: Tue, 14 Jan 2025 08:06:26 +0100 Subject: [PATCH 030/201] Make MarkerGeometry a class with const getter --- src/waveform/renderers/waveformmark.cpp | 64 ++++++++++++++++--------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/src/waveform/renderers/waveformmark.cpp b/src/waveform/renderers/waveformmark.cpp index 809e633e551..7c093d84b45 100644 --- a/src/waveform/renderers/waveformmark.cpp +++ b/src/waveform/renderers/waveformmark.cpp @@ -204,13 +204,8 @@ bool WaveformMark::contains(QPoint point, Qt::Orientation orientation) const { // Helper struct to calculate the geometry and fontsize needed by generateImage // to draw the label and text -struct MarkerGeometry { - bool m_isSymbol; // is the label normal text or a single symbol (e.g. open circle arrow) - QFont m_font; - QRectF m_contentRect; - QRectF m_labelRect; - QSizeF m_imageSize; - +class MarkerGeometry { + public: MarkerGeometry(const QString& label, bool useIcon, Qt::Alignment align, @@ -314,6 +309,29 @@ struct MarkerGeometry { return QSize{static_cast(m_imageSize.width() * devicePixelRatio), static_cast(m_imageSize.height() * devicePixelRatio)}; } + + const QFont font() const { + return m_font; + } + + const QRectF& contentRect() const { + return m_contentRect; + } + + const QRectF& labelRect() const { + return m_labelRect; + } + + const QSizeF& imageSize() const { + return m_imageSize; + } + + private: + bool m_isSymbol; // is the label normal text or a single symbol (e.g. open circle arrow) + QFont m_font; + QRectF m_contentRect; + QRectF m_labelRect; + QSizeF m_imageSize; }; QImage WaveformMark::generateImage(float devicePixelRatio) { @@ -355,7 +373,7 @@ QImage WaveformMark::generateImage(float devicePixelRatio) { // Determine drawing geometries const MarkerGeometry markerGeometry{label, useIcon, m_align, m_breadth, m_level}; - m_label.setAreaRect(markerGeometry.m_labelRect); + m_label.setAreaRect(markerGeometry.labelRect()); // Create the image QImage image{markerGeometry.getImageSize(devicePixelRatio), @@ -374,53 +392,53 @@ QImage WaveformMark::generateImage(float devicePixelRatio) { painter.setWorldMatrixEnabled(false); // Draw marker lines - const auto hcenter = markerGeometry.m_imageSize.width() / 2.f; + const auto hcenter = markerGeometry.imageSize().width() / 2.f; m_linePosition = static_cast(hcenter); // Draw the center line painter.setPen(fillColor()); - painter.drawLine(QLineF(hcenter, 0.f, hcenter, markerGeometry.m_imageSize.height())); + painter.drawLine(QLineF(hcenter, 0.f, hcenter, markerGeometry.imageSize().height())); painter.setPen(borderColor()); painter.drawLine(QLineF(hcenter - 1.f, 0.f, hcenter - 1.f, - markerGeometry.m_imageSize.height())); + markerGeometry.imageSize().height())); painter.drawLine(QLineF(hcenter + 1.f, 0.f, hcenter + 1.f, - markerGeometry.m_imageSize.height())); + markerGeometry.imageSize().height())); if (useIcon || label.length() != 0) { painter.setPen(borderColor()); // Draw the label rounded rect with border QPainterPath path; - path.addRoundedRect(markerGeometry.m_labelRect, 2.f, 2.f); + path.addRoundedRect(markerGeometry.labelRect(), 2.f, 2.f); painter.fillPath(path, fillColor()); painter.drawPath(path); // Center m_contentRect.width() and m_contentRect.height() inside m_labelRect // and apply the offset x,y so the text ends up in the centered width,height. - QPointF pos(markerGeometry.m_labelRect.x() + - (markerGeometry.m_labelRect.width() - - markerGeometry.m_contentRect.width()) / + QPointF pos(markerGeometry.labelRect().x() + + (markerGeometry.labelRect().width() - + markerGeometry.contentRect().width()) / 2.f - - markerGeometry.m_contentRect.x(), - markerGeometry.m_labelRect.y() + - (markerGeometry.m_labelRect.height() - - markerGeometry.m_contentRect.height()) / + markerGeometry.contentRect().x(), + markerGeometry.labelRect().y() + + (markerGeometry.labelRect().height() - + markerGeometry.contentRect().height()) / 2.f - - markerGeometry.m_contentRect.y()); + markerGeometry.contentRect().y()); if (useIcon) { QSvgRenderer svgRenderer(m_iconPath); - svgRenderer.render(&painter, QRectF(pos, markerGeometry.m_contentRect.size())); + svgRenderer.render(&painter, QRectF(pos, markerGeometry.contentRect().size())); } else { // Draw the text painter.setBrush(Qt::transparent); painter.setPen(labelColor()); - painter.setFont(markerGeometry.m_font); + painter.setFont(markerGeometry.font()); painter.drawText(pos, label); } From 5f693d0128fb4e92a31de4c0e31348db98540581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Tue, 14 Jan 2025 08:16:40 +0100 Subject: [PATCH 031/201] Don't use runtime asserts --- src/waveform/renderers/waveformmark.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/waveform/renderers/waveformmark.cpp b/src/waveform/renderers/waveformmark.cpp index 7c093d84b45..fe13783b25d 100644 --- a/src/waveform/renderers/waveformmark.cpp +++ b/src/waveform/renderers/waveformmark.cpp @@ -335,7 +335,7 @@ class MarkerGeometry { }; QImage WaveformMark::generateImage(float devicePixelRatio) { - assert(needsImageUpdate()); + DEBUG_ASSERT(needsImageUpdate()); // Load the pixmap from file. // If that succeeds loading the text and stroke is skipped. From 5f82622350b700607152087fe0c64b83e1496cc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Tue, 14 Jan 2025 08:21:39 +0100 Subject: [PATCH 032/201] Return early if there is nothing to paint. --- .../allshader/waveformrendermark.cpp | 27 ++++++++++++------- src/waveform/renderers/waveformmark.cpp | 7 +++++ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index 397792e1802..11d0c0d463d 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -231,6 +231,11 @@ void allshader::WaveformRenderMark::paintGL() { static_cast(pMark->m_pGraphics.get()) ->texture(); + if (!pTexture->isCreated()) { + // This happens if the height is zero + continue; + } + const float currentMarkPoint = std::round( static_cast( @@ -383,20 +388,22 @@ void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, floa // Note that in the legacy waveform widgets this is drawn directly // in the WaveformWidgetRenderer itself. Doing it here is cleaner. void allshader::WaveformRenderMark::updatePlayPosMarkTexture() { - float imgwidth; - float imgheight; + const float imgheight = m_waveformRenderer->getBreadth(); + const float imgwidth = 11.f; - const float height = m_waveformRenderer->getBreadth(); - const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); + if (imgheight == 0.0f) { + return; + } + const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); const float lineX = 5.5f; - imgwidth = 11.f; - imgheight = height; - QImage image(static_cast(imgwidth * devicePixelRatio), static_cast(imgheight * devicePixelRatio), QImage::Format_ARGB32_Premultiplied); + if (image.isNull()) { + return; + } image.setDevicePixelRatio(devicePixelRatio); image.fill(QColor(0, 0, 0, 0).rgba()); @@ -417,8 +424,8 @@ void allshader::WaveformRenderMark::updatePlayPosMarkTexture() { // lines next to playpos // Note: don't draw lines where they would overlap the triangles, // otherwise both translucent strokes add up to a darker tone. - painter.drawLine(QLineF(lineX + 1.f, 4.f, lineX + 1.f, height)); - painter.drawLine(QLineF(lineX - 1.f, 4.f, lineX - 1.f, height)); + painter.drawLine(QLineF(lineX + 1.f, 4.f, lineX + 1.f, imgheight)); + painter.drawLine(QLineF(lineX - 1.f, 4.f, lineX - 1.f, imgheight)); // triangle at top edge // Increase line/waveform contrast @@ -433,7 +440,7 @@ void allshader::WaveformRenderMark::updatePlayPosMarkTexture() { painter.setPen(fgColor); painter.setOpacity(1.0); // play position line - painter.drawLine(QLineF(lineX, 0.f, lineX, height)); + painter.drawLine(QLineF(lineX, 0.f, lineX, imgheight)); // triangle at top edge { QPointF baseL = QPointF(lineX - 4.f, 0.f); diff --git a/src/waveform/renderers/waveformmark.cpp b/src/waveform/renderers/waveformmark.cpp index fe13783b25d..8b7a754d842 100644 --- a/src/waveform/renderers/waveformmark.cpp +++ b/src/waveform/renderers/waveformmark.cpp @@ -337,6 +337,10 @@ class MarkerGeometry { QImage WaveformMark::generateImage(float devicePixelRatio) { DEBUG_ASSERT(needsImageUpdate()); + if (m_breadth == 0.0f) { + return {}; + } + // Load the pixmap from file. // If that succeeds loading the text and stroke is skipped. @@ -378,6 +382,9 @@ QImage WaveformMark::generateImage(float devicePixelRatio) { // Create the image QImage image{markerGeometry.getImageSize(devicePixelRatio), QImage::Format_ARGB32_Premultiplied}; + if (image.isNull()) { + return image; + } image.setDevicePixelRatio(devicePixelRatio); // Fill with transparent pixels From 8ae7d2f7206d38b08e63ab97d08ddd838ec2686a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Tue, 14 Jan 2025 19:26:03 +0100 Subject: [PATCH 033/201] Improve variable names --- .../renderers/allshader/waveformrendermark.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index 11d0c0d463d..32b352a4fad 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -388,18 +388,18 @@ void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, floa // Note that in the legacy waveform widgets this is drawn directly // in the WaveformWidgetRenderer itself. Doing it here is cleaner. void allshader::WaveformRenderMark::updatePlayPosMarkTexture() { - const float imgheight = m_waveformRenderer->getBreadth(); - const float imgwidth = 11.f; + const float imgHeight = m_waveformRenderer->getBreadth(); + const float imgWidth = 11.f; - if (imgheight == 0.0f) { + if (imgHeight == 0.0f) { return; } const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); const float lineX = 5.5f; - QImage image(static_cast(imgwidth * devicePixelRatio), - static_cast(imgheight * devicePixelRatio), + QImage image(static_cast(imgWidth * devicePixelRatio), + static_cast(imgHeight * devicePixelRatio), QImage::Format_ARGB32_Premultiplied); if (image.isNull()) { return; @@ -424,8 +424,8 @@ void allshader::WaveformRenderMark::updatePlayPosMarkTexture() { // lines next to playpos // Note: don't draw lines where they would overlap the triangles, // otherwise both translucent strokes add up to a darker tone. - painter.drawLine(QLineF(lineX + 1.f, 4.f, lineX + 1.f, imgheight)); - painter.drawLine(QLineF(lineX - 1.f, 4.f, lineX - 1.f, imgheight)); + painter.drawLine(QLineF(lineX + 1.f, 4.f, lineX + 1.f, imgHeight)); + painter.drawLine(QLineF(lineX - 1.f, 4.f, lineX - 1.f, imgHeight)); // triangle at top edge // Increase line/waveform contrast @@ -440,7 +440,7 @@ void allshader::WaveformRenderMark::updatePlayPosMarkTexture() { painter.setPen(fgColor); painter.setOpacity(1.0); // play position line - painter.drawLine(QLineF(lineX, 0.f, lineX, imgheight)); + painter.drawLine(QLineF(lineX, 0.f, lineX, imgHeight)); // triangle at top edge { QPointF baseL = QPointF(lineX - 4.f, 0.f); From 4c368f40efcb0c753041e5f7f6f1993ef0cbd85d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Tue, 14 Jan 2025 19:30:27 +0100 Subject: [PATCH 034/201] Use VERIFY_OR_DEBUG_ASSERT() to check for valid images --- src/waveform/renderers/allshader/waveformrendermark.cpp | 2 +- src/waveform/renderers/waveformmark.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index 32b352a4fad..31a467aafd8 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -401,7 +401,7 @@ void allshader::WaveformRenderMark::updatePlayPosMarkTexture() { QImage image(static_cast(imgWidth * devicePixelRatio), static_cast(imgHeight * devicePixelRatio), QImage::Format_ARGB32_Premultiplied); - if (image.isNull()) { + VERIFY_OR_DEBUG_ASSERT(!image.isNull()) { return; } image.setDevicePixelRatio(devicePixelRatio); diff --git a/src/waveform/renderers/waveformmark.cpp b/src/waveform/renderers/waveformmark.cpp index 8b7a754d842..68efa62cf4f 100644 --- a/src/waveform/renderers/waveformmark.cpp +++ b/src/waveform/renderers/waveformmark.cpp @@ -382,7 +382,7 @@ QImage WaveformMark::generateImage(float devicePixelRatio) { // Create the image QImage image{markerGeometry.getImageSize(devicePixelRatio), QImage::Format_ARGB32_Premultiplied}; - if (image.isNull()) { + VERIFY_OR_DEBUG_ASSERT(!image.isNull()) { return image; } image.setDevicePixelRatio(devicePixelRatio); From 3cd854b8059d7e8a7d9b6cc220d654c9cdffd006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Fri, 10 Jan 2025 12:31:02 +0100 Subject: [PATCH 035/201] Drop special case for Bionic not using ffmpeg --- packaging/CPackDebUploadPPA.cmake | 6 ------ packaging/debian/control.in | 1 + tools/debian_buildenv.sh | 14 +------------- 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/packaging/CPackDebUploadPPA.cmake b/packaging/CPackDebUploadPPA.cmake index 69af8e187a4..9a5f756f81f 100644 --- a/packaging/CPackDebUploadPPA.cmake +++ b/packaging/CPackDebUploadPPA.cmake @@ -88,12 +88,6 @@ endif() foreach(RELEASE ${CPACK_DEBIAN_DISTRIBUTION_RELEASES}) - if (RELEASE STREQUAL "bionic") - set(CPACK_DEBIAN_PACKAGE_BUILD_DEPENDS_EXTRA "libmp4v2-dev,") - else() - set(CPACK_DEBIAN_PACKAGE_BUILD_DEPENDS_EXTRA "libavformat-dev,") - endif() - configure_file(${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}/packaging/debian/control.in ${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}/debian/control @ONLY) diff --git a/packaging/debian/control.in b/packaging/debian/control.in index a2600ce2f10..b770c5cd500 100644 --- a/packaging/debian/control.in +++ b/packaging/debian/control.in @@ -27,6 +27,7 @@ Build-Depends: debhelper (>= 11), libogg-dev, libsndfile1-dev, libasound2-dev, + libavformat-dev, libvorbis-dev, libfaad-dev, libportmidi-dev, diff --git a/tools/debian_buildenv.sh b/tools/debian_buildenv.sh index 95ef67967ec..d4543fc71ee 100755 --- a/tools/debian_buildenv.sh +++ b/tools/debian_buildenv.sh @@ -10,19 +10,6 @@ case "$1" in ;; setup) - source /etc/lsb-release 2>/dev/null - case "${DISTRIB_CODENAME}" in - bionic) # Ubuntu 18.04 LTS - PACKAGES_EXTRA=( - libmp4v2-dev - ) - ;; - *) # libmp4v2 was removed from Debian 10 & Ubuntu 20.04 due to lack of maintenance, so use FFMPEG instead - PACKAGES_EXTRA=( - libavformat-dev - ) - esac - sudo apt-get update # If jackd2 is installed as per dpkg database, install libjack-jackd2-dev. @@ -58,6 +45,7 @@ case "$1" in fonts-ubuntu \ g++ \ lcov \ + libavformat-dev \ libbenchmark-dev \ libchromaprint-dev \ libdistro-info-perl \ From 1a787d3396d2c0126e45975fe367e1b5acf17b6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Fri, 10 Jan 2025 12:31:02 +0100 Subject: [PATCH 036/201] Install either libqt6shadertools6-dev or qt6-shadertools-dev --- packaging/CPackDebUploadPPA.cmake | 6 ++++++ packaging/debian/control.in | 1 - tools/debian_buildenv.sh | 15 +++++++++++++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/packaging/CPackDebUploadPPA.cmake b/packaging/CPackDebUploadPPA.cmake index 9a5f756f81f..7808abd5201 100644 --- a/packaging/CPackDebUploadPPA.cmake +++ b/packaging/CPackDebUploadPPA.cmake @@ -88,6 +88,12 @@ endif() foreach(RELEASE ${CPACK_DEBIAN_DISTRIBUTION_RELEASES}) + if (RELEASE STREQUAL "jammy") + set(CPACK_DEBIAN_PACKAGE_BUILD_DEPENDS_EXTRA "libqt6shadertools6-dev,") + else() + set(CPACK_DEBIAN_PACKAGE_BUILD_DEPENDS_EXTRA "qt6-shadertools-dev,") + endif() + configure_file(${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}/packaging/debian/control.in ${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}/debian/control @ONLY) diff --git a/packaging/debian/control.in b/packaging/debian/control.in index b770c5cd500..b13e4232688 100644 --- a/packaging/debian/control.in +++ b/packaging/debian/control.in @@ -16,7 +16,6 @@ Build-Depends: debhelper (>= 11), qml6-module-qtquick-layouts, libqt6core5compat6-dev, libqt6opengl6-dev, - libqt6shadertools6-dev, libqt6sql6-sqlite, libqt6svg6-dev, cmake (>= 3.13), diff --git a/tools/debian_buildenv.sh b/tools/debian_buildenv.sh index d4543fc71ee..992ffae2e75 100755 --- a/tools/debian_buildenv.sh +++ b/tools/debian_buildenv.sh @@ -10,6 +10,19 @@ case "$1" in ;; setup) + source /etc/lsb-release 2>/dev/null + case "${DISTRIB_CODENAME}" in + focal|jammy|bullseye) # <= Ubuntu 22.04.5 LTS + PACKAGES_EXTRA=( + libqt6shadertools6-dev + ) + ;; + *) + PACKAGES_EXTRA=( + qt6-shadertools-dev + ) + esac + sudo apt-get update # If jackd2 is installed as per dpkg database, install libjack-jackd2-dev. @@ -68,9 +81,7 @@ case "$1" in libportmidi-dev \ libprotobuf-dev \ libqt6core5compat6-dev\ - libqt6shadertools6-dev \ libqt6opengl6-dev \ - libqt6shadertools6-dev \ libqt6sql6-sqlite \ libqt6svg6-dev \ librubberband-dev \ From 90e62ad71b0ccf8edebc465f4d2b292d61635764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Thu, 16 Jan 2025 08:17:35 +0100 Subject: [PATCH 037/201] Fix variable name after merge This aims to fix the PPA build --- packaging/CPackDebUploadPPA.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/CPackDebUploadPPA.cmake b/packaging/CPackDebUploadPPA.cmake index 09258bb5077..3edbc0c29ce 100644 --- a/packaging/CPackDebUploadPPA.cmake +++ b/packaging/CPackDebUploadPPA.cmake @@ -122,8 +122,8 @@ if(DEB_BUILD) ) endif() -foreach(RELEASE ${CPACK_DEBIAN_DISTRIBUTION_RELEASES}) - if(RELEASE STREQUAL "jammy") +foreach(release ${CPACK_DEBIAN_DISTRIBUTION_RELEASES}) + if(release STREQUAL "jammy") set(CPACK_DEBIAN_PACKAGE_BUILD_DEPENDS_EXTRA "libqt6shadertools6-dev,") else() set(CPACK_DEBIAN_PACKAGE_BUILD_DEPENDS_EXTRA "qt6-shadertools-dev,") From 49c2d624c01f921af584f0ee4c8ebedbf23074be Mon Sep 17 00:00:00 2001 From: ronso0 Date: Thu, 16 Jan 2025 13:05:08 +0100 Subject: [PATCH 038/201] remove obsolete focusOutEvent() from WPushButton focus is disabled for WWidget and enabled only for special widgets like spinboxes --- src/widget/wpushbutton.cpp | 12 ------------ src/widget/wpushbutton.h | 1 - 2 files changed, 13 deletions(-) diff --git a/src/widget/wpushbutton.cpp b/src/widget/wpushbutton.cpp index 19e1b73478b..5e5a80f089a 100644 --- a/src/widget/wpushbutton.cpp +++ b/src/widget/wpushbutton.cpp @@ -460,18 +460,6 @@ bool WPushButton::event(QEvent* e) { return WWidget::event(e); } -void WPushButton::focusOutEvent(QFocusEvent* e) { - qDebug() << "focusOutEvent" << e->reason(); - if (m_bPressed && e->reason() != Qt::MouseFocusReason) { - // Since we support multi touch there is no reason to reset - // the pressed flag if the Primary touch point is moved to an - // other widget - m_bPressed = false; - restyleAndRepaint(); - } - QWidget::focusOutEvent(e); -} - void WPushButton::mouseReleaseEvent(QMouseEvent * e) { const bool leftClick = e->button() == Qt::LeftButton; const bool rightClick = e->button() == Qt::RightButton; diff --git a/src/widget/wpushbutton.h b/src/widget/wpushbutton.h index a1970cf02ae..fc4e1cd7079 100644 --- a/src/widget/wpushbutton.h +++ b/src/widget/wpushbutton.h @@ -71,7 +71,6 @@ class WPushButton : public WWidget { void paintEvent(QPaintEvent* e) override; void mousePressEvent(QMouseEvent* e) override; void mouseReleaseEvent(QMouseEvent* e) override; - void focusOutEvent(QFocusEvent* e) override; void fillDebugTooltip(QStringList* debug) override; protected: From ac52d87436cabc1eb2e5a5167ad28620bd992613 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Thu, 16 Jan 2025 13:03:19 +0100 Subject: [PATCH 039/201] HotcueButton: show drag cursor also when hovering drag source button i.e. don't show the 'drop not allowed' cursor. Drop is still rejected. --- src/widget/whotcuebutton.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/widget/whotcuebutton.cpp b/src/widget/whotcuebutton.cpp index da168cdcfc2..8909163cc7e 100644 --- a/src/widget/whotcuebutton.cpp +++ b/src/widget/whotcuebutton.cpp @@ -207,10 +207,6 @@ void WHotcueButton::mouseMoveEvent(QMouseEvent* pEvent) { } void WHotcueButton::dragEnterEvent(QDragEnterEvent* pEvent) { - if (pEvent->source() == this) { - pEvent->ignore(); - return; - } TrackPointer pTrack = PlayerInfo::instance().getTrackInfo(m_group); if (!pTrack) { return; @@ -221,8 +217,7 @@ void WHotcueButton::dragEnterEvent(QDragEnterEvent* pEvent) { } HotcueDragInfo dragData = HotcueDragInfo::fromByteArray(mimeDataBytes); if (dragData.isValid() && - dragData.trackId == pTrack->getId() && - dragData.hotcue != m_hotcue) { + dragData.trackId == pTrack->getId()) { pEvent->acceptProposedAction(); } } From 02c8bd2bbdf95c76a4e050d057990ccd2909c635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Thu, 16 Jan 2025 10:15:04 +0100 Subject: [PATCH 040/201] Fix missing initalization in Rotary() --- src/util/rotary.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/util/rotary.h b/src/util/rotary.h index 7c58ad4b9d4..c0571c7a274 100644 --- a/src/util/rotary.h +++ b/src/util/rotary.h @@ -11,7 +11,8 @@ class Rotary { public: Rotary(qsizetype filterLength) - : m_filterHistory(filterLength, 0.0) { + : m_filterHistory(filterLength, 0.0), + m_headIndex{0} { DEBUG_ASSERT(filterLength > 0); }; // Low pass filtered rotary event From 2edbd967b1494ce7c0dd4e15f1d9482c5062a8d7 Mon Sep 17 00:00:00 2001 From: m0dB Date: Sun, 1 Dec 2024 21:14:24 +0100 Subject: [PATCH 041/201] use rendergraph in allshader/waveformwidget, derive allshader waveformrenderer classes from rendergraph::openglnode --- CMakeLists.txt | 2 + .../allshader/waveformrenderbackground.cpp | 4 +- .../allshader/waveformrenderbackground.h | 7 +- .../allshader/waveformrenderbeat.cpp | 1 - .../renderers/allshader/waveformrenderbeat.h | 7 +- .../renderers/allshader/waveformrenderer.h | 16 +-- .../allshader/waveformrendererabstract.h | 26 ---- .../allshader/waveformrendererendoftrack.cpp | 1 - .../allshader/waveformrendererendoftrack.h | 7 +- .../allshader/waveformrendererfiltered.cpp | 1 - .../allshader/waveformrendererfiltered.h | 5 +- .../allshader/waveformrendererhsv.cpp | 1 - .../renderers/allshader/waveformrendererhsv.h | 5 +- .../allshader/waveformrendererpreroll.cpp | 1 - .../allshader/waveformrendererpreroll.h | 10 +- .../allshader/waveformrendererrgb.cpp | 1 - .../renderers/allshader/waveformrendererrgb.h | 5 +- .../allshader/waveformrenderersignalbase.cpp | 12 +- .../allshader/waveformrenderersignalbase.h | 15 +-- .../allshader/waveformrenderersimple.cpp | 1 - .../allshader/waveformrenderersimple.h | 5 +- .../allshader/waveformrendererslipmode.cpp | 1 - .../allshader/waveformrendererslipmode.h | 7 +- .../allshader/waveformrendererstem.cpp | 1 - .../allshader/waveformrendererstem.h | 5 +- .../allshader/waveformrenderertextured.cpp | 2 - .../allshader/waveformrenderertextured.h | 6 +- .../allshader/waveformrendermark.cpp | 7 +- .../renderers/allshader/waveformrendermark.h | 17 +-- .../allshader/waveformrendermarkrange.cpp | 1 - .../allshader/waveformrendermarkrange.h | 7 +- .../renderers/waveformrendererabstract.h | 7 -- .../widgets/allshader/waveformwidget.cpp | 111 ++++++++++-------- .../widgets/allshader/waveformwidget.h | 27 ++++- 34 files changed, 173 insertions(+), 159 deletions(-) delete mode 100644 src/waveform/renderers/allshader/waveformrendererabstract.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7283ed6a7da..8df0b2f393e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4574,6 +4574,8 @@ endif() # rendergraph add_subdirectory(src/rendergraph/opengl) +target_link_libraries(mixxx-lib PUBLIC rendergraph_gl) +target_compile_definitions(mixxx-lib PRIVATE rendergraph=rendergraph_gl) # WavPack audio file support find_package(wavpack) diff --git a/src/waveform/renderers/allshader/waveformrenderbackground.cpp b/src/waveform/renderers/allshader/waveformrenderbackground.cpp index beddbc000a5..504beade43c 100644 --- a/src/waveform/renderers/allshader/waveformrenderbackground.cpp +++ b/src/waveform/renderers/allshader/waveformrenderbackground.cpp @@ -11,10 +11,10 @@ WaveformRenderBackground::WaveformRenderBackground( } void WaveformRenderBackground::setup(const QDomNode& node, - const SkinContext& context) { + const SkinContext& skinContext) { m_backgroundColor = m_waveformRenderer->getWaveformSignalColors()->getBgColor(); - QString backgroundPixmapPath = context.selectString(node, "BgPixmap"); + QString backgroundPixmapPath = skinContext.selectString(node, "BgPixmap"); if (!backgroundPixmapPath.isEmpty()) { qWarning() << "WaveformView BgPixmap is not supported by " "allshader::WaveformRenderBackground"; diff --git a/src/waveform/renderers/allshader/waveformrenderbackground.h b/src/waveform/renderers/allshader/waveformrenderbackground.h index 8a913e27742..7567d2da7df 100644 --- a/src/waveform/renderers/allshader/waveformrenderbackground.h +++ b/src/waveform/renderers/allshader/waveformrenderbackground.h @@ -2,6 +2,7 @@ #include +#include "rendergraph/openglnode.h" #include "util/class.h" #include "waveform/renderers/allshader/waveformrenderer.h" @@ -9,11 +10,13 @@ namespace allshader { class WaveformRenderBackground; } -class allshader::WaveformRenderBackground final : public allshader::WaveformRenderer { +class allshader::WaveformRenderBackground final + : public allshader::WaveformRenderer, + public rendergraph::OpenGLNode { public: explicit WaveformRenderBackground(WaveformWidgetRenderer* waveformWidgetRenderer); - void setup(const QDomNode& node, const SkinContext& context) override; + void setup(const QDomNode& node, const SkinContext& skinContext) override; void paintGL() override; private: diff --git a/src/waveform/renderers/allshader/waveformrenderbeat.cpp b/src/waveform/renderers/allshader/waveformrenderbeat.cpp index 3f793efd706..39da6e21401 100644 --- a/src/waveform/renderers/allshader/waveformrenderbeat.cpp +++ b/src/waveform/renderers/allshader/waveformrenderbeat.cpp @@ -17,7 +17,6 @@ WaveformRenderBeat::WaveformRenderBeat(WaveformWidgetRenderer* waveformWidget, } void WaveformRenderBeat::initializeGL() { - WaveformRenderer::initializeGL(); m_shader.init(); } diff --git a/src/waveform/renderers/allshader/waveformrenderbeat.h b/src/waveform/renderers/allshader/waveformrenderbeat.h index d3b2b79d1bc..9433ac6e39e 100644 --- a/src/waveform/renderers/allshader/waveformrenderbeat.h +++ b/src/waveform/renderers/allshader/waveformrenderbeat.h @@ -2,6 +2,7 @@ #include +#include "rendergraph/openglnode.h" #include "shaders/unicolorshader.h" #include "util/class.h" #include "waveform/renderers/allshader/vertexdata.h" @@ -14,13 +15,15 @@ namespace allshader { class WaveformRenderBeat; } -class allshader::WaveformRenderBeat final : public allshader::WaveformRenderer { +class allshader::WaveformRenderBeat final + : public allshader::WaveformRenderer, + public rendergraph::OpenGLNode { public: explicit WaveformRenderBeat(WaveformWidgetRenderer* waveformWidget, ::WaveformRendererAbstract::PositionSource type = ::WaveformRendererAbstract::Play); - void setup(const QDomNode& node, const SkinContext& context) override; + void setup(const QDomNode& node, const SkinContext& skinContext) override; void paintGL() override; void initializeGL() override; diff --git a/src/waveform/renderers/allshader/waveformrenderer.h b/src/waveform/renderers/allshader/waveformrenderer.h index 04b4b427935..b619bae71ec 100644 --- a/src/waveform/renderers/allshader/waveformrenderer.h +++ b/src/waveform/renderers/allshader/waveformrenderer.h @@ -1,6 +1,5 @@ #pragma once -#include "waveform/renderers/allshader/waveformrendererabstract.h" #include "waveform/renderers/waveformrendererabstract.h" class WaveformWidgetRenderer; @@ -9,8 +8,7 @@ namespace allshader { class WaveformRenderer; } -class allshader::WaveformRenderer : public ::WaveformRendererAbstract, - public allshader::WaveformRendererAbstract { +class allshader::WaveformRenderer : public ::WaveformRendererAbstract { public: explicit WaveformRenderer(WaveformWidgetRenderer* widget); @@ -21,16 +19,4 @@ class allshader::WaveformRenderer : public ::WaveformRendererAbstract, // QOpenGLWindow has bad performance), we leave this empty. // Should never be called. void draw(QPainter* painter, QPaintEvent* event) override final; - - allshader::WaveformRendererAbstract* allshaderWaveformRenderer() override final { - // This class is indirectly derived from - // WaveformWidgetRenderer, which has a member - // QList m_rendererStack; - // In the case of allshader::WaveformRenderer widgets, - // all the items on this stack are derived from - // allshader::WaveformRendererAbstract and we use this method to - // access them as such. (We could also have used a - // dynamic cast (or even static cast instead) - return this; - } }; diff --git a/src/waveform/renderers/allshader/waveformrendererabstract.h b/src/waveform/renderers/allshader/waveformrendererabstract.h deleted file mode 100644 index 5c530a00e22..00000000000 --- a/src/waveform/renderers/allshader/waveformrendererabstract.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include - -namespace allshader { -class WaveformRendererAbstract; -} - -class allshader::WaveformRendererAbstract : public QOpenGLFunctions { - public: - /// This interface class is called by class allshader::WaveformWidget. - /// Class allshader::WaveformWidget is derived from the allshader-based - /// class WGLWidget and, in its implementation of the WGLWidget virtual - /// methods, calls a stack of allshader::WaveformRendererAbstract instances, for - /// the different layers of the waveform graphics (background, beat - /// markers, the actual waveform, etc). In other word, this interface - /// mimics the WGLWidget virtuals, but to be called as layers of an - /// actual WGLWidget. - virtual ~WaveformRendererAbstract() = default; - virtual void initializeGL() { - initializeOpenGLFunctions(); - } - virtual void resizeGL(int /* w */, int /* h */) { - } - virtual void paintGL() = 0; -}; diff --git a/src/waveform/renderers/allshader/waveformrendererendoftrack.cpp b/src/waveform/renderers/allshader/waveformrendererendoftrack.cpp index 10df7121020..b0c15678701 100644 --- a/src/waveform/renderers/allshader/waveformrendererendoftrack.cpp +++ b/src/waveform/renderers/allshader/waveformrendererendoftrack.cpp @@ -47,7 +47,6 @@ void WaveformRendererEndOfTrack::setup(const QDomNode& node, const SkinContext& } void WaveformRendererEndOfTrack::initializeGL() { - WaveformRenderer::initializeGL(); m_shader.init(); } diff --git a/src/waveform/renderers/allshader/waveformrendererendoftrack.h b/src/waveform/renderers/allshader/waveformrendererendoftrack.h index 664a2cac16e..46b8a2a677b 100644 --- a/src/waveform/renderers/allshader/waveformrendererendoftrack.h +++ b/src/waveform/renderers/allshader/waveformrendererendoftrack.h @@ -3,6 +3,7 @@ #include #include +#include "rendergraph/openglnode.h" #include "shaders/endoftrackshader.h" #include "util/class.h" #include "util/performancetimer.h" @@ -16,12 +17,14 @@ namespace allshader { class WaveformRendererEndOfTrack; } -class allshader::WaveformRendererEndOfTrack final : public allshader::WaveformRenderer { +class allshader::WaveformRendererEndOfTrack final + : public allshader::WaveformRenderer, + public rendergraph::OpenGLNode { public: explicit WaveformRendererEndOfTrack( WaveformWidgetRenderer* waveformWidget); - void setup(const QDomNode& node, const SkinContext& context) override; + void setup(const QDomNode& node, const SkinContext& skinContext) override; bool init() override; diff --git a/src/waveform/renderers/allshader/waveformrendererfiltered.cpp b/src/waveform/renderers/allshader/waveformrendererfiltered.cpp index 0ddf164cc3e..f48b1136f59 100644 --- a/src/waveform/renderers/allshader/waveformrendererfiltered.cpp +++ b/src/waveform/renderers/allshader/waveformrendererfiltered.cpp @@ -19,7 +19,6 @@ void WaveformRendererFiltered::onSetup(const QDomNode& node) { } void WaveformRendererFiltered::initializeGL() { - WaveformRendererSignalBase::initializeGL(); m_shader.init(); } diff --git a/src/waveform/renderers/allshader/waveformrendererfiltered.h b/src/waveform/renderers/allshader/waveformrendererfiltered.h index 158ff880c01..43441adafa5 100644 --- a/src/waveform/renderers/allshader/waveformrendererfiltered.h +++ b/src/waveform/renderers/allshader/waveformrendererfiltered.h @@ -1,5 +1,6 @@ #pragma once +#include "rendergraph/openglnode.h" #include "shaders/unicolorshader.h" #include "util/class.h" #include "waveform/renderers/allshader/vertexdata.h" @@ -9,7 +10,9 @@ namespace allshader { class WaveformRendererFiltered; } -class allshader::WaveformRendererFiltered final : public allshader::WaveformRendererSignalBase { +class allshader::WaveformRendererFiltered final + : public allshader::WaveformRendererSignalBase, + public rendergraph::OpenGLNode { public: explicit WaveformRendererFiltered(WaveformWidgetRenderer* waveformWidget, bool rgbStacked); diff --git a/src/waveform/renderers/allshader/waveformrendererhsv.cpp b/src/waveform/renderers/allshader/waveformrendererhsv.cpp index 31b9785acdf..75b34dbae00 100644 --- a/src/waveform/renderers/allshader/waveformrendererhsv.cpp +++ b/src/waveform/renderers/allshader/waveformrendererhsv.cpp @@ -19,7 +19,6 @@ void WaveformRendererHSV::onSetup(const QDomNode& node) { } void WaveformRendererHSV::initializeGL() { - WaveformRendererSignalBase::initializeGL(); m_shader.init(); } diff --git a/src/waveform/renderers/allshader/waveformrendererhsv.h b/src/waveform/renderers/allshader/waveformrendererhsv.h index 0fa0b6bf863..a6fdbe95f51 100644 --- a/src/waveform/renderers/allshader/waveformrendererhsv.h +++ b/src/waveform/renderers/allshader/waveformrendererhsv.h @@ -1,5 +1,6 @@ #pragma once +#include "rendergraph/openglnode.h" #include "shaders/rgbshader.h" #include "util/class.h" #include "waveform/renderers/allshader/rgbdata.h" @@ -10,7 +11,9 @@ namespace allshader { class WaveformRendererHSV; } -class allshader::WaveformRendererHSV final : public allshader::WaveformRendererSignalBase { +class allshader::WaveformRendererHSV final + : public allshader::WaveformRendererSignalBase, + public rendergraph::OpenGLNode { public: explicit WaveformRendererHSV(WaveformWidgetRenderer* waveformWidget); diff --git a/src/waveform/renderers/allshader/waveformrendererpreroll.cpp b/src/waveform/renderers/allshader/waveformrendererpreroll.cpp index eea01c5bdad..89948347d87 100644 --- a/src/waveform/renderers/allshader/waveformrendererpreroll.cpp +++ b/src/waveform/renderers/allshader/waveformrendererpreroll.cpp @@ -75,7 +75,6 @@ void WaveformRendererPreroll::setup( } void WaveformRendererPreroll::initializeGL() { - WaveformRenderer::initializeGL(); m_shader.init(); } diff --git a/src/waveform/renderers/allshader/waveformrendererpreroll.h b/src/waveform/renderers/allshader/waveformrendererpreroll.h index 742320424c5..0fd2cc59942 100644 --- a/src/waveform/renderers/allshader/waveformrendererpreroll.h +++ b/src/waveform/renderers/allshader/waveformrendererpreroll.h @@ -4,6 +4,7 @@ #include #include +#include "rendergraph/openglnode.h" #include "shaders/patternshader.h" #include "util/class.h" #include "util/opengltexture2d.h" @@ -16,18 +17,19 @@ class QOpenGLTexture; namespace allshader { class WaveformRendererPreroll; -class WaveformRendererSlipPreroll; } -class allshader::WaveformRendererPreroll : public allshader::WaveformRenderer { +class allshader::WaveformRendererPreroll final + : public allshader::WaveformRenderer, + public rendergraph::OpenGLNode { public: explicit WaveformRendererPreroll( - WaveformWidgetRenderer* waveformWidgetRenderer, + WaveformWidgetRenderer* waveformWidget, ::WaveformRendererAbstract::PositionSource type = ::WaveformRendererAbstract::Play); ~WaveformRendererPreroll() override; - void setup(const QDomNode& node, const SkinContext& context) override; + void setup(const QDomNode& node, const SkinContext& skinContext) override; void paintGL() override; void initializeGL() override; diff --git a/src/waveform/renderers/allshader/waveformrendererrgb.cpp b/src/waveform/renderers/allshader/waveformrendererrgb.cpp index 54cea537640..31cd94f1a5d 100644 --- a/src/waveform/renderers/allshader/waveformrendererrgb.cpp +++ b/src/waveform/renderers/allshader/waveformrendererrgb.cpp @@ -27,7 +27,6 @@ void WaveformRendererRGB::onSetup(const QDomNode& node) { } void WaveformRendererRGB::initializeGL() { - WaveformRendererSignalBase::initializeGL(); m_shader.init(); } diff --git a/src/waveform/renderers/allshader/waveformrendererrgb.h b/src/waveform/renderers/allshader/waveformrendererrgb.h index e9c12b52a42..10f2262a751 100644 --- a/src/waveform/renderers/allshader/waveformrendererrgb.h +++ b/src/waveform/renderers/allshader/waveformrendererrgb.h @@ -1,5 +1,6 @@ #pragma once +#include "rendergraph/openglnode.h" #include "shaders/rgbshader.h" #include "util/class.h" #include "waveform/renderers/allshader/rgbdata.h" @@ -10,7 +11,9 @@ namespace allshader { class WaveformRendererRGB; } -class allshader::WaveformRendererRGB final : public allshader::WaveformRendererSignalBase { +class allshader::WaveformRendererRGB final + : public allshader::WaveformRendererSignalBase, + public rendergraph::OpenGLNode { public: explicit WaveformRendererRGB(WaveformWidgetRenderer* waveformWidget, ::WaveformRendererAbstract::PositionSource type = diff --git a/src/waveform/renderers/allshader/waveformrenderersignalbase.cpp b/src/waveform/renderers/allshader/waveformrenderersignalbase.cpp index 853a3f2e47d..620db520fd0 100644 --- a/src/waveform/renderers/allshader/waveformrenderersignalbase.cpp +++ b/src/waveform/renderers/allshader/waveformrenderersignalbase.cpp @@ -1,8 +1,16 @@ #include "waveform/renderers/allshader/waveformrenderersignalbase.h" -using namespace allshader; +namespace allshader { -allshader::WaveformRendererSignalBase::WaveformRendererSignalBase( +WaveformRendererSignalBase::WaveformRendererSignalBase( WaveformWidgetRenderer* waveformWidget) : ::WaveformRendererSignalBase(waveformWidget) { } + +void WaveformRendererSignalBase::draw(QPainter* painter, QPaintEvent* event) { + Q_UNUSED(painter); + Q_UNUSED(event); + DEBUG_ASSERT(false); +} + +} // namespace allshader diff --git a/src/waveform/renderers/allshader/waveformrenderersignalbase.h b/src/waveform/renderers/allshader/waveformrenderersignalbase.h index 0e72d9364c7..dafa41cd79b 100644 --- a/src/waveform/renderers/allshader/waveformrenderersignalbase.h +++ b/src/waveform/renderers/allshader/waveformrenderersignalbase.h @@ -4,7 +4,6 @@ #include #include "util/class.h" -#include "waveform/renderers/allshader/waveformrendererabstract.h" #include "waveform/renderers/waveformrenderersignalbase.h" class WaveformWidgetRenderer; @@ -13,8 +12,7 @@ namespace allshader { class WaveformRendererSignalBase; } -class allshader::WaveformRendererSignalBase : public ::WaveformRendererSignalBase, - public allshader::WaveformRendererAbstract { +class allshader::WaveformRendererSignalBase : public ::WaveformRendererSignalBase { public: enum class Option { None = 0b0, @@ -24,6 +22,8 @@ class allshader::WaveformRendererSignalBase : public ::WaveformRendererSignalBas }; Q_DECLARE_FLAGS(Options, Option) + void draw(QPainter* painter, QPaintEvent* event) override final; + static constexpr float m_maxValue{static_cast(std::numeric_limits::max())}; explicit WaveformRendererSignalBase(WaveformWidgetRenderer* waveformWidget); @@ -32,14 +32,5 @@ class allshader::WaveformRendererSignalBase : public ::WaveformRendererSignalBas return false; } - void draw(QPainter* painter, QPaintEvent* event) override { - Q_UNUSED(painter); - Q_UNUSED(event); - } - - allshader::WaveformRendererAbstract* allshaderWaveformRenderer() override { - return this; - } - DISALLOW_COPY_AND_ASSIGN(WaveformRendererSignalBase); }; diff --git a/src/waveform/renderers/allshader/waveformrenderersimple.cpp b/src/waveform/renderers/allshader/waveformrenderersimple.cpp index 02e9453afdf..90a707d2373 100644 --- a/src/waveform/renderers/allshader/waveformrenderersimple.cpp +++ b/src/waveform/renderers/allshader/waveformrenderersimple.cpp @@ -18,7 +18,6 @@ void WaveformRendererSimple::onSetup(const QDomNode& node) { } void WaveformRendererSimple::initializeGL() { - WaveformRendererSignalBase::initializeGL(); m_shader.init(); } diff --git a/src/waveform/renderers/allshader/waveformrenderersimple.h b/src/waveform/renderers/allshader/waveformrenderersimple.h index a1e92c8d2f0..ba79752539c 100644 --- a/src/waveform/renderers/allshader/waveformrenderersimple.h +++ b/src/waveform/renderers/allshader/waveformrenderersimple.h @@ -1,5 +1,6 @@ #pragma once +#include "rendergraph/openglnode.h" #include "shaders/unicolorshader.h" #include "util/class.h" #include "waveform/renderers/allshader/vertexdata.h" @@ -9,7 +10,9 @@ namespace allshader { class WaveformRendererSimple; } -class allshader::WaveformRendererSimple final : public allshader::WaveformRendererSignalBase { +class allshader::WaveformRendererSimple final + : public allshader::WaveformRendererSignalBase, + public rendergraph::OpenGLNode { public: explicit WaveformRendererSimple(WaveformWidgetRenderer* waveformWidget); diff --git a/src/waveform/renderers/allshader/waveformrendererslipmode.cpp b/src/waveform/renderers/allshader/waveformrendererslipmode.cpp index 4311f00a4ce..8ddf0be80e5 100644 --- a/src/waveform/renderers/allshader/waveformrendererslipmode.cpp +++ b/src/waveform/renderers/allshader/waveformrendererslipmode.cpp @@ -63,7 +63,6 @@ void WaveformRendererSlipMode::setup(const QDomNode& node, const SkinContext& co } void WaveformRendererSlipMode::initializeGL() { - WaveformRenderer::initializeGL(); m_shader.init(); } diff --git a/src/waveform/renderers/allshader/waveformrendererslipmode.h b/src/waveform/renderers/allshader/waveformrendererslipmode.h index 5e7b189ca57..b481142797f 100644 --- a/src/waveform/renderers/allshader/waveformrendererslipmode.h +++ b/src/waveform/renderers/allshader/waveformrendererslipmode.h @@ -3,6 +3,7 @@ #include #include +#include "rendergraph/openglnode.h" #include "shaders/slipmodeshader.h" #include "util/class.h" #include "util/performancetimer.h" @@ -16,12 +17,14 @@ namespace allshader { class WaveformRendererSlipMode; } -class allshader::WaveformRendererSlipMode final : public allshader::WaveformRenderer { +class allshader::WaveformRendererSlipMode final + : public allshader::WaveformRenderer, + public rendergraph::OpenGLNode { public: explicit WaveformRendererSlipMode( WaveformWidgetRenderer* waveformWidget); - void setup(const QDomNode& node, const SkinContext& context) override; + void setup(const QDomNode& node, const SkinContext& skinContext) override; bool init() override; diff --git a/src/waveform/renderers/allshader/waveformrendererstem.cpp b/src/waveform/renderers/allshader/waveformrendererstem.cpp index 5827904f513..80612f99470 100644 --- a/src/waveform/renderers/allshader/waveformrendererstem.cpp +++ b/src/waveform/renderers/allshader/waveformrendererstem.cpp @@ -26,7 +26,6 @@ void WaveformRendererStem::onSetup(const QDomNode& node) { } void WaveformRendererStem::initializeGL() { - WaveformRendererSignalBase::initializeGL(); m_shader.init(); m_textureShader.init(); auto group = m_pEQEnabled->getKey().group; diff --git a/src/waveform/renderers/allshader/waveformrendererstem.h b/src/waveform/renderers/allshader/waveformrendererstem.h index 757bfe763d3..99728194574 100644 --- a/src/waveform/renderers/allshader/waveformrendererstem.h +++ b/src/waveform/renderers/allshader/waveformrendererstem.h @@ -2,6 +2,7 @@ #include +#include "rendergraph/openglnode.h" #include "shaders/rgbashader.h" #include "shaders/textureshader.h" #include "util/class.h" @@ -15,7 +16,9 @@ namespace allshader { class WaveformRendererStem; } -class allshader::WaveformRendererStem final : public allshader::WaveformRendererSignalBase { +class allshader::WaveformRendererStem final + : public allshader::WaveformRendererSignalBase, + public rendergraph::OpenGLNode { public: explicit WaveformRendererStem(WaveformWidgetRenderer* waveformWidget, ::WaveformRendererAbstract::PositionSource type = diff --git a/src/waveform/renderers/allshader/waveformrenderertextured.cpp b/src/waveform/renderers/allshader/waveformrenderertextured.cpp index dc137b73208..de21a013259 100644 --- a/src/waveform/renderers/allshader/waveformrenderertextured.cpp +++ b/src/waveform/renderers/allshader/waveformrenderertextured.cpp @@ -219,8 +219,6 @@ void WaveformRendererTextured::createFrameBuffers() { } void WaveformRendererTextured::initializeGL() { - WaveformRendererSignalBase::initializeGL(); - m_textureRenderedWaveformCompletion = 0; if (!m_frameShaderProgram) { diff --git a/src/waveform/renderers/allshader/waveformrenderertextured.h b/src/waveform/renderers/allshader/waveformrenderertextured.h index 16680f1bc5d..ccd82ae5a67 100644 --- a/src/waveform/renderers/allshader/waveformrenderertextured.h +++ b/src/waveform/renderers/allshader/waveformrenderertextured.h @@ -2,6 +2,7 @@ #ifndef QT_OPENGL_ES_2 +#include "rendergraph/openglnode.h" #include "shaders/rgbshader.h" #include "track/track_decl.h" #include "util/class.h" @@ -19,8 +20,9 @@ class WaveformRendererTextured; } // Based on GLSLWaveformRendererSignal (waveform/renderers/glslwaveformrenderersignal.h) -class allshader::WaveformRendererTextured : public QObject, - public allshader::WaveformRendererSignalBase { +class allshader::WaveformRendererTextured final : public QObject, + public allshader::WaveformRendererSignalBase, + public rendergraph::OpenGLNode { Q_OBJECT public: explicit WaveformRendererTextured(WaveformWidgetRenderer* waveformWidget, diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index 3505e60519a..7dacb73279b 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -79,8 +79,13 @@ bool allshader::WaveformRenderMark::init() { return true; } +void allshader::WaveformRenderMark::draw(QPainter* painter, QPaintEvent* event) { + Q_UNUSED(painter); + Q_UNUSED(event); + DEBUG_ASSERT(false); +} + void allshader::WaveformRenderMark::initializeGL() { - allshader::WaveformRendererAbstract::initializeGL(); m_digitsRenderer.init(); m_rgbaShader.init(); m_textureShader.init(); diff --git a/src/waveform/renderers/allshader/waveformrendermark.h b/src/waveform/renderers/allshader/waveformrendermark.h index 5f4b812388a..eba21888e3c 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.h +++ b/src/waveform/renderers/allshader/waveformrendermark.h @@ -2,11 +2,11 @@ #include +#include "rendergraph/openglnode.h" #include "shaders/rgbashader.h" #include "shaders/textureshader.h" #include "util/opengltexture2d.h" #include "waveform/renderers/allshader/digitsrenderer.h" -#include "waveform/renderers/allshader/waveformrendererabstract.h" #include "waveform/renderers/waveformrendermarkbase.h" class QDomNode; @@ -17,23 +17,16 @@ namespace allshader { class WaveformRenderMark; } -class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, - public allshader::WaveformRendererAbstract { +class allshader::WaveformRenderMark final + : public ::WaveformRenderMarkBase, + public rendergraph::OpenGLNode { public: explicit WaveformRenderMark(WaveformWidgetRenderer* waveformWidget, ::WaveformRendererAbstract::PositionSource type = ::WaveformRendererAbstract::Play); - void draw(QPainter* painter, QPaintEvent* event) override { - Q_UNUSED(painter); - Q_UNUSED(event); - } - - allshader::WaveformRendererAbstract* allshaderWaveformRenderer() override { - return this; - } - bool init() override; + void draw(QPainter* painter, QPaintEvent* event) override; void initializeGL() override; void paintGL() override; diff --git a/src/waveform/renderers/allshader/waveformrendermarkrange.cpp b/src/waveform/renderers/allshader/waveformrendermarkrange.cpp index 6b3a33d4936..eaacffdbdba 100644 --- a/src/waveform/renderers/allshader/waveformrendermarkrange.cpp +++ b/src/waveform/renderers/allshader/waveformrendermarkrange.cpp @@ -9,7 +9,6 @@ allshader::WaveformRenderMarkRange::WaveformRenderMarkRange(WaveformWidgetRender } void allshader::WaveformRenderMarkRange::initializeGL() { - WaveformRenderer::initializeGL(); m_shader.init(); } diff --git a/src/waveform/renderers/allshader/waveformrendermarkrange.h b/src/waveform/renderers/allshader/waveformrendermarkrange.h index 0c46a8dd5b8..ac2cb7ac24b 100644 --- a/src/waveform/renderers/allshader/waveformrendermarkrange.h +++ b/src/waveform/renderers/allshader/waveformrendermarkrange.h @@ -3,6 +3,7 @@ #include #include +#include "rendergraph/openglnode.h" #include "shaders/unicolorshader.h" #include "util/class.h" #include "waveform/renderers/allshader/waveformrenderer.h" @@ -15,11 +16,13 @@ namespace allshader { class WaveformRenderMarkRange; } -class allshader::WaveformRenderMarkRange final : public allshader::WaveformRenderer { +class allshader::WaveformRenderMarkRange final + : public allshader::WaveformRenderer, + public rendergraph::OpenGLNode { public: explicit WaveformRenderMarkRange(WaveformWidgetRenderer* waveformWidget); - void setup(const QDomNode& node, const SkinContext& context) override; + void setup(const QDomNode& node, const SkinContext& skinContext) override; void initializeGL() override; void paintGL() override; diff --git a/src/waveform/renderers/waveformrendererabstract.h b/src/waveform/renderers/waveformrendererabstract.h index 6c15dd523d5..7fc2b78529d 100644 --- a/src/waveform/renderers/waveformrendererabstract.h +++ b/src/waveform/renderers/waveformrendererabstract.h @@ -9,10 +9,6 @@ QT_FORWARD_DECLARE_CLASS(QPainter) class SkinContext; class WaveformWidgetRenderer; -namespace allshader { -class WaveformRendererAbstract; -} - class WaveformRendererAbstract { public: /// The type of cursor for which the waveform is rendered @@ -32,9 +28,6 @@ class WaveformRendererAbstract { virtual void onResize() {} virtual void onSetTrack() {} - virtual allshader::WaveformRendererAbstract* allshaderWaveformRenderer() { - return nullptr; - } protected: bool isDirty() const { diff --git a/src/waveform/widgets/allshader/waveformwidget.cpp b/src/waveform/widgets/allshader/waveformwidget.cpp index 2b507ff7afc..87e5d0d829e 100644 --- a/src/waveform/widgets/allshader/waveformwidget.cpp +++ b/src/waveform/widgets/allshader/waveformwidget.cpp @@ -6,7 +6,6 @@ #include "waveform/renderers/allshader/waveformrenderbackground.h" #include "waveform/renderers/allshader/waveformrenderbeat.h" -#include "waveform/renderers/allshader/waveformrendererabstract.h" #include "waveform/renderers/allshader/waveformrendererendoftrack.h" #include "waveform/renderers/allshader/waveformrendererfiltered.h" #include "waveform/renderers/allshader/waveformrendererhsv.h" @@ -27,52 +26,67 @@ WaveformWidget::WaveformWidget(QWidget* parent, const QString& group, WaveformRendererSignalBase::Options options) : WGLWidget(parent), WaveformWidgetAbstract(group) { - addRenderer(); - addRenderer(); - addRenderer(); - addRenderer(); + auto pTopNode = std::make_unique(); + auto pOpacityNode = std::make_unique(); + + pTopNode->appendChildNode(addRendererNode()); + pOpacityNode->appendChildNode(addRendererNode()); + pOpacityNode->appendChildNode(addRendererNode()); + pOpacityNode->appendChildNode(addRendererNode()); + m_pWaveformRenderMarkRange = static_cast(pOpacityNode->lastChild()); #ifdef __STEM__ // The following two renderers work in tandem: if the rendered waveform is // for a stem track, WaveformRendererSignalBase will skip rendering and let // WaveformRendererStem do the rendering, and vice-versa. - addRenderer(); + pOpacityNode->appendChildNode(addRendererNode()); #endif - allshader::WaveformRendererSignalBase* waveformSignalRenderer = - addWaveformSignalRenderer( - type, options, ::WaveformRendererAbstract::Play); - - addRenderer(); - addRenderer(); - - // if the signal renderer supports slip, we add it again, now for slip, together with the - // other slip renderers - if (waveformSignalRenderer && waveformSignalRenderer->supportsSlip()) { + pOpacityNode->appendChildNode(addWaveformSignalRendererNode( + type, options, ::WaveformRendererAbstract::Play)); + pOpacityNode->appendChildNode(addRendererNode()); + pOpacityNode->appendChildNode(addRendererNode()); + m_pWaveformRenderMark = static_cast(pOpacityNode->lastChild()); + + // if the added signal renderer supports slip, we add it again, now for + // slip, together with the other slip renderers + if (m_pWaveformRendererSignal && m_pWaveformRendererSignal->supportsSlip()) { // The following renderer will add an overlay waveform if a slip is in progress - addRenderer(); - addRenderer(::WaveformRendererAbstract::Slip); + pOpacityNode->appendChildNode(addRendererNode()); + pOpacityNode->appendChildNode( + addRendererNode( + ::WaveformRendererAbstract::Slip)); #ifdef __STEM__ - addRenderer(::WaveformRendererAbstract::Slip); + pOpacityNode->appendChildNode( + addRendererNode( + ::WaveformRendererAbstract::Slip)); #endif - addWaveformSignalRenderer(type, options, ::WaveformRendererAbstract::Slip); - addRenderer(::WaveformRendererAbstract::Slip); - addRenderer(::WaveformRendererAbstract::Slip); + pOpacityNode->appendChildNode(addWaveformSignalRendererNode( + type, options, ::WaveformRendererAbstract::Slip)); + pOpacityNode->appendChildNode( + addRendererNode( + ::WaveformRendererAbstract::Slip)); + pOpacityNode->appendChildNode( + addRendererNode( + ::WaveformRendererAbstract::Slip)); } m_initSuccess = init(); + + pTopNode->appendChildNode(std::move(pOpacityNode)); + m_pOpacityNode = static_cast(pTopNode->lastChild()); + + m_pEngine = std::make_unique(std::move(pTopNode)); } WaveformWidget::~WaveformWidget() { makeCurrentIfNeeded(); - for (auto* pRenderer : std::as_const(m_rendererStack)) { - delete pRenderer; - } m_rendererStack.clear(); + m_pEngine.reset(); doneCurrent(); } -allshader::WaveformRendererSignalBase* -WaveformWidget::addWaveformSignalRenderer(WaveformWidgetType::Type type, +std::unique_ptr +WaveformWidget::addWaveformSignalRendererNode(WaveformWidgetType::Type type, WaveformRendererSignalBase::Options options, ::WaveformRendererAbstract::PositionSource positionSource) { #ifndef QT_OPENGL_ES_2 @@ -81,7 +95,8 @@ WaveformWidget::addWaveformSignalRenderer(WaveformWidgetType::Type type, case ::WaveformWidgetType::RGB: case ::WaveformWidgetType::Filtered: case ::WaveformWidgetType::Stacked: - return addRenderer(type, positionSource, options); + return addWaveformSignalRendererNode( + type, positionSource, options); default: break; } @@ -90,15 +105,16 @@ WaveformWidget::addWaveformSignalRenderer(WaveformWidgetType::Type type, switch (type) { case ::WaveformWidgetType::Simple: - return addRenderer(); + return addWaveformSignalRendererNode(); case ::WaveformWidgetType::RGB: - return addRenderer(positionSource, options); + return addWaveformSignalRendererNode(positionSource, options); case ::WaveformWidgetType::HSV: - return addRenderer(); + return addWaveformSignalRendererNode(); case ::WaveformWidgetType::Filtered: - return addRenderer(false); + return addWaveformSignalRendererNode(false); case ::WaveformWidgetType::Stacked: - return addRenderer(true); // true for RGB Stacked + return addWaveformSignalRendererNode( + true); // true for RGB Stacked default: break; } @@ -117,15 +133,11 @@ mixxx::Duration WaveformWidget::render() { } void WaveformWidget::paintGL() { - if (shouldOnlyDrawBackground()) { - if (!m_rendererStack.empty()) { - m_rendererStack[0]->allshaderWaveformRenderer()->paintGL(); - } - } else { - for (auto* pRenderer : std::as_const(m_rendererStack)) { - pRenderer->allshaderWaveformRenderer()->paintGL(); - } - } + // opacity of 0.f effectively skips the subtree rendering + m_pOpacityNode->setOpacity(shouldOnlyDrawBackground() ? 0.f : 1.f); + + m_pEngine->preprocess(); + m_pEngine->render(); } void WaveformWidget::castToQWidget() { @@ -133,15 +145,18 @@ void WaveformWidget::castToQWidget() { } void WaveformWidget::initializeGL() { - for (auto* pRenderer : std::as_const(m_rendererStack)) { - pRenderer->allshaderWaveformRenderer()->initializeGL(); - } +} + +void WaveformWidget::resizeRenderer(int, int, float) { + // defer to resizeGL } void WaveformWidget::resizeGL(int w, int h) { - for (auto* pRenderer : std::as_const(m_rendererStack)) { - pRenderer->allshaderWaveformRenderer()->resizeGL(w, h); - } + w = static_cast(std::lroundf(static_cast(w) / devicePixelRatio())); + h = static_cast(std::lroundf(static_cast(h) / devicePixelRatio())); + + m_pEngine->resize(w, h); + WaveformWidgetRenderer::resizeRenderer(w, h, devicePixelRatio()); } void WaveformWidget::paintEvent(QPaintEvent* event) { diff --git a/src/waveform/widgets/allshader/waveformwidget.h b/src/waveform/widgets/allshader/waveformwidget.h index f6c0877975e..bf0b3a18267 100644 --- a/src/waveform/widgets/allshader/waveformwidget.h +++ b/src/waveform/widgets/allshader/waveformwidget.h @@ -1,5 +1,7 @@ #pragma once +#include "rendergraph/engine.h" +#include "rendergraph/opacitynode.h" #include "waveform/renderers/allshader/waveformrenderersignalbase.h" #include "waveform/widgets/waveformwidgetabstract.h" #include "waveform/widgets/waveformwidgetvars.h" @@ -7,6 +9,8 @@ namespace allshader { class WaveformWidget; +class WaveformRenderMark; +class WaveformRenderMarkRange; } class allshader::WaveformWidget final : public ::WGLWidget, @@ -23,6 +27,8 @@ class allshader::WaveformWidget final : public ::WGLWidget, return m_type; } + void resizeRenderer(int width, int height, float devicePixelRatio) override; + // override for WaveformWidgetAbstract mixxx::Duration render() override; @@ -42,12 +48,31 @@ class allshader::WaveformWidget final : public ::WGLWidget, void wheelEvent(QWheelEvent* event) override; void leaveEvent(QEvent* event) override; - allshader::WaveformRendererSignalBase* addWaveformSignalRenderer( + template + inline std::unique_ptr addRendererNode(Args&&... args) { + return std::unique_ptr(addRenderer(std::forward(args)...)); + } + + template + inline std::unique_ptr addWaveformSignalRendererNode(Args&&... args) { + auto pRenderer = addRenderer(std::forward(args)...); + if (!m_pWaveformRendererSignal) { + m_pWaveformRendererSignal = pRenderer; + } + return std::unique_ptr(pRenderer); + } + + std::unique_ptr addWaveformSignalRendererNode( WaveformWidgetType::Type type, WaveformRendererSignalBase::Options options, ::WaveformRendererAbstract::PositionSource positionSource); WaveformWidgetType::Type m_type; + std::unique_ptr m_pEngine; + rendergraph::OpacityNode* m_pOpacityNode; + WaveformRenderMark* m_pWaveformRenderMark; + WaveformRenderMarkRange* m_pWaveformRenderMarkRange; + WaveformRendererSignalBase* m_pWaveformRendererSignal; DISALLOW_COPY_AND_ASSIGN(WaveformWidget); }; From 3f9a2b184747e014ac25104ab163ad973325cc5e Mon Sep 17 00:00:00 2001 From: m0dB Date: Mon, 9 Dec 2024 17:33:18 +0100 Subject: [PATCH 042/201] avoid rendering on images with 0 or negative width or height --- .../renderers/allshader/digitsrenderer.cpp | 4 ++++ .../renderers/allshader/waveformrendermark.cpp | 14 +++++++++++--- src/waveform/renderers/waveformmark.cpp | 14 +++++++++----- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/waveform/renderers/allshader/digitsrenderer.cpp b/src/waveform/renderers/allshader/digitsrenderer.cpp index cd7d9947e5d..212613c34c8 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.cpp +++ b/src/waveform/renderers/allshader/digitsrenderer.cpp @@ -70,6 +70,10 @@ float allshader::DigitsRenderer::height() const { void allshader::DigitsRenderer::updateTexture( float fontPointSize, float maxHeight, float devicePixelRatio) { + if (std::lround(maxHeight * devicePixelRatio) <= 0) { + return; + } + if (fontPointSize == m_fontPointSize && maxHeight == m_maxHeight) { return; } diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index 7dacb73279b..9e9f1632896 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -396,9 +396,17 @@ void allshader::WaveformRenderMark::updatePlayPosMarkTexture() { const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); const float lineX = 5.5f; - QImage image(static_cast(imgWidth * devicePixelRatio), - static_cast(imgHeight * devicePixelRatio), - QImage::Format_ARGB32_Premultiplied); + imgwidth = 11.f; + imgheight = height; + + const QSize size{static_cast(std::lround(imgwidth * devicePixelRatio)), + static_cast(std::lround(imgheight * devicePixelRatio))}; + + if (size.width() <= 0 || size.height() <= 0) { + return; + } + + QImage image(size, QImage::Format_ARGB32_Premultiplied); VERIFY_OR_DEBUG_ASSERT(!image.isNull()) { return; } diff --git a/src/waveform/renderers/waveformmark.cpp b/src/waveform/renderers/waveformmark.cpp index 5d36bfd9f88..e90f2f4e02b 100644 --- a/src/waveform/renderers/waveformmark.cpp +++ b/src/waveform/renderers/waveformmark.cpp @@ -308,8 +308,8 @@ class MarkerGeometry { } } QSize getImageSize(float devicePixelRatio) const { - return QSize{static_cast(m_imageSize.width() * devicePixelRatio), - static_cast(m_imageSize.height() * devicePixelRatio)}; + return QSize{static_cast(std::lround(m_imageSize.width() * devicePixelRatio)), + static_cast(std::lround(m_imageSize.height() * devicePixelRatio))}; } const QFont font() const { @@ -381,9 +381,13 @@ QImage WaveformMark::generateImage(float devicePixelRatio) { m_label.setAreaRect(markerGeometry.labelRect()); - // Create the image - QImage image{markerGeometry.getImageSize(devicePixelRatio), - QImage::Format_ARGB32_Premultiplied}; + const QSize size{markerGeometry.getImageSize(devicePixelRatio)}; + + if (size.width() <= 0 || size.height() <= 0) { + return QImage{}; + } + + QImage image{size, QImage::Format_ARGB32_Premultiplied}; VERIFY_OR_DEBUG_ASSERT(!image.isNull()) { return image; } From cd9d060cf33bd5c7bfbfb80868e52c841ff292be Mon Sep 17 00:00:00 2001 From: m0dB Date: Mon, 9 Dec 2024 17:00:52 +0100 Subject: [PATCH 043/201] added rendergraph shaders --- CMakeLists.txt | 2 +- src/rendergraph/CMakeLists.txt | 5 +- src/rendergraph/shaders/CMakeLists.txt | 53 +++++++++++++++ src/rendergraph/shaders/README.md | 29 ++++++++ .../shaders/generate_shaders_gl.pl | 68 +++++++++++++++++++ .../shaders/generated_shaders_gl.cmake | 13 ++++ src/rendergraph/shaders/pattern.frag | 9 +++ src/rendergraph/shaders/pattern.frag.gl | 11 +++ src/rendergraph/shaders/pattern.vert | 15 ++++ src/rendergraph/shaders/pattern.vert.gl | 19 ++++++ src/rendergraph/shaders/rgb.frag | 8 +++ src/rendergraph/shaders/rgb.frag.gl | 9 +++ src/rendergraph/shaders/rgb.vert | 15 ++++ src/rendergraph/shaders/rgb.vert.gl | 19 ++++++ src/rendergraph/shaders/rgba.frag | 8 +++ src/rendergraph/shaders/rgba.frag.gl | 9 +++ src/rendergraph/shaders/rgba.vert | 15 ++++ src/rendergraph/shaders/rgba.vert.gl | 19 ++++++ src/rendergraph/shaders/texture.frag | 9 +++ src/rendergraph/shaders/texture.frag.gl | 11 +++ src/rendergraph/shaders/texture.vert | 15 ++++ src/rendergraph/shaders/texture.vert.gl | 19 ++++++ src/rendergraph/shaders/unicolor.frag | 13 ++++ src/rendergraph/shaders/unicolor.frag.gl | 15 ++++ src/rendergraph/shaders/unicolor.vert | 13 ++++ src/rendergraph/shaders/unicolor.vert.gl | 17 +++++ 26 files changed, 436 insertions(+), 2 deletions(-) create mode 100644 src/rendergraph/shaders/CMakeLists.txt create mode 100644 src/rendergraph/shaders/README.md create mode 100755 src/rendergraph/shaders/generate_shaders_gl.pl create mode 100644 src/rendergraph/shaders/generated_shaders_gl.cmake create mode 100644 src/rendergraph/shaders/pattern.frag create mode 100644 src/rendergraph/shaders/pattern.frag.gl create mode 100644 src/rendergraph/shaders/pattern.vert create mode 100644 src/rendergraph/shaders/pattern.vert.gl create mode 100644 src/rendergraph/shaders/rgb.frag create mode 100644 src/rendergraph/shaders/rgb.frag.gl create mode 100644 src/rendergraph/shaders/rgb.vert create mode 100644 src/rendergraph/shaders/rgb.vert.gl create mode 100644 src/rendergraph/shaders/rgba.frag create mode 100644 src/rendergraph/shaders/rgba.frag.gl create mode 100644 src/rendergraph/shaders/rgba.vert create mode 100644 src/rendergraph/shaders/rgba.vert.gl create mode 100644 src/rendergraph/shaders/texture.frag create mode 100644 src/rendergraph/shaders/texture.frag.gl create mode 100644 src/rendergraph/shaders/texture.vert create mode 100644 src/rendergraph/shaders/texture.vert.gl create mode 100644 src/rendergraph/shaders/unicolor.frag create mode 100644 src/rendergraph/shaders/unicolor.frag.gl create mode 100644 src/rendergraph/shaders/unicolor.vert create mode 100644 src/rendergraph/shaders/unicolor.vert.gl diff --git a/CMakeLists.txt b/CMakeLists.txt index 8df0b2f393e..0cc13a22921 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4573,7 +4573,7 @@ if(VINYLCONTROL) endif() # rendergraph -add_subdirectory(src/rendergraph/opengl) +add_subdirectory(src/rendergraph) target_link_libraries(mixxx-lib PUBLIC rendergraph_gl) target_compile_definitions(mixxx-lib PRIVATE rendergraph=rendergraph_gl) diff --git a/src/rendergraph/CMakeLists.txt b/src/rendergraph/CMakeLists.txt index 8f14cb2f313..23569ef30ca 100644 --- a/src/rendergraph/CMakeLists.txt +++ b/src/rendergraph/CMakeLists.txt @@ -39,5 +39,8 @@ set( ) add_subdirectory(opengl) -add_subdirectory(scenegraph) +################################# +# TODO: uncomment in follow-up PR +# add_subdirectory(scenegraph) +################################# add_subdirectory(shaders) diff --git a/src/rendergraph/shaders/CMakeLists.txt b/src/rendergraph/shaders/CMakeLists.txt new file mode 100644 index 00000000000..42478758f46 --- /dev/null +++ b/src/rendergraph/shaders/CMakeLists.txt @@ -0,0 +1,53 @@ +# included from src/rendergraph/CMakeLists.txt + +set( + shaders + pattern.frag + pattern.vert + rgb.frag + rgb.vert + rgba.frag + rgba.vert + texture.frag + texture.vert + unicolor.frag + unicolor.vert +) + +qt6_add_shaders(rendergraph_sg "shaders-qsb" + BATCHABLE + PRECOMPILE + OPTIMIZED + PREFIX + /shaders/rendergraph + FILES + ${shaders} +) + +# USE_QSHADER_FOR_GL is set in src/rendergraph/CMakeLists.txt when Qt >= 6.6 +if(USE_QSHADER_FOR_GL) + # Add the .qsb shader bundles; rendergraph::MaterialShader will use + # QShader to extract the GLSL shader from the bundle. + message(STATUS "Adding qsb shaders to rendergraph_gl") + qt6_add_shaders(rendergraph_gl "shaders-qsb" + BATCHABLE + PRECOMPILE + OPTIMIZED + PREFIX + /shaders/rendergraph + FILES + ${shaders} + ) +else() + # Use GLSL shaders extracted from the .qsb shader bundles using + # generate_shaders_gl.pl + message(STATUS "Adding gl shaders to rendergraph_gl") + include(generated_shaders_gl.cmake) + + qt_add_resources(rendergraph_gl "shaders-gl" + PREFIX + /shaders/rendergraph + FILES + ${generated_shaders_gl} + ) +endif() diff --git a/src/rendergraph/shaders/README.md b/src/rendergraph/shaders/README.md new file mode 100644 index 00000000000..31f8a53fff1 --- /dev/null +++ b/src/rendergraph/shaders/README.md @@ -0,0 +1,29 @@ +# rendergraph shaders + +The CMakeLists.txt in this folder generates qsb shader bundles from spirv shaders, to +be used by `rendergraph::MaterialShader`. + +## For use with QML / Qt scene graph + +The qsb files can be used directly through `QSGShader`. This includes the scenegraph +implementation of rendergraph. + +## For use with OpenGL + +Depending on the Qt version, the opengl implementation of `rendergraph::MaterialShader` +uses either the .qsb shader bundles directly, or the extracted GLSL shaders: + +### Qt >= 6.6 + +The GLSL shaders are extracted programmatically with `QShader` and then used with +`QOpenGLShader`. + +### Qt < 6.6 + +The GLSL shader have to extracted from the qsb shader bundles to be used by `QOpenGLShader`. +This can be done using the script `generate_shaders_gl.pl`. To use this script, make sure +that the qsb and spirv commands are in your path. qsb is part of Qt. spirv is part of the +Vulkan SDK and can be downloaded from + +The script also generates the file ```generated_shaders_gl.cmake``` which sets a cmake +variable containing a list of all GLSL shaders, used by the CMakeLists.txt in this folder. diff --git a/src/rendergraph/shaders/generate_shaders_gl.pl b/src/rendergraph/shaders/generate_shaders_gl.pl new file mode 100755 index 00000000000..c528eb94747 --- /dev/null +++ b/src/rendergraph/shaders/generate_shaders_gl.pl @@ -0,0 +1,68 @@ +#!/usr/bin/perl + +my @files = (glob("*.vert"),glob("*.frag")); + +open(GENERATED,">generated_shaders_gl.cmake"); +print(GENERATED "set(generated_shaders_gl\n"); +for $file (@files) +{ + system("qsb","--glsl","120",$file,"-o","/tmp/$$-$file.qsb"); + open(INFILE,"qsb --dump /tmp/$$-$file.qsb|"); + open(OUTFILE,">$file.gl"); + $ok = 0; + $comment_added = 0; + print "Generating $file.gl from $file\n"; + while () + { + if ($in_shader_block == 2) + { + if (m/^\*\*/) + { + $in_shader_block = 0; + $ok = 1; + } + else + { + if (!$comment_added) + { + if (!m/^#/) + { + print(OUTFILE "//// GENERATED - EDITS WILL BE OVERWRITTEN\n"); + $comment_added = 1; + } + } + print OUTFILE "$_"; + } + } + elsif ($in_shader_block == 1) + { + chomp($_); + if ($_ eq "Contents:") + { + $in_shader_block = 2; + } + } + else + { + chomp($_); + if ($_ eq "Shader 1: GLSL 120 [Standard]") + { + $in_shader_block = 1; + } + } + } + close INFILE; + close OUTFILE; + if($ok) + { + print(GENERATED " $file.gl\n"); + } + else + { + print STDERR "Failed to generated $file.gl"; + unlink("$file.gl") + } + unlink("/tmp/$$-$file.qsb"); +} +print(GENERATED ")\n"); +close GENERATED; diff --git a/src/rendergraph/shaders/generated_shaders_gl.cmake b/src/rendergraph/shaders/generated_shaders_gl.cmake new file mode 100644 index 00000000000..f3f89002e51 --- /dev/null +++ b/src/rendergraph/shaders/generated_shaders_gl.cmake @@ -0,0 +1,13 @@ +set( + generated_shaders_gl + pattern.vert.gl + rgb.vert.gl + rgba.vert.gl + texture.vert.gl + unicolor.vert.gl + pattern.frag.gl + rgb.frag.gl + rgba.frag.gl + texture.frag.gl + unicolor.frag.gl +) diff --git a/src/rendergraph/shaders/pattern.frag b/src/rendergraph/shaders/pattern.frag new file mode 100644 index 00000000000..5aa3d1556b1 --- /dev/null +++ b/src/rendergraph/shaders/pattern.frag @@ -0,0 +1,9 @@ +#version 440 + +layout(binding = 1) uniform sampler2D texture1; +layout(location = 0) in vec2 vTexcoord; +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = texture(texture1, fract(vTexcoord)); +} diff --git a/src/rendergraph/shaders/pattern.frag.gl b/src/rendergraph/shaders/pattern.frag.gl new file mode 100644 index 00000000000..376c71668ba --- /dev/null +++ b/src/rendergraph/shaders/pattern.frag.gl @@ -0,0 +1,11 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +uniform sampler2D texture1; + +varying vec2 vTexcoord; + +void main() +{ + gl_FragData[0] = texture2D(texture1, fract(vTexcoord)); +} diff --git a/src/rendergraph/shaders/pattern.vert b/src/rendergraph/shaders/pattern.vert new file mode 100644 index 00000000000..07b3d7f1f3b --- /dev/null +++ b/src/rendergraph/shaders/pattern.vert @@ -0,0 +1,15 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; +} +ubuf; + +layout(location = 0) in vec4 position; +layout(location = 1) in vec2 texcoord; +layout(location = 0) out vec2 vTexcoord; + +void main() { + vTexcoord = texcoord; + gl_Position = ubuf.matrix * position; +} diff --git a/src/rendergraph/shaders/pattern.vert.gl b/src/rendergraph/shaders/pattern.vert.gl new file mode 100644 index 00000000000..a3d58014be3 --- /dev/null +++ b/src/rendergraph/shaders/pattern.vert.gl @@ -0,0 +1,19 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; +}; + +uniform buf ubuf; + +varying vec2 vTexcoord; +attribute vec2 texcoord; +attribute vec4 position; + +void main() +{ + vTexcoord = texcoord; + gl_Position = ubuf.matrix * position; +} diff --git a/src/rendergraph/shaders/rgb.frag b/src/rendergraph/shaders/rgb.frag new file mode 100644 index 00000000000..0a808489f5b --- /dev/null +++ b/src/rendergraph/shaders/rgb.frag @@ -0,0 +1,8 @@ +#version 440 + +layout(location = 0) in vec3 vColor; +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = vec4(vColor, 1.0); +} diff --git a/src/rendergraph/shaders/rgb.frag.gl b/src/rendergraph/shaders/rgb.frag.gl new file mode 100644 index 00000000000..b8a61f8682f --- /dev/null +++ b/src/rendergraph/shaders/rgb.frag.gl @@ -0,0 +1,9 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +varying vec3 vColor; + +void main() +{ + gl_FragData[0] = vec4(vColor, 1.0); +} diff --git a/src/rendergraph/shaders/rgb.vert b/src/rendergraph/shaders/rgb.vert new file mode 100644 index 00000000000..6568d01f187 --- /dev/null +++ b/src/rendergraph/shaders/rgb.vert @@ -0,0 +1,15 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; +} +ubuf; + +layout(location = 0) in vec4 position; +layout(location = 1) in vec3 color; +layout(location = 0) out vec3 vColor; + +void main() { + vColor = color; + gl_Position = ubuf.matrix * position; +} diff --git a/src/rendergraph/shaders/rgb.vert.gl b/src/rendergraph/shaders/rgb.vert.gl new file mode 100644 index 00000000000..53e86e4501c --- /dev/null +++ b/src/rendergraph/shaders/rgb.vert.gl @@ -0,0 +1,19 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; +}; + +uniform buf ubuf; + +varying vec3 vColor; +attribute vec3 color; +attribute vec4 position; + +void main() +{ + vColor = color; + gl_Position = ubuf.matrix * position; +} diff --git a/src/rendergraph/shaders/rgba.frag b/src/rendergraph/shaders/rgba.frag new file mode 100644 index 00000000000..5cf90a770ea --- /dev/null +++ b/src/rendergraph/shaders/rgba.frag @@ -0,0 +1,8 @@ +#version 440 + +layout(location = 0) in vec4 vColor; +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = vec4(vColor.xyz * vColor.w, vColor.w); // premultiple alpha +} diff --git a/src/rendergraph/shaders/rgba.frag.gl b/src/rendergraph/shaders/rgba.frag.gl new file mode 100644 index 00000000000..a831457b968 --- /dev/null +++ b/src/rendergraph/shaders/rgba.frag.gl @@ -0,0 +1,9 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +varying vec4 vColor; + +void main() +{ + gl_FragData[0] = vec4(vColor.xyz * vColor.w, vColor.w); +} diff --git a/src/rendergraph/shaders/rgba.vert b/src/rendergraph/shaders/rgba.vert new file mode 100644 index 00000000000..d5ce8b2d9db --- /dev/null +++ b/src/rendergraph/shaders/rgba.vert @@ -0,0 +1,15 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; +} +ubuf; + +layout(location = 0) in vec4 position; +layout(location = 1) in vec4 color; +layout(location = 0) out vec4 vColor; + +void main() { + vColor = color; + gl_Position = ubuf.matrix * position; +} diff --git a/src/rendergraph/shaders/rgba.vert.gl b/src/rendergraph/shaders/rgba.vert.gl new file mode 100644 index 00000000000..df2bcf93236 --- /dev/null +++ b/src/rendergraph/shaders/rgba.vert.gl @@ -0,0 +1,19 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; +}; + +uniform buf ubuf; + +varying vec4 vColor; +attribute vec4 color; +attribute vec4 position; + +void main() +{ + vColor = color; + gl_Position = ubuf.matrix * position; +} diff --git a/src/rendergraph/shaders/texture.frag b/src/rendergraph/shaders/texture.frag new file mode 100644 index 00000000000..bbe37bccd69 --- /dev/null +++ b/src/rendergraph/shaders/texture.frag @@ -0,0 +1,9 @@ +#version 440 + +layout(binding = 1) uniform sampler2D texture1; +layout(location = 0) in vec2 vTexcoord; +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = texture(texture1, vTexcoord); +} diff --git a/src/rendergraph/shaders/texture.frag.gl b/src/rendergraph/shaders/texture.frag.gl new file mode 100644 index 00000000000..b2d03f1352c --- /dev/null +++ b/src/rendergraph/shaders/texture.frag.gl @@ -0,0 +1,11 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +uniform sampler2D texture1; + +varying vec2 vTexcoord; + +void main() +{ + gl_FragData[0] = texture2D(texture1, vTexcoord); +} diff --git a/src/rendergraph/shaders/texture.vert b/src/rendergraph/shaders/texture.vert new file mode 100644 index 00000000000..07b3d7f1f3b --- /dev/null +++ b/src/rendergraph/shaders/texture.vert @@ -0,0 +1,15 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; +} +ubuf; + +layout(location = 0) in vec4 position; +layout(location = 1) in vec2 texcoord; +layout(location = 0) out vec2 vTexcoord; + +void main() { + vTexcoord = texcoord; + gl_Position = ubuf.matrix * position; +} diff --git a/src/rendergraph/shaders/texture.vert.gl b/src/rendergraph/shaders/texture.vert.gl new file mode 100644 index 00000000000..a3d58014be3 --- /dev/null +++ b/src/rendergraph/shaders/texture.vert.gl @@ -0,0 +1,19 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; +}; + +uniform buf ubuf; + +varying vec2 vTexcoord; +attribute vec2 texcoord; +attribute vec4 position; + +void main() +{ + vTexcoord = texcoord; + gl_Position = ubuf.matrix * position; +} diff --git a/src/rendergraph/shaders/unicolor.frag b/src/rendergraph/shaders/unicolor.frag new file mode 100644 index 00000000000..7099bb8f3dd --- /dev/null +++ b/src/rendergraph/shaders/unicolor.frag @@ -0,0 +1,13 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; + vec4 color; +} +ubuf; + +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = vec4(ubuf.color.xyz * ubuf.color.w, ubuf.color.w); // premultiply alpha +} diff --git a/src/rendergraph/shaders/unicolor.frag.gl b/src/rendergraph/shaders/unicolor.frag.gl new file mode 100644 index 00000000000..bfef503120b --- /dev/null +++ b/src/rendergraph/shaders/unicolor.frag.gl @@ -0,0 +1,15 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; + vec4 color; +}; + +uniform buf ubuf; + +void main() +{ + gl_FragData[0] = vec4(ubuf.color.xyz * ubuf.color.w, ubuf.color.w); +} diff --git a/src/rendergraph/shaders/unicolor.vert b/src/rendergraph/shaders/unicolor.vert new file mode 100644 index 00000000000..9e268c18fba --- /dev/null +++ b/src/rendergraph/shaders/unicolor.vert @@ -0,0 +1,13 @@ +#version 440 + +layout(std140, binding = 0) uniform buf { + mat4 matrix; + vec4 color; +} +ubuf; + +layout(location = 0) in vec4 position; + +void main() { + gl_Position = ubuf.matrix * position; +} diff --git a/src/rendergraph/shaders/unicolor.vert.gl b/src/rendergraph/shaders/unicolor.vert.gl new file mode 100644 index 00000000000..d126f3dab58 --- /dev/null +++ b/src/rendergraph/shaders/unicolor.vert.gl @@ -0,0 +1,17 @@ +#version 120 +//// GENERATED - EDITS WILL BE OVERWRITTEN + +struct buf +{ + mat4 matrix; + vec4 color; +}; + +uniform buf ubuf; + +attribute vec4 position; + +void main() +{ + gl_Position = ubuf.matrix * position; +} From efb9535e7c0d5f64daa89ee9d9913cca646b7de1 Mon Sep 17 00:00:00 2001 From: m0dB Date: Sat, 14 Dec 2024 19:34:53 +0100 Subject: [PATCH 044/201] fixed double/float conversions --- src/waveform/widgets/allshader/waveformwidget.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/waveform/widgets/allshader/waveformwidget.cpp b/src/waveform/widgets/allshader/waveformwidget.cpp index 87e5d0d829e..e6f5536a71d 100644 --- a/src/waveform/widgets/allshader/waveformwidget.cpp +++ b/src/waveform/widgets/allshader/waveformwidget.cpp @@ -152,11 +152,11 @@ void WaveformWidget::resizeRenderer(int, int, float) { } void WaveformWidget::resizeGL(int w, int h) { - w = static_cast(std::lroundf(static_cast(w) / devicePixelRatio())); - h = static_cast(std::lroundf(static_cast(h) / devicePixelRatio())); + w = static_cast(std::lround(static_cast(w) / devicePixelRatioF())); + h = static_cast(std::lround(static_cast(h) / devicePixelRatioF())); m_pEngine->resize(w, h); - WaveformWidgetRenderer::resizeRenderer(w, h, devicePixelRatio()); + WaveformWidgetRenderer::resizeRenderer(w, h, static_cast(devicePixelRatio())); } void WaveformWidget::paintEvent(QPaintEvent* event) { From 1fe28155ae8024c80493bce4604f91348b2d8577 Mon Sep 17 00:00:00 2001 From: m0dB Date: Sat, 14 Dec 2024 20:20:07 +0100 Subject: [PATCH 045/201] play nice with precompiled headers --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0cc13a22921..a70b1266dcd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4575,7 +4575,7 @@ endif() # rendergraph add_subdirectory(src/rendergraph) target_link_libraries(mixxx-lib PUBLIC rendergraph_gl) -target_compile_definitions(mixxx-lib PRIVATE rendergraph=rendergraph_gl) +target_compile_definitions(mixxx-lib PUBLIC rendergraph=rendergraph_gl) # WavPack audio file support find_package(wavpack) From 31c99f6d9a6caf20aaa3e45701df4fc6340f5016 Mon Sep 17 00:00:00 2001 From: Jan Holthuis Date: Tue, 17 Dec 2024 22:29:36 +0100 Subject: [PATCH 046/201] chore(rendergraph): Port `generate_shaders_gl.pl` to Python --- .../shaders/generate_shaders_gl.pl | 68 -------- tools/rg_generate_shaders_gl.py | 159 ++++++++++++++++++ 2 files changed, 159 insertions(+), 68 deletions(-) delete mode 100755 src/rendergraph/shaders/generate_shaders_gl.pl create mode 100755 tools/rg_generate_shaders_gl.py diff --git a/src/rendergraph/shaders/generate_shaders_gl.pl b/src/rendergraph/shaders/generate_shaders_gl.pl deleted file mode 100755 index c528eb94747..00000000000 --- a/src/rendergraph/shaders/generate_shaders_gl.pl +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/perl - -my @files = (glob("*.vert"),glob("*.frag")); - -open(GENERATED,">generated_shaders_gl.cmake"); -print(GENERATED "set(generated_shaders_gl\n"); -for $file (@files) -{ - system("qsb","--glsl","120",$file,"-o","/tmp/$$-$file.qsb"); - open(INFILE,"qsb --dump /tmp/$$-$file.qsb|"); - open(OUTFILE,">$file.gl"); - $ok = 0; - $comment_added = 0; - print "Generating $file.gl from $file\n"; - while () - { - if ($in_shader_block == 2) - { - if (m/^\*\*/) - { - $in_shader_block = 0; - $ok = 1; - } - else - { - if (!$comment_added) - { - if (!m/^#/) - { - print(OUTFILE "//// GENERATED - EDITS WILL BE OVERWRITTEN\n"); - $comment_added = 1; - } - } - print OUTFILE "$_"; - } - } - elsif ($in_shader_block == 1) - { - chomp($_); - if ($_ eq "Contents:") - { - $in_shader_block = 2; - } - } - else - { - chomp($_); - if ($_ eq "Shader 1: GLSL 120 [Standard]") - { - $in_shader_block = 1; - } - } - } - close INFILE; - close OUTFILE; - if($ok) - { - print(GENERATED " $file.gl\n"); - } - else - { - print STDERR "Failed to generated $file.gl"; - unlink("$file.gl") - } - unlink("/tmp/$$-$file.qsb"); -} -print(GENERATED ")\n"); -close GENERATED; diff --git a/tools/rg_generate_shaders_gl.py b/tools/rg_generate_shaders_gl.py new file mode 100755 index 00000000000..a591bbc9fed --- /dev/null +++ b/tools/rg_generate_shaders_gl.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 +""" +Converts a fragment or vertex shader file into a GL shader file. + +You can use it like this: + + $ ./rg_generate_shaders_gl.py --cmake-output \\ + ../src/rendergraph/shaders/generated_shaders_gl.cmake \\ + ../src/rendergraph/shaders/*.{vert,frag} +""" +import argparse +import logging +import os +import pathlib +import re +import shutil +import subprocess +import tempfile +import typing + + +def find_executable( + executable_name: str, additional_paths: list[str] | None = None +) -> pathlib.Path: + """Find an executable by name in $PATH and in the additional paths.""" + if executable_path := shutil.which(executable_name): + return pathlib.Path(executable_path) + + if additional_paths: + if executable_path := shutil.which( + executable_name, path=os.pathsep.join(additional_paths) + ): + return pathlib.Path(executable_path) + + raise OSError(f"Executable {executable_name!r} not found!") + + +QSB_EXECUTABLE = find_executable( + "qsb", + additional_paths=[ + "/usr/lib/qt6/bin", + "/lib/qt6/bin", + "/usr/local/lib/qt6/bin", + ], +) + + +def parse_shader(input_filepath: pathlib.Path) -> typing.Iterator[str]: + """Parse a Fragment/Vertex shader file and yield lines for a GL file.""" + with tempfile.NamedTemporaryFile() as fp: + subprocess.check_call( + [ + QSB_EXECUTABLE, + "--glsl", + "120", + "--output", + fp.name, + input_filepath, + ] + ) + output = subprocess.check_output( + [QSB_EXECUTABLE, "--dump", fp.name], + encoding="utf-8", + universal_newlines=True, + ) + + comment_added = False + ok = False + in_shader_block = 0 + buffered_blank_line = False + for line in output.splitlines(): + if in_shader_block == 2: + if re.match(r"^\*\*", line): + ok = True + else: + if not comment_added and not re.match(r"^#", line): + yield "//// GENERATED - EDITS WILL BE OVERWRITTEN" + comment_added = True + if line: + if buffered_blank_line: + yield "" + buffered_blank_line = False + yield line + else: + buffered_blank_line = True + elif in_shader_block == 1: + if line.rstrip() == "Contents:": + in_shader_block = 2 + else: + if line.rstrip() == "Shader 1: GLSL 120 [Standard]": + in_shader_block = 1 + if not ok: + raise EOFError("end of file reached before end marker reached") + + +def get_paths(paths: list[pathlib.Path]) -> typing.Iterator[pathlib.Path]: + for path in paths: + if path.is_dir(): + yield from path.glob("*.vert") + yield from path.glob("*.frag") + else: + yield path + + +def main(argv: list[str] | None = None) -> int: + logging.basicConfig(level=logging.DEBUG, format="%(message)s") + + logger = logging.getLogger(__name__) + + description, _, epilog = __doc__.strip().partition("\n\n") + parser = argparse.ArgumentParser( + description=description, + epilog=epilog, + formatter_class=argparse.RawDescriptionHelpFormatter, + ) + parser.add_argument( + "file", + nargs="+", + type=pathlib.Path, + help="Input files (.vert, .frag) or directory", + ) + parser.add_argument( + "--cmake-output", + type=argparse.FileType("w"), + required=True, + help="CMake Output files (.cmake)", + ) + args = parser.parse_args(argv) + + generated_shaders: list[pathlib.Path] = [] + + for file in sorted(get_paths(args.file)): + logger.info("Reading file: %s", file) + try: + lines = list(parse_shader(file)) + except EOFError as err: + logger.error("Failed to parse %s: %s", file, err) + continue + + output_file = file.with_suffix(f"{file.suffix}.gl") + logger.info("Writing file: %s", output_file) + with output_file.open("w") as fp: + for line in lines: + fp.write(f"{line}\n") + + generated_shaders.append(output_file) + + args.cmake_output.write("set(\n") + args.cmake_output.write(" generated_shaders_gl\n") + for generated_file in generated_shaders: + args.cmake_output.write(f" {generated_file.name}\n") + args.cmake_output.write(")\n") + logger.info("Generated %d shader files.", len(generated_shaders)) + + return 0 + + +if __name__ == "__main__": + main() From e3154772d87733316883ab012d30a9101f8559b1 Mon Sep 17 00:00:00 2001 From: Jan Holthuis Date: Tue, 17 Dec 2024 22:32:08 +0100 Subject: [PATCH 047/201] chore(rendergraph): Order `generated_shaders_gl.cmake` alphabetically --- src/rendergraph/shaders/generated_shaders_gl.cmake | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rendergraph/shaders/generated_shaders_gl.cmake b/src/rendergraph/shaders/generated_shaders_gl.cmake index f3f89002e51..0a5d6de030b 100644 --- a/src/rendergraph/shaders/generated_shaders_gl.cmake +++ b/src/rendergraph/shaders/generated_shaders_gl.cmake @@ -1,13 +1,13 @@ set( generated_shaders_gl - pattern.vert.gl - rgb.vert.gl - rgba.vert.gl - texture.vert.gl - unicolor.vert.gl pattern.frag.gl + pattern.vert.gl rgb.frag.gl + rgb.vert.gl rgba.frag.gl + rgba.vert.gl texture.frag.gl + texture.vert.gl unicolor.frag.gl + unicolor.vert.gl ) From 78481fc224436cf6377111f713994d072786277e Mon Sep 17 00:00:00 2001 From: Jan Holthuis Date: Tue, 17 Dec 2024 23:07:55 +0100 Subject: [PATCH 048/201] ci(pre-commit): Automatically update rendergraph shaders on changes --- .pre-commit-config.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cbc896f2c40..14510090503 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -189,3 +189,10 @@ repos: additional_dependencies: - lxml==5.3.0 files: ^(res/translations/.*\.ts)$ + - id: update-rendergraph-shaders + name: update-rendergraph-shaders + description: "Regenerate GL shaders for rendergraph" + entry: python tools/rg_generate_shaders_gl.py --cmake-output src/rendergraph/shaders/generated_shaders_gl.cmake src/rendergraph/shaders/ + pass_filenames: false + language: python + files: ^src/rendergraph/shaders/.*\.(vert|frag|gl)$ From 182199f4683a2c45dded1afcf18379d139f7c755 Mon Sep 17 00:00:00 2001 From: m0dB Date: Sun, 22 Dec 2024 12:25:11 +0100 Subject: [PATCH 049/201] remove generate_shaders_gl from precommit --- .pre-commit-config.yaml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 14510090503..cbc896f2c40 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -189,10 +189,3 @@ repos: additional_dependencies: - lxml==5.3.0 files: ^(res/translations/.*\.ts)$ - - id: update-rendergraph-shaders - name: update-rendergraph-shaders - description: "Regenerate GL shaders for rendergraph" - entry: python tools/rg_generate_shaders_gl.py --cmake-output src/rendergraph/shaders/generated_shaders_gl.cmake src/rendergraph/shaders/ - pass_filenames: false - language: python - files: ^src/rendergraph/shaders/.*\.(vert|frag|gl)$ From 4e16ade9d34465162f2b59d8708455fef321c9f6 Mon Sep 17 00:00:00 2001 From: m0dB Date: Sun, 22 Dec 2024 12:25:36 +0100 Subject: [PATCH 050/201] moved generate_shaders_gl.py script to rendergraph/tools, updated readme --- src/rendergraph/shaders/README.md | 8 ++++---- .../rendergraph/tools/generate_shaders_gl.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) rename tools/rg_generate_shaders_gl.py => src/rendergraph/tools/generate_shaders_gl.py (96%) diff --git a/src/rendergraph/shaders/README.md b/src/rendergraph/shaders/README.md index 31f8a53fff1..f6c2d328217 100644 --- a/src/rendergraph/shaders/README.md +++ b/src/rendergraph/shaders/README.md @@ -21,9 +21,9 @@ The GLSL shaders are extracted programmatically with `QShader` and then used wit ### Qt < 6.6 The GLSL shader have to extracted from the qsb shader bundles to be used by `QOpenGLShader`. -This can be done using the script `generate_shaders_gl.pl`. To use this script, make sure -that the qsb and spirv commands are in your path. qsb is part of Qt. spirv is part of the -Vulkan SDK and can be downloaded from +This can be done using the script `generate_shaders_gl.py` in the ../tools directory. To +use this script, make sure that the qsb and spirv commands are in your path. qsb is part of +Qt. spirv is part of the Vulkan SDK and can be downloaded from -The script also generates the file ```generated_shaders_gl.cmake``` which sets a cmake +The script also generates the file `generated_shaders_gl.cmake` which sets a cmake variable containing a list of all GLSL shaders, used by the CMakeLists.txt in this folder. diff --git a/tools/rg_generate_shaders_gl.py b/src/rendergraph/tools/generate_shaders_gl.py similarity index 96% rename from tools/rg_generate_shaders_gl.py rename to src/rendergraph/tools/generate_shaders_gl.py index a591bbc9fed..75c7f4365e1 100755 --- a/tools/rg_generate_shaders_gl.py +++ b/src/rendergraph/tools/generate_shaders_gl.py @@ -4,9 +4,9 @@ You can use it like this: - $ ./rg_generate_shaders_gl.py --cmake-output \\ - ../src/rendergraph/shaders/generated_shaders_gl.cmake \\ - ../src/rendergraph/shaders/*.{vert,frag} + $ ./generate_shaders_gl.py --cmake-output \\ + ../shaders/generated_shaders_gl.cmake \\ + ../shaders/*.{vert,frag} """ import argparse import logging From 9104a3dc82cf288bbc62e0566c155d6e3e7ebe0a Mon Sep 17 00:00:00 2001 From: m0dB Date: Sun, 22 Dec 2024 12:43:41 +0100 Subject: [PATCH 051/201] add missing pointer initialization --- src/waveform/widgets/allshader/waveformwidget.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/waveform/widgets/allshader/waveformwidget.cpp b/src/waveform/widgets/allshader/waveformwidget.cpp index e6f5536a71d..c359a585c05 100644 --- a/src/waveform/widgets/allshader/waveformwidget.cpp +++ b/src/waveform/widgets/allshader/waveformwidget.cpp @@ -25,7 +25,9 @@ WaveformWidget::WaveformWidget(QWidget* parent, WaveformWidgetType::Type type, const QString& group, WaveformRendererSignalBase::Options options) - : WGLWidget(parent), WaveformWidgetAbstract(group) { + : WGLWidget(parent), + WaveformWidgetAbstract(group), + m_pWaveformRendererSignal(nullptr) { auto pTopNode = std::make_unique(); auto pOpacityNode = std::make_unique(); From efae93e68d39605a39b91e7647520dd6284b4f86 Mon Sep 17 00:00:00 2001 From: Jan Holthuis Date: Sun, 22 Dec 2024 15:00:00 +0100 Subject: [PATCH 052/201] fix(generate_shaders_gl): Replace new union expression with old syntax This makes it compatible with Python 3.9 (default on macOS). --- src/rendergraph/tools/generate_shaders_gl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rendergraph/tools/generate_shaders_gl.py b/src/rendergraph/tools/generate_shaders_gl.py index 75c7f4365e1..e96d50d017a 100755 --- a/src/rendergraph/tools/generate_shaders_gl.py +++ b/src/rendergraph/tools/generate_shaders_gl.py @@ -20,7 +20,7 @@ def find_executable( - executable_name: str, additional_paths: list[str] | None = None + executable_name: str, additional_paths: typing.Optional[list[str]] = None ) -> pathlib.Path: """Find an executable by name in $PATH and in the additional paths.""" if executable_path := shutil.which(executable_name): @@ -102,7 +102,7 @@ def get_paths(paths: list[pathlib.Path]) -> typing.Iterator[pathlib.Path]: yield path -def main(argv: list[str] | None = None) -> int: +def main(argv: typing.Optional[list[str]] = None) -> int: logging.basicConfig(level=logging.DEBUG, format="%(message)s") logger = logging.getLogger(__name__) From 0d99bd6e26df9dbb87e185d55eab148c488a655c Mon Sep 17 00:00:00 2001 From: m0dB Date: Sun, 22 Dec 2024 15:46:36 +0100 Subject: [PATCH 053/201] moved script back, edited info, edited readme --- src/rendergraph/shaders/README.md | 11 ++- tools/rg_generate_shaders_gl.py | 159 ++++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+), 3 deletions(-) create mode 100755 tools/rg_generate_shaders_gl.py diff --git a/src/rendergraph/shaders/README.md b/src/rendergraph/shaders/README.md index f6c2d328217..7ed87b03499 100644 --- a/src/rendergraph/shaders/README.md +++ b/src/rendergraph/shaders/README.md @@ -21,9 +21,14 @@ The GLSL shaders are extracted programmatically with `QShader` and then used wit ### Qt < 6.6 The GLSL shader have to extracted from the qsb shader bundles to be used by `QOpenGLShader`. -This can be done using the script `generate_shaders_gl.py` in the ../tools directory. To -use this script, make sure that the qsb and spirv commands are in your path. qsb is part of -Qt. spirv is part of the Vulkan SDK and can be downloaded from +This can be done using the script `rg_generate_shaders_gl.py` in the mixxx/tools directory: + +```console +$ ../../../tools/rg_generate_shaders_gl.py --cmake generated_shaders_gl.cmake *.vert *.frag +``` + +To use this script, make sure that the qsb and spirv commands are in your path. qsb is part +of Qt. spirv is part of the Vulkan SDK and can be downloaded from The script also generates the file `generated_shaders_gl.cmake` which sets a cmake variable containing a list of all GLSL shaders, used by the CMakeLists.txt in this folder. diff --git a/tools/rg_generate_shaders_gl.py b/tools/rg_generate_shaders_gl.py new file mode 100755 index 00000000000..6cd23fff576 --- /dev/null +++ b/tools/rg_generate_shaders_gl.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 +""" +Converts a fragment or vertex shader file into a GL shader file. + +You can use it like this: + + $ ./rg_generate_shaders_gl.py --cmake-output \\ + ../src/rendergraph/shaders/generated_shaders_gl.cmake \\ + ../src/rendergraph/shaders/*.{vert,frag} +""" +import argparse +import logging +import os +import pathlib +import re +import shutil +import subprocess +import tempfile +import typing + + +def find_executable( + executable_name: str, additional_paths: typing.Optional[list[str]] = None +) -> pathlib.Path: + """Find an executable by name in $PATH and in the additional paths.""" + if executable_path := shutil.which(executable_name): + return pathlib.Path(executable_path) + + if additional_paths: + if executable_path := shutil.which( + executable_name, path=os.pathsep.join(additional_paths) + ): + return pathlib.Path(executable_path) + + raise OSError(f"Executable {executable_name!r} not found!") + + +QSB_EXECUTABLE = find_executable( + "qsb", + additional_paths=[ + "/usr/lib/qt6/bin", + "/lib/qt6/bin", + "/usr/local/lib/qt6/bin", + ], +) + + +def parse_shader(input_filepath: pathlib.Path) -> typing.Iterator[str]: + """Parse a Fragment/Vertex shader file and yield lines for a GL file.""" + with tempfile.NamedTemporaryFile() as fp: + subprocess.check_call( + [ + QSB_EXECUTABLE, + "--glsl", + "120", + "--output", + fp.name, + input_filepath, + ] + ) + output = subprocess.check_output( + [QSB_EXECUTABLE, "--dump", fp.name], + encoding="utf-8", + universal_newlines=True, + ) + + comment_added = False + ok = False + in_shader_block = 0 + buffered_blank_line = False + for line in output.splitlines(): + if in_shader_block == 2: + if re.match(r"^\*\*", line): + ok = True + else: + if not comment_added and not re.match(r"^#", line): + yield "//// GENERATED - EDITS WILL BE OVERWRITTEN" + comment_added = True + if line: + if buffered_blank_line: + yield "" + buffered_blank_line = False + yield line + else: + buffered_blank_line = True + elif in_shader_block == 1: + if line.rstrip() == "Contents:": + in_shader_block = 2 + else: + if line.rstrip() == "Shader 1: GLSL 120 [Standard]": + in_shader_block = 1 + if not ok: + raise EOFError("end of file reached before end marker reached") + + +def get_paths(paths: list[pathlib.Path]) -> typing.Iterator[pathlib.Path]: + for path in paths: + if path.is_dir(): + yield from path.glob("*.vert") + yield from path.glob("*.frag") + else: + yield path + + +def main(argv: typing.Optional[list[str]] = None) -> int: + logging.basicConfig(level=logging.DEBUG, format="%(message)s") + + logger = logging.getLogger(__name__) + + description, _, epilog = __doc__.strip().partition("\n\n") + parser = argparse.ArgumentParser( + description=description, + epilog=epilog, + formatter_class=argparse.RawDescriptionHelpFormatter, + ) + parser.add_argument( + "file", + nargs="+", + type=pathlib.Path, + help="Input files (.vert, .frag) or directory", + ) + parser.add_argument( + "--cmake-output", + type=argparse.FileType("w"), + required=True, + help="CMake Output files (.cmake)", + ) + args = parser.parse_args(argv) + + generated_shaders: list[pathlib.Path] = [] + + for file in sorted(get_paths(args.file)): + logger.info("Reading file: %s", file) + try: + lines = list(parse_shader(file)) + except EOFError as err: + logger.error("Failed to parse %s: %s", file, err) + continue + + output_file = file.with_suffix(f"{file.suffix}.gl") + logger.info("Writing file: %s", output_file) + with output_file.open("w") as fp: + for line in lines: + fp.write(f"{line}\n") + + generated_shaders.append(output_file) + + args.cmake_output.write("set(\n") + args.cmake_output.write(" generated_shaders_gl\n") + for generated_file in generated_shaders: + args.cmake_output.write(f" {generated_file.name}\n") + args.cmake_output.write(")\n") + logger.info("Generated %d shader files.", len(generated_shaders)) + + return 0 + + +if __name__ == "__main__": + main() From 931260ccc225251cc9c7a27bce47fb6925665ba2 Mon Sep 17 00:00:00 2001 From: m0dB Date: Thu, 26 Dec 2024 04:44:48 +0100 Subject: [PATCH 054/201] removed all Q_UNUSED --- src/waveform/renderers/allshader/waveformrenderer.cpp | 4 +--- src/waveform/renderers/allshader/waveformrendererfiltered.cpp | 3 +-- src/waveform/renderers/allshader/waveformrendererhsv.cpp | 3 +-- src/waveform/renderers/allshader/waveformrendererrgb.cpp | 3 +-- .../renderers/allshader/waveformrenderersignalbase.cpp | 4 +--- src/waveform/renderers/allshader/waveformrenderersimple.cpp | 3 +-- src/waveform/renderers/allshader/waveformrendererstem.cpp | 3 +-- src/waveform/renderers/allshader/waveformrenderertextured.cpp | 3 +-- src/waveform/renderers/allshader/waveformrendermark.cpp | 4 +--- 9 files changed, 9 insertions(+), 21 deletions(-) diff --git a/src/waveform/renderers/allshader/waveformrenderer.cpp b/src/waveform/renderers/allshader/waveformrenderer.cpp index 16ee93f82ed..4c983ef583b 100644 --- a/src/waveform/renderers/allshader/waveformrenderer.cpp +++ b/src/waveform/renderers/allshader/waveformrenderer.cpp @@ -8,9 +8,7 @@ WaveformRenderer::WaveformRenderer(WaveformWidgetRenderer* widget) : ::WaveformRendererAbstract(widget) { } -void WaveformRenderer::draw(QPainter* painter, QPaintEvent* event) { - Q_UNUSED(painter); - Q_UNUSED(event); +void WaveformRenderer::draw(QPainter*, QPaintEvent*) { DEBUG_ASSERT(false); } diff --git a/src/waveform/renderers/allshader/waveformrendererfiltered.cpp b/src/waveform/renderers/allshader/waveformrendererfiltered.cpp index f48b1136f59..d7c40cd01ed 100644 --- a/src/waveform/renderers/allshader/waveformrendererfiltered.cpp +++ b/src/waveform/renderers/allshader/waveformrendererfiltered.cpp @@ -14,8 +14,7 @@ WaveformRendererFiltered::WaveformRendererFiltered( m_bRgbStacked(bRgbStacked) { } -void WaveformRendererFiltered::onSetup(const QDomNode& node) { - Q_UNUSED(node); +void WaveformRendererFiltered::onSetup(const QDomNode&) { } void WaveformRendererFiltered::initializeGL() { diff --git a/src/waveform/renderers/allshader/waveformrendererhsv.cpp b/src/waveform/renderers/allshader/waveformrendererhsv.cpp index 75b34dbae00..60ec42822be 100644 --- a/src/waveform/renderers/allshader/waveformrendererhsv.cpp +++ b/src/waveform/renderers/allshader/waveformrendererhsv.cpp @@ -14,8 +14,7 @@ WaveformRendererHSV::WaveformRendererHSV( : WaveformRendererSignalBase(waveformWidget) { } -void WaveformRendererHSV::onSetup(const QDomNode& node) { - Q_UNUSED(node); +void WaveformRendererHSV::onSetup(const QDomNode&) { } void WaveformRendererHSV::initializeGL() { diff --git a/src/waveform/renderers/allshader/waveformrendererrgb.cpp b/src/waveform/renderers/allshader/waveformrendererrgb.cpp index 31cd94f1a5d..6d7a3d7393f 100644 --- a/src/waveform/renderers/allshader/waveformrendererrgb.cpp +++ b/src/waveform/renderers/allshader/waveformrendererrgb.cpp @@ -22,8 +22,7 @@ WaveformRendererRGB::WaveformRendererRGB(WaveformWidgetRenderer* waveformWidget, m_options(options) { } -void WaveformRendererRGB::onSetup(const QDomNode& node) { - Q_UNUSED(node); +void WaveformRendererRGB::onSetup(const QDomNode&) { } void WaveformRendererRGB::initializeGL() { diff --git a/src/waveform/renderers/allshader/waveformrenderersignalbase.cpp b/src/waveform/renderers/allshader/waveformrenderersignalbase.cpp index 620db520fd0..ea906dba69e 100644 --- a/src/waveform/renderers/allshader/waveformrenderersignalbase.cpp +++ b/src/waveform/renderers/allshader/waveformrenderersignalbase.cpp @@ -7,9 +7,7 @@ WaveformRendererSignalBase::WaveformRendererSignalBase( : ::WaveformRendererSignalBase(waveformWidget) { } -void WaveformRendererSignalBase::draw(QPainter* painter, QPaintEvent* event) { - Q_UNUSED(painter); - Q_UNUSED(event); +void WaveformRendererSignalBase::draw(QPainter*, QPaintEvent*) { DEBUG_ASSERT(false); } diff --git a/src/waveform/renderers/allshader/waveformrenderersimple.cpp b/src/waveform/renderers/allshader/waveformrenderersimple.cpp index 90a707d2373..8cc06626f07 100644 --- a/src/waveform/renderers/allshader/waveformrenderersimple.cpp +++ b/src/waveform/renderers/allshader/waveformrenderersimple.cpp @@ -13,8 +13,7 @@ WaveformRendererSimple::WaveformRendererSimple( : WaveformRendererSignalBase(waveformWidget) { } -void WaveformRendererSimple::onSetup(const QDomNode& node) { - Q_UNUSED(node); +void WaveformRendererSimple::onSetup(const QDomNode&) { } void WaveformRendererSimple::initializeGL() { diff --git a/src/waveform/renderers/allshader/waveformrendererstem.cpp b/src/waveform/renderers/allshader/waveformrendererstem.cpp index 80612f99470..c73e170d397 100644 --- a/src/waveform/renderers/allshader/waveformrendererstem.cpp +++ b/src/waveform/renderers/allshader/waveformrendererstem.cpp @@ -21,8 +21,7 @@ WaveformRendererStem::WaveformRendererStem( m_isSlipRenderer(type == ::WaveformRendererAbstract::Slip) { } -void WaveformRendererStem::onSetup(const QDomNode& node) { - Q_UNUSED(node); +void WaveformRendererStem::onSetup(const QDomNode&) { } void WaveformRendererStem::initializeGL() { diff --git a/src/waveform/renderers/allshader/waveformrenderertextured.cpp b/src/waveform/renderers/allshader/waveformrenderertextured.cpp index de21a013259..97ac6a48059 100644 --- a/src/waveform/renderers/allshader/waveformrenderertextured.cpp +++ b/src/waveform/renderers/allshader/waveformrenderertextured.cpp @@ -235,8 +235,7 @@ void WaveformRendererTextured::initializeGL() { } } -void WaveformRendererTextured::onSetup(const QDomNode& node) { - Q_UNUSED(node); +void WaveformRendererTextured::onSetup(const QDomNode&) { } void WaveformRendererTextured::onSetTrack() { diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index 9e9f1632896..de797d41d05 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -79,9 +79,7 @@ bool allshader::WaveformRenderMark::init() { return true; } -void allshader::WaveformRenderMark::draw(QPainter* painter, QPaintEvent* event) { - Q_UNUSED(painter); - Q_UNUSED(event); +void allshader::WaveformRenderMark::draw(QPainter*, QPaintEvent*) { DEBUG_ASSERT(false); } From 6c21fcff57a44713f35baf862e899217bbaa0fc2 Mon Sep 17 00:00:00 2001 From: m0dB Date: Thu, 26 Dec 2024 04:47:08 +0100 Subject: [PATCH 055/201] removed copy of generate_shaders_gl.py --- src/rendergraph/tools/generate_shaders_gl.py | 159 ------------------- 1 file changed, 159 deletions(-) delete mode 100755 src/rendergraph/tools/generate_shaders_gl.py diff --git a/src/rendergraph/tools/generate_shaders_gl.py b/src/rendergraph/tools/generate_shaders_gl.py deleted file mode 100755 index e96d50d017a..00000000000 --- a/src/rendergraph/tools/generate_shaders_gl.py +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env python3 -""" -Converts a fragment or vertex shader file into a GL shader file. - -You can use it like this: - - $ ./generate_shaders_gl.py --cmake-output \\ - ../shaders/generated_shaders_gl.cmake \\ - ../shaders/*.{vert,frag} -""" -import argparse -import logging -import os -import pathlib -import re -import shutil -import subprocess -import tempfile -import typing - - -def find_executable( - executable_name: str, additional_paths: typing.Optional[list[str]] = None -) -> pathlib.Path: - """Find an executable by name in $PATH and in the additional paths.""" - if executable_path := shutil.which(executable_name): - return pathlib.Path(executable_path) - - if additional_paths: - if executable_path := shutil.which( - executable_name, path=os.pathsep.join(additional_paths) - ): - return pathlib.Path(executable_path) - - raise OSError(f"Executable {executable_name!r} not found!") - - -QSB_EXECUTABLE = find_executable( - "qsb", - additional_paths=[ - "/usr/lib/qt6/bin", - "/lib/qt6/bin", - "/usr/local/lib/qt6/bin", - ], -) - - -def parse_shader(input_filepath: pathlib.Path) -> typing.Iterator[str]: - """Parse a Fragment/Vertex shader file and yield lines for a GL file.""" - with tempfile.NamedTemporaryFile() as fp: - subprocess.check_call( - [ - QSB_EXECUTABLE, - "--glsl", - "120", - "--output", - fp.name, - input_filepath, - ] - ) - output = subprocess.check_output( - [QSB_EXECUTABLE, "--dump", fp.name], - encoding="utf-8", - universal_newlines=True, - ) - - comment_added = False - ok = False - in_shader_block = 0 - buffered_blank_line = False - for line in output.splitlines(): - if in_shader_block == 2: - if re.match(r"^\*\*", line): - ok = True - else: - if not comment_added and not re.match(r"^#", line): - yield "//// GENERATED - EDITS WILL BE OVERWRITTEN" - comment_added = True - if line: - if buffered_blank_line: - yield "" - buffered_blank_line = False - yield line - else: - buffered_blank_line = True - elif in_shader_block == 1: - if line.rstrip() == "Contents:": - in_shader_block = 2 - else: - if line.rstrip() == "Shader 1: GLSL 120 [Standard]": - in_shader_block = 1 - if not ok: - raise EOFError("end of file reached before end marker reached") - - -def get_paths(paths: list[pathlib.Path]) -> typing.Iterator[pathlib.Path]: - for path in paths: - if path.is_dir(): - yield from path.glob("*.vert") - yield from path.glob("*.frag") - else: - yield path - - -def main(argv: typing.Optional[list[str]] = None) -> int: - logging.basicConfig(level=logging.DEBUG, format="%(message)s") - - logger = logging.getLogger(__name__) - - description, _, epilog = __doc__.strip().partition("\n\n") - parser = argparse.ArgumentParser( - description=description, - epilog=epilog, - formatter_class=argparse.RawDescriptionHelpFormatter, - ) - parser.add_argument( - "file", - nargs="+", - type=pathlib.Path, - help="Input files (.vert, .frag) or directory", - ) - parser.add_argument( - "--cmake-output", - type=argparse.FileType("w"), - required=True, - help="CMake Output files (.cmake)", - ) - args = parser.parse_args(argv) - - generated_shaders: list[pathlib.Path] = [] - - for file in sorted(get_paths(args.file)): - logger.info("Reading file: %s", file) - try: - lines = list(parse_shader(file)) - except EOFError as err: - logger.error("Failed to parse %s: %s", file, err) - continue - - output_file = file.with_suffix(f"{file.suffix}.gl") - logger.info("Writing file: %s", output_file) - with output_file.open("w") as fp: - for line in lines: - fp.write(f"{line}\n") - - generated_shaders.append(output_file) - - args.cmake_output.write("set(\n") - args.cmake_output.write(" generated_shaders_gl\n") - for generated_file in generated_shaders: - args.cmake_output.write(f" {generated_file.name}\n") - args.cmake_output.write(")\n") - logger.info("Generated %d shader files.", len(generated_shaders)) - - return 0 - - -if __name__ == "__main__": - main() From a00d35c9c0757f63f44a6b01e6cd81bfcf444f14 Mon Sep 17 00:00:00 2001 From: m0dB Date: Thu, 26 Dec 2024 05:52:11 +0100 Subject: [PATCH 056/201] added appendChildNode with templatized result as suggested by Nico, cleared up code when creating a waveformsignalrenderer, and appending it as a node --- .../opengl/rendergraph/nodeinterface.h | 7 ++- .../scenegraph/rendergraph/nodeinterface.h | 6 ++- .../allshader/waveformrendererfiltered.h | 4 ++ .../renderers/allshader/waveformrendererhsv.h | 4 ++ .../renderers/allshader/waveformrendererrgb.h | 4 ++ .../allshader/waveformrenderersignalbase.h | 3 ++ .../allshader/waveformrenderersimple.h | 4 ++ .../allshader/waveformrendererstem.h | 4 ++ .../allshader/waveformrenderertextured.h | 4 ++ .../widgets/allshader/waveformwidget.cpp | 50 ++++++++++++------- .../widgets/allshader/waveformwidget.h | 7 +-- 11 files changed, 69 insertions(+), 28 deletions(-) diff --git a/src/rendergraph/opengl/rendergraph/nodeinterface.h b/src/rendergraph/opengl/rendergraph/nodeinterface.h index 45159724c49..7781dd24237 100644 --- a/src/rendergraph/opengl/rendergraph/nodeinterface.h +++ b/src/rendergraph/opengl/rendergraph/nodeinterface.h @@ -7,14 +7,17 @@ namespace rendergraph { template class NodeInterface : public T_Node { public: - void appendChildNode(std::unique_ptr pNode) { + template + T_AppendNode* appendChildNode(std::unique_ptr pNode) { // Transfers ownership to this. - BaseNode* pRawNode = pNode.release(); + T_AppendNode* pRawNode = pNode.release(); // Note: Ideally we would use unique_ptrs internally, but // Qt uses raw pointers for QSGNode hierarchy. For simplicity // we mimic this. T_Node::appendChildNode(pRawNode); + + return pRawNode; } std::unique_ptr detachChildNode(BaseNode* pNode) { diff --git a/src/rendergraph/scenegraph/rendergraph/nodeinterface.h b/src/rendergraph/scenegraph/rendergraph/nodeinterface.h index f5c42cadf46..48ae7ad67dc 100644 --- a/src/rendergraph/scenegraph/rendergraph/nodeinterface.h +++ b/src/rendergraph/scenegraph/rendergraph/nodeinterface.h @@ -8,11 +8,13 @@ namespace rendergraph { template class NodeInterface : public T_Node { public: - void appendChildNode(std::unique_ptr pNode) { - BaseNode* pRawNode = pNode.release(); + template + T_AppendNode* appendChildNode(std::unique_ptr pNode) { + T_AppendNode* pRawNode = pNode.release(); pRawNode->setFlag(QSGNode::OwnedByParent, true); T_Node::appendChildNode(pRawNode); DEBUG_ASSERT(pRawNode->flags() & QSGNode::OwnedByParent); + return pRawNode; } std::unique_ptr detachChildNode(BaseNode* pNode) { DEBUG_ASSERT(pNode->flags() & QSGNode::OwnedByParent); diff --git a/src/waveform/renderers/allshader/waveformrendererfiltered.h b/src/waveform/renderers/allshader/waveformrendererfiltered.h index 43441adafa5..273e58585d3 100644 --- a/src/waveform/renderers/allshader/waveformrendererfiltered.h +++ b/src/waveform/renderers/allshader/waveformrendererfiltered.h @@ -22,6 +22,10 @@ class allshader::WaveformRendererFiltered final void initializeGL() override; void paintGL() override; + rendergraph::BaseNode* asNode() override { + return this; + } + private: const bool m_bRgbStacked; mixxx::UnicolorShader m_shader; diff --git a/src/waveform/renderers/allshader/waveformrendererhsv.h b/src/waveform/renderers/allshader/waveformrendererhsv.h index a6fdbe95f51..afed72306d3 100644 --- a/src/waveform/renderers/allshader/waveformrendererhsv.h +++ b/src/waveform/renderers/allshader/waveformrendererhsv.h @@ -23,6 +23,10 @@ class allshader::WaveformRendererHSV final void initializeGL() override; void paintGL() override; + rendergraph::BaseNode* asNode() override { + return this; + } + private: mixxx::RGBShader m_shader; VertexData m_vertices; diff --git a/src/waveform/renderers/allshader/waveformrendererrgb.h b/src/waveform/renderers/allshader/waveformrendererrgb.h index 10f2262a751..ec4496c4bd2 100644 --- a/src/waveform/renderers/allshader/waveformrendererrgb.h +++ b/src/waveform/renderers/allshader/waveformrendererrgb.h @@ -30,6 +30,10 @@ class allshader::WaveformRendererRGB final return true; } + rendergraph::BaseNode* asNode() override { + return this; + } + private: mixxx::RGBShader m_shader; VertexData m_vertices; diff --git a/src/waveform/renderers/allshader/waveformrenderersignalbase.h b/src/waveform/renderers/allshader/waveformrenderersignalbase.h index dafa41cd79b..b9cf0ce93a1 100644 --- a/src/waveform/renderers/allshader/waveformrenderersignalbase.h +++ b/src/waveform/renderers/allshader/waveformrenderersignalbase.h @@ -3,6 +3,7 @@ #include #include +#include "rendergraph/node.h" #include "util/class.h" #include "waveform/renderers/waveformrenderersignalbase.h" @@ -32,5 +33,7 @@ class allshader::WaveformRendererSignalBase : public ::WaveformRendererSignalBas return false; } + virtual rendergraph::BaseNode* asNode() = 0; + DISALLOW_COPY_AND_ASSIGN(WaveformRendererSignalBase); }; diff --git a/src/waveform/renderers/allshader/waveformrenderersimple.h b/src/waveform/renderers/allshader/waveformrenderersimple.h index ba79752539c..1654ff7b2de 100644 --- a/src/waveform/renderers/allshader/waveformrenderersimple.h +++ b/src/waveform/renderers/allshader/waveformrenderersimple.h @@ -22,6 +22,10 @@ class allshader::WaveformRendererSimple final void initializeGL() override; void paintGL() override; + rendergraph::BaseNode* asNode() override { + return this; + } + private: mixxx::UnicolorShader m_shader; VertexData m_vertices[2]; diff --git a/src/waveform/renderers/allshader/waveformrendererstem.h b/src/waveform/renderers/allshader/waveformrendererstem.h index 99728194574..215c4e367db 100644 --- a/src/waveform/renderers/allshader/waveformrendererstem.h +++ b/src/waveform/renderers/allshader/waveformrendererstem.h @@ -30,6 +30,10 @@ class allshader::WaveformRendererStem final void initializeGL() override; void paintGL() override; + rendergraph::BaseNode* asNode() override { + return this; + } + private: mixxx::RGBAShader m_shader; mixxx::TextureShader m_textureShader; diff --git a/src/waveform/renderers/allshader/waveformrenderertextured.h b/src/waveform/renderers/allshader/waveformrenderertextured.h index ccd82ae5a67..c2477970adc 100644 --- a/src/waveform/renderers/allshader/waveformrenderertextured.h +++ b/src/waveform/renderers/allshader/waveformrenderertextured.h @@ -42,6 +42,10 @@ class allshader::WaveformRendererTextured final : public QObject, void onSetTrack() override; + rendergraph::BaseNode* asNode() override { + return this; + } + public slots: void slotWaveformUpdated(); diff --git a/src/waveform/widgets/allshader/waveformwidget.cpp b/src/waveform/widgets/allshader/waveformwidget.cpp index c359a585c05..f8b88ed927a 100644 --- a/src/waveform/widgets/allshader/waveformwidget.cpp +++ b/src/waveform/widgets/allshader/waveformwidget.cpp @@ -21,6 +21,11 @@ namespace allshader { +std::unique_ptr convert( + std::unique_ptr&& pRenderer) { + return std::unique_ptr(pRenderer.release()->asNode()); +} + WaveformWidget::WaveformWidget(QWidget* parent, WaveformWidgetType::Type type, const QString& group, @@ -34,8 +39,8 @@ WaveformWidget::WaveformWidget(QWidget* parent, pTopNode->appendChildNode(addRendererNode()); pOpacityNode->appendChildNode(addRendererNode()); pOpacityNode->appendChildNode(addRendererNode()); - pOpacityNode->appendChildNode(addRendererNode()); - m_pWaveformRenderMarkRange = static_cast(pOpacityNode->lastChild()); + m_pWaveformRenderMarkRange = pOpacityNode->appendChildNode( + addRendererNode()); #ifdef __STEM__ // The following two renderers work in tandem: if the rendered waveform is @@ -43,11 +48,16 @@ WaveformWidget::WaveformWidget(QWidget* parent, // WaveformRendererStem do the rendering, and vice-versa. pOpacityNode->appendChildNode(addRendererNode()); #endif - pOpacityNode->appendChildNode(addWaveformSignalRendererNode( - type, options, ::WaveformRendererAbstract::Play)); + std::unique_ptr pWaveformRendererSignal = addWaveformSignalRenderer( + type, options, ::WaveformRendererAbstract::Play); + m_pWaveformRendererSignal = pWaveformRendererSignal.get(); + if (pWaveformRendererSignal) { + // convert std::unique_ptr + // to std::unique_ptr + pOpacityNode->appendChildNode(convert(std::move(pWaveformRendererSignal))); + } pOpacityNode->appendChildNode(addRendererNode()); - pOpacityNode->appendChildNode(addRendererNode()); - m_pWaveformRenderMark = static_cast(pOpacityNode->lastChild()); + m_pWaveformRenderMark = pOpacityNode->appendChildNode(addRendererNode()); // if the added signal renderer supports slip, we add it again, now for // slip, together with the other slip renderers @@ -62,8 +72,11 @@ WaveformWidget::WaveformWidget(QWidget* parent, addRendererNode( ::WaveformRendererAbstract::Slip)); #endif - pOpacityNode->appendChildNode(addWaveformSignalRendererNode( - type, options, ::WaveformRendererAbstract::Slip)); + std::unique_ptr pSlipNode = addWaveformSignalRenderer( + type, options, ::WaveformRendererAbstract::Slip); + // convert std::unique_ptr + // to std::unique_ptr + pOpacityNode->appendChildNode(convert(std::move(pSlipNode))); pOpacityNode->appendChildNode( addRendererNode( ::WaveformRendererAbstract::Slip)); @@ -74,8 +87,7 @@ WaveformWidget::WaveformWidget(QWidget* parent, m_initSuccess = init(); - pTopNode->appendChildNode(std::move(pOpacityNode)); - m_pOpacityNode = static_cast(pTopNode->lastChild()); + m_pOpacityNode = pTopNode->appendChildNode(std::move(pOpacityNode)); m_pEngine = std::make_unique(std::move(pTopNode)); } @@ -87,17 +99,17 @@ WaveformWidget::~WaveformWidget() { doneCurrent(); } -std::unique_ptr -WaveformWidget::addWaveformSignalRendererNode(WaveformWidgetType::Type type, +std::unique_ptr +WaveformWidget::addWaveformSignalRenderer(WaveformWidgetType::Type type, WaveformRendererSignalBase::Options options, ::WaveformRendererAbstract::PositionSource positionSource) { #ifndef QT_OPENGL_ES_2 - if (options & allshader::WaveformRendererSignalBase::Option::HighDetail) { + if (options & WaveformRendererSignalBase::Option::HighDetail) { switch (type) { case ::WaveformWidgetType::RGB: case ::WaveformWidgetType::Filtered: case ::WaveformWidgetType::Stacked: - return addWaveformSignalRendererNode( + return addWaveformSignalRenderer( type, positionSource, options); default: break; @@ -107,15 +119,15 @@ WaveformWidget::addWaveformSignalRendererNode(WaveformWidgetType::Type type, switch (type) { case ::WaveformWidgetType::Simple: - return addWaveformSignalRendererNode(); + return addWaveformSignalRenderer(); case ::WaveformWidgetType::RGB: - return addWaveformSignalRendererNode(positionSource, options); + return addWaveformSignalRenderer(positionSource, options); case ::WaveformWidgetType::HSV: - return addWaveformSignalRendererNode(); + return addWaveformSignalRenderer(); case ::WaveformWidgetType::Filtered: - return addWaveformSignalRendererNode(false); + return addWaveformSignalRenderer(false); case ::WaveformWidgetType::Stacked: - return addWaveformSignalRendererNode( + return addWaveformSignalRenderer( true); // true for RGB Stacked default: break; diff --git a/src/waveform/widgets/allshader/waveformwidget.h b/src/waveform/widgets/allshader/waveformwidget.h index bf0b3a18267..88a871f1fe7 100644 --- a/src/waveform/widgets/allshader/waveformwidget.h +++ b/src/waveform/widgets/allshader/waveformwidget.h @@ -54,15 +54,12 @@ class allshader::WaveformWidget final : public ::WGLWidget, } template - inline std::unique_ptr addWaveformSignalRendererNode(Args&&... args) { + inline std::unique_ptr addWaveformSignalRenderer(Args&&... args) { auto pRenderer = addRenderer(std::forward(args)...); - if (!m_pWaveformRendererSignal) { - m_pWaveformRendererSignal = pRenderer; - } return std::unique_ptr(pRenderer); } - std::unique_ptr addWaveformSignalRendererNode( + std::unique_ptr addWaveformSignalRenderer( WaveformWidgetType::Type type, WaveformRendererSignalBase::Options options, ::WaveformRendererAbstract::PositionSource positionSource); From 0084dcfa403ba5e59f6a5943162c315c8aaaddde Mon Sep 17 00:00:00 2001 From: m0dB Date: Thu, 26 Dec 2024 05:54:35 +0100 Subject: [PATCH 057/201] comment --- src/waveform/widgets/allshader/waveformwidget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/waveform/widgets/allshader/waveformwidget.cpp b/src/waveform/widgets/allshader/waveformwidget.cpp index f8b88ed927a..8c54b9f861a 100644 --- a/src/waveform/widgets/allshader/waveformwidget.cpp +++ b/src/waveform/widgets/allshader/waveformwidget.cpp @@ -95,6 +95,7 @@ WaveformWidget::WaveformWidget(QWidget* parent, WaveformWidget::~WaveformWidget() { makeCurrentIfNeeded(); m_rendererStack.clear(); + // destruction of nodes needs to happen within the opengl context m_pEngine.reset(); doneCurrent(); } From d7c0af56d9e030462f869dd24be3cc8dcf84004c Mon Sep 17 00:00:00 2001 From: m0dB Date: Thu, 26 Dec 2024 06:00:52 +0100 Subject: [PATCH 058/201] comment --- src/waveform/widgets/allshader/waveformwidget.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/waveform/widgets/allshader/waveformwidget.cpp b/src/waveform/widgets/allshader/waveformwidget.cpp index 8c54b9f861a..dd3727102f8 100644 --- a/src/waveform/widgets/allshader/waveformwidget.cpp +++ b/src/waveform/widgets/allshader/waveformwidget.cpp @@ -163,7 +163,9 @@ void WaveformWidget::initializeGL() { } void WaveformWidget::resizeRenderer(int, int, float) { - // defer to resizeGL + // This is called when the widget is resized, but as this is a WGLWidget, we + // also get the resizeGL call and use that instead, as it has the opengl + // context set. } void WaveformWidget::resizeGL(int w, int h) { From b6aa301f3616fe4f07dfd050aff4a2a3cbfd57b6 Mon Sep 17 00:00:00 2001 From: m0dB Date: Sat, 18 Jan 2025 21:05:07 +0100 Subject: [PATCH 059/201] fix post rebase --- src/waveform/renderers/allshader/waveformrendermark.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index de797d41d05..2f85b24149c 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -394,11 +394,8 @@ void allshader::WaveformRenderMark::updatePlayPosMarkTexture() { const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); const float lineX = 5.5f; - imgwidth = 11.f; - imgheight = height; - - const QSize size{static_cast(std::lround(imgwidth * devicePixelRatio)), - static_cast(std::lround(imgheight * devicePixelRatio))}; + const QSize size{static_cast(std::lround(imgWidth * devicePixelRatio)), + static_cast(std::lround(imgHeight * devicePixelRatio))}; if (size.width() <= 0 || size.height() <= 0) { return; From 2635bc214f5dbacbce670023bd35715335a4e82e Mon Sep 17 00:00:00 2001 From: m0dB Date: Sat, 18 Jan 2025 21:18:40 +0100 Subject: [PATCH 060/201] remove needless convert and asNode --- .../allshader/waveformrendererfiltered.h | 4 ---- .../renderers/allshader/waveformrendererhsv.h | 4 ---- .../renderers/allshader/waveformrendererrgb.h | 4 ---- .../allshader/waveformrenderersignalbase.h | 2 -- .../allshader/waveformrenderersimple.h | 4 ---- .../renderers/allshader/waveformrendererstem.h | 4 ---- .../allshader/waveformrenderertextured.h | 4 ---- .../widgets/allshader/waveformwidget.cpp | 17 ++++++----------- 8 files changed, 6 insertions(+), 37 deletions(-) diff --git a/src/waveform/renderers/allshader/waveformrendererfiltered.h b/src/waveform/renderers/allshader/waveformrendererfiltered.h index 273e58585d3..43441adafa5 100644 --- a/src/waveform/renderers/allshader/waveformrendererfiltered.h +++ b/src/waveform/renderers/allshader/waveformrendererfiltered.h @@ -22,10 +22,6 @@ class allshader::WaveformRendererFiltered final void initializeGL() override; void paintGL() override; - rendergraph::BaseNode* asNode() override { - return this; - } - private: const bool m_bRgbStacked; mixxx::UnicolorShader m_shader; diff --git a/src/waveform/renderers/allshader/waveformrendererhsv.h b/src/waveform/renderers/allshader/waveformrendererhsv.h index afed72306d3..a6fdbe95f51 100644 --- a/src/waveform/renderers/allshader/waveformrendererhsv.h +++ b/src/waveform/renderers/allshader/waveformrendererhsv.h @@ -23,10 +23,6 @@ class allshader::WaveformRendererHSV final void initializeGL() override; void paintGL() override; - rendergraph::BaseNode* asNode() override { - return this; - } - private: mixxx::RGBShader m_shader; VertexData m_vertices; diff --git a/src/waveform/renderers/allshader/waveformrendererrgb.h b/src/waveform/renderers/allshader/waveformrendererrgb.h index ec4496c4bd2..10f2262a751 100644 --- a/src/waveform/renderers/allshader/waveformrendererrgb.h +++ b/src/waveform/renderers/allshader/waveformrendererrgb.h @@ -30,10 +30,6 @@ class allshader::WaveformRendererRGB final return true; } - rendergraph::BaseNode* asNode() override { - return this; - } - private: mixxx::RGBShader m_shader; VertexData m_vertices; diff --git a/src/waveform/renderers/allshader/waveformrenderersignalbase.h b/src/waveform/renderers/allshader/waveformrenderersignalbase.h index b9cf0ce93a1..91dbbe8046c 100644 --- a/src/waveform/renderers/allshader/waveformrenderersignalbase.h +++ b/src/waveform/renderers/allshader/waveformrenderersignalbase.h @@ -33,7 +33,5 @@ class allshader::WaveformRendererSignalBase : public ::WaveformRendererSignalBas return false; } - virtual rendergraph::BaseNode* asNode() = 0; - DISALLOW_COPY_AND_ASSIGN(WaveformRendererSignalBase); }; diff --git a/src/waveform/renderers/allshader/waveformrenderersimple.h b/src/waveform/renderers/allshader/waveformrenderersimple.h index 1654ff7b2de..ba79752539c 100644 --- a/src/waveform/renderers/allshader/waveformrenderersimple.h +++ b/src/waveform/renderers/allshader/waveformrenderersimple.h @@ -22,10 +22,6 @@ class allshader::WaveformRendererSimple final void initializeGL() override; void paintGL() override; - rendergraph::BaseNode* asNode() override { - return this; - } - private: mixxx::UnicolorShader m_shader; VertexData m_vertices[2]; diff --git a/src/waveform/renderers/allshader/waveformrendererstem.h b/src/waveform/renderers/allshader/waveformrendererstem.h index 215c4e367db..99728194574 100644 --- a/src/waveform/renderers/allshader/waveformrendererstem.h +++ b/src/waveform/renderers/allshader/waveformrendererstem.h @@ -30,10 +30,6 @@ class allshader::WaveformRendererStem final void initializeGL() override; void paintGL() override; - rendergraph::BaseNode* asNode() override { - return this; - } - private: mixxx::RGBAShader m_shader; mixxx::TextureShader m_textureShader; diff --git a/src/waveform/renderers/allshader/waveformrenderertextured.h b/src/waveform/renderers/allshader/waveformrenderertextured.h index c2477970adc..ccd82ae5a67 100644 --- a/src/waveform/renderers/allshader/waveformrenderertextured.h +++ b/src/waveform/renderers/allshader/waveformrenderertextured.h @@ -42,10 +42,6 @@ class allshader::WaveformRendererTextured final : public QObject, void onSetTrack() override; - rendergraph::BaseNode* asNode() override { - return this; - } - public slots: void slotWaveformUpdated(); diff --git a/src/waveform/widgets/allshader/waveformwidget.cpp b/src/waveform/widgets/allshader/waveformwidget.cpp index dd3727102f8..035863716f3 100644 --- a/src/waveform/widgets/allshader/waveformwidget.cpp +++ b/src/waveform/widgets/allshader/waveformwidget.cpp @@ -21,11 +21,6 @@ namespace allshader { -std::unique_ptr convert( - std::unique_ptr&& pRenderer) { - return std::unique_ptr(pRenderer.release()->asNode()); -} - WaveformWidget::WaveformWidget(QWidget* parent, WaveformWidgetType::Type type, const QString& group, @@ -52,9 +47,9 @@ WaveformWidget::WaveformWidget(QWidget* parent, type, options, ::WaveformRendererAbstract::Play); m_pWaveformRendererSignal = pWaveformRendererSignal.get(); if (pWaveformRendererSignal) { - // convert std::unique_ptr - // to std::unique_ptr - pOpacityNode->appendChildNode(convert(std::move(pWaveformRendererSignal))); + auto pNode = dynamic_cast(pWaveformRendererSignal.release()); + DEBUG_ASSERT(pNode); + pOpacityNode->appendChildNode(std::unique_ptr(pNode)); } pOpacityNode->appendChildNode(addRendererNode()); m_pWaveformRenderMark = pOpacityNode->appendChildNode(addRendererNode()); @@ -74,9 +69,9 @@ WaveformWidget::WaveformWidget(QWidget* parent, #endif std::unique_ptr pSlipNode = addWaveformSignalRenderer( type, options, ::WaveformRendererAbstract::Slip); - // convert std::unique_ptr - // to std::unique_ptr - pOpacityNode->appendChildNode(convert(std::move(pSlipNode))); + auto pNode = dynamic_cast(pSlipNode.release()); + DEBUG_ASSERT(pNode); + pOpacityNode->appendChildNode(std::unique_ptr(pNode)); pOpacityNode->appendChildNode( addRendererNode( ::WaveformRendererAbstract::Slip)); From 84f8966f79389d0f87573f20be23b7bd4f34d9fa Mon Sep 17 00:00:00 2001 From: m0dB Date: Sat, 18 Jan 2025 23:07:28 +0100 Subject: [PATCH 061/201] only build shaders for added target --- src/rendergraph/shaders/CMakeLists.txt | 72 ++++++++++++++------------ 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/src/rendergraph/shaders/CMakeLists.txt b/src/rendergraph/shaders/CMakeLists.txt index 42478758f46..3db40bb2c30 100644 --- a/src/rendergraph/shaders/CMakeLists.txt +++ b/src/rendergraph/shaders/CMakeLists.txt @@ -14,40 +14,44 @@ set( unicolor.vert ) -qt6_add_shaders(rendergraph_sg "shaders-qsb" - BATCHABLE - PRECOMPILE - OPTIMIZED - PREFIX - /shaders/rendergraph - FILES - ${shaders} -) - -# USE_QSHADER_FOR_GL is set in src/rendergraph/CMakeLists.txt when Qt >= 6.6 -if(USE_QSHADER_FOR_GL) - # Add the .qsb shader bundles; rendergraph::MaterialShader will use - # QShader to extract the GLSL shader from the bundle. - message(STATUS "Adding qsb shaders to rendergraph_gl") - qt6_add_shaders(rendergraph_gl "shaders-qsb" - BATCHABLE - PRECOMPILE - OPTIMIZED - PREFIX - /shaders/rendergraph - FILES - ${shaders} +if(TARGET rendergraph_sg) + qt6_add_shaders(rendergraph_sg "shaders-qsb" + BATCHABLE + PRECOMPILE + OPTIMIZED + PREFIX + /shaders/rendergraph + FILES + ${shaders} ) -else() - # Use GLSL shaders extracted from the .qsb shader bundles using - # generate_shaders_gl.pl - message(STATUS "Adding gl shaders to rendergraph_gl") - include(generated_shaders_gl.cmake) +endif() - qt_add_resources(rendergraph_gl "shaders-gl" - PREFIX - /shaders/rendergraph - FILES - ${generated_shaders_gl} - ) +if(TARGET rendergraph_gl) + # USE_QSHADER_FOR_GL is set in src/rendergraph/CMakeLists.txt when Qt >= 6.6 + if(USE_QSHADER_FOR_GL) + # Add the .qsb shader bundles; rendergraph::MaterialShader will use + # QShader to extract the GLSL shader from the bundle. + message(STATUS "Adding qsb shaders to rendergraph_gl") + qt6_add_shaders(rendergraph_gl "shaders-qsb" + BATCHABLE + PRECOMPILE + OPTIMIZED + PREFIX + /shaders/rendergraph + FILES + ${shaders} + ) + else() + # Use GLSL shaders extracted from the .qsb shader bundles using + # generate_shaders_gl.pl + message(STATUS "Adding gl shaders to rendergraph_gl") + include(generated_shaders_gl.cmake) + + qt_add_resources(rendergraph_gl "shaders-gl" + PREFIX + /shaders/rendergraph + FILES + ${generated_shaders_gl} + ) + endif() endif() From f6a7fe420db120eb3476cae448851a2d65fabe1f Mon Sep 17 00:00:00 2001 From: m0dB Date: Mon, 9 Dec 2024 17:57:01 +0100 Subject: [PATCH 062/201] use geometrynodes for waveformrender beat --- .../allshader/waveformrenderbeat.cpp | 83 +++++++++---------- .../renderers/allshader/waveformrenderbeat.h | 24 +++--- 2 files changed, 53 insertions(+), 54 deletions(-) diff --git a/src/waveform/renderers/allshader/waveformrenderbeat.cpp b/src/waveform/renderers/allshader/waveformrenderbeat.cpp index 39da6e21401..aa7de4ecfe5 100644 --- a/src/waveform/renderers/allshader/waveformrenderbeat.cpp +++ b/src/waveform/renderers/allshader/waveformrenderbeat.cpp @@ -2,34 +2,49 @@ #include +#include "rendergraph/geometry.h" +#include "rendergraph/material/unicolormaterial.h" +#include "rendergraph/vertexupdaters/vertexupdater.h" #include "skin/legacy/skincontext.h" #include "track/track.h" -#include "waveform/renderers/allshader/matrixforwidgetgeometry.h" #include "waveform/renderers/waveformwidgetrenderer.h" #include "widget/wskincolor.h" +using namespace rendergraph; + namespace allshader { WaveformRenderBeat::WaveformRenderBeat(WaveformWidgetRenderer* waveformWidget, ::WaveformRendererAbstract::PositionSource type) - : WaveformRenderer(waveformWidget), + : ::WaveformRendererAbstract(waveformWidget), m_isSlipRenderer(type == ::WaveformRendererAbstract::Slip) { + initForRectangles(0); + setUsePreprocess(true); } -void WaveformRenderBeat::initializeGL() { - m_shader.init(); +void WaveformRenderBeat::setup(const QDomNode& node, const SkinContext& skinContext) { + m_color = QColor(skinContext.selectString(node, "BeatColor")); + m_color = WSkinColor::getCorrectColor(m_color).toRgb(); } -void WaveformRenderBeat::setup(const QDomNode& node, const SkinContext& context) { - m_color = QColor(context.selectString(node, "BeatColor")); - m_color = WSkinColor::getCorrectColor(m_color).toRgb(); +void WaveformRenderBeat::draw(QPainter* painter, QPaintEvent* event) { + Q_UNUSED(painter); + Q_UNUSED(event); + DEBUG_ASSERT(false); } -void WaveformRenderBeat::paintGL() { - TrackPointer trackInfo = m_waveformRenderer->getTrackInfo(); +void WaveformRenderBeat::preprocess() { + if (!preprocessInner()) { + geometry().allocate(0); + markDirtyGeometry(); + } +} + +bool WaveformRenderBeat::preprocessInner() { + const TrackPointer trackInfo = m_waveformRenderer->getTrackInfo(); if (!trackInfo || (m_isSlipRenderer && !m_waveformRenderer->isSlipActive())) { - return; + return false; } auto positionType = m_isSlipRenderer ? ::WaveformRendererAbstract::Slip @@ -37,24 +52,21 @@ void WaveformRenderBeat::paintGL() { mixxx::BeatsPointer trackBeats = trackInfo->getBeats(); if (!trackBeats) { - return; + return false; } int alpha = m_waveformRenderer->getBeatGridAlpha(); if (alpha == 0) { - return; + return false; } const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - m_color.setAlphaF(alpha / 100.0f); const double trackSamples = m_waveformRenderer->getTrackSamples(); - if (trackSamples <= 0) { - return; + if (trackSamples <= 0.0) { + return false; } const double firstDisplayedPosition = @@ -68,7 +80,7 @@ void WaveformRenderBeat::paintGL() { lastDisplayedPosition * trackSamples); if (!startPosition.isValid() || !endPosition.isValid()) { - return; + return false; } const float rendererBreadth = m_waveformRenderer->getBreadth(); @@ -87,8 +99,9 @@ void WaveformRenderBeat::paintGL() { } const int reserved = numBeatsInRange * numVerticesPerLine; - m_vertices.clear(); - m_vertices.reserve(reserved); + geometry().allocate(reserved); + + VertexUpdater vertexUpdater{geometry().vertexDataAs()}; for (auto it = trackBeats->iteratorFrom(startPosition); it != trackBeats->cend() && *it <= endPosition; @@ -103,33 +116,17 @@ void WaveformRenderBeat::paintGL() { const float x1 = static_cast(xBeatPoint); const float x2 = x1 + 1.f; - m_vertices.addRectangle(x1, - 0.f, - x2, - m_isSlipRenderer ? rendererBreadth / 2 : rendererBreadth); + vertexUpdater.addRectangle({x1, 0.f}, + {x2, m_isSlipRenderer ? rendererBreadth / 2 : rendererBreadth}); } + markDirtyGeometry(); - DEBUG_ASSERT(reserved == m_vertices.size()); - - const int positionLocation = m_shader.positionLocation(); - const int matrixLocation = m_shader.matrixLocation(); - const int colorLocation = m_shader.colorLocation(); - - m_shader.bind(); - m_shader.enableAttributeArray(positionLocation); - - const QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, false); - - m_shader.setAttributeArray( - positionLocation, GL_FLOAT, m_vertices.constData(), 2); - - m_shader.setUniformValue(matrixLocation, matrix); - m_shader.setUniformValue(colorLocation, m_color); + DEBUG_ASSERT(reserved == vertexUpdater.index()); - glDrawArrays(GL_TRIANGLES, 0, m_vertices.size()); + material().setUniform(1, m_color); + markDirtyMaterial(); - m_shader.disableAttributeArray(positionLocation); - m_shader.release(); + return true; } } // namespace allshader diff --git a/src/waveform/renderers/allshader/waveformrenderbeat.h b/src/waveform/renderers/allshader/waveformrenderbeat.h index 9433ac6e39e..f14520d8d39 100644 --- a/src/waveform/renderers/allshader/waveformrenderbeat.h +++ b/src/waveform/renderers/allshader/waveformrenderbeat.h @@ -1,12 +1,11 @@ #pragma once #include +#include -#include "rendergraph/openglnode.h" -#include "shaders/unicolorshader.h" +#include "rendergraph/geometrynode.h" #include "util/class.h" -#include "waveform/renderers/allshader/vertexdata.h" -#include "waveform/renderers/allshader/waveformrenderer.h" +#include "waveform/renderers/waveformrendererabstract.h" class QDomNode; class SkinContext; @@ -16,23 +15,26 @@ class WaveformRenderBeat; } class allshader::WaveformRenderBeat final - : public allshader::WaveformRenderer, - public rendergraph::OpenGLNode { + : public ::WaveformRendererAbstract, + public rendergraph::GeometryNode { public: explicit WaveformRenderBeat(WaveformWidgetRenderer* waveformWidget, ::WaveformRendererAbstract::PositionSource type = ::WaveformRendererAbstract::Play); + // Pure virtual from WaveformRendererAbstract, not used + void draw(QPainter* painter, QPaintEvent* event) override final; + void setup(const QDomNode& node, const SkinContext& skinContext) override; - void paintGL() override; - void initializeGL() override; + + // Virtuals for rendergraph::Node + void preprocess() override; private: - mixxx::UnicolorShader m_shader; QColor m_color; - VertexData m_vertices; - bool m_isSlipRenderer; + bool preprocessInner(); + DISALLOW_COPY_AND_ASSIGN(WaveformRenderBeat); }; From dbe04f57adfd106a97e0e2ea9031d70a0bcc976a Mon Sep 17 00:00:00 2001 From: m0dB Date: Mon, 9 Dec 2024 20:49:34 +0100 Subject: [PATCH 063/201] use geometrynode for waveformrendererslipmode --- .../allshader/waveformrendererslipmode.cpp | 130 ++++++++++-------- .../allshader/waveformrendererslipmode.h | 22 +-- 2 files changed, 87 insertions(+), 65 deletions(-) diff --git a/src/waveform/renderers/allshader/waveformrendererslipmode.cpp b/src/waveform/renderers/allshader/waveformrendererslipmode.cpp index 8ddf0be80e5..d548455a7dc 100644 --- a/src/waveform/renderers/allshader/waveformrendererslipmode.cpp +++ b/src/waveform/renderers/allshader/waveformrendererslipmode.cpp @@ -1,9 +1,14 @@ #include "waveform/renderers/allshader/waveformrendererslipmode.h" #include +#include #include #include "control/controlproxy.h" +#include "rendergraph/geometry.h" +#include "rendergraph/material/rgbamaterial.h" +#include "rendergraph/vertexupdaters/rgbavertexupdater.h" +#include "util/colorcomponents.h" #include "waveform/renderers/waveformwidgetrenderer.h" #include "waveform/waveformwidgetfactory.h" #include "widget/wskincolor.h" @@ -11,106 +16,119 @@ namespace { constexpr int kBlinkingPeriodMillis = 1600; -// Position matrix passed to OpenGL when drawing the shader -constexpr float positionArray[] = {-1.f, -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f}; // Used as default outline color in case no value is provided in the theme - -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) -constexpr -#else -const -#endif - QColor kDefaultColor = QColor(224, 224, 224); +constexpr QColor kDefaultColor = QColor(224, 224, 224); } // anonymous namespace +using namespace rendergraph; + namespace allshader { WaveformRendererSlipMode::WaveformRendererSlipMode( WaveformWidgetRenderer* waveformWidget) - : WaveformRenderer(waveformWidget), + : ::WaveformRendererAbstract(waveformWidget), m_slipBorderTopOutlineSize(10.f), m_slipBorderBottomOutlineSize(10.f) { + initForRectangles(0); + setUsePreprocess(true); +} + +void WaveformRendererSlipMode::draw(QPainter* painter, QPaintEvent* event) { + Q_UNUSED(painter); + Q_UNUSED(event); + DEBUG_ASSERT(false); } bool WaveformRendererSlipMode::init() { m_timer.restart(); - m_pSlipMode = std::make_unique( - m_waveformRenderer->getGroup(), "slip_enabled"); + m_pSlipModeControl.reset(new ControlProxy( + m_waveformRenderer->getGroup(), "slip_enabled")); return true; } -void WaveformRendererSlipMode::setup(const QDomNode& node, const SkinContext& context) { - const QString slipModeOutlineColorName = context.selectString(node, "SlipBorderOutlineColor"); +void WaveformRendererSlipMode::setup(const QDomNode& node, const SkinContext& skinContext) { + const QString slipModeOutlineColorName = + skinContext.selectString(node, "SlipBorderOutlineColor"); if (!slipModeOutlineColorName.isNull()) { m_color = WSkinColor::getCorrectColor(QColor(slipModeOutlineColorName)); } else { m_color = kDefaultColor; } - const float slipBorderTopOutlineSize = context.selectFloat( + const float slipBorderTopOutlineSize = skinContext.selectFloat( node, "SlipBorderTopOutlineSize", m_slipBorderTopOutlineSize); if (slipBorderTopOutlineSize >= 0) { m_slipBorderTopOutlineSize = slipBorderTopOutlineSize; } - const float slipBorderBottomOutlineSize = context.selectFloat( + const float slipBorderBottomOutlineSize = skinContext.selectFloat( node, "SlipBorderBottomOutlineSize", m_slipBorderBottomOutlineSize); if (slipBorderBottomOutlineSize >= 0) { m_slipBorderBottomOutlineSize = slipBorderBottomOutlineSize; } } -void WaveformRendererSlipMode::initializeGL() { - m_shader.init(); +void WaveformRendererSlipMode::preprocess() { + if (!preprocessInner()) { + geometry().allocate(0); + markDirtyGeometry(); + } } -void WaveformRendererSlipMode::paintGL() { - if (!m_pSlipMode->toBool() || !m_waveformRenderer->isSlipActive()) { - return; +bool WaveformRendererSlipMode::preprocessInner() { + if (!m_pSlipModeControl->toBool() || !m_waveformRenderer->isSlipActive()) { + return false; } const int elapsed = m_timer.elapsed().toIntegerMillis() % kBlinkingPeriodMillis; - const double blinkIntensity = - static_cast(2 * abs(elapsed - kBlinkingPeriodMillis / 2)) / + const float blinkIntensity = + static_cast( + 2 * std::abs(elapsed - kBlinkingPeriodMillis / 2)) / kBlinkingPeriodMillis; - const double alpha = 0.25 + 0.5 * blinkIntensity; - - if (alpha != 0.0) { - QColor color = m_color; - color.setAlphaF(static_cast(alpha)); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - const int colorLocation = m_shader.colorLocation(); - const int borderLocation = m_shader.boarderLocation(); - const int positionLocation = m_shader.positionLocation(); - const int gradientLocation = m_shader.dimensionLocation(); + const float alpha = std::clamp(0.25 + 0.5 * blinkIntensity, 0.0, 1.0); + + const float posx1 = 0.f; + const float posx2 = m_waveformRenderer->getLength(); + const float posy1 = 0.f; + const float posy2 = m_waveformRenderer->getBreadth() / 2.f; + + const float sideBorderOutlineSide = m_slipBorderTopOutlineSize; + + const QVector4D bgColor{0.f, 0.f, 0.f, 1.f}; + const QVector4D borderColor{m_color.redF(), m_color.greenF(), m_color.blueF(), alpha}; + const QVector4D borderColor0{m_color.redF(), m_color.greenF(), m_color.blueF(), 0.f}; + + const int numVerticesPerLine = 6; // 2 triangles + geometry().allocate(numVerticesPerLine * 5); // border on 4 sides + bgColor filler in center + + RGBAVertexUpdater vertexUpdater{geometry().vertexDataAs()}; + + vertexUpdater.addRectangle( + {posx1, posy1}, + {posx2, posy2}, + bgColor); + + vertexUpdater.addRectangle( + {posx1, posy1}, {posx2, posy1 + m_slipBorderTopOutlineSize}, borderColor); + vertexUpdater.addRectangle({posx1, posy1 + m_slipBorderTopOutlineSize}, + {posx1 + sideBorderOutlineSide, + posy2}, + borderColor); + vertexUpdater.addRectangle({posx2 - sideBorderOutlineSide, posy1 + m_slipBorderTopOutlineSize}, + {posx2, posy2}, + borderColor); + vertexUpdater.addRectangleVGradient({posx1, posy2}, + {posx2, posy2 + m_slipBorderBottomOutlineSize}, + borderColor, + borderColor0); + markDirtyGeometry(); + markDirtyMaterial(); - m_shader.bind(); - m_shader.enableAttributeArray(positionLocation); - - m_shader.setUniformValue(colorLocation, color); - m_shader.setUniformValue(borderLocation, - m_slipBorderTopOutlineSize, - m_slipBorderBottomOutlineSize); - - m_shader.setAttributeArray( - positionLocation, GL_FLOAT, positionArray, 2); - - m_shader.setUniformValue(gradientLocation, - static_cast(m_waveformRenderer->getLength()) / 2, - static_cast(m_waveformRenderer->getBreadth()) / 2); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - m_shader.disableAttributeArray(positionLocation); - m_shader.release(); - } + return true; } } // namespace allshader diff --git a/src/waveform/renderers/allshader/waveformrendererslipmode.h b/src/waveform/renderers/allshader/waveformrendererslipmode.h index b481142797f..345beb792dc 100644 --- a/src/waveform/renderers/allshader/waveformrendererslipmode.h +++ b/src/waveform/renderers/allshader/waveformrendererslipmode.h @@ -3,11 +3,11 @@ #include #include -#include "rendergraph/openglnode.h" -#include "shaders/slipmodeshader.h" +#include "rendergraph/geometrynode.h" +#include "rendergraph/opacitynode.h" #include "util/class.h" #include "util/performancetimer.h" -#include "waveform/renderers/allshader/waveformrenderer.h" +#include "waveform/renderers/waveformrendererabstract.h" class ControlProxy; class QDomNode; @@ -18,22 +18,24 @@ class WaveformRendererSlipMode; } class allshader::WaveformRendererSlipMode final - : public allshader::WaveformRenderer, - public rendergraph::OpenGLNode { + : public ::WaveformRendererAbstract, + public rendergraph::GeometryNode { public: explicit WaveformRendererSlipMode( WaveformWidgetRenderer* waveformWidget); + // Pure virtual from WaveformRendererAbstract, not used + void draw(QPainter* painter, QPaintEvent* event) override final; + void setup(const QDomNode& node, const SkinContext& skinContext) override; bool init() override; - void initializeGL() override; - void paintGL() override; + // Virtual for rendergraph::Node + void preprocess() override; private: - mixxx::SlipModeShader m_shader; - std::unique_ptr m_pSlipMode; + std::unique_ptr m_pSlipModeControl; float m_slipBorderTopOutlineSize; float m_slipBorderBottomOutlineSize; @@ -41,5 +43,7 @@ class allshader::WaveformRendererSlipMode final QColor m_color; PerformanceTimer m_timer; + bool preprocessInner(); + DISALLOW_COPY_AND_ASSIGN(WaveformRendererSlipMode); }; From b892060679abb13933c94dc96fbe9f5341573bd0 Mon Sep 17 00:00:00 2001 From: m0dB <79429057+m0dB@users.noreply.github.com> Date: Sun, 19 Jan 2025 17:13:21 +0100 Subject: [PATCH 064/201] Update src/waveform/renderers/allshader/waveformrenderbeat.cpp using QStringLiterial Co-authored-by: Antoine Colombier <7086688+acolombier@users.noreply.github.com> --- src/waveform/renderers/allshader/waveformrenderbeat.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/waveform/renderers/allshader/waveformrenderbeat.cpp b/src/waveform/renderers/allshader/waveformrenderbeat.cpp index aa7de4ecfe5..131a9a8886f 100644 --- a/src/waveform/renderers/allshader/waveformrenderbeat.cpp +++ b/src/waveform/renderers/allshader/waveformrenderbeat.cpp @@ -23,7 +23,7 @@ WaveformRenderBeat::WaveformRenderBeat(WaveformWidgetRenderer* waveformWidget, } void WaveformRenderBeat::setup(const QDomNode& node, const SkinContext& skinContext) { - m_color = QColor(skinContext.selectString(node, "BeatColor")); + m_color = QColor(skinContext.selectString(node, QStringLiteral("BeatColor"))); m_color = WSkinColor::getCorrectColor(m_color).toRgb(); } From 0f689719619577e172c00531dfc0427400fcd516 Mon Sep 17 00:00:00 2001 From: m0dB <79429057+m0dB@users.noreply.github.com> Date: Sun, 19 Jan 2025 17:29:05 +0100 Subject: [PATCH 065/201] Update src/waveform/renderers/allshader/waveformrendererslipmode.cpp float conversion Co-authored-by: Antoine Colombier <7086688+acolombier@users.noreply.github.com> --- src/waveform/renderers/allshader/waveformrendererslipmode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/waveform/renderers/allshader/waveformrendererslipmode.cpp b/src/waveform/renderers/allshader/waveformrendererslipmode.cpp index d548455a7dc..a8d6b6df47c 100644 --- a/src/waveform/renderers/allshader/waveformrendererslipmode.cpp +++ b/src/waveform/renderers/allshader/waveformrendererslipmode.cpp @@ -89,7 +89,7 @@ bool WaveformRendererSlipMode::preprocessInner() { 2 * std::abs(elapsed - kBlinkingPeriodMillis / 2)) / kBlinkingPeriodMillis; - const float alpha = std::clamp(0.25 + 0.5 * blinkIntensity, 0.0, 1.0); + const float alpha = std::clamp(0.25f + 0.5f * blinkIntensity, 0.0f, 1.0f); const float posx1 = 0.f; const float posx2 = m_waveformRenderer->getLength(); From a5e6d3821b3320d2361d4bbfa3c4668c8ae6f951 Mon Sep 17 00:00:00 2001 From: m0dB Date: Sun, 19 Jan 2025 17:32:04 +0100 Subject: [PATCH 066/201] qstringliteral --- .../renderers/allshader/waveformrendererslipmode.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/waveform/renderers/allshader/waveformrendererslipmode.cpp b/src/waveform/renderers/allshader/waveformrendererslipmode.cpp index a8d6b6df47c..af122d2d5bf 100644 --- a/src/waveform/renderers/allshader/waveformrendererslipmode.cpp +++ b/src/waveform/renderers/allshader/waveformrendererslipmode.cpp @@ -45,26 +45,27 @@ bool WaveformRendererSlipMode::init() { m_timer.restart(); m_pSlipModeControl.reset(new ControlProxy( - m_waveformRenderer->getGroup(), "slip_enabled")); + m_waveformRenderer->getGroup(), QStringLiteral("slip_enabled"))); return true; } void WaveformRendererSlipMode::setup(const QDomNode& node, const SkinContext& skinContext) { const QString slipModeOutlineColorName = - skinContext.selectString(node, "SlipBorderOutlineColor"); + skinContext.selectString(node, QStringLiteral("SlipBorderOutlineColor")); + ; if (!slipModeOutlineColorName.isNull()) { m_color = WSkinColor::getCorrectColor(QColor(slipModeOutlineColorName)); } else { m_color = kDefaultColor; } const float slipBorderTopOutlineSize = skinContext.selectFloat( - node, "SlipBorderTopOutlineSize", m_slipBorderTopOutlineSize); + node, QStringLiteral("SlipBorderTopOutlineSize"), m_slipBorderTopOutlineSize); if (slipBorderTopOutlineSize >= 0) { m_slipBorderTopOutlineSize = slipBorderTopOutlineSize; } const float slipBorderBottomOutlineSize = skinContext.selectFloat( - node, "SlipBorderBottomOutlineSize", m_slipBorderBottomOutlineSize); + node, QStringLiteral("SlipBorderBottomOutlineSize"), m_slipBorderBottomOutlineSize); if (slipBorderBottomOutlineSize >= 0) { m_slipBorderBottomOutlineSize = slipBorderBottomOutlineSize; } From d80b8288da389a316218642cae63eb6c740a07c9 Mon Sep 17 00:00:00 2001 From: Falk Ebert Date: Sun, 19 Jan 2025 19:30:35 +0100 Subject: [PATCH 067/201] Fix RUBBERBANDV3 macro in rubberbandwrapper.cpp --- src/engine/bufferscalers/rubberbandwrapper.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/engine/bufferscalers/rubberbandwrapper.cpp b/src/engine/bufferscalers/rubberbandwrapper.cpp index 0f69d3ca487..7c5e119ed1c 100644 --- a/src/engine/bufferscalers/rubberbandwrapper.cpp +++ b/src/engine/bufferscalers/rubberbandwrapper.cpp @@ -7,7 +7,8 @@ using RubberBand::RubberBandStretcher; -#define RUBBERBANDV3 (RUBBERBAND_API_MAJOR_VERSION >= 2 && RUBBERBAND_API_MINOR_VERSION >= 7) +#define RUBBERBANDV3 (RUBBERBAND_API_MAJOR_VERSION >= 3 || \ + (RUBBERBAND_API_MAJOR_VERSION == 2 && RUBBERBAND_API_MINOR_VERSION >= 7)) namespace { From 30da7d9a7ddfba18d4f07797d7a0dae7085cff2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 19 Jan 2025 21:16:06 +0100 Subject: [PATCH 068/201] Use parented_ptr for menus --- src/widget/wtrackmenu.cpp | 36 +++++++++++++++++++++--------------- src/widget/wtrackmenu.h | 24 ++++++++++++------------ 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/src/widget/wtrackmenu.cpp b/src/widget/wtrackmenu.cpp index e4c9476953c..bf8ca07b66a 100644 --- a/src/widget/wtrackmenu.cpp +++ b/src/widget/wtrackmenu.cpp @@ -162,58 +162,64 @@ void WTrackMenu::popup(const QPoint& pos, QAction* at) { void WTrackMenu::createMenus() { if (featureIsEnabled(Feature::LoadTo)) { - m_pLoadToMenu = new QMenu(this); + m_pLoadToMenu = make_parented(this); m_pLoadToMenu->setTitle(tr("Load to")); - m_pDeckMenu = new QMenu(m_pLoadToMenu); + m_pDeckMenu = make_parented(m_pLoadToMenu); m_pDeckMenu->setTitle(tr("Deck")); - m_pSamplerMenu = new QMenu(m_pLoadToMenu); + m_pSamplerMenu = make_parented(m_pLoadToMenu); m_pSamplerMenu->setTitle(tr("Sampler")); } if (featureIsEnabled(Feature::Playlist)) { - m_pPlaylistMenu = new QMenu(this); + m_pPlaylistMenu = make_parented(this); m_pPlaylistMenu->setTitle(tr("Add to Playlist")); connect(m_pPlaylistMenu, &QMenu::aboutToShow, this, &WTrackMenu::slotPopulatePlaylistMenu); } if (featureIsEnabled(Feature::Crate)) { - m_pCrateMenu = new QMenu(this); + m_pCrateMenu = make_parented(this); m_pCrateMenu->setTitle(tr("Crates")); m_pCrateMenu->setObjectName("CratesMenu"); connect(m_pCrateMenu, &QMenu::aboutToShow, this, &WTrackMenu::slotPopulateCrateMenu); } if (featureIsEnabled(Feature::Metadata)) { - m_pMetadataMenu = new QMenu(this); + m_pMetadataMenu = make_parented(this); m_pMetadataMenu->setTitle(tr("Metadata")); - m_pMetadataUpdateExternalCollectionsMenu = new QMenu(m_pMetadataMenu); + m_pMetadataUpdateExternalCollectionsMenu = make_parented(m_pMetadataMenu); m_pMetadataUpdateExternalCollectionsMenu->setTitle(tr("Update external collections")); - m_pCoverMenu = new WCoverArtMenu(m_pMetadataMenu); + m_pCoverMenu = make_parented(m_pMetadataMenu); m_pCoverMenu->setTitle(tr("Cover Art")); - connect(m_pCoverMenu, &WCoverArtMenu::coverInfoSelected, this, &WTrackMenu::slotCoverInfoSelected); - connect(m_pCoverMenu, &WCoverArtMenu::reloadCoverArt, this, &WTrackMenu::slotReloadCoverArt); + connect(m_pCoverMenu.get(), + &WCoverArtMenu::coverInfoSelected, + this, + &WTrackMenu::slotCoverInfoSelected); + connect(m_pCoverMenu.get(), + &WCoverArtMenu::reloadCoverArt, + this, + &WTrackMenu::slotReloadCoverArt); } if (featureIsEnabled(Feature::BPM)) { - m_pBPMMenu = new QMenu(this); + m_pBPMMenu = make_parented(this); m_pBPMMenu->setTitle(tr("Adjust BPM")); } if (featureIsEnabled(Feature::Color)) { - m_pColorMenu = new QMenu(this); + m_pColorMenu = make_parented(this); m_pColorMenu->setTitle(tr("Select Color")); } if (featureIsEnabled(Feature::Reset)) { - m_pClearMetadataMenu = new QMenu(this); + m_pClearMetadataMenu = make_parented(this); //: Reset metadata in right click track context menu in library m_pClearMetadataMenu->setTitle(tr("Clear")); } if (featureIsEnabled(Feature::Analyze)) { - m_pAnalyzeMenu = new QMenu(this); + m_pAnalyzeMenu = make_parented(this); m_pAnalyzeMenu->setTitle(tr("Analyze")); } @@ -274,7 +280,7 @@ void WTrackMenu::createMenus() { // permanently delete files, put the action into a submenu for safety // reasons and display different messages in the delete dialogs. #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) - m_pRemoveFromDiskMenu = new QMenu(this); + m_pRemoveFromDiskMenu = make_parented(this); m_pRemoveFromDiskMenu->setTitle(tr("Delete Track Files")); #endif } diff --git a/src/widget/wtrackmenu.h b/src/widget/wtrackmenu.h index 210b9aad46b..06cf01a4b29 100644 --- a/src/widget/wtrackmenu.h +++ b/src/widget/wtrackmenu.h @@ -255,18 +255,18 @@ class WTrackMenu : public QMenu { QString m_deckGroup; // Submenus - QMenu* m_pLoadToMenu{}; - QMenu* m_pDeckMenu{}; - QMenu* m_pSamplerMenu{}; - QMenu* m_pPlaylistMenu{}; - QMenu* m_pCrateMenu{}; - QMenu* m_pMetadataMenu{}; - QMenu* m_pMetadataUpdateExternalCollectionsMenu{}; - QMenu* m_pClearMetadataMenu{}; - QMenu* m_pAnalyzeMenu{}; - QMenu* m_pBPMMenu{}; - QMenu* m_pColorMenu{}; - WCoverArtMenu* m_pCoverMenu{}; + parented_ptr m_pLoadToMenu; + parented_ptr m_pDeckMenu; + parented_ptr m_pSamplerMenu; + parented_ptr m_pPlaylistMenu; + parented_ptr m_pCrateMenu; + parented_ptr m_pMetadataMenu; + parented_ptr m_pMetadataUpdateExternalCollectionsMenu; + parented_ptr m_pClearMetadataMenu; + parented_ptr m_pAnalyzeMenu; + parented_ptr m_pBPMMenu; + parented_ptr m_pColorMenu; + parented_ptr m_pCoverMenu; parented_ptr m_pSearchRelatedMenu; parented_ptr m_pFindOnWebMenu; #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) From 5ffa310e2042f4354d2db5a8f2e88e9b02da4e89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 19 Jan 2025 21:40:56 +0100 Subject: [PATCH 069/201] Use parented_ptr for actions --- src/widget/wtrackmenu.cpp | 99 ++++++++++++++++++++------------------- src/widget/wtrackmenu.h | 94 ++++++++++++++++++------------------- 2 files changed, 97 insertions(+), 96 deletions(-) diff --git a/src/widget/wtrackmenu.cpp b/src/widget/wtrackmenu.cpp index bf8ca07b66a..5c845ac960c 100644 --- a/src/widget/wtrackmenu.cpp +++ b/src/widget/wtrackmenu.cpp @@ -293,18 +293,18 @@ void WTrackMenu::createActions() { kHideRemoveShortcutKey); if (featureIsEnabled(Feature::AutoDJ)) { - m_pAutoDJBottomAct = new QAction(tr("Add to Auto DJ Queue (bottom)"), this); + m_pAutoDJBottomAct = make_parented(tr("Add to Auto DJ Queue (bottom)"), this); connect(m_pAutoDJBottomAct, &QAction::triggered, this, &WTrackMenu::slotAddToAutoDJBottom); - m_pAutoDJTopAct = new QAction(tr("Add to Auto DJ Queue (top)"), this); + m_pAutoDJTopAct = make_parented(tr("Add to Auto DJ Queue (top)"), this); connect(m_pAutoDJTopAct, &QAction::triggered, this, &WTrackMenu::slotAddToAutoDJTop); - m_pAutoDJReplaceAct = new QAction(tr("Add to Auto DJ Queue (replace)"), this); + m_pAutoDJReplaceAct = make_parented(tr("Add to Auto DJ Queue (replace)"), this); connect(m_pAutoDJReplaceAct, &QAction::triggered, this, &WTrackMenu::slotAddToAutoDJReplace); } if (featureIsEnabled(Feature::LoadTo)) { - m_pAddToPreviewDeck = new QAction(tr("Preview Deck"), m_pLoadToMenu); + m_pAddToPreviewDeck = make_parented(tr("Preview Deck"), m_pLoadToMenu); // currently there is only one preview deck so just map it here. QString previewDeckGroup = PlayerManager::groupForPreviewDeck(0); connect(m_pAddToPreviewDeck, &QAction::triggered, this, [this, previewDeckGroup] { loadSelectionToGroup(previewDeckGroup); }); @@ -313,21 +313,21 @@ void WTrackMenu::createActions() { if (featureIsEnabled(Feature::Remove)) { // Keyboard shortcuts are set here just to have them displayed in the menu. // Actual keypress is handled in WTrackTableView::keyPressEvent(). - m_pRemoveAct = new QAction(tr("Remove"), this); + m_pRemoveAct = make_parented(tr("Remove"), this); m_pRemoveAct->setShortcut(hideRemoveKeySequence); connect(m_pRemoveAct, &QAction::triggered, this, &WTrackMenu::slotRemove); - m_pRemovePlaylistAct = new QAction(tr("Remove from Playlist"), this); + m_pRemovePlaylistAct = make_parented(tr("Remove from Playlist"), this); m_pRemovePlaylistAct->setShortcut(hideRemoveKeySequence); connect(m_pRemovePlaylistAct, &QAction::triggered, this, &WTrackMenu::slotRemove); - m_pRemoveCrateAct = new QAction(tr("Remove from Crate"), this); + m_pRemoveCrateAct = make_parented(tr("Remove from Crate"), this); m_pRemoveCrateAct->setShortcut(hideRemoveKeySequence); connect(m_pRemoveCrateAct, &QAction::triggered, this, &WTrackMenu::slotRemove); } if (featureIsEnabled(Feature::HideUnhidePurge)) { - m_pHideAct = new QAction(tr("Hide from Library"), this); + m_pHideAct = make_parented(tr("Hide from Library"), this); // This is just for having the shortcut displayed next to the action in the menu. // The actual keypress is handled in WTrackTableView::keyPressEvent(). // Note: don't show the hotkey for more than one action @@ -336,18 +336,19 @@ void WTrackMenu::createActions() { } connect(m_pHideAct, &QAction::triggered, this, &WTrackMenu::slotHide); - m_pUnhideAct = new QAction(tr("Unhide from Library"), this); + m_pUnhideAct = make_parented(tr("Unhide from Library"), this); connect(m_pUnhideAct, &QAction::triggered, this, &WTrackMenu::slotUnhide); - m_pPurgeAct = new QAction(tr("Purge from Library"), this); + m_pPurgeAct = make_parented(tr("Purge from Library"), this); connect(m_pPurgeAct, &QAction::triggered, this, &WTrackMenu::slotPurge); } if (featureIsEnabled(Feature::RemoveFromDisk)) { #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) - m_pRemoveFromDiskAct = new QAction(tr("Move Track File(s) to Trash"), this); + m_pRemoveFromDiskAct = make_parented(tr("Move Track File(s) to Trash"), this); #else - m_pRemoveFromDiskAct = new QAction(tr("Delete Files from Disk"), m_pRemoveFromDiskMenu); + m_pRemoveFromDiskAct = make_parented( + tr("Delete Files from Disk"), m_pRemoveFromDiskMenu); #endif connect(m_pRemoveFromDiskAct, &QAction::triggered, @@ -356,7 +357,7 @@ void WTrackMenu::createActions() { } if (featureIsEnabled(Feature::Metadata)) { - m_pStarRatingAction = new WStarRatingAction(this); + m_pStarRatingAction = make_parented(this); m_pStarRatingAction->setObjectName("RatingAction"); connect(m_pStarRatingAction, &WStarRatingAction::ratingSet, @@ -365,7 +366,7 @@ void WTrackMenu::createActions() { } if (featureIsEnabled(Feature::Properties)) { - m_pPropertiesAct = new QAction(tr("Properties"), this); + m_pPropertiesAct = make_parented(tr("Properties"), this); // This is just for having the shortcut displayed next to the action // when the menu is invoked from the tracks table. // The keypress is caught in WTrackTableView::keyPressEvent @@ -380,32 +381,32 @@ void WTrackMenu::createActions() { } if (featureIsEnabled(Feature::FileBrowser)) { - m_pFileBrowserAct = new QAction(tr("Open in File Browser"), this); + m_pFileBrowserAct = make_parented(tr("Open in File Browser"), this); connect(m_pFileBrowserAct, &QAction::triggered, this, &WTrackMenu::slotOpenInFileBrowser); } if (featureIsEnabled(Feature::SelectInLibrary)) { - m_pSelectInLibraryAct = new QAction(tr("Select in Library"), this); + m_pSelectInLibraryAct = make_parented(tr("Select in Library"), this); connect(m_pSelectInLibraryAct, &QAction::triggered, this, &WTrackMenu::slotSelectInLibrary); } if (featureIsEnabled(Feature::Metadata)) { m_pImportMetadataFromFileAct = - new QAction(tr("Import From File Tags"), m_pMetadataMenu); + make_parented(tr("Import From File Tags"), m_pMetadataMenu); connect(m_pImportMetadataFromFileAct, &QAction::triggered, this, &WTrackMenu::slotImportMetadataFromFileTags); m_pImportMetadataFromMusicBrainzAct = - new QAction(tr("Import From MusicBrainz"), m_pMetadataMenu); + make_parented(tr("Import From MusicBrainz"), m_pMetadataMenu); connect(m_pImportMetadataFromMusicBrainzAct, &QAction::triggered, this, &WTrackMenu::slotShowDlgTagFetcher); m_pExportMetadataAct = - new QAction(tr("Export To File Tags"), m_pMetadataMenu); + make_parented(tr("Export To File Tags"), m_pMetadataMenu); connect(m_pExportMetadataAct, &QAction::triggered, this, @@ -433,64 +434,64 @@ void WTrackMenu::createActions() { if (featureIsEnabled(Feature::Reset)) { // Clear metadata actions - m_pClearBeatsAction = new QAction(tr("BPM and Beatgrid"), m_pClearMetadataMenu); + m_pClearBeatsAction = make_parented(tr("BPM and Beatgrid"), m_pClearMetadataMenu); connect(m_pClearBeatsAction, &QAction::triggered, this, &WTrackMenu::slotClearBeats); - m_pClearPlayCountAction = new QAction(tr("Play Count"), m_pClearMetadataMenu); + m_pClearPlayCountAction = make_parented(tr("Play Count"), m_pClearMetadataMenu); connect(m_pClearPlayCountAction, &QAction::triggered, this, &WTrackMenu::slotClearPlayCount); - m_pClearRatingAction = new QAction(tr("Rating"), m_pClearMetadataMenu); + m_pClearRatingAction = make_parented(tr("Rating"), m_pClearMetadataMenu); connect(m_pClearRatingAction, &QAction::triggered, this, &WTrackMenu::slotClearRating); - m_pClearMainCueAction = new QAction(tr("Cue Point"), m_pClearMetadataMenu); + m_pClearMainCueAction = make_parented(tr("Cue Point"), m_pClearMetadataMenu); connect(m_pClearMainCueAction, &QAction::triggered, this, &WTrackMenu::slotResetMainCue); - m_pClearHotCuesAction = new QAction(tr("Hotcues"), m_pClearMetadataMenu); + m_pClearHotCuesAction = make_parented(tr("Hotcues"), m_pClearMetadataMenu); connect(m_pClearHotCuesAction, &QAction::triggered, this, &WTrackMenu::slotClearHotCues); - m_pClearIntroCueAction = new QAction(tr("Intro"), m_pClearMetadataMenu); + m_pClearIntroCueAction = make_parented(tr("Intro"), m_pClearMetadataMenu); connect(m_pClearIntroCueAction, &QAction::triggered, this, &WTrackMenu::slotResetIntroCue); - m_pClearOutroCueAction = new QAction(tr("Outro"), m_pClearMetadataMenu); + m_pClearOutroCueAction = make_parented(tr("Outro"), m_pClearMetadataMenu); connect(m_pClearOutroCueAction, &QAction::triggered, this, &WTrackMenu::slotResetOutroCue); - m_pClearLoopsAction = new QAction(tr("Loops"), m_pClearMetadataMenu); + m_pClearLoopsAction = make_parented(tr("Loops"), m_pClearMetadataMenu); connect(m_pClearLoopsAction, &QAction::triggered, this, &WTrackMenu::slotClearLoops); - m_pClearKeyAction = new QAction(tr("Key"), m_pClearMetadataMenu); + m_pClearKeyAction = make_parented(tr("Key"), m_pClearMetadataMenu); connect(m_pClearKeyAction, &QAction::triggered, this, &WTrackMenu::slotClearKey); - m_pClearReplayGainAction = new QAction(tr("ReplayGain"), m_pClearMetadataMenu); + m_pClearReplayGainAction = make_parented(tr("ReplayGain"), m_pClearMetadataMenu); connect(m_pClearReplayGainAction, &QAction::triggered, this, &WTrackMenu::slotClearReplayGain); - m_pClearWaveformAction = new QAction(tr("Waveform"), m_pClearMetadataMenu); + m_pClearWaveformAction = make_parented(tr("Waveform"), m_pClearMetadataMenu); connect(m_pClearWaveformAction, &QAction::triggered, this, &WTrackMenu::slotClearWaveform); - m_pClearCommentAction = new QAction(tr("Comment"), m_pClearMetadataMenu); + m_pClearCommentAction = make_parented(tr("Comment"), m_pClearMetadataMenu); connect(m_pClearCommentAction, &QAction::triggered, this, &WTrackMenu::slotClearComment); - m_pClearAllMetadataAction = new QAction(tr("All"), m_pClearMetadataMenu); + m_pClearAllMetadataAction = make_parented(tr("All"), m_pClearMetadataMenu); connect(m_pClearAllMetadataAction, &QAction::triggered, this, &WTrackMenu::slotClearAllMetadata); } if (featureIsEnabled(Feature::BPM)) { - m_pBpmLockAction = new QAction(tr("Lock BPM"), m_pBPMMenu); - m_pBpmUnlockAction = new QAction(tr("Unlock BPM"), m_pBPMMenu); + m_pBpmLockAction = make_parented(tr("Lock BPM"), m_pBPMMenu); + m_pBpmUnlockAction = make_parented(tr("Unlock BPM"), m_pBPMMenu); connect(m_pBpmLockAction, &QAction::triggered, this, &WTrackMenu::slotLockBpm); connect(m_pBpmUnlockAction, &QAction::triggered, this, &WTrackMenu::slotUnlockBpm); //BPM edit actions - m_pBpmDoubleAction = new QAction(tr("Double BPM"), m_pBPMMenu); + m_pBpmDoubleAction = make_parented(tr("Double BPM"), m_pBPMMenu); storeActionTextAndScaleInProperties(m_pBpmDoubleAction, 2.0); - m_pBpmHalveAction = new QAction(tr("Halve BPM"), m_pBPMMenu); + m_pBpmHalveAction = make_parented(tr("Halve BPM"), m_pBPMMenu); storeActionTextAndScaleInProperties(m_pBpmHalveAction, 0.5); - m_pBpmTwoThirdsAction = new QAction(tr("2/3 BPM"), m_pBPMMenu); + m_pBpmTwoThirdsAction = make_parented(tr("2/3 BPM"), m_pBPMMenu); storeActionTextAndScaleInProperties(m_pBpmTwoThirdsAction, 2.0 / 3.0); - m_pBpmThreeFourthsAction = new QAction(tr("3/4 BPM"), m_pBPMMenu); + m_pBpmThreeFourthsAction = make_parented(tr("3/4 BPM"), m_pBPMMenu); storeActionTextAndScaleInProperties(m_pBpmThreeFourthsAction, 3.0 / 4.0); - m_pBpmFourThirdsAction = new QAction(tr("4/3 BPM"), m_pBPMMenu); + m_pBpmFourThirdsAction = make_parented(tr("4/3 BPM"), m_pBPMMenu); storeActionTextAndScaleInProperties(m_pBpmFourThirdsAction, 4.0 / 3.0); - m_pBpmThreeHalvesAction = new QAction(tr("3/2 BPM"), m_pBPMMenu); + m_pBpmThreeHalvesAction = make_parented(tr("3/2 BPM"), m_pBPMMenu); storeActionTextAndScaleInProperties(m_pBpmThreeHalvesAction, 3.0 / 2.0); connect(m_pBpmDoubleAction, &QAction::triggered, this, [this] { @@ -512,13 +513,13 @@ void WTrackMenu::createActions() { slotScaleBpm(mixxx::Beats::BpmScale::ThreeHalves); }); - m_pBpmResetAction = new QAction(tr("Clear BPM and Beatgrid"), m_pBPMMenu); + m_pBpmResetAction = make_parented(tr("Clear BPM and Beatgrid"), m_pBPMMenu); connect(m_pBpmResetAction, &QAction::triggered, this, &WTrackMenu::slotClearBeats); - m_pBpmUndoAction = new QAction(tr("Undo last BPM/beats change"), m_pBPMMenu); + m_pBpmUndoAction = make_parented(tr("Undo last BPM/beats change"), m_pBPMMenu); connect(m_pBpmUndoAction, &QAction::triggered, this, @@ -526,19 +527,19 @@ void WTrackMenu::createActions() { } if (featureIsEnabled(Feature::Analyze)) { - m_pAnalyzeAction = new QAction(tr("Analyze"), this); + m_pAnalyzeAction = make_parented(tr("Analyze"), this); connect(m_pAnalyzeAction, &QAction::triggered, this, &WTrackMenu::slotAnalyze); - m_pReanalyzeAction = new QAction(tr("Reanalyze"), this); + m_pReanalyzeAction = make_parented(tr("Reanalyze"), this); connect(m_pReanalyzeAction, &QAction::triggered, this, &WTrackMenu::slotReanalyze); - m_pReanalyzeConstBpmAction = new QAction(tr("Reanalyze (constant BPM)"), this); + m_pReanalyzeConstBpmAction = make_parented(tr("Reanalyze (constant BPM)"), this); connect(m_pReanalyzeConstBpmAction, &QAction::triggered, this, &WTrackMenu::slotReanalyzeWithFixedTempo); - m_pReanalyzeVarBpmAction = new QAction(tr("Reanalyze (variable BPM)"), this); + m_pReanalyzeVarBpmAction = make_parented(tr("Reanalyze (variable BPM)"), this); connect(m_pReanalyzeVarBpmAction, &QAction::triggered, this, @@ -549,8 +550,8 @@ void WTrackMenu::createActions() { // for WTrackmenu instantiated by WTrackProperty and other deck widgets, thus // don't create it if a track model is set. if (!m_pTrackModel && featureIsEnabled(Feature::UpdateReplayGainFromPregain)) { - m_pUpdateReplayGainAct = - new QAction(tr("Update ReplayGain from Deck Gain"), m_pClearMetadataMenu); + m_pUpdateReplayGainAct = make_parented( + tr("Update ReplayGain from Deck Gain"), m_pClearMetadataMenu); connect(m_pUpdateReplayGainAct, &QAction::triggered, this, @@ -559,7 +560,7 @@ void WTrackMenu::createActions() { if (featureIsEnabled(Feature::Color)) { ColorPaletteSettings colorPaletteSettings(m_pConfig); - m_pColorPickerAction = new WColorPickerAction(WColorPicker::Option::AllowNoColor, + m_pColorPickerAction = make_parented(WColorPicker::Option::AllowNoColor, colorPaletteSettings.getTrackColorPalette(), m_pColorMenu); m_pColorPickerAction->setObjectName("TrackColorPickerAction"); diff --git a/src/widget/wtrackmenu.h b/src/widget/wtrackmenu.h index 06cf01a4b29..076e3bf92e1 100644 --- a/src/widget/wtrackmenu.h +++ b/src/widget/wtrackmenu.h @@ -274,77 +274,77 @@ class WTrackMenu : public QMenu { #endif // Update ReplayGain from Track - QAction* m_pUpdateReplayGainAct{}; + parented_ptr m_pUpdateReplayGainAct; // Reload Track Metadata Action: - QAction* m_pImportMetadataFromFileAct{}; - QAction* m_pImportMetadataFromMusicBrainzAct{}; + parented_ptr m_pImportMetadataFromFileAct; + parented_ptr m_pImportMetadataFromMusicBrainzAct; // Save Track Metadata Action: - QAction* m_pExportMetadataAct{}; + parented_ptr m_pExportMetadataAct; // Load Track to PreviewDeck - QAction* m_pAddToPreviewDeck{}; + parented_ptr m_pAddToPreviewDeck; // Send to Auto-DJ Action - QAction* m_pAutoDJBottomAct{}; - QAction* m_pAutoDJTopAct{}; - QAction* m_pAutoDJReplaceAct{}; + parented_ptr m_pAutoDJBottomAct; + parented_ptr m_pAutoDJTopAct; + parented_ptr m_pAutoDJReplaceAct; // Remove from table - QAction* m_pRemoveAct{}; - QAction* m_pRemovePlaylistAct{}; - QAction* m_pRemoveCrateAct{}; - QAction* m_pHideAct{}; - QAction* m_pUnhideAct{}; - QAction* m_pPurgeAct{}; - QAction* m_pRemoveFromDiskAct{}; + parented_ptr m_pRemoveAct; + parented_ptr m_pRemovePlaylistAct; + parented_ptr m_pRemoveCrateAct; + parented_ptr m_pHideAct; + parented_ptr m_pUnhideAct; + parented_ptr m_pPurgeAct; + parented_ptr m_pRemoveFromDiskAct; // Show track-editor action - QAction* m_pPropertiesAct{}; + parented_ptr m_pPropertiesAct; // Open file in default file browser - QAction* m_pFileBrowserAct{}; + parented_ptr m_pFileBrowserAct; // Select track in library - QAction* m_pSelectInLibraryAct{}; + parented_ptr m_pSelectInLibraryAct; // BPM feature - QAction* m_pBpmLockAction{}; - QAction* m_pBpmUnlockAction{}; - QAction* m_pBpmDoubleAction{}; - QAction* m_pBpmHalveAction{}; - QAction* m_pBpmTwoThirdsAction{}; - QAction* m_pBpmThreeFourthsAction{}; - QAction* m_pBpmFourThirdsAction{}; - QAction* m_pBpmThreeHalvesAction{}; - QAction* m_pBpmResetAction{}; - QAction* m_pBpmUndoAction{}; + parented_ptr m_pBpmLockAction; + parented_ptr m_pBpmUnlockAction; + parented_ptr m_pBpmDoubleAction; + parented_ptr m_pBpmHalveAction; + parented_ptr m_pBpmTwoThirdsAction; + parented_ptr m_pBpmThreeFourthsAction; + parented_ptr m_pBpmFourThirdsAction; + parented_ptr m_pBpmThreeHalvesAction; + parented_ptr m_pBpmResetAction; + parented_ptr m_pBpmUndoAction; // Track rating and color - WStarRatingAction* m_pStarRatingAction{}; - WColorPickerAction* m_pColorPickerAction{}; + parented_ptr m_pStarRatingAction; + parented_ptr m_pColorPickerAction; // Analysis actions - QAction* m_pAnalyzeAction{}; - QAction* m_pReanalyzeAction{}; - QAction* m_pReanalyzeConstBpmAction{}; - QAction* m_pReanalyzeVarBpmAction{}; + parented_ptr m_pAnalyzeAction; + parented_ptr m_pReanalyzeAction; + parented_ptr m_pReanalyzeConstBpmAction; + parented_ptr m_pReanalyzeVarBpmAction; // Clear track metadata actions - QAction* m_pClearBeatsAction{}; - QAction* m_pClearPlayCountAction{}; - QAction* m_pClearRatingAction{}; - QAction* m_pClearMainCueAction{}; - QAction* m_pClearHotCuesAction{}; - QAction* m_pClearIntroCueAction{}; - QAction* m_pClearOutroCueAction{}; - QAction* m_pClearLoopsAction{}; - QAction* m_pClearWaveformAction{}; - QAction* m_pClearCommentAction{}; - QAction* m_pClearKeyAction{}; - QAction* m_pClearReplayGainAction{}; - QAction* m_pClearAllMetadataAction{}; + parented_ptr m_pClearBeatsAction; + parented_ptr m_pClearPlayCountAction; + parented_ptr m_pClearRatingAction; + parented_ptr m_pClearMainCueAction; + parented_ptr m_pClearHotCuesAction; + parented_ptr m_pClearIntroCueAction; + parented_ptr m_pClearOutroCueAction; + parented_ptr m_pClearLoopsAction; + parented_ptr m_pClearWaveformAction; + parented_ptr m_pClearCommentAction; + parented_ptr m_pClearKeyAction; + parented_ptr m_pClearReplayGainAction; + parented_ptr m_pClearAllMetadataAction; const UserSettingsPointer m_pConfig; Library* const m_pLibrary; From fee16b981b6fcdb15cdddd55736628b1a462b565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 19 Jan 2025 22:26:41 +0100 Subject: [PATCH 070/201] Use parented_ptr in del dialogs to fix memory leaks --- src/widget/wtrackmenu.cpp | 72 ++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/src/widget/wtrackmenu.cpp b/src/widget/wtrackmenu.cpp index 5c845ac960c..8183846dc81 100644 --- a/src/widget/wtrackmenu.cpp +++ b/src/widget/wtrackmenu.cpp @@ -1570,7 +1570,7 @@ void WTrackMenu::slotPopulateCrateMenu() { #endif } m_pCrateMenu->addSeparator(); - QAction* newCrateAction = new QAction(tr("Add to New Crate"), m_pCrateMenu); + auto newCrateAction = make_parented(tr("Add to New Crate"), m_pCrateMenu); m_pCrateMenu->addAction(newCrateAction); connect(newCrateAction, &QAction::triggered, this, &WTrackMenu::addSelectionToNewCrate); m_bCrateMenuLoaded = true; @@ -2316,16 +2316,18 @@ void WTrackMenu::slotRemoveFromDisk() { } { + QDialog dlgDelConfirm; + // Prepare the delete confirmation dialog. // First, create the list view for the files to be deleted // NOTE(ronso0) We could also make this a table to allow showing // artist and title if file names don't suffice to identify tracks. - QListWidget* delListWidget = new QListWidget(); - delListWidget->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, + auto pDelListWidget = make_parented(&dlgDelConfirm); + pDelListWidget->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::MinimumExpanding)); - delListWidget->setFocusPolicy(Qt::ClickFocus); - delListWidget->addItems(locations); - mixxx::widgethelper::growListWidget(*delListWidget, *this); + pDelListWidget->setFocusPolicy(Qt::ClickFocus); + pDelListWidget->addItems(locations); + mixxx::widgethelper::growListWidget(*pDelListWidget, *this); QString delWarningText; if (m_pTrackModel) { @@ -2359,17 +2361,17 @@ void WTrackMenu::slotRemoveFromDisk() { } // Setup the warning message and dialog buttons - QLabel* delWarning = new QLabel(); - delWarning->setText(delWarningText); - delWarning->setTextFormat(Qt::RichText); - delWarning->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, + auto pDelWarning = make_parented(&dlgDelConfirm); + pDelWarning->setText(delWarningText); + pDelWarning->setTextFormat(Qt::RichText); + pDelWarning->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); - QDialogButtonBox* delButtons = new QDialogButtonBox(); - QPushButton* cancelBtn = delButtons->addButton( + auto pDelButtons = make_parented(&dlgDelConfirm); + QPushButton* cancelBtn = pDelButtons->addButton( tr("Cancel"), QDialogButtonBox::RejectRole); - QPushButton* deleteBtn = delButtons->addButton( + QPushButton* deleteBtn = pDelButtons->addButton( #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) tr("Delete Files"), #else @@ -2379,13 +2381,11 @@ void WTrackMenu::slotRemoveFromDisk() { cancelBtn->setDefault(true); // Populate the main layout - QVBoxLayout* delLayout = new QVBoxLayout(); - delLayout->addWidget(delListWidget); - delLayout->addWidget(delWarning); - delLayout->addWidget(delButtons); + auto pDelLayout = make_parented(&dlgDelConfirm); + pDelLayout->addWidget(pDelListWidget); + pDelLayout->addWidget(pDelWarning); + pDelLayout->addWidget(pDelButtons); - // Create and populate the dialog - QDialog dlgDelConfirm; dlgDelConfirm.setModal(true); // just to be sure #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) dlgDelConfirm.setWindowTitle(tr("Delete Track Files")); @@ -2396,7 +2396,7 @@ void WTrackMenu::slotRemoveFromDisk() { // would close the dialog. connect(cancelBtn, &QPushButton::clicked, &dlgDelConfirm, &QDialog::reject); connect(deleteBtn, &QPushButton::clicked, &dlgDelConfirm, &QDialog::accept); - dlgDelConfirm.setLayout(delLayout); + dlgDelConfirm.setLayout(pDelLayout); if (dlgDelConfirm.exec() == QDialog::Rejected) { return; @@ -2490,8 +2490,11 @@ void WTrackMenu::slotRemoveFromDisk() { } return; } + + QDialog dlgNotDeleted; + // Else show a message with a list of tracks that could not be deleted. - QLabel* notDeletedLabel = new QLabel; + auto pNotDeletedLabel = make_parented(&dlgNotDeleted); QString msgText; if (m_pTrackModel) { msgText = @@ -2509,28 +2512,27 @@ void WTrackMenu::slotRemoveFromDisk() { msgText = tr("This track file could not be deleted from disk"); #endif } - notDeletedLabel->setText(msgText); - notDeletedLabel->setTextFormat(Qt::RichText); + pNotDeletedLabel->setText(msgText); + pNotDeletedLabel->setTextFormat(Qt::RichText); - QListWidget* notDeletedListWidget = new QListWidget; - notDeletedListWidget->setFocusPolicy(Qt::ClickFocus); - notDeletedListWidget->addItems(tracksToKeep); - mixxx::widgethelper::growListWidget(*notDeletedListWidget, *this); + auto pNotDeletedListWidget = make_parented(&dlgNotDeleted); + pNotDeletedListWidget->setFocusPolicy(Qt::ClickFocus); + pNotDeletedListWidget->addItems(tracksToKeep); + mixxx::widgethelper::growListWidget(*pNotDeletedListWidget, *this); - QDialogButtonBox* notDeletedButtons = new QDialogButtonBox(); - QPushButton* closeBtn = notDeletedButtons->addButton( + auto pNotDeletedButtons = make_parented(&dlgNotDeleted); + QPushButton* closeBtn = pNotDeletedButtons->addButton( tr("Close"), QDialogButtonBox::AcceptRole); - QVBoxLayout* notDeletedLayout = new QVBoxLayout; - notDeletedLayout->addWidget(notDeletedLabel); - notDeletedLayout->addWidget(notDeletedListWidget); - notDeletedLayout->addWidget(notDeletedButtons); + auto pNotDeletedLayout = make_parented(&dlgNotDeleted); + pNotDeletedLayout->addWidget(pNotDeletedLabel); + pNotDeletedLayout->addWidget(pNotDeletedListWidget); + pNotDeletedLayout->addWidget(pNotDeletedButtons); - QDialog dlgNotDeleted; dlgNotDeleted.setModal(true); dlgNotDeleted.setWindowTitle(tr("Remaining Track File(s)")); - dlgNotDeleted.setLayout(notDeletedLayout); + dlgNotDeleted.setLayout(pNotDeletedLayout); // Required for being able to close the dialog connect(closeBtn, &QPushButton::clicked, &dlgNotDeleted, &QDialog::close); dlgNotDeleted.exec(); From 212dd248e4f27e28ad47c0a454011dd2b895c389 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 19 Jan 2025 23:01:00 +0100 Subject: [PATCH 071/201] Use more parented_ptr --- src/widget/wtrackmenu.cpp | 24 ++++++++++++------------ src/widget/wtrackmenu.h | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/widget/wtrackmenu.cpp b/src/widget/wtrackmenu.cpp index 8183846dc81..ec909bdb31a 100644 --- a/src/widget/wtrackmenu.cpp +++ b/src/widget/wtrackmenu.cpp @@ -417,13 +417,13 @@ void WTrackMenu::createActions() { for (auto* const pExternalTrackCollection : m_pLibrary->trackCollectionManager()->externalCollections()) { UpdateExternalTrackCollection updateInExternalTrackCollection; - updateInExternalTrackCollection.externalTrackCollection = pExternalTrackCollection; - updateInExternalTrackCollection.action = new QAction( + updateInExternalTrackCollection.pExternalTrackCollection = pExternalTrackCollection; + updateInExternalTrackCollection.pAction = make_parented( pExternalTrackCollection->name(), m_pMetadataMenu); - updateInExternalTrackCollection.action->setToolTip( + updateInExternalTrackCollection.pAction->setToolTip( pExternalTrackCollection->description()); m_updateInExternalTrackCollections += updateInExternalTrackCollection; - connect(updateInExternalTrackCollection.action, + connect(updateInExternalTrackCollection.pAction, &QAction::triggered, this, [this, pExternalTrackCollection] { @@ -661,7 +661,7 @@ void WTrackMenu::setupActions() { for (const auto& updateInExternalTrackCollection : std::as_const(m_updateInExternalTrackCollections)) { m_pMetadataUpdateExternalCollectionsMenu->addAction( - updateInExternalTrackCollection.action); + updateInExternalTrackCollection.pAction); } if (!m_pMetadataUpdateExternalCollectionsMenu->isEmpty()) { m_pMetadataMenu->addMenu(m_pMetadataUpdateExternalCollectionsMenu); @@ -673,9 +673,9 @@ void WTrackMenu::setupActions() { [this] { for (const auto& updateInExternalTrackCollection : std::as_const(m_updateInExternalTrackCollections)) { - updateInExternalTrackCollection.action->setEnabled( + updateInExternalTrackCollection.pAction->setEnabled( updateInExternalTrackCollection - .externalTrackCollection + .pExternalTrackCollection ->isConnected()); } }); @@ -954,7 +954,7 @@ void WTrackMenu::updateMenus() { bool deckEnabled = (!deckPlaying || allowLoadTrackIntoPlayingDeck) && singleTrackSelected; - QAction* pAction = new QAction(tr("Deck %1").arg(i), this); + auto pAction = make_parented(tr("Deck %1").arg(i), this); pAction->setEnabled(deckEnabled); m_pDeckMenu->addAction(pAction); connect(pAction, &QAction::triggered, this, [this, deckGroup] { loadSelectionToGroup(deckGroup); }); @@ -973,7 +973,7 @@ void WTrackMenu::updateMenus() { samplersInMenu = 0; int limit = iNumSamplers > i + 15 ? i + 15 : iNumSamplers; const QString label = samplerTrString(i) + QStringLiteral("- %1").arg(limit); - pMenu = new QMenu(label, m_pSamplerMenu); + pMenu = make_parented(label, m_pSamplerMenu); m_pSamplerMenu->addMenu(pMenu); } samplersInMenu++; @@ -982,7 +982,7 @@ void WTrackMenu::updateMenus() { bool samplerPlaying = ControlObject::get( ConfigKey(samplerGroup, "play")) > 0.0; bool samplerEnabled = !samplerPlaying && singleTrackSelected; - QAction* pAction = new QAction(samplerTrString(i), pMenu); + auto pAction = make_parented(samplerTrString(i), pMenu); pAction->setEnabled(samplerEnabled); pMenu->addAction(pAction); connect(pAction, @@ -1431,7 +1431,7 @@ void WTrackMenu::slotPopulatePlaylistMenu() { // No leak because making the menu the parent means they will be // auto-deleted int plId = id; - auto* pAction = new QAction( + auto pAction = make_parented( mixxx::escapeTextPropertyWithoutShortcuts(name), m_pPlaylistMenu); bool locked = playlistDao.isPlaylistLocked(plId); @@ -1445,7 +1445,7 @@ void WTrackMenu::slotPopulatePlaylistMenu() { }); } m_pPlaylistMenu->addSeparator(); - QAction* newPlaylistAction = new QAction(tr("Create New Playlist"), m_pPlaylistMenu); + auto newPlaylistAction = make_parented(tr("Create New Playlist"), m_pPlaylistMenu); m_pPlaylistMenu->addAction(newPlaylistAction); connect(newPlaylistAction, &QAction::triggered, this, [this] { addSelectionToPlaylist(-1); }); m_bPlaylistMenuLoaded = true; diff --git a/src/widget/wtrackmenu.h b/src/widget/wtrackmenu.h index 076e3bf92e1..9ee40790d69 100644 --- a/src/widget/wtrackmenu.h +++ b/src/widget/wtrackmenu.h @@ -358,8 +358,8 @@ class WTrackMenu : public QMenu { std::unique_ptr m_pDlgTagFetcher; struct UpdateExternalTrackCollection { - QPointer externalTrackCollection; - QAction* action{}; + QPointer pExternalTrackCollection; + QAction* pAction{}; }; QList m_updateInExternalTrackCollections; From 032e22add710a032ff35886dd03afce0bcc6049f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 19 Jan 2025 23:52:40 +0100 Subject: [PATCH 072/201] Use QModernWindowsStylePlugin for Qt >= 6.7 --- CMakeLists.txt | 23 +++++++++++++++++++---- src/mixxxapplication.cpp | 4 ++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d2faca758df..63f8b1a3aeb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,4 @@ + cmake_minimum_required(VERSION 3.21) message(STATUS "CMAKE_VERSION: ${CMAKE_VERSION}") @@ -2899,8 +2900,16 @@ if(Qt_IS_STATIC) if(WIN32) target_link_libraries(mixxx-lib PRIVATE Qt${QT_VERSION_MAJOR}::QWindowsIntegrationPlugin - Qt${QT_VERSION_MAJOR}::QWindowsVistaStylePlugin ) + if(QT_VERSION VERSION_LESS 6.7) + target_link_libraries(mixxx-lib PRIVATE + Qt${QT_VERSION_MAJOR}::QWindowsVistaStylePlugin + ) + else() + target_link_libraries(mixxx-lib PRIVATE + Qt${QT_VERSION_MAJOR}::QModernWindowsStylePlugin + ) + endif() endif() if(APPLE) @@ -3047,9 +3056,15 @@ else() install(IMPORTED_RUNTIME_ARTIFACTS Qt${QT_VERSION_MAJOR}::QWindowsIntegrationPlugin DESTINATION "${MIXXX_INSTALL_DATADIR}/platforms" COMPONENT applocal) - install(IMPORTED_RUNTIME_ARTIFACTS Qt${QT_VERSION_MAJOR}::QWindowsVistaStylePlugin - DESTINATION "${MIXXX_INSTALL_DATADIR}/styles" - COMPONENT applocal) + if(QT_VERSION VERSION_LESS 6.7) + install(IMPORTED_RUNTIME_ARTIFACTS Qt${QT_VERSION_MAJOR}::QWindowsVistaStylePlugin + DESTINATION "${MIXXX_INSTALL_DATADIR}/styles" + COMPONENT applocal) + else() + install(IMPORTED_RUNTIME_ARTIFACTS Qt${QT_VERSION_MAJOR}::QModernWindowsStylePlugin + DESTINATION "${MIXXX_INSTALL_DATADIR}/styles" + COMPONENT applocal) + endif() endif() if(APPLE) install(IMPORTED_RUNTIME_ARTIFACTS Qt${QT_VERSION_MAJOR}::QCocoaIntegrationPlugin diff --git a/src/mixxxapplication.cpp b/src/mixxxapplication.cpp index 76096ccd6aa..1467bf46b2a 100644 --- a/src/mixxxapplication.cpp +++ b/src/mixxxapplication.cpp @@ -29,7 +29,11 @@ Q_IMPORT_PLUGIN(QWasmIntegrationPlugin) #elif defined(Q_OS_WIN) Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin) +#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) +Q_IMPORT_PLUGIN(QModernWindowsStylePlugin) +#else Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin) +#endif #elif defined(Q_OS_IOS) Q_IMPORT_PLUGIN(QIOSIntegrationPlugin) #elif defined(Q_OS_MACOS) From 8d89c9a3ac98f6338bc064d907996f3c7e4f225d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Tue, 21 Jan 2025 00:00:44 +0100 Subject: [PATCH 073/201] Update Translation template. Found 3117 source text(s) (2 new and 3115 already existing) --- res/translations/mixxx.ts | 306 ++++++++++++++++++++------------------ 1 file changed, 158 insertions(+), 148 deletions(-) diff --git a/res/translations/mixxx.ts b/res/translations/mixxx.ts index e661fc062ef..9e3c9eb8017 100644 --- a/res/translations/mixxx.ts +++ b/res/translations/mixxx.ts @@ -5890,124 +5890,134 @@ You can always drag-and-drop tracks on screen to clone a deck. - - + + Effect Chain Presets - + Drag and drop to rearrange lists and copy chains between lists. Create and edit chain presets in the effect units in the main window. Please refer the manual for further details. - + Chain presets from these lists will be selectable in the given order in the main window and from controllers (depending on the controller mapping). - + Effects in this chain preset: - + effect 1 name - + effect 2 name - + effect 3 name - + Import - + Rename - + Export - + Delete - + Quick Effect Chain Presets - - + + Visible Effects - + Drag and drop to rearrange lists and show or hide effects. - + Hidden Effects - + + ❯ + + + + + ❮ + + + + Effect load behavior - + Keep metaknob position - + Reset metaknob to effect default - + Effect Info - + Version: - + Description: - + Author: - + Name: - + Type: @@ -15666,244 +15676,244 @@ This can not be undone! - + Adjust BPM - + Select Color - - + + Analyze - - + + Delete Track Files - + Add to Auto DJ Queue (bottom) - + Add to Auto DJ Queue (top) - + Add to Auto DJ Queue (replace) - + Preview Deck - + Remove - + Remove from Playlist - + Remove from Crate - + Hide from Library - + Unhide from Library - + Purge from Library - + Move Track File(s) to Trash - + Delete Files from Disk - + Properties - + Open in File Browser - + Select in Library - + Import From File Tags - + Import From MusicBrainz - + Export To File Tags - + BPM and Beatgrid - + Play Count - + Rating - + Cue Point - + Hotcues - + Intro - + Outro - + Key - + ReplayGain - + Waveform - + Comment - + All - + Lock BPM - + Unlock BPM - + Double BPM - + Halve BPM - + 2/3 BPM - + 3/4 BPM - + 4/3 BPM - + 3/2 BPM - + Reanalyze - + Reanalyze (constant BPM) - + Reanalyze (variable BPM) - + Update ReplayGain from Deck Gain - + Deck %1 - + Importing metadata of %n track(s) from file tags @@ -15911,7 +15921,7 @@ This can not be undone! - + Marking metadata of %n track(s) to be exported into file tags @@ -15919,50 +15929,50 @@ This can not be undone! - - + + Create New Playlist - + Enter name for new playlist: - + New Playlist - - - + + + Playlist Creation Failed - + A playlist by that name already exists. - + A playlist cannot have a blank name. - + An unknown error occurred while creating playlist: - + Add to New Crate - + Scaling BPM of %n track(s) @@ -15970,7 +15980,7 @@ This can not be undone! - + Undo BPM/beats change of %n track(s) @@ -15978,7 +15988,7 @@ This can not be undone! - + Locking BPM of %n track(s) @@ -15986,7 +15996,7 @@ This can not be undone! - + Unlocking BPM of %n track(s) @@ -15994,7 +16004,7 @@ This can not be undone! - + Setting rating of %n track(s) @@ -16002,7 +16012,7 @@ This can not be undone! - + Setting color of %n track(s) @@ -16010,7 +16020,7 @@ This can not be undone! - + Resetting play count of %n track(s) @@ -16018,7 +16028,7 @@ This can not be undone! - + Resetting beats of %n track(s) @@ -16026,7 +16036,7 @@ This can not be undone! - + Clearing rating of %n track(s) @@ -16034,7 +16044,7 @@ This can not be undone! - + Clearing comment of %n track(s) @@ -16042,7 +16052,7 @@ This can not be undone! - + Removing main cue from %n track(s) @@ -16050,7 +16060,7 @@ This can not be undone! - + Removing outro cue from %n track(s) @@ -16058,7 +16068,7 @@ This can not be undone! - + Removing intro cue from %n track(s) @@ -16066,7 +16076,7 @@ This can not be undone! - + Removing loop cues from %n track(s) @@ -16074,7 +16084,7 @@ This can not be undone! - + Removing hot cues from %n track(s) @@ -16082,7 +16092,7 @@ This can not be undone! - + Resetting keys of %n track(s) @@ -16090,7 +16100,7 @@ This can not be undone! - + Resetting replay gain of %n track(s) @@ -16098,7 +16108,7 @@ This can not be undone! - + Resetting waveform of %n track(s) @@ -16106,7 +16116,7 @@ This can not be undone! - + Resetting all performance metadata of %n track(s) @@ -16114,164 +16124,164 @@ This can not be undone! - + Move these files to the trash bin? - + Permanently delete these files from disk? - - + + This can not be undone! - + Cancel - + Delete Files - + Okay - + Move Track File(s) to Trash? - + Track Files Deleted - + Track Files Moved To Trash - + %1 track files were moved to trash and purged from the Mixxx database. - + %1 track files were deleted from disk and purged from the Mixxx database. - + Track File Deleted - + Track file was deleted from disk and purged from the Mixxx database. - + The following %1 file(s) could not be deleted from disk - + This track file could not be deleted from disk - + Remaining Track File(s) - + Close - + Clear Reset metadata in right click track context menu in library - + Loops - + Clear BPM and Beatgrid - + Undo last BPM/beats change - + Move this track file to the trash bin? - + Permanently delete this track file from disk? - + All decks where these tracks are loaded will be stopped and the tracks will be ejected. - + All decks where this track is loaded will be stopped and the track will be ejected. - + Removing %n track file(s) from disk... - + Note: if you are in the Computer or Recording view you need to click the current view again to see changes. - + Track File Moved To Trash - + Track file was moved to trash and purged from the Mixxx database. - + The following %1 file(s) could not be moved to trash - + This track file could not be moved to trash - + Setting cover art of %n track(s) @@ -16279,7 +16289,7 @@ This can not be undone! - + Reloading cover art of %n track(s) @@ -16298,37 +16308,37 @@ This can not be undone! WTrackTableView - + Confirm track hide - + Are you sure you want to hide the selected tracks? - + Are you sure you want to remove the selected tracks from AutoDJ queue? - + Are you sure you want to remove the selected tracks from this crate? - + Are you sure you want to remove the selected tracks from this playlist? - + Don't ask again during this session - + Confirm track removal From f420ce55c8905076667f8e690c59c947db71b844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Tue, 21 Jan 2025 08:31:32 +0100 Subject: [PATCH 074/201] Show actual translator file path to track fall back translations --- src/util/translations.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/translations.h b/src/util/translations.h index 6774d8ea341..b18d9e3039e 100644 --- a/src/util/translations.h +++ b/src/util/translations.h @@ -127,7 +127,7 @@ class Translations { qDebug() << "Loaded" << translation << "translations for locale" << locale.name() - << "from" << translationsPath; + << "from" << pTranslator->filePath(); pApp->installTranslator(pTranslator); return true; } From 82ae992d4a4343b3b4843534b85c420253cb334f Mon Sep 17 00:00:00 2001 From: Joerg Date: Thu, 23 Jan 2025 19:43:00 +0100 Subject: [PATCH 075/201] Added missing QTlsBackendOpenSSLPlugin to make MusicBrainz work with Qt6 builds --- CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 63f8b1a3aeb..4381fe533cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2880,6 +2880,7 @@ if(Qt_IS_STATIC) Qt${QT_VERSION_MAJOR}::QICOPlugin Qt${QT_VERSION_MAJOR}::QJpegPlugin Qt${QT_VERSION_MAJOR}::QSvgPlugin + Qt${QT_VERSION_MAJOR}::QTlsBackendOpenSSLPlugin # sqldrivers Qt${QT_VERSION_MAJOR}::QSQLiteDriverPlugin @@ -2955,6 +2956,11 @@ else() DESTINATION "${MIXXX_INSTALL_DATADIR}/sqldrivers" COMPONENT applocal) + install(IMPORTED_RUNTIME_ARTIFACTS + Qt${QT_VERSION_MAJOR}::QTlsBackendOpenSSLPlugin + DESTINATION "${MIXXX_INSTALL_DATADIR}/tls" + COMPONENT applocal) + if(QML) install(IMPORTED_RUNTIME_ARTIFACTS Qt${QT_VERSION_MAJOR}::LabsQmlModels From cc4c8a60fa5cb510308dc69751b23a8874a7ce0f Mon Sep 17 00:00:00 2001 From: Joerg Date: Fri, 24 Jan 2025 00:19:59 +0100 Subject: [PATCH 076/201] Added QTlsBackendOpenSSLPlugin for static linked builds too --- src/mixxxapplication.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mixxxapplication.cpp b/src/mixxxapplication.cpp index 1467bf46b2a..585e0b52e89 100644 --- a/src/mixxxapplication.cpp +++ b/src/mixxxapplication.cpp @@ -50,10 +50,12 @@ Q_IMPORT_PLUGIN(QMinimalIntegrationPlugin) #endif Q_IMPORT_PLUGIN(QSQLiteDriverPlugin) +Q_IMPORT_PLUGIN(QTlsBackendOpenSSLPlugin) Q_IMPORT_PLUGIN(QSvgPlugin) Q_IMPORT_PLUGIN(QICOPlugin) Q_IMPORT_PLUGIN(QJpegPlugin) Q_IMPORT_PLUGIN(QGifPlugin) + #endif // QT_STATIC namespace { From ba47475214c2818e092f431a61e0667820e9a6bb Mon Sep 17 00:00:00 2001 From: ronso0 Date: Fri, 24 Jan 2025 15:44:27 +0100 Subject: [PATCH 077/201] Controller learning: show a dialog for non-existant controls --- src/controllers/dlgcontrollerlearning.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/controllers/dlgcontrollerlearning.cpp b/src/controllers/dlgcontrollerlearning.cpp index 90e0d6cbaa2..7d204ec8c5f 100644 --- a/src/controllers/dlgcontrollerlearning.cpp +++ b/src/controllers/dlgcontrollerlearning.cpp @@ -484,6 +484,22 @@ void DlgControllerLearning::loadControl(const ConfigKey& key, } void DlgControllerLearning::controlPicked(const ConfigKey& control) { + if (!ControlObject::exists(control)) { + QMessageBox msg(QMessageBox::Warning, + VersionStore::applicationName(), + tr("The selected control does not exist.
    " + "This likely a bug. Please report it on the Mixxx bug " + "tracker.
    " + "" + "https://github.com/mixxxdj/mixxx/issues" + "

    " + "You tried to learn: %1,%2") + .arg(control.group, control.item)); + msg.setTextFormat(Qt::RichText); // make the link clickable + msg.setWindowFlags(Qt::WindowStaysOnTopHint); // position it above the Learning dialog + msg.exec(); + return; + } QString title = m_pControlPickerMenu->controlTitleForConfigKey(control); QString description = m_pControlPickerMenu->descriptionForConfigKey(control); loadControl(control, title, description); From 69a8457a3682a4d2c8e72ba0c59806f93aeab42a Mon Sep 17 00:00:00 2001 From: ronso0 Date: Fri, 24 Jan 2025 15:53:54 +0100 Subject: [PATCH 078/201] (fix) Controller learning: correct control for 'Mic & Aux show/hide' --- src/controllers/controlpickermenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/controlpickermenu.cpp b/src/controllers/controlpickermenu.cpp index 746d7f1ed23..bf05d733d88 100644 --- a/src/controllers/controlpickermenu.cpp +++ b/src/controllers/controlpickermenu.cpp @@ -1395,7 +1395,7 @@ ControlPickerMenu::ControlPickerMenu(QWidget* pParent) tr("Show/hide the sampler section"), pGuiMenu); addControl("[Skin]", - "show_microphone", + "show_microphones", tr("Microphone & Auxiliary Show/Hide"), tr("Show/hide the microphone & auxiliary section"), pGuiMenu); From 015c672dc21a42a772df6d2a359ca155fc389886 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Fri, 24 Jan 2025 15:55:35 +0100 Subject: [PATCH 079/201] (fix) Controller Learning: prevent keyboard accelerator with & in QAction title --- src/controllers/controlpickermenu.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/controllers/controlpickermenu.cpp b/src/controllers/controlpickermenu.cpp index bf05d733d88..dd636fbbc69 100644 --- a/src/controllers/controlpickermenu.cpp +++ b/src/controllers/controlpickermenu.cpp @@ -1396,7 +1396,10 @@ ControlPickerMenu::ControlPickerMenu(QWidget* pParent) pGuiMenu); addControl("[Skin]", "show_microphones", - tr("Microphone & Auxiliary Show/Hide"), + // && prevents auto-detecting mnemonic /keyboard accelerator which + // would render to underlined whitespace here. + //: keep double & to prevent creation of keyboard accelerator + tr("Microphone && Auxiliary Show/Hide"), tr("Show/hide the microphone & auxiliary section"), pGuiMenu); addControl("[Skin]", From 378cd5484078bdbaf62413cce878a05d4fa5cebe Mon Sep 17 00:00:00 2001 From: ronso0 Date: Sat, 25 Jan 2025 01:08:52 +0100 Subject: [PATCH 080/201] (fix) Controller preferences: ask to save when closing with pending changes --- src/controllers/dlgprefcontroller.cpp | 4 ++++ src/controllers/dlgprefcontroller.h | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/controllers/dlgprefcontroller.cpp b/src/controllers/dlgprefcontroller.cpp index 5c3bf0dfa02..8b72fb163dc 100644 --- a/src/controllers/dlgprefcontroller.cpp +++ b/src/controllers/dlgprefcontroller.cpp @@ -521,6 +521,10 @@ void DlgPrefController::slotUpdate() { m_GuiInitialized = true; } +void DlgPrefController::slotHide() { + slotUpdate(); +} + void DlgPrefController::slotResetToDefaults() { if (m_pMapping) { m_pMapping->resetSettings(); diff --git a/src/controllers/dlgprefcontroller.h b/src/controllers/dlgprefcontroller.h index 9a3fbd7e6ed..81f9af92d02 100644 --- a/src/controllers/dlgprefcontroller.h +++ b/src/controllers/dlgprefcontroller.h @@ -37,6 +37,9 @@ class DlgPrefController : public DlgPreferencePage { void slotUpdate() override; /// Called when the user clicks the global "Apply" button. void slotApply() override; + /// Called when the preferences are hidden, e.g. when closing the window + /// with the [X] button or keyboard shortcut + void slotHide() override; /// Called when the user clicks the global "Reset to Defaults" button. void slotResetToDefaults() override; From b6af7034bfa7f11253933cb8733ebf748cea8467 Mon Sep 17 00:00:00 2001 From: JoergAtGithub <64457745+JoergAtGithub@users.noreply.github.com> Date: Sat, 25 Jan 2025 09:23:52 +0100 Subject: [PATCH 081/201] QTlsBackendOpenSSL without suffix Plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Daniel Schürmann --- src/mixxxapplication.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mixxxapplication.cpp b/src/mixxxapplication.cpp index 585e0b52e89..6b429312b2c 100644 --- a/src/mixxxapplication.cpp +++ b/src/mixxxapplication.cpp @@ -50,7 +50,7 @@ Q_IMPORT_PLUGIN(QMinimalIntegrationPlugin) #endif Q_IMPORT_PLUGIN(QSQLiteDriverPlugin) -Q_IMPORT_PLUGIN(QTlsBackendOpenSSLPlugin) +Q_IMPORT_PLUGIN(QTlsBackendOpenSSL) Q_IMPORT_PLUGIN(QSvgPlugin) Q_IMPORT_PLUGIN(QICOPlugin) Q_IMPORT_PLUGIN(QJpegPlugin) From d57961f0516420903f65b67f3ce88f58fe76b512 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Tue, 26 Nov 2024 08:25:50 +0100 Subject: [PATCH 082/201] Update FindFFTW3.cmake to not find version 2 and use PkgConfig --- CMakeLists.txt | 4 +- cmake/modules/FindChromaprint.cmake | 4 +- cmake/modules/FindFFTW.cmake | 75 -------------------------- cmake/modules/FindFFTW3.cmake | 82 +++++++++++++++++++++++++++++ cmake/modules/FindKeyFinder.cmake | 4 +- cmake/modules/Findrubberband.cmake | 4 +- 6 files changed, 90 insertions(+), 83 deletions(-) delete mode 100644 cmake/modules/FindFFTW.cmake create mode 100644 cmake/modules/FindFFTW3.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 4381fe533cd..a0c12a0f267 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2499,7 +2499,7 @@ if(KEYFINDER) target_link_libraries(mixxx-lib PRIVATE KeyFinder::KeyFinder) else() # If KeyFinder is built statically, we need FFTW - find_package(FFTW REQUIRED) + find_package(FFTW3 REQUIRED) set(KeyFinder_INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/lib/keyfinder-install") set(KeyFinder_LIBRARY "${CMAKE_INSTALL_LIBDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}keyfinder${CMAKE_STATIC_LIBRARY_SUFFIX}") @@ -2560,7 +2560,7 @@ if(KEYFINDER) add_library(mixxx-keyfinder STATIC IMPORTED) add_dependencies(mixxx-keyfinder libkeyfinder) set_target_properties(mixxx-keyfinder PROPERTIES IMPORTED_LOCATION "${KeyFinder_INSTALL_DIR}/${KeyFinder_LIBRARY}") - target_link_libraries(mixxx-keyfinder INTERFACE FFTW::FFTW) + target_link_libraries(mixxx-keyfinder INTERFACE FFTW3::fftw3) target_include_directories(mixxx-keyfinder INTERFACE "${KeyFinder_INSTALL_DIR}/include") target_link_libraries(mixxx-lib PRIVATE mixxx-keyfinder) endif() diff --git a/cmake/modules/FindChromaprint.cmake b/cmake/modules/FindChromaprint.cmake index d7b136318de..9a8b21177c6 100644 --- a/cmake/modules/FindChromaprint.cmake +++ b/cmake/modules/FindChromaprint.cmake @@ -96,9 +96,9 @@ if(Chromaprint_FOUND) CHROMAPRINT_NODLL ) endif() - find_package(FFTW REQUIRED) + find_package(FFTW3 REQUIRED) set_property(TARGET Chromaprint::Chromaprint APPEND PROPERTY INTERFACE_LINK_LIBRARIES - FFTW::FFTW + FFTW3::fftw3 ) endif() endif() diff --git a/cmake/modules/FindFFTW.cmake b/cmake/modules/FindFFTW.cmake deleted file mode 100644 index a3fb8265c6b..00000000000 --- a/cmake/modules/FindFFTW.cmake +++ /dev/null @@ -1,75 +0,0 @@ -# This file is part of Mixxx, Digital DJ'ing software. -# Copyright (C) 2001-2024 Mixxx Development Team -# Distributed under the GNU General Public Licence (GPL) version 2 or any later -# later version. See the LICENSE file for details. - -#[=======================================================================[.rst: -FindFFTW --------- - -Finds the FFTW library. - -Imported Targets -^^^^^^^^^^^^^^^^ - -This module provides the following imported targets, if found: - -``FFTW::FFTW`` - The FFTW library - -Result Variables -^^^^^^^^^^^^^^^^ - -This will define the following variables: - -``FFTW_FOUND`` - True if the system has the FFTW library. -``FFTW_INCLUDE_DIRS`` - Include directories needed to use FFTW. -``FFTW_LIBRARIES`` - Libraries needed to link to FFTW. - -Cache Variables -^^^^^^^^^^^^^^^ - -The following cache variables may also be set: - -``FFTW_INCLUDE_DIR`` - The directory containing ``fftw3.h``. -``FFTW_LIBRARY`` - The path to the FFTW library. - -#]=======================================================================] - -find_path(FFTW_INCLUDE_DIR - NAMES fftw3.h - DOC "FFTW include directory") -mark_as_advanced(FFTW_INCLUDE_DIR) - -find_library(FFTW_LIBRARY - NAMES fftw fftw3 fftw-3.3 - DOC "FFTW library" -) -mark_as_advanced(FFTW_LIBRARY) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args( - FFTW - DEFAULT_MSG - FFTW_LIBRARY - FFTW_INCLUDE_DIR -) - -if(FFTW_FOUND) - set(FFTW_LIBRARIES "${FFTW_LIBRARY}") - set(FFTW_INCLUDE_DIRS "${FFTW_INCLUDE_DIR}") - - if(NOT TARGET FFTW::FFTW) - add_library(FFTW::FFTW UNKNOWN IMPORTED) - set_target_properties(FFTW::FFTW - PROPERTIES - IMPORTED_LOCATION "${FFTW_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${FFTW_INCLUDE_DIR}" - ) - endif() -endif() diff --git a/cmake/modules/FindFFTW3.cmake b/cmake/modules/FindFFTW3.cmake new file mode 100644 index 00000000000..f1b7885936a --- /dev/null +++ b/cmake/modules/FindFFTW3.cmake @@ -0,0 +1,82 @@ +#[=======================================================================[.rst: +FindFFTW3 +-------- + +Finds the FFTW3 library. + +Imported Targets +^^^^^^^^^^^^^^^^ + +This module provides the following imported targets, if found: + +``FFTW3::fftw3`` + The FFTW3 library + +Result Variables +^^^^^^^^^^^^^^^^ + +This will define the following variables: + +``FFTW3_FOUND`` + True if the system has the FFTW3 library. +``FFTW3_INCLUDE_DIRS`` + Include directories needed to use FFTW3. +``FFTW3_LIBRARIES`` + Libraries needed to link to FFTW3. + +Cache Variables +^^^^^^^^^^^^^^^ + +The following cache variables may also be set: + +``FFTW3_INCLUDE_DIR`` + The directory containing ``fftw3.h``. +``FFTW3_LIBRARY`` + The path to the FFTW3 library. + +#]=======================================================================] + +find_package(PkgConfig QUIET) +if(PkgConfig_FOUND) + pkg_check_modules(PC_Fftw3 QUIET fftw3) +endif() + +find_path(FFTW3_INCLUDE_DIR + NAMES fftw3.h + HINTS ${PC_Fftw3_INCLUDE_DIRS} + DOC "FFTW3 include directory") +mark_as_advanced(FFTW3_INCLUDE_DIR) + +find_library(FFTW3_LIBRARY + NAMES fftw3 fftw-3.3 + HINTS ${PC_Fftw3_LIBRARY_DIRS} + DOC "FFTW3 library" +) +mark_as_advanced(FFTW3_LIBRARY) + +if(DEFINED PC_Fftw3_VERSION AND NOT PC_Fftw3_VERSION STREQUAL "") + set(FFTW3_VERSION "${PC_Fftw3_VERSION}") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + FFTW3 + REQUIRED_VARS FFTW3_LIBRARY FFTW3_INCLUDE_DIR + VERSION_VAR FFTW3_VERSION +) + +if(FFTW3_FOUND) + set(FFTW3_LIBRARIES "${FFTW3_LIBRARY}") + set(FFTW3_INCLUDE_DIRS "${FFTW3_INCLUDE_DIR}") + set(FFTW3_DEFINITIONS ${PC_Fftw3_CFLAGS_OTHER}) + + if(NOT TARGET FFTW3::fftw3) + add_library(FFTW3::fftw3 UNKNOWN IMPORTED) + set_target_properties(FFTW3::fftw3 + PROPERTIES + IMPORTED_LOCATION "${FFTW3_LIBRARY}" + INTERFACE_COMPILE_OPTIONS "${PC_Fftw3_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${FFTW3_INCLUDE_DIR}" + ) + endif() +endif() diff --git a/cmake/modules/FindKeyFinder.cmake b/cmake/modules/FindKeyFinder.cmake index d6c9fc230ee..c54750a728f 100644 --- a/cmake/modules/FindKeyFinder.cmake +++ b/cmake/modules/FindKeyFinder.cmake @@ -89,9 +89,9 @@ if(KeyFinder_FOUND) ) is_static_library(KeyFinder_IS_STATIC KeyFinder::KeyFinder) if(KeyFinder_IS_STATIC) - find_package(FFTW REQUIRED) + find_package(FFTW3 REQUIRED) set_property(TARGET KeyFinder::KeyFinder APPEND PROPERTY INTERFACE_LINK_LIBRARIES - FFTW::FFTW + FFTW3::fftw3 ) endif() endif() diff --git a/cmake/modules/Findrubberband.cmake b/cmake/modules/Findrubberband.cmake index a0c0da2786b..2a5f629c672 100644 --- a/cmake/modules/Findrubberband.cmake +++ b/cmake/modules/Findrubberband.cmake @@ -91,10 +91,10 @@ if(rubberband_FOUND) set_property(TARGET rubberband::rubberband APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${SAMPLERATE_LIBRARY} ) - find_package(FFTW) + find_package(FFTW3) if (FFTW_FOUND) set_property(TARGET rubberband::rubberband APPEND PROPERTY INTERFACE_LINK_LIBRARIES - FFTW::FFTW + FFTW3::fftw3 ) endif() find_package(Sleef) From b923d195475b213d8b7995a6bbcce4ff06e03e7c Mon Sep 17 00:00:00 2001 From: ronso0 Date: Thu, 10 Oct 2024 14:28:48 +0200 Subject: [PATCH 083/201] Overview: abort play pos dragging if cursor is relased outside the valid area --- src/widget/woverview.cpp | 50 ++++++++++++++++++++++++++++++++++++---- src/widget/woverview.h | 12 +++++++++- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 9d008293bd4..5d4bb0963f0 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -26,6 +26,12 @@ #include "widget/controlwidgetconnection.h" #include "wskincolor.h" +namespace { +// Horizontal and vertical margin around the widget where we accept play pos dragging. +constexpr int kDragOutsideLimitX = 100; +constexpr int kDragOutsideLimitY = 50; +} // anonymous namespace + WOverview::WOverview( const QString& group, PlayerManager* pPlayerManager, @@ -50,6 +56,8 @@ WOverview::WOverview( m_iPlayPos(0), m_bTimeRulerActive(false), m_orientation(Qt::Horizontal), + m_dragMarginH(kDragOutsideLimitX), + m_dragMarginV(kDragOutsideLimitY), m_iLabelFontSize(10), m_a(1.0), m_b(0.0), @@ -478,6 +486,23 @@ void WOverview::receiveCuesUpdated() { void WOverview::mouseMoveEvent(QMouseEvent* e) { if (m_bLeftClickDragging) { + if (isPosInAllowedPosDragZone(e->pos())) { + m_bTimeRulerActive = true; + m_timeRulerPos = e->pos(); + unsetCursor(); + } else { + // Remove the time ruler to indicate dragging position is invalid, + // don't abort dragging! + m_iPickupPos = m_iPlayPos; + m_bTimeRulerActive = false; + + setCursor(Qt::ForbiddenCursor); + // Remember to restore cursor everywhere where we cancel dragging. + // Update immediately. + update(); + return; + } + if (m_orientation == Qt::Horizontal) { m_iPickupPos = math_clamp(e->pos().x(), 0, width() - 1); } else { @@ -498,7 +523,7 @@ void WOverview::mouseMoveEvent(QMouseEvent* e) { m_pHoveredMark = m_marks.findHoveredMark(e->pos(), m_orientation); - //qDebug() << "WOverview::mouseMoveEvent" << e->pos() << m_iPos; + // qDebug() << "WOverview::mouseMoveEvent" << e->pos(); update(); } @@ -506,16 +531,28 @@ void WOverview::mouseReleaseEvent(QMouseEvent* e) { mouseMoveEvent(e); if (m_bPassthroughEnabled) { m_bLeftClickDragging = false; + // We may be dragging, and we may be outside the valid dragging area. + // If so, we've set the 'invalid drag' cursor. Restore the cursor now. + unsetCursor(); return; } //qDebug() << "WOverview::mouseReleaseEvent" << e->pos() << m_iPos << ">>" << dValue; if (e->button() == Qt::LeftButton) { if (m_bLeftClickDragging) { - m_iPlayPos = m_iPickupPos; - double dValue = positionToValue(m_iPickupPos); - setControlParameterUp(dValue); - m_bLeftClickDragging = false; + unsetCursor(); + if (isPosInAllowedPosDragZone(e->pos())) { + m_iPlayPos = m_iPickupPos; + double dValue = positionToValue(m_iPickupPos); + setControlParameterUp(dValue); + m_bLeftClickDragging = false; + } else { + // Abort dragging if we are way outside the widget. + m_iPickupPos = m_iPlayPos; + m_bLeftClickDragging = false; + m_bTimeRulerActive = false; + return; + } } m_bTimeRulerActive = false; } else if (e->button() == Qt::RightButton) { @@ -530,6 +567,7 @@ void WOverview::mousePressEvent(QMouseEvent* e) { mouseMoveEvent(e); if (m_bPassthroughEnabled) { m_bLeftClickDragging = false; + unsetCursor(); return; } double trackSamples = getTrackSamples(); @@ -560,6 +598,7 @@ void WOverview::mousePressEvent(QMouseEvent* e) { m_iPickupPos = m_iPlayPos; m_bLeftClickDragging = false; m_bTimeRulerActive = false; + unsetCursor(); } else if (m_pHoveredMark == nullptr) { m_bTimeRulerActive = true; m_timeRulerPos = e->pos(); @@ -648,6 +687,7 @@ void WOverview::paintEvent(QPaintEvent* pEvent) { if (m_bPassthroughEnabled) { drawPassthroughOverlay(&painter); m_pPassthroughLabel->show(); + unsetCursor(); } else { m_pPassthroughLabel->hide(); } diff --git a/src/widget/woverview.h b/src/widget/woverview.h index 19f69dc9659..f28e78a21f1 100644 --- a/src/widget/woverview.h +++ b/src/widget/woverview.h @@ -122,6 +122,15 @@ class WOverview : public WWidget, public TrackDropTarget { return m_orientation == Qt::Horizontal ? height() : width(); } + inline bool isPosInAllowedPosDragZone(const QPoint pos) { + const QRect dragZone = rect().marginsAdded(QMargins( + m_dragMarginH, + m_dragMarginV, + m_dragMarginH, + m_dragMarginV)); + return dragZone.contains(pos); + } + ConstWaveformPointer getWaveform() const { return m_pWaveform; } @@ -162,6 +171,8 @@ class WOverview : public WWidget, public TrackDropTarget { int m_iPlayPos; bool m_bTimeRulerActive; Qt::Orientation m_orientation; + int m_dragMarginH; + int m_dragMarginV; int m_iLabelFontSize; // Coefficient value-position linear transposition @@ -213,5 +224,4 @@ class WOverview : public WWidget, public TrackDropTarget { std::vector m_markRanges; WaveformMarkLabel m_cuePositionLabel; WaveformMarkLabel m_cueTimeDistanceLabel; - }; From 5bdc394279932c5261761ef123bd4f3258b91f03 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Sun, 26 Jan 2025 15:27:59 +0100 Subject: [PATCH 084/201] (fix) Controllers: allwo to close mapping Save/Overwrite dialog with Esc or X button --- src/controllers/dlgprefcontroller.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/controllers/dlgprefcontroller.cpp b/src/controllers/dlgprefcontroller.cpp index 8b72fb163dc..2c86cf0c182 100644 --- a/src/controllers/dlgprefcontroller.cpp +++ b/src/controllers/dlgprefcontroller.cpp @@ -724,6 +724,14 @@ bool DlgPrefController::saveMapping() { tr("Save As"), QMessageBox::AcceptRole); QPushButton* pOverwrite = overwriteMsgBox.addButton( tr("Overwrite"), QMessageBox::AcceptRole); + // QMessageBox handles Esc or pressing the X window button only if there + // is a button with either RejectRole or NoRole, so let's add one. + // https://doc.qt.io/qt-6/qmessagebox.html#escapeButton + QPushButton* pCancel = overwriteMsgBox.addButton(QMessageBox::Cancel); + // Hide Cancel since we don't really need it (we have the X button), + // furthermore it'll likely be auto-positioned in between Save and Overwrite + // which is not optimal (rules for order depend on OS). + pCancel->hide(); overwriteMsgBox.setDefaultButton(pSaveAsNew); overwriteMsgBox.exec(); From 87beaabb6f9cb765e1c993446ee5ea25c427b03d Mon Sep 17 00:00:00 2001 From: ronso0 Date: Sun, 26 Jan 2025 22:08:59 +0100 Subject: [PATCH 085/201] move Key Wheel action to View menu (as the shortcut already says) --- src/widget/wmainmenubar.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/widget/wmainmenubar.cpp b/src/widget/wmainmenubar.cpp index 9fa9e2d9119..0723efbec41 100644 --- a/src/widget/wmainmenubar.cpp +++ b/src/widget/wmainmenubar.cpp @@ -315,6 +315,22 @@ void WMainMenuBar::initialize() { ConfigKey(kSkinGroup, QStringLiteral("show_library_coverart"))); pViewMenu->addAction(pViewShowCoverArt); + //: menu title + QString keywheelTitle = tr("Show Keywheel"); + //: tooltip text + QString keywheelText = tr("Show keywheel"); + m_pViewKeywheel = new QAction(keywheelTitle, this); + m_pViewKeywheel->setCheckable(true); + m_pViewKeywheel->setShortcut( + QKeySequence(m_pKbdConfig->getValue( + ConfigKey("[KeyboardShortcuts]", "ViewMenu_ShowKeywheel"), + tr("F12", "Menubar|View|Show Keywheel")))); + m_pViewKeywheel->setShortcutContext(Qt::ApplicationShortcut); + m_pViewKeywheel->setStatusTip(keywheelText); + m_pViewKeywheel->setWhatsThis(buildWhatsThis(keywheelTitle, keywheelText)); + connect(m_pViewKeywheel, &QAction::triggered, this, &WMainMenuBar::showKeywheel); + pViewMenu->addAction(m_pViewKeywheel); + QString maximizeLibraryTitle = tr("Maximize Library"); QString maximizeLibraryText = tr("Maximize the track library to take up all the available screen space.") + " " + mayNotBeSupported; @@ -608,22 +624,6 @@ void WMainMenuBar::initialize() { externalLinkSuffix = QChar(' ') + QChar(0x2197); // north-east arrow #endif - //: menu title - QString keywheelTitle = tr("Show Keywheel"); - //: tooltip text - QString keywheelText = tr("Show keywheel"); - m_pViewKeywheel = new QAction(keywheelTitle, this); - m_pViewKeywheel->setCheckable(true); - m_pViewKeywheel->setShortcut( - QKeySequence(m_pKbdConfig->getValue( - ConfigKey("[KeyboardShortcuts]", "ViewMenu_ShowKeywheel"), - tr("F12", "Menubar|View|Show Keywheel")))); - m_pViewKeywheel->setShortcutContext(Qt::ApplicationShortcut); - m_pViewKeywheel->setStatusTip(keywheelText); - m_pViewKeywheel->setWhatsThis(buildWhatsThis(keywheelTitle, keywheelText)); - connect(m_pViewKeywheel, &QAction::triggered, this, &WMainMenuBar::showKeywheel); - pHelpMenu->addAction(m_pViewKeywheel); - // Community Support QString supportTitle = tr("&Community Support") + externalLinkSuffix; QString supportText = tr("Get help with Mixxx"); From a906aaa438350b19a9fe714517a0b30b3c6ada6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 26 Jan 2025 23:31:26 +0100 Subject: [PATCH 086/201] Us index based loop, because we need the index. --- src/rendergraph/common/rendergraph/uniformset.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rendergraph/common/rendergraph/uniformset.cpp b/src/rendergraph/common/rendergraph/uniformset.cpp index 68e077613bd..c0e0e10e115 100644 --- a/src/rendergraph/common/rendergraph/uniformset.cpp +++ b/src/rendergraph/common/rendergraph/uniformset.cpp @@ -3,9 +3,8 @@ using namespace rendergraph; UniformSet::UniformSet(std::initializer_list list, const std::vector& names) { - int i = 0; - for (auto item : list) { - add(Uniform{item.m_type, names[i++]}); + for (std::size_t i = 0; i < list.size() && i < names.size(); ++i) { + add(Uniform{list.begin()[i].m_type, names[i]}); } } From f3211d55be172acc2486940c5b5238bff00c9a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 26 Jan 2025 23:33:55 +0100 Subject: [PATCH 087/201] Use auto* for pointer --- src/waveform/widgets/allshader/waveformwidget.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/waveform/widgets/allshader/waveformwidget.cpp b/src/waveform/widgets/allshader/waveformwidget.cpp index 035863716f3..4315b920fb4 100644 --- a/src/waveform/widgets/allshader/waveformwidget.cpp +++ b/src/waveform/widgets/allshader/waveformwidget.cpp @@ -47,7 +47,7 @@ WaveformWidget::WaveformWidget(QWidget* parent, type, options, ::WaveformRendererAbstract::Play); m_pWaveformRendererSignal = pWaveformRendererSignal.get(); if (pWaveformRendererSignal) { - auto pNode = dynamic_cast(pWaveformRendererSignal.release()); + auto* pNode = dynamic_cast(pWaveformRendererSignal.release()); DEBUG_ASSERT(pNode); pOpacityNode->appendChildNode(std::unique_ptr(pNode)); } @@ -69,7 +69,7 @@ WaveformWidget::WaveformWidget(QWidget* parent, #endif std::unique_ptr pSlipNode = addWaveformSignalRenderer( type, options, ::WaveformRendererAbstract::Slip); - auto pNode = dynamic_cast(pSlipNode.release()); + auto* pNode = dynamic_cast(pSlipNode.release()); DEBUG_ASSERT(pNode); pOpacityNode->appendChildNode(std::unique_ptr(pNode)); pOpacityNode->appendChildNode( From 419f78af468dd790400367922785cc253c50c2f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Mon, 27 Jan 2025 01:36:54 +0100 Subject: [PATCH 088/201] create num_items outsit the loop. Co-authored-by: Jan Holthuis --- src/rendergraph/common/rendergraph/uniformset.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rendergraph/common/rendergraph/uniformset.cpp b/src/rendergraph/common/rendergraph/uniformset.cpp index c0e0e10e115..3e96bc3838c 100644 --- a/src/rendergraph/common/rendergraph/uniformset.cpp +++ b/src/rendergraph/common/rendergraph/uniformset.cpp @@ -3,7 +3,8 @@ using namespace rendergraph; UniformSet::UniformSet(std::initializer_list list, const std::vector& names) { - for (std::size_t i = 0; i < list.size() && i < names.size(); ++i) { + const auto num_items = std::min(list.size(), names.size()); + for (std::size_t i = 0; i < num_items; ++i) { add(Uniform{list.begin()[i].m_type, names[i]}); } } From 0f780e302b3055192486f0da1ab8aebb79b324ba Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 20 Jan 2025 06:20:07 +0100 Subject: [PATCH 089/201] fix(midi-components): revert deepMerge to avoid TypeError Fixes #14197 --- res/controllers/common-controller-scripts.js | 52 -------------------- res/controllers/midi-components-0.0.js | 2 +- 2 files changed, 1 insertion(+), 53 deletions(-) diff --git a/res/controllers/common-controller-scripts.js b/res/controllers/common-controller-scripts.js index 4199439026e..5045a28f461 100644 --- a/res/controllers/common-controller-scripts.js +++ b/res/controllers/common-controller-scripts.js @@ -140,58 +140,6 @@ var colorCodeToObject = function(colorCode) { var script = function() { }; -/** - * Discriminates whether an object was created using the `{}` synthax. - * - * Returns true when was an object was created using the `{}` synthax. - * False if the object is an instance of a class like Date or Proxy or an Array. - * - * isSimpleObject({}) // true - * isSimpleObject(null) // false - * isSimpleObject(undefined) // false - * isSimpleObject(new Date) // false - * isSimpleObject(new (class {})()) // false - * @param {any} obj Object to test - * @returns {boolean} true if obj was created using the `{}` or `new Object()` synthax, false otherwise - */ -const isSimpleObject = function(obj) { - return obj !== null && typeof obj === "object" && obj.constructor.name === "Object"; -}; - -script.isSimpleObject = isSimpleObject; - -/** - * Deeply merges 2 objects (Arrays and Objects only, not Map for instance). - * @param target {object | Array} Object to merge source into - * @param source {object | Array} Object to merge into source - */ -const deepMerge = function(target, source) { - if (target === source || target === undefined || target === null || source === undefined || source === null) { - return; - } - - if (Array.isArray(target) && Array.isArray(source)) { - const objTarget = target.reduce((acc, val, idx) => Object.assign(acc, {[idx]: val}), {}); - const objSource = source.reduce((acc, val, idx) => Object.assign(acc, {[idx]: val}), {}); - deepMerge(objTarget, objSource); - target.length = 0; - target.push(...Object.values(objTarget)); - } else if (isSimpleObject(target) && isSimpleObject(source)) { - Object.keys(source).forEach(key => { - if ( - Array.isArray(target[key]) && Array.isArray(source[key]) || - isSimpleObject(target[key]) && isSimpleObject(source[key]) - ) { - deepMerge(target[key], source[key]); - } else if (source[key] !== undefined && source[key] !== null) { - Object.assign(target, {[key]: source[key]}); - } - }); - } -}; - -script.deepMerge = deepMerge; - // ----------------- Mapping constants --------------------- // Library column value, which can be used to interact with the CO for "[Library] sort_column" diff --git a/res/controllers/midi-components-0.0.js b/res/controllers/midi-components-0.0.js index 225e5c6e083..9e1fbdc9fac 100644 --- a/res/controllers/midi-components-0.0.js +++ b/res/controllers/midi-components-0.0.js @@ -660,7 +660,7 @@ }); } - script.deepMerge(this, newLayer); + Object.assign(this, newLayer); if (reconnectComponents === true) { this.forEachComponent(function(component) { From fbf0853ad48976a2983e147e72de36c924f8a5da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jan 2025 18:25:22 +0000 Subject: [PATCH 090/201] Bump coverallsapp/github-action from 2.3.4 to 2.3.6 Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 2.3.4 to 2.3.6. - [Release notes](https://github.com/coverallsapp/github-action/releases) - [Commits](https://github.com/coverallsapp/github-action/compare/v2.3.4...v2.3.6) --- updated-dependencies: - dependency-name: coverallsapp/github-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/build-checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-checks.yml b/.github/workflows/build-checks.yml index de855c17cb3..1ec7e9b90b4 100644 --- a/.github/workflows/build-checks.yml +++ b/.github/workflows/build-checks.yml @@ -156,7 +156,7 @@ jobs: - name: "Upload Coverage Report to coveralls.io" if: matrix.name == 'coverage' continue-on-error: true - uses: coverallsapp/github-action@v2.3.4 + uses: coverallsapp/github-action@v2.3.6 with: flag-name: ubuntu-22.04 path-to-lcov: build/lcov.info From 2baccc2f09f78cd873d6a8bb67f292c7b23e7e06 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Tue, 28 Jan 2025 00:16:29 +0100 Subject: [PATCH 091/201] Traktor S4mk3: set 4 decks, avoid CO warnings for decks 3/4, eg. VU meter --- res/controllers/Traktor-Kontrol-S4-MK3.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/res/controllers/Traktor-Kontrol-S4-MK3.js b/res/controllers/Traktor-Kontrol-S4-MK3.js index a36353cb66a..8b3897e79fe 100644 --- a/res/controllers/Traktor-Kontrol-S4-MK3.js +++ b/res/controllers/Traktor-Kontrol-S4-MK3.js @@ -2877,6 +2877,9 @@ class S4Mk3MixerColumn extends ComponentContainer { class S4MK3 { constructor() { + if (engine.getValue("[App]", "num_decks") < 4) { + engine.setValue("[App]", "num_decks", 4); + } if (engine.getValue("[App]", "num_samplers") < 16) { engine.setValue("[App]", "num_samplers", 16); } From 998b57919222f69f52aac521e685f194572fc809 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Tue, 28 Jan 2025 00:54:33 +0100 Subject: [PATCH 092/201] (fix) Library scanner: update cached 'missing' flag when file was redicovered --- src/library/dao/trackdao.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/library/dao/trackdao.cpp b/src/library/dao/trackdao.cpp index 934914b9cba..966f776f1c0 100644 --- a/src/library/dao/trackdao.cpp +++ b/src/library/dao/trackdao.cpp @@ -2049,18 +2049,20 @@ bool TrackDAO::verifyRemainingTracks( "SET fs_deleted=:fs_deleted, needs_verification=0 " "WHERE location=:location"); + const int fsDeletedColumn = query.record().indexOf("fs_deleted"); const int locationColumn = query.record().indexOf("location"); QString trackLocation; while (query.next()) { trackLocation = query.value(locationColumn).toString(); int fs_deleted = 0; + int old_fs_deleded = query.value(fsDeletedColumn).toInt(); for (const auto& rootDir : libraryRootDirs) { if (trackLocation.startsWith(rootDir.location())) { // Track is under the library root, // but was not verified. - // This happens if the track was deleted - // a symlink duplicate or on a non normalized - // path like on non case sensitive file systems. + // This happens if the track was deleted, + // a symlink duplicate or on a non-normalized + // path like on non-case-sensitive file systems. fs_deleted = 1; break; } @@ -2076,6 +2078,16 @@ bool TrackDAO::verifyRemainingTracks( LOG_FAILED_QUERY(query2); } emit progressVerifyTracksOutside(trackLocation); + if (fs_deleted != old_fs_deleded) { + // Emit update so e.g. the tack model knows. + // Otherwise the table view may still paint it with 'track missing' + // color even though it has been re-discovered. + TrackId id = getTrackIdByLocation(trackLocation); + if (id.isValid()) { + QSet ids{id}; + emit tracksChanged(ids); + } + } if (*pCancel) { return false; } From 4953c79ea7a91a846937b68bf23a77ff171c4890 Mon Sep 17 00:00:00 2001 From: Christian Date: Sun, 26 Jan 2025 12:29:12 +0100 Subject: [PATCH 093/201] refactor(controllers): remove deepMerge --- res/controllers/common-controller-scripts.js | 51 -------------------- res/controllers/midi-components-0.0.js | 26 ---------- 2 files changed, 77 deletions(-) diff --git a/res/controllers/common-controller-scripts.js b/res/controllers/common-controller-scripts.js index fcdf516a586..36d0ff74298 100644 --- a/res/controllers/common-controller-scripts.js +++ b/res/controllers/common-controller-scripts.js @@ -140,57 +140,6 @@ var colorCodeToObject = function(colorCode) { var script = function() { }; -/** - * Discriminates whether an object was created using the `{}` synthax. - * - * Returns true when was an object was created using the `{}` synthax. - * False if the object is an instance of a class like Date or Proxy or an Array. - * - * isSimpleObject({}) // true - * isSimpleObject(null) // false - * isSimpleObject(undefined) // false - * isSimpleObject(new Date) // false - * isSimpleObject(new (class {})()) // false - * @param {any} obj Object to test - * @returns {boolean} true if obj was created using the `{}` or `new Object()` synthax, false otherwise - */ -const isSimpleObject = function(obj) { - return obj !== null && typeof obj === "object" && obj.constructor.name === "Object"; -}; - -/** - * Deeply merges 2 objects (Arrays and Objects only, not Map for instance). - * @param target {object | Array} Object to merge source into - * @param source {object | Array} Object to merge into source - * @deprecated Use {@link Object.assign} instead - */ -script.deepMerge = function(target, source) { - console.warn("script.deepMerge is deprecated; use Object.assign instead"); - - if (target === source || target === undefined || target === null || source === undefined || source === null) { - return; - } - - if (Array.isArray(target) && Array.isArray(source)) { - const objTarget = target.reduce((acc, val, idx) => Object.assign(acc, {[idx]: val}), {}); - const objSource = source.reduce((acc, val, idx) => Object.assign(acc, {[idx]: val}), {}); - deepMerge(objTarget, objSource); - target.length = 0; - target.push(...Object.values(objTarget)); - } else if (isSimpleObject(target) && isSimpleObject(source)) { - Object.keys(source).forEach(key => { - if ( - Array.isArray(target[key]) && Array.isArray(source[key]) || - isSimpleObject(target[key]) && isSimpleObject(source[key]) - ) { - deepMerge(target[key], source[key]); - } else if (source[key] !== undefined && source[key] !== null) { - Object.assign(target, {[key]: source[key]}); - } - }); - } -}; - // ----------------- Mapping constants --------------------- // Library column value, which can be used to interact with the CO for "[Library] sort_column" diff --git a/res/controllers/midi-components-0.0.js b/res/controllers/midi-components-0.0.js index 7470afd59fc..031f51cb901 100644 --- a/res/controllers/midi-components-0.0.js +++ b/res/controllers/midi-components-0.0.js @@ -653,33 +653,8 @@ /** * @param newLayer Layer to apply to this * @param reconnectComponents Whether components should be reconnected or not - * @deprecated since 2.5.0. Use @{ComponentContainer#setLayer} instead */ applyLayer: function(newLayer, reconnectComponents) { - console.warn("ComponentContainer.applyLayer is deprecated; use ComponentContainer.setLayer instead"); - if (reconnectComponents !== false) { - reconnectComponents = true; - } - if (reconnectComponents === true) { - this.forEachComponent(function(component) { - component.disconnect(); - }); - } - - script.deepMerge(this, newLayer); - - if (reconnectComponents === true) { - this.forEachComponent(function(component) { - component.connect(); - component.trigger(); - }); - } - }, - /** - * @param newLayer Layer to apply to this - * @param reconnectComponents Whether components should be reconnected or not - */ - setLayer(newLayer, reconnectComponents) { if (reconnectComponents !== false) { reconnectComponents = true; } @@ -697,7 +672,6 @@ component.trigger(); }); } - }, shutdown: function() { this.forEachComponent(function(component) { From f93d80a2b767ad373f99f56a000f308d83ab1b06 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Tue, 28 Jan 2025 16:07:51 +0100 Subject: [PATCH 094/201] (fix) menubar: don't show 'hide' dialog when switching skins --- src/mixxxmainwindow.cpp | 8 +++++++- src/mixxxmainwindow.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/mixxxmainwindow.cpp b/src/mixxxmainwindow.cpp index 9cd0271ceeb..4939706c519 100644 --- a/src/mixxxmainwindow.cpp +++ b/src/mixxxmainwindow.cpp @@ -99,6 +99,7 @@ MixxxMainWindow::MixxxMainWindow(std::shared_ptr pCoreServi #ifdef __LINUX__ m_supportsGlobalMenuBar(supportsGlobalMenu()), #endif + m_inRebootMixxxView(false), m_pDeveloperToolsDlg(nullptr), m_pPrefDlg(nullptr), m_toolTipsCfg(mixxx::preferences::Tooltips::On) { @@ -1159,6 +1160,7 @@ void MixxxMainWindow::slotTooltipModeChanged(mixxx::preferences::Tooltips tt) { void MixxxMainWindow::rebootMixxxView() { qDebug() << "Now in rebootMixxxView..."; + m_inRebootMixxxView = true; ScopedWaitCursor cursor; // safe geometry for later restoration @@ -1192,6 +1194,7 @@ void MixxxMainWindow::rebootMixxxView() { QMessageBox::critical(this, tr("Error in skin file"), tr("The selected skin cannot be loaded.")); + m_inRebootMixxxView = false; // m_pWidgetParent is NULL, we can't continue. return; } @@ -1220,6 +1223,7 @@ void MixxxMainWindow::rebootMixxxView() { setGeometry(initGeometry); } + m_inRebootMixxxView = false; qDebug() << "rebootMixxxView DONE"; } @@ -1310,7 +1314,9 @@ bool MixxxMainWindow::eventFilter(QObject* obj, QEvent* event) { if (!m_supportsGlobalMenuBar || isFullScreenNow) #endif { - alwaysHideMenuBarDlg(); + if (!m_inRebootMixxxView) { + alwaysHideMenuBarDlg(); + } slotUpdateMenuBarAltKeyConnection(); } #endif diff --git a/src/mixxxmainwindow.h b/src/mixxxmainwindow.h index 1c673578ce3..fd7f1cbb38e 100644 --- a/src/mixxxmainwindow.h +++ b/src/mixxxmainwindow.h @@ -137,6 +137,7 @@ class MixxxMainWindow : public QMainWindow { #ifdef __LINUX__ const bool m_supportsGlobalMenuBar; #endif + bool m_inRebootMixxxView; DlgDeveloperTools* m_pDeveloperToolsDlg; From 2b7f5f8b288ef66de1d2794f7ba98f42c3184370 Mon Sep 17 00:00:00 2001 From: Christian Date: Sun, 26 Jan 2025 12:21:22 +0100 Subject: [PATCH 095/201] refactor(controllers): drop lodash dependency --- res/controllers/Behringer BCR2000.midi.xml | 3 +- res/controllers/Behringer DDM4000.midi.xml | 3 +- .../Behringer-Extension-scripts.js | 84 ++++++++++++++----- 3 files changed, 65 insertions(+), 25 deletions(-) diff --git a/res/controllers/Behringer BCR2000.midi.xml b/res/controllers/Behringer BCR2000.midi.xml index 9f557abd74e..a03ad4935c3 100644 --- a/res/controllers/Behringer BCR2000.midi.xml +++ b/res/controllers/Behringer BCR2000.midi.xml @@ -1,5 +1,5 @@ - + Behringer BCR2000 Christian @@ -7,7 +7,6 @@ - diff --git a/res/controllers/Behringer DDM4000.midi.xml b/res/controllers/Behringer DDM4000.midi.xml index 64b92d076cd..873d83e6f25 100644 --- a/res/controllers/Behringer DDM4000.midi.xml +++ b/res/controllers/Behringer DDM4000.midi.xml @@ -1,5 +1,5 @@ - + Behringer DDM4000 Christian @@ -7,7 +7,6 @@ - diff --git a/res/controllers/Behringer-Extension-scripts.js b/res/controllers/Behringer-Extension-scripts.js index cc912e64d3a..17095652404 100644 --- a/res/controllers/Behringer-Extension-scripts.js +++ b/res/controllers/Behringer-Extension-scripts.js @@ -9,6 +9,49 @@ /** @private */ var engine = global.engine; + /** + * Determines the merge strategy for a value when merging a component container definition. + * + * @param value anything + * @returns {boolean} merge strategy for the value: true for 'assign', false for 'deep merge' + * @private + */ + const doAssign = value => { + return typeof value !== "object" + || value === null + || value instanceof components.ComponentContainer + || value instanceof components.Component; + }; + + /** + * Merges a list of component container definitions into a target object. + * + * This is not a generic deep merge algorithm for JS objects. It does not handle circular references. + * If a definition contains a reference to an object instance of a component or component container + * (e.g. `ShiftButton.target`), the instance is taken over by reference, no deep copy is made. + * + * @param {object} targetObject Target object for the merged definitions + * @param {Array} sources Source definitions + * @returns {object} The target object + * @see `GenericMidiController` on component container definition + * @private + */ + const mergeDefinitions = (targetObject, ...sources) => sources.reduce((target, source) => { + for (const [key, value] of Object.entries(source || {})) { + if (doAssign(value)) { + target[key] = value; + } else if (value !== undefined) { + if (Array.isArray(value) && !Array.isArray(target[key])) { + target[key] = []; + } else if (typeof target[key] !== "object" || target[key] === null) { + target[key] = {}; + } + mergeDefinitions(target[key], value); + } + }; + return target; + }, targetObject); + /** * Contains functions to print a message to the log. * `debug` output is suppressed unless the caller owns a truthy property `debug`. @@ -34,7 +77,7 @@ * Determine an ID from a component's MIDI address * * @param {Array} midiAddress MIDI address consisting of two integers - * @return {string} ID for the MIDI address; `undefined` on error + * @returns {string} ID for the MIDI address; `undefined` on error * @private */ var findComponentId = function(midiAddress) { @@ -51,7 +94,7 @@ * Create a human-readable string to identify a component. * * @param {components.Component} component A component - * @return {string} A short string that describes the component; `undefined` on error + * @returns {string} A short string that describes the component; `undefined` on error * @private */ var stringifyComponent = function(component) { @@ -92,11 +135,11 @@ * * @param {object} parent Constructor of parent whose prototype is used as base * @param {object} members Own members that are not inherited - * @return {object} A new prototype based on parent with the given members + * @returns {object} A new prototype based on parent with the given members * @private */ var deriveFrom = function(parent, members) { - return _.merge(Object.create(parent.prototype), members || {}); + return Object.assign(Object.create(parent.prototype), members); }; /** @@ -231,7 +274,7 @@ * @see https://github.com/mixxxdj/mixxx/wiki/Script-Timers */ var Timer = function(options) { - _.assign(this, options); + Object.assign(this, options); this.disable(); }; Timer.prototype = { @@ -274,7 +317,7 @@ var Throttler = function(options) { options = options || {}; options.delay = options.delay || 0; - _.assign(this, options); + Object.assign(this, options); this.locked = false; this.jobs = []; this.unlockTimer = new Timer( @@ -810,7 +853,7 @@ * Create a new ComponentContainer within this registry. * * @param {string} name Name of the ComponentContainer - * @return {components.ComponentContainer} The ComponentContainer; `undefined` on error + * @returns {components.ComponentContainer} The ComponentContainer; `undefined` on error * @public */ createContainer: function(name) { @@ -827,7 +870,7 @@ * Retrieve an existing ComponentContainer. * * @param {string} name Name of an existing ComponentContainer - * @return {components.ComponentContainer} The ComponentContainer; `undefined` on error + * @returns {components.ComponentContainer} The ComponentContainer; `undefined` on error * @public */ getContainer: function(name) { @@ -884,7 +927,7 @@ * * @param {components.Component} component A component * @param {string} containerName Name of a ComponentContainer - * @return {string} ID of the stored component; `undefined` on error + * @returns {string} ID of the stored component; `undefined` on error * @public */ register: function(component, containerName) { @@ -926,7 +969,7 @@ * * @param {components.Component} component A component * @param {string} containerName Name of an existing ComponentContainer - * @return {string} ID of the removed component; `undefined` on error + * @returns {string} ID of the removed component; `undefined` on error * @public */ unregister: function(component, containerName) { @@ -977,7 +1020,7 @@ /** * Retrieve the Default layer. * - * @return {object} The Default layer + * @returns {object} The Default layer * @private */ defaultLayer: function() { @@ -992,7 +1035,7 @@ /** * Retrieve the Shift layer. * - * @return {object} The Shift layer + * @returns {object} The Shift layer * @private */ shiftLayer: function() { @@ -1004,7 +1047,7 @@ * * @param {number} status First byte of the component's MIDI address * @param {number} control Second byte of the component's MIDI address - * @return {components.Component} Component on the active layer matching the MIDI address; + * @returns {components.Component} Component on the active layer matching the MIDI address; * undefined on error. When the active layer does not contain * a matching component, the Default layer is used as * fallback. @@ -1035,7 +1078,7 @@ * @param {LayerManager~registryOperation} The operation to run * @param {components.Component} component A component * @param {boolean} shift Iff true, use Shift Layer, otherwise use Default Layer - * @return Result of the operation + * @returns Result of the operation * @private */ onRegistry: function(operation, component, shift) { @@ -1317,7 +1360,7 @@ * @param {object} deckDefinitions Definition of decks * @param {object} effectUnitDefinitions Definition of effect units * @param {object} containerDefinitions Definition of additional component containers - * @return {object} Layer manager + * @returns {object} Layer manager * @see `LayerManager` * @private */ @@ -1388,8 +1431,8 @@ createDeck: function(deckDefinition, componentStorage) { var deck = new components.Deck(deckDefinition.deckNumbers); deckDefinition.components.forEach(function(componentDefinition, index) { - var options = _.merge({group: deck.currentDeck}, componentDefinition.options); - var definition = _.merge(componentDefinition, {options: options}); + const options = Object.assign({group: deck.currentDeck}, componentDefinition.options); + const definition = Object.assign(componentDefinition, {options: options}); deck[index] = this.createComponent(definition); }, this); if (deckDefinition.equalizerUnit) { @@ -1450,7 +1493,7 @@ * @param {Array} publisherStorage Storage for publisher components * @param {Array} rebindTriggers Names of functions that trigger rebinding a * publisher to its source component - * @return {components.ComponentContainer} The given component container argument + * @returns {components.ComponentContainer} The given component container argument * @private */ setupMidi: function(definition, implementation, publisherStorage, rebindTriggers) { @@ -1540,8 +1583,7 @@ var container = new containerType(containerDefinition.options); if (containerDefinition.components) { containerDefinition.components.forEach(function(componentDefinition, index) { - var definition = _.merge( - {}, containerDefinition.defaultDefinition || {}, componentDefinition); + const definition = mergeDefinitions({}, containerDefinition.defaultDefinition, componentDefinition); container[index] = this.createComponent(definition); }, this); } @@ -1614,5 +1656,5 @@ exports.Publisher = Publisher; exports.LayerManager = LayerManager; exports.GenericMidiController = GenericMidiController; - global.behringer = _.assign(global.behringer, {extension: exports}); + global.behringer = Object.assign(global.behringer || {}, {extension: exports}); })(this); From 051b0cde78b970716b9b8dfdfe0498d5450abd1c Mon Sep 17 00:00:00 2001 From: Christian Date: Sun, 26 Jan 2025 13:30:23 +0100 Subject: [PATCH 096/201] style(controllers): prefer const & let over var --- .../Behringer-BCR2000-preset-scripts.js | 56 ++--- res/controllers/Behringer-BCR2000-scripts.js | 8 +- res/controllers/Behringer-DDM4000-scripts.js | 58 +++--- .../Behringer-Extension-scripts.js | 194 +++++++++--------- 4 files changed, 157 insertions(+), 159 deletions(-) diff --git a/res/controllers/Behringer-BCR2000-preset-scripts.js b/res/controllers/Behringer-BCR2000-preset-scripts.js index 14524c14111..d98e6c6cca3 100644 --- a/res/controllers/Behringer-BCR2000-preset-scripts.js +++ b/res/controllers/Behringer-BCR2000-preset-scripts.js @@ -4,22 +4,22 @@ (function(global) { /* Controller-specific constants */ - var ROW_SIZE = 8; - var PUSHENCODERGROUP_COUNT = 4; - var BUTTONROW_COUNT = 2; - var ENCODERROW_COUNT = 3; - var BUTTONBOX_SIZE = 4; - var PRESET_MIN = 1; - var PRESET_MAX = 32; - var STATUS_CONTROL_CHANGE = 0xB0; - var STATUS_PROGRAM_CHANGE = 0xC0; + const ROW_SIZE = 8; + const PUSHENCODERGROUP_COUNT = 4; + const BUTTONROW_COUNT = 2; + const ENCODERROW_COUNT = 3; + const BUTTONBOX_SIZE = 4; + const PRESET_MIN = 1; + const PRESET_MAX = 32; + const STATUS_CONTROL_CHANGE = 0xB0; + const STATUS_PROGRAM_CHANGE = 0xC0; /* Preset-specific constants */ - var PUSHENCODERGROUP_START = 0x01; - var PUSHENCODERGROUP_BUTTON_OFFSET = 0x20; - var BUTTONROW_START = 0x41; - var ENCODERROW_START = 0x51; - var BUTTONBOX_START = 0x69; + const PUSHENCODERGROUP_START = 0x01; + const PUSHENCODERGROUP_BUTTON_OFFSET = 0x20; + const BUTTONROW_START = 0x41; + const ENCODERROW_START = 0x51; + const BUTTONBOX_START = 0x69; /** * Select a preset in the controller. @@ -27,7 +27,7 @@ * @param {number} A preset number (integer 1..32) * @public */ - var setPreset = function(presetNumber) { + const setPreset = function(presetNumber) { if (presetNumber) { presetNumber = Math.max(PRESET_MIN, presetNumber); presetNumber = Math.min(PRESET_MAX, presetNumber); @@ -43,7 +43,7 @@ * @return {Array} Array containing values * @private */ - var createElements = function(size, elementFactory) { + const createElements = function(size, elementFactory) { return Object.keys(Array.apply(0, Array(size))).map( function(_v, i) { return elementFactory.call(this, i); }); }; @@ -64,10 +64,10 @@ * @return {Array} Array of MIDI addresses for the given range * @private */ - var calculateRange = function(startAddress, rangeNumber, size) { + const calculateRange = function(startAddress, rangeNumber, size) { size = size || ROW_SIZE; - var rangeOffset = rangeNumber * ROW_SIZE; - var rangeStart = startAddress + rangeOffset; + const rangeOffset = rangeNumber * ROW_SIZE; + const rangeStart = startAddress + rangeOffset; return createElements(size, function(i) { return rangeStart + i; }); }; @@ -81,7 +81,7 @@ * @return {Array} Address range for the encoders in the encoder group * @private */ - var createPushEncoderGroup = function(groupNumber) { + const createPushEncoderGroup = function(groupNumber) { return calculateRange(PUSHENCODERGROUP_START, groupNumber).map(function(encoder) { return {"encoder": encoder, "button": encoder + PUSHENCODERGROUP_BUTTON_OFFSET}; }); @@ -95,7 +95,7 @@ * @return {Array} Address range for the encoders in the encoder row * @private */ - var createEncoderRow = function(rowNumber) { + const createEncoderRow = function(rowNumber) { return calculateRange(ENCODERROW_START, rowNumber); }; @@ -107,7 +107,7 @@ * @return {Array} Address range for the buttons in the button row * @private */ - var createButtonRow = function(rowNumber) { + const createButtonRow = function(rowNumber) { return calculateRange(BUTTONROW_START, rowNumber); }; @@ -118,17 +118,17 @@ * @return {Array} Address range for the buttons in the button box * @private */ - var createButtonBox = function() { + const createButtonBox = function() { return calculateRange(BUTTONBOX_START, 0, BUTTONBOX_SIZE); }; /* Definition of MIDI controls */ - var pushEncoderGroups = createElements(PUSHENCODERGROUP_COUNT, createPushEncoderGroup); - var buttonRows = createElements(BUTTONROW_COUNT, createButtonRow); - var encoderRows = createElements(ENCODERROW_COUNT, createEncoderRow); - var buttonBox = createButtonBox(); + const pushEncoderGroups = createElements(PUSHENCODERGROUP_COUNT, createPushEncoderGroup); + const buttonRows = createElements(BUTTONROW_COUNT, createButtonRow); + const encoderRows = createElements(ENCODERROW_COUNT, createEncoderRow); + const buttonBox = createButtonBox(); - var exports = {}; + const exports = {}; exports.STATUS_CONTROL_CHANGE = STATUS_CONTROL_CHANGE; exports.setPreset = setPreset; exports.pushEncoderGroups = pushEncoderGroups; diff --git a/res/controllers/Behringer-BCR2000-scripts.js b/res/controllers/Behringer-BCR2000-scripts.js index 9d151908d35..9369a3f6e2c 100644 --- a/res/controllers/Behringer-BCR2000-scripts.js +++ b/res/controllers/Behringer-BCR2000-scripts.js @@ -9,10 +9,10 @@ var BCR2000 = new behringer.extension.GenericMidiController({ configurationProvider: function() { /* Shortcut variables */ - var c = components; - var e = behringer.extension; - var p = BCR2000Preset; - var cc = p.STATUS_CONTROL_CHANGE; + const c = components; + const e = behringer.extension; + const p = BCR2000Preset; + const cc = p.STATUS_CONTROL_CHANGE; return { init: function() { diff --git a/res/controllers/Behringer-DDM4000-scripts.js b/res/controllers/Behringer-DDM4000-scripts.js index bf98175b9f2..d89e850e724 100644 --- a/res/controllers/Behringer-DDM4000-scripts.js +++ b/res/controllers/Behringer-DDM4000-scripts.js @@ -8,18 +8,18 @@ var behringer = behringer; var DDM4000 = new behringer.extension.GenericMidiController({ configurationProvider: function() { - var DEFAULT_LONGPRESS_DURATION = 500; - var DEFAULT_BLINK_DURATION = 425; - var THROTTLE_DELAY = 40; + const DEFAULT_LONGPRESS_DURATION = 500; + const DEFAULT_BLINK_DURATION = 425; + const THROTTLE_DELAY = 40; /* Shortcut variables */ - var c = components; - var e = behringer.extension; - var cc = 0xB0; - var note = 0x90; - var toggle = c.Button.prototype.types.toggle; + const c = components; + const e = behringer.extension; + const cc = 0xB0; + const note = 0x90; + const toggle = c.Button.prototype.types.toggle; - var CrossfaderAssignLED = function(options) { + const CrossfaderAssignLED = function(options) { options = options || {}; options.outKey = options.outKey || "orientation"; e.CustomButton.call(this, options); @@ -31,16 +31,16 @@ var DDM4000 = new behringer.extension.GenericMidiController({ right: 2 } }); - var left = CrossfaderAssignLED.prototype.position.left; - var center = CrossfaderAssignLED.prototype.position.center; - var right = CrossfaderAssignLED.prototype.position.right; + const left = CrossfaderAssignLED.prototype.position.left; + const center = CrossfaderAssignLED.prototype.position.center; + const right = CrossfaderAssignLED.prototype.position.right; - var CrossfaderUnit = function(options) { - var unitOptions = options || {}; + const CrossfaderUnit = function(options) { + const unitOptions = options || {}; unitOptions.group = unitOptions.group || "[Master]"; c.ComponentContainer.call(this, unitOptions); - var Crossfader = function(options) { + const Crossfader = function(options) { options = options || {}; options.inKey = options.inKey || options.key || "crossfader"; options.group = options.group || unitOptions.group; @@ -56,9 +56,9 @@ var DDM4000 = new behringer.extension.GenericMidiController({ engine.setValue("[Master]", "crossfader_set_default", 1); }, }); - var crossfader = new Crossfader(options.crossfader); + const crossfader = new Crossfader(options.crossfader); - var CrossfaderToggleButton = function(options) { + const CrossfaderToggleButton = function(options) { options = options || {}; if (options.type === undefined) { options.type = c.Button.prototype.types.toggle; @@ -96,7 +96,7 @@ var DDM4000 = new behringer.extension.GenericMidiController({ * @param {number} options Options object * @public */ - var CrossfaderReverseTapButton = function(options) { + const CrossfaderReverseTapButton = function(options) { options = options || {}; options.inKey = options.inKey || "xFaderReverse"; c.Button.call(this, options); @@ -108,7 +108,7 @@ var DDM4000 = new behringer.extension.GenericMidiController({ }, }); - var Blinker = function(target, blinkDuration, outValueScale) { + const Blinker = function(target, blinkDuration, outValueScale) { this.target = target; this.outValueScale = outValueScale || components.Component.prototype.outValueScale; @@ -125,11 +125,11 @@ var DDM4000 = new behringer.extension.GenericMidiController({ }, }; - var SamplerBank = function(bankOptions) { + const SamplerBank = function(bankOptions) { c.ComponentContainer.call(this); - var bank = this; + const bank = this; - var PlayButton = function(options) { + const PlayButton = function(options) { options = options || {}; options.inKey = options.inKey || "cue_gotoandplay"; options.outKey = options.outKey || "track_loaded"; @@ -151,7 +151,7 @@ var DDM4000 = new behringer.extension.GenericMidiController({ group: bankOptions.group, }); - var PlayIndicatorLED = function(options) { + const PlayIndicatorLED = function(options) { options = options || {}; options.outKey = options.outKey || "play_indicator"; this.blinker = new Blinker(this, options.blinkDuration); @@ -171,7 +171,7 @@ var DDM4000 = new behringer.extension.GenericMidiController({ blinkDuration: DEFAULT_BLINK_DURATION, }); - var ReverseMode = function(options) { + const ReverseMode = function(options) { options = options || {}; options.key = options.key || "reverse"; c.Button.call(this, options); @@ -179,7 +179,7 @@ var DDM4000 = new behringer.extension.GenericMidiController({ ReverseMode.prototype = e.deriveFrom(c.Button); this.reverseMode = new ReverseMode({midi: bankOptions.reverse, group: bankOptions.group}); - var LoopMode = function(options) { + const LoopMode = function(options) { options = options || {}; options.key = options.inKey || "beatloop_activate"; c.Button.call(this, options); @@ -187,11 +187,11 @@ var DDM4000 = new behringer.extension.GenericMidiController({ }; LoopMode.prototype = e.deriveFrom(c.Button, { outValueScale: function(value) { - var button = c.Button.prototype; + const button = c.Button.prototype; bank.playButton.type = value ? button.types.toggle : button.types.push; if (!value) { - var beatloopSize = engine.getValue(this.group, "beatloop_size"); - var key = "beatloop_" + beatloopSize; + const beatloopSize = engine.getValue(this.group, "beatloop_size"); + const key = `beatloop_${beatloopSize}`; engine.setValue(this.group, key, 0); } return button.outValueScale(value); @@ -199,7 +199,7 @@ var DDM4000 = new behringer.extension.GenericMidiController({ }); this.loopMode = new LoopMode({midi: bankOptions.loop, group: bankOptions.group}); - var ModeButton = function(options) { + const ModeButton = function(options) { options = options || {}; options.key = options.key || "mode"; options.longPressTimeout = options.longPressTimeout || DEFAULT_LONGPRESS_DURATION; diff --git a/res/controllers/Behringer-Extension-scripts.js b/res/controllers/Behringer-Extension-scripts.js index 17095652404..d65070c2891 100644 --- a/res/controllers/Behringer-Extension-scripts.js +++ b/res/controllers/Behringer-Extension-scripts.js @@ -4,10 +4,10 @@ (function(global) { /** @private */ - var components = global.components; + const components = global.components; /** @private */ - var engine = global.engine; + const engine = global.engine; /** * Determines the merge strategy for a value when merging a component container definition. @@ -59,7 +59,7 @@ * @param {string} message Message * @private */ - var log = { + const log = { debug: function(message) { if (this.debug) { print("[DEBUG] " + message); @@ -80,7 +80,7 @@ * @returns {string} ID for the MIDI address; `undefined` on error * @private */ - var findComponentId = function(midiAddress) { + const findComponentId = function(midiAddress) { if (Array.isArray(midiAddress) && midiAddress.length === 2 && typeof midiAddress[0] === "number" && typeof midiAddress[1] === "number") { return "[" + midiAddress.map(function(x) { @@ -97,14 +97,14 @@ * @returns {string} A short string that describes the component; `undefined` on error * @private */ - var stringifyComponent = function(component) { + const stringifyComponent = function(component) { if (!component) { return; } - var key = component.inKey || component.outKey; - var value = component.group + "," + key; + const key = component.inKey || component.outKey; + let value = `${component.group},${key}`; if (component.midi) { - var id = findComponentId(component.midi); + const id = findComponentId(component.midi); if (id !== undefined) { value = id + ": " + value; } @@ -118,7 +118,7 @@ * @param {number} value A number between 0 and 1. * @private */ - var convertToMidiValue = function(value) { + const convertToMidiValue = function(value) { /* * Math.round() is important to keep input and output in sync. * Example: @@ -138,7 +138,7 @@ * @returns {object} A new prototype based on parent with the given members * @private */ - var deriveFrom = function(parent, members) { + const deriveFrom = function(parent, members) { return Object.assign(Object.create(parent.prototype), members); }; @@ -150,7 +150,7 @@ * @private * @see `Throttler` */ - var throttle = function(action, owner) { + const throttle = function(action, owner) { if (owner.throttler) { owner.throttler.schedule(action, owner); } else { @@ -166,7 +166,7 @@ * @param {object} options Options object * @public */ - var ParameterComponent = function(options) { + const ParameterComponent = function(options) { components.Component.call(this, options); }; ParameterComponent.prototype = deriveFrom(components.Component, { @@ -188,7 +188,7 @@ * @param {components.Component|components.ComponentContainer} options.target Target component * @public */ - var ShiftButton = function(options) { + const ShiftButton = function(options) { components.Button.call(this, options); }; ShiftButton.prototype = deriveFrom(components.Button, { @@ -210,7 +210,7 @@ * @param {object} options Options object * @public */ - var Trigger = function(options) { + const Trigger = function(options) { components.Component.call(this, options); }; Trigger.prototype = deriveFrom(components.Component, { @@ -226,7 +226,7 @@ * @param {number} options.offValue Value for `off`; optional, default: opposite of `onValue` * @public */ - var CustomButton = function(options) { + const CustomButton = function(options) { options = options || {}; if (options.onValue === undefined) { // do not use '||' to allow 0 options.onValue = 1; @@ -273,7 +273,7 @@ * @public * @see https://github.com/mixxxdj/mixxx/wiki/Script-Timers */ - var Timer = function(options) { + const Timer = function(options) { Object.assign(this, options); this.disable(); }; @@ -314,7 +314,7 @@ * @param {number} options.delay Minimal delay between two consecutive actions (in ms) * @public */ - var Throttler = function(options) { + const Throttler = function(options) { options = options || {}; options.delay = options.delay || 0; Object.assign(this, options); @@ -331,14 +331,14 @@ notify: function() { if (this.jobs.length > 0 && this.acquireLock()) { - var job = this.jobs.shift(); + const job = this.jobs.shift(); job.action.call(job.owner); this.unlockTimer.start(); } }, acquireLock: function() { - var unlocked = !this.locked; + const unlocked = !this.locked; if (unlocked) { this.locked = true; } @@ -359,9 +359,9 @@ * @param {object} options Options object * @public */ - var LongPressButton = function(options) { + const LongPressButton = function(options) { components.Button.call(this, options); - var action = function() { + const action = function() { this.isLongPressed = true; this.onLongPress(); }; @@ -399,9 +399,9 @@ * @param {number} options.blinkDuration Blink duration in ms; optional, default: 500 * @public */ - var BlinkingButton = function(options) { + const BlinkingButton = function(options) { options = options || {}; - var blinkAction = function() { + const blinkAction = function() { this.send(components.Button.prototype.outValueScale.call( this, this.flashing = !this.flashing)); }; @@ -436,14 +436,14 @@ * @param {object} options Options object * @public */ - var DirectionEncoder = function(options) { + const DirectionEncoder = function(options) { components.Encoder.call(this, options); this.previousValue = this.inGetValue(); // available only after call of Encoder constructor }; DirectionEncoder.prototype = deriveFrom(components.Encoder, { min: 0, inValueScale: function(value) { - var direction = 0; + let direction = 0; if (!(this.relative && this.isShifted)) { if (value > this.previousValue || value === this.max) { direction = 1; @@ -471,13 +471,13 @@ * @param {number} options.bound A positive integer defining the range bounds * @public */ - var RangeAwareEncoder = function(options) { + const RangeAwareEncoder = function(options) { components.Encoder.call(this, options); }; RangeAwareEncoder.prototype = deriveFrom(components.Encoder, { outValueScale: function(value) { /* -bound..+bound => 0..1 */ - var normalizedValue = (value + this.bound) / (2 * this.bound); + const normalizedValue = (value + this.bound) / (2 * this.bound); /* 0..1 => 0..127 */ return convertToMidiValue.call(this, normalizedValue); }, @@ -492,7 +492,7 @@ * @param {number} options.bound A positive integer defining the range bounds * @public */ - var RangeAwarePot = function(options) { + const RangeAwarePot = function(options) { components.Pot.call(this, options); }; RangeAwarePot.prototype = deriveFrom(components.Pot, { @@ -512,7 +512,7 @@ * @param {number} options.maxValue A positive integer defining the maximum enumeration value * @public */ - var EnumToggleButton = function(options) { + const EnumToggleButton = function(options) { options = options || {}; if (options.maxValue === undefined && options.values === undefined) { log.error("An EnumToggleButton requires either `values` or a `maxValue`."); @@ -526,9 +526,9 @@ EnumToggleButton.prototype = deriveFrom(components.Button, { input: function(channel, control, value, status, _group) { if (this.isPress(channel, control, value, status)) { - var newValue; + let newValue; if (this.values) { - var index = this.values.indexOf(this.inGetValue()); + const index = this.values.indexOf(this.inGetValue()); newValue = this.values[(index + 1) % this.values.length]; } else { newValue = (this.inGetValue() + 1) % (this.maxValue + 1); @@ -549,7 +549,7 @@ * @public * @see https://github.com/mixxxdj/mixxx/wiki/Midi-Scripting#soft-takeover */ - var EnumEncoder = function(options) { + const EnumEncoder = function(options) { options = options || {}; if (options.values === undefined) { log.error("EnumEncoder constructor was called without specifying enum values."); @@ -563,7 +563,7 @@ }; EnumEncoder.prototype = deriveFrom(components.Encoder, { input: function(_channel, _control, value, _status, _group) { - var scaledValue = this.inValueScale(value); + const scaledValue = this.inValueScale(value); if (!this.softTakeover || this.previousValue === undefined || this.previousValue === this.inGetValue()) { @@ -572,14 +572,14 @@ this.previousValue = scaledValue; }, inValueScale: function(value) { - var normalizedValue = value / this.max; - var index = Math.round(normalizedValue * this.maxIndex); + const normalizedValue = value / this.max; + const index = Math.round(normalizedValue * this.maxIndex); return this.values[index]; }, outValueScale: function(value) { - var index = this.values.indexOf(value); + const index = this.values.indexOf(value); if (index !== -1) { - var normalizedValue = index / this.maxIndex; + const normalizedValue = index / this.maxIndex; return convertToMidiValue.call(this, normalizedValue); } else { log.warn("'" + value + "' is not in supported values " + "[" + this.values + "]"); @@ -595,7 +595,7 @@ * @param {object} options Options object * @public */ - var LoopEncoder = function(options) { + const LoopEncoder = function(options) { options = options || {}; if (options.values === undefined) { /* taken from src/engine/controls/loopingcontrol.cpp */ @@ -620,7 +620,7 @@ * @param {string} options.sizeControl (optional) Name of a control that contains `size` * @public */ - var LoopMoveEncoder = function(options) { + const LoopMoveEncoder = function(options) { options = options || {}; options.inKey = options.inKey || "loop_move"; options.size = options.size || 0.5; @@ -628,8 +628,8 @@ }; LoopMoveEncoder.prototype = deriveFrom(DirectionEncoder, { inValueScale: function(value) { - var direction = DirectionEncoder.prototype.inValueScale.call(this, value); - var beats = this.sizeControl + const direction = DirectionEncoder.prototype.inValueScale.call(this, value); + const beats = this.sizeControl ? engine.getValue(this.group, this.sizeControl) : this.size; return direction * beats; @@ -644,18 +644,18 @@ * @param {object} options Options object * @public */ - var BackLoopButton = function(options) { + const BackLoopButton = function(options) { options = options || {}; options.key = options.key || "loop_enabled"; components.Button.call(this, options); }; BackLoopButton.prototype = deriveFrom(components.Button, { inSetValue: function(value) { - var script = global.script; - var group = this.group; + const script = global.script; + const group = this.group; if (value) { - var loopSize = engine.getValue(group, "beatloop_size"); - var beatjumpSize = engine.getValue(group, "beatjump_size"); + const loopSize = engine.getValue(group, "beatloop_size"); + const beatjumpSize = engine.getValue(group, "beatjump_size"); engine.setValue(group, "beatjump_size", loopSize); script.triggerControl(group, "beatjump_backward"); script.triggerControl(group, "beatloop_activate"); @@ -675,7 +675,7 @@ * (`0`: additive, `1`: constant) * @public */ - var CrossfaderCurvePot = function(options) { + const CrossfaderCurvePot = function(options) { options = options || {}; options.group = options.group || "[Mixer Profile]"; if (options.mode) { @@ -717,7 +717,7 @@ * controller * @public */ - var Publisher = function(options) { + const Publisher = function(options) { if (options.source === undefined) { log.error("Missing source component"); return; @@ -744,16 +744,16 @@ }, }); - var EffectUnit = function(rack, deckGroup) { + const EffectUnit = function(rack, deckGroup) { components.ComponentContainer.call(this); - var effectGroup = "[" + rack + "_" + deckGroup + "_Effect1]"; - var channelGroup = "[" + rack + "_" + deckGroup + "]"; + const effectGroup = `[${rack}_${deckGroup}_Effect1]`; + const channelGroup = `[${rack}_${deckGroup}]`; - var ParameterKnob = function(parameterNumber) { + const ParameterKnob = function(parameterNumber) { components.Pot.call(this, {group: effectGroup, key: "parameter" + parameterNumber}); }; ParameterKnob.prototype = deriveFrom(components.Pot); - var ParameterButton = function(parameterNumber) { + const ParameterButton = function(parameterNumber) { components.Button.call(this, {group: effectGroup, key: "button_parameter" + parameterNumber}); }; ParameterButton.prototype = deriveFrom( @@ -766,16 +766,14 @@ this.mix = new components.Pot({group: channelGroup, key: "mix"}); this.parameterKnobs = new components.ComponentContainer(); - var parameterKnobCount = engine.getValue(effectGroup, "num_parameters"); - for (var knobIndex = 1; knobIndex <= parameterKnobCount; knobIndex++) { - this.parameterKnobs[knobIndex] = new ParameterKnob(knobIndex); - } + const parameterKnobCount = engine.getValue(effectGroup, "num_parameters"); + [...Array(parameterKnobCount)].map((x, i) => i+1).forEach(knobIndex => + this.parameterKnobs[knobIndex] = new ParameterKnob(knobIndex)); this.parameterButtons = new components.ComponentContainer(); - var parameterButtonCount = engine.getValue(effectGroup, "num_button_parameters"); - for (var buttonIndex = 1; buttonIndex <= parameterButtonCount; buttonIndex++) { - this.parameterButtons[buttonIndex] = new ParameterButton(buttonIndex); - } + const parameterButtonCount = engine.getValue(effectGroup, "num_button_parameters"); + [...Array(parameterButtonCount)].map((x, i) => i+1).forEach(buttonIndex => + this.parameterButtons[buttonIndex] = new ParameterButton(buttonIndex)); }; EffectUnit.prototype = deriveFrom(components.ComponentContainer); @@ -800,7 +798,7 @@ * @yields {EqualizerUnit} * @public */ - var EqualizerUnit = function(deckGroup) { + const EqualizerUnit = function(deckGroup) { EffectUnit.call(this, "EqualizerRack1", deckGroup); }; EqualizerUnit.prototype = deriveFrom(EffectUnit); @@ -829,7 +827,7 @@ * @yields {QuickEffectUnit} * @public */ - var QuickEffectUnit = function(deckGroup) { + const QuickEffectUnit = function(deckGroup) { EffectUnit.call(this, "QuickEffectRack1", deckGroup); }; QuickEffectUnit.prototype = deriveFrom(EffectUnit); @@ -841,7 +839,7 @@ * @param {Array} initialContainers Initial container names * @public */ - var ComponentRegistry = function(initialContainers) { + const ComponentRegistry = function(initialContainers) { this.containers = new components.ComponentContainer(); if (Array.isArray(initialContainers)) { initialContainers.forEach(function(name) { this.createContainer(name); }, this); @@ -940,14 +938,14 @@ + stringifyComponent(component) + " without MIDI address"); return; } - var id = findComponentId(component.midi); + const id = findComponentId(component.midi); if (!Object.prototype.hasOwnProperty.call(this.containers, containerName)) { this.createContainer(containerName); } - var container = this.getContainer(containerName); - var store = true; + const container = this.getContainer(containerName); + let store = true; if (Object.prototype.hasOwnProperty.call(container, id)) { - var old = container[id]; + const old = container[id]; if (old !== component) { this.unregister(old, containerName); } else { @@ -974,8 +972,8 @@ */ unregister: function(component, containerName) { log.debug(containerName + ": unregister " + stringifyComponent(component)); - var container = this.getContainer(containerName); - var id = findComponentId(component.midi); + const container = this.getContainer(containerName); + const id = findComponentId(component.midi); delete container[id]; return id; }, @@ -1004,7 +1002,7 @@ * @param {boolean} options.debug Optional flag to emit debug messages to the log * @public */ - var LayerManager = function(options) { + const LayerManager = function(options) { this.componentRegistry = new ComponentRegistry([ LayerManager.prototype.defaultContainerName, LayerManager.prototype.shiftContainerName]); @@ -1024,7 +1022,7 @@ * @private */ defaultLayer: function() { - var defaultContainer = this.componentRegistry.getContainer(this.defaultContainerName); + const defaultContainer = this.componentRegistry.getContainer(this.defaultContainerName); return Object.keys(this.shiftLayer()).reduce( function(shiftCounterparts, name) { shiftCounterparts[name] = defaultContainer[name]; @@ -1054,13 +1052,13 @@ * @private */ findComponent: function(status, control) { - var id = findComponentId([status, control]); + const id = findComponentId([status, control]); if (id === undefined) { return; } - var component = this.activeLayer[id]; + let component = this.activeLayer[id]; if (component === undefined) { - var defaultComponents + const defaultComponents = this.componentRegistry.getContainer(this.defaultContainerName); component = defaultComponents[id]; } @@ -1082,7 +1080,7 @@ * @private */ onRegistry: function(operation, component, shift) { - var layerName = shift === true ? this.shiftContainerName : this.defaultContainerName; + const layerName = shift === true ? this.shiftContainerName : this.defaultContainerName; return operation.call(this.componentRegistry, component, layerName); } /** @@ -1113,7 +1111,7 @@ * @public */ unregister: function(component, shift) { - var id = this.onRegistry(this.componentRegistry.unregister, component, shift); + const id = this.onRegistry(this.componentRegistry.unregister, component, shift); delete this.activeLayer[id]; }, @@ -1177,7 +1175,7 @@ * @public */ input: function(channel, control, value, status /* ignored: ,group */) { - var component = this.findComponent(status, control); + const component = this.findComponent(status, control); if (component === undefined) { return; } @@ -1275,7 +1273,7 @@ * @param {function} components.configurationProvider Mapping configuration provider * @public */ - var GenericMidiController = function(options) { + const GenericMidiController = function(options) { if (!options || typeof options.configurationProvider !== "function") { log.error("The required function 'configurationProvider' is missing."); return; @@ -1297,7 +1295,7 @@ this.controllerId = controllerId; this.debug = debug; - var delay = this.config.throttleDelay; + const delay = this.config.throttleDelay; if (delay > 0) { log.debug("Component registration is throttled using a delay of " + delay + "ms"); this.throttler = new Throttler({delay: delay}); @@ -1367,9 +1365,9 @@ createLayerManager: function(target, deckDefinitions, effectUnitDefinitions, containerDefinitions) { - var layerManager = new LayerManager({debug: this.debug}); - var controller = this; - var registerComponents = function(definition, implementation) { + const layerManager = new LayerManager({debug: this.debug}); + const controller = this; + const registerComponents = function(definition, implementation) { controller.registerComponents(layerManager, definition, implementation); }; @@ -1405,7 +1403,7 @@ if (Array.isArray(context.definitions)) { context.definitions.forEach(function(definition) { throttle(function() { - var implementation = context.factory.call(this, definition, target); + const implementation = context.factory.call(this, definition, target); target.push(implementation); context.register(definition, implementation); }, this); @@ -1429,7 +1427,7 @@ * @private */ createDeck: function(deckDefinition, componentStorage) { - var deck = new components.Deck(deckDefinition.deckNumbers); + const deck = new components.Deck(deckDefinition.deckNumbers); deckDefinition.components.forEach(function(componentDefinition, index) { const options = Object.assign({group: deck.currentDeck}, componentDefinition.options); const definition = Object.assign(componentDefinition, {options: options}); @@ -1478,7 +1476,7 @@ * @private */ createPublisher: function(source, publisherStorage) { - var publisher = new Publisher({source: source}); + const publisher = new Publisher({source: source}); publisherStorage.push(publisher); return publisher; }, @@ -1506,14 +1504,14 @@ /* Add publishers for pots */ if (definition.feedback) { - var triggers = rebindTriggers || []; - var createPublisher = this.createPublisher; // `this` is bound to implementation + const triggers = rebindTriggers || []; + const createPublisher = this.createPublisher; // `this` is bound to implementation implementation.forEachComponent(function(effectComponent) { if (effectComponent instanceof components.Pot) { - var publisher = createPublisher(effectComponent, publisherStorage); - var prototype = Object.getPrototypeOf(effectComponent); + const publisher = createPublisher(effectComponent, publisherStorage); + const prototype = Object.getPrototypeOf(effectComponent); triggers.forEach(function(functionName) { - var delegate = prototype[functionName]; + const delegate = prototype[functionName]; if (typeof delegate === "function") { prototype[functionName] = function() { delegate.apply(this, arguments); @@ -1549,12 +1547,12 @@ * @private */ createEffectUnit: function(effectUnitDefinition, componentStorage) { - var effectUnit = this.setupMidi( + const effectUnit = this.setupMidi( effectUnitDefinition, new components.EffectUnit(effectUnitDefinition.unitNumbers, true), componentStorage, ["onFocusChange", "shift", "unshift"]); - var shiftType = effectUnitDefinition.sendShiftedFor; + const shiftType = effectUnitDefinition.sendShiftedFor; /* * `shiftType` is expected to be a JS component (e.g. `c.Button` or `c.Component`) * which in terms of JS means that it is of type `function`. If something else is given @@ -1579,8 +1577,8 @@ * @private */ createComponentContainer: function(containerDefinition) { - var containerType = containerDefinition.type || components.ComponentContainer; - var container = new containerType(containerDefinition.options); + const containerType = containerDefinition.type || components.ComponentContainer; + const container = new containerType(containerDefinition.options); if (containerDefinition.components) { containerDefinition.components.forEach(function(componentDefinition, index) { const definition = mergeDefinitions({}, containerDefinition.defaultDefinition, componentDefinition); @@ -1601,7 +1599,7 @@ * @private */ createComponent: function(definition) { - var component = null; + let component = null; if (definition && definition.type) { if (definition.type.prototype instanceof components.ComponentContainer) { component = this.createComponentContainer(definition); @@ -1628,14 +1626,14 @@ layerManager.register(implementation, definition && definition.shift === true); } else if (implementation instanceof components.ComponentContainer) { Object.keys(implementation).forEach(function(name) { - var definitionName = definition ? definition[name] : null; + const definitionName = definition ? definition[name] : null; this.registerComponents(layerManager, definitionName, implementation[name]); }, this); } }, }); - var exports = {}; + const exports = {}; exports.deriveFrom = deriveFrom; exports.ParameterComponent = ParameterComponent; exports.ShiftButton = ShiftButton; From 0a1563db6b58cb912412a488b942c85076ca716c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Mon, 27 Jan 2025 00:58:45 +0100 Subject: [PATCH 097/201] Use [Channel1_Stem1] pattern for Stem COs. Improve speed a bit. --- res/qml/EqColumn.qml | 2 +- res/skins/LateNight/stem_channel.xml | 2 +- src/engine/channels/enginedeck.cpp | 25 +++++++++---------- src/engine/channels/enginedeck.h | 1 + src/mixer/basetrackplayer.cpp | 6 ++--- src/mixer/playermanager.h | 12 ++++----- src/test/stemcontrolobjecttest.cpp | 6 ++--- .../allshader/waveformrendererstem.cpp | 12 ++++----- 8 files changed, 32 insertions(+), 34 deletions(-) diff --git a/res/qml/EqColumn.qml b/res/qml/EqColumn.qml index cea2c51006c..0404379cbe8 100644 --- a/res/qml/EqColumn.qml +++ b/res/qml/EqColumn.qml @@ -19,7 +19,7 @@ Column { } function stemGroup(group, index) { - return `${group.substr(0, group.length-1)}Stem${index + 1}]` + return `${group.substr(0, group.length-1)}_Stem${index + 1}]` } Row { diff --git a/res/skins/LateNight/stem_channel.xml b/res/skins/LateNight/stem_channel.xml index 3af404857aa..118f79ad316 100644 --- a/res/skins/LateNight/stem_channel.xml +++ b/res/skins/LateNight/stem_channel.xml @@ -1,5 +1,5 @@