diff --git a/src/effects/effectchain.cpp b/src/effects/effectchain.cpp index 42c380e89ea8..e580aa75c41c 100644 --- a/src/effects/effectchain.cpp +++ b/src/effects/effectchain.cpp @@ -219,6 +219,23 @@ void EffectChain::loadChainPreset(EffectChainPresetPointer pChainPreset) { setControlLoadedPresetIndex(presetIndex()); } +bool EffectChain::isEmpty() { + for (const auto& pEffectSlot : std::as_const(m_effectSlots)) { + if (pEffectSlot->isLoaded()) { + return false; + } + } + return true; +} + +bool EffectChain::isEmptyPlaceholderPresetLoaded() { + return isEmpty() && presetName() == kNoEffectString; +} + +void EffectChain::loadEmptyNamelessPreset() { + loadChainPreset(m_pChainPresetManager->createEmptyNamelessChainPreset()); +} + void EffectChain::sendParameterUpdate() { EffectsRequest* pRequest = new EffectsRequest(); pRequest->type = EffectsRequest::SET_EFFECT_CHAIN_PARAMETERS; @@ -309,7 +326,7 @@ EffectSlotPointer EffectChain::getEffectSlot(unsigned int slotNumber) { } void EffectChain::slotControlClear(double v) { - for (EffectSlotPointer pEffectSlot : std::as_const(m_effectSlots)) { + for (const auto& pEffectSlot : std::as_const(m_effectSlots)) { pEffectSlot->slotClear(v); } } @@ -337,8 +354,7 @@ void EffectChain::slotControlChainPresetSelector(double value) { } void EffectChain::slotControlLoadedChainPresetRequest(double value) { - // subtract 1 to make the ControlObject 1-indexed like other ControlObjects - int index = static_cast(value) - 1; + int index = static_cast(value); if (index < 0 || index >= numPresets()) { return; } @@ -346,9 +362,8 @@ void EffectChain::slotControlLoadedChainPresetRequest(double value) { loadChainPreset(presetAtIndex(index)); } -void EffectChain::setControlLoadedPresetIndex(uint index) { - // add 1 to make the ControlObject 1-indexed like other ControlObjects - m_pControlLoadedChainPreset->setAndConfirm(index + 1); +void EffectChain::setControlLoadedPresetIndex(int index) { + m_pControlLoadedChainPreset->setAndConfirm(index); } void EffectChain::slotControlNextChainPreset(double value) { @@ -418,6 +433,9 @@ void EffectChain::disableForInputChannel(const ChannelHandleAndGroup& handleGrou } int EffectChain::presetIndex() const { + // 0-indexed, 0 is the empty '---' preset. + // This can be -1 if the name is not found in the presets list, + // which is default state of standard effect chains. return m_pChainPresetManager->presetIndex(m_presetName); } diff --git a/src/effects/effectchain.h b/src/effects/effectchain.h index f0aadc4ab247..60d171e4fd1a 100644 --- a/src/effects/effectchain.h +++ b/src/effects/effectchain.h @@ -72,6 +72,12 @@ class EffectChain : public QObject { virtual void loadChainPreset(EffectChainPresetPointer pPreset); + bool isEmpty(); + + bool isEmptyPlaceholderPresetLoaded(); + + void loadEmptyNamelessPreset(); + public slots: void slotControlClear(double value); @@ -135,7 +141,7 @@ class EffectChain : public QObject { std::unique_ptr m_pControlNextChainPreset; std::unique_ptr m_pControlPrevChainPreset; - void setControlLoadedPresetIndex(uint index); + void setControlLoadedPresetIndex(int index); // These COs do not affect how the effects are processed; // they are defined here for skins and controller mappings to communicate diff --git a/src/effects/effectslot.cpp b/src/effects/effectslot.cpp index 76cf56ea49ef..0aa518b15577 100644 --- a/src/effects/effectslot.cpp +++ b/src/effects/effectslot.cpp @@ -282,6 +282,14 @@ void EffectSlot::loadEffectInner(const EffectManifestPointer pManifest, return; } + // Don't load an effect into the '---' preset. The preset would remain + // selected in WEffectChainPresetSelector and WEffectChainPresetButton and + // therefore couldn't be used to clear the chain. + // Instead, load an empty, nameless preset, then load the desired effect. + if (m_pChain->isEmptyPlaceholderPresetLoaded()) { + m_pChain->loadEmptyNamelessPreset(); + } + m_pManifest = pManifest; addToEngine(); diff --git a/src/effects/presets/effectchainpresetmanager.cpp b/src/effects/presets/effectchainpresetmanager.cpp index 637f43767ab4..43d7407756b4 100644 --- a/src/effects/presets/effectchainpresetmanager.cpp +++ b/src/effects/presets/effectchainpresetmanager.cpp @@ -38,7 +38,7 @@ EffectChainPresetPointer loadPresetFromFile(const QString& filePath) { return pEffectChainPreset; } -EffectChainPresetPointer createEmptyChainPreset() { +EffectChainPresetPointer createEmptyReadOnlyChainPreset() { EffectManifestPointer pEmptyManifest(new EffectManifest()); pEmptyManifest->setName(kNoEffectString); // Center the Super knob, eliminates the colored (bipolar) knob ring @@ -394,6 +394,20 @@ void EffectChainPresetManager::setPresetOrder( m_effectChainPresets.value(chainPresetName)); } + // After having changed the presets order in DlgPrefEffects, we received the + // new lists. The '---' was not displayed there, so it's not in the list. + // Make sure the empty preset '---' is the first item in the list. + const auto& pEmptyPreset = m_effectChainPresets.value(kNoEffectString); + VERIFY_OR_DEBUG_ASSERT(pEmptyPreset) { + return; + } + int index = m_effectChainPresetsSorted.indexOf(pEmptyPreset); + if (index == -1) { // not in list, re-add it + m_effectChainPresetsSorted.prepend(pEmptyPreset); + } else if (index != 0) { // not first item, move to top + m_effectChainPresetsSorted.move(index, 0); + } + emit effectChainPresetListUpdated(); } @@ -409,7 +423,9 @@ void EffectChainPresetManager::setQuickEffectPresetOrder( m_effectChainPresets.value(chainPresetName)); } - // Ensure empty '---' preset is the first list item + // After having changed the presets order in DlgPrefEffects, we received the + // new lists. The '---' was not displayed there, so it's not in the list. + // Make sure the empty preset '---' is the first item in the list. const auto& pEmptyPreset = m_effectChainPresets.value(kNoEffectString); VERIFY_OR_DEBUG_ASSERT(pEmptyPreset) { return; @@ -612,8 +628,9 @@ void EffectChainPresetManager::resetToDefaults() { prependRemainingPresetsToLists(); // Re-add the empty chain preset - EffectChainPresetPointer pEmptyChainPreset = createEmptyChainPreset(); + EffectChainPresetPointer pEmptyChainPreset = createEmptyReadOnlyChainPreset(); m_effectChainPresets.insert(pEmptyChainPreset->name(), pEmptyChainPreset); + m_effectChainPresetsSorted.prepend(pEmptyChainPreset); m_quickEffectChainPresetsSorted.prepend(pEmptyChainPreset); emit effectChainPresetListUpdated(); @@ -660,6 +677,13 @@ bool EffectChainPresetManager::savePresetXml(EffectChainPresetPointer pPreset) { return success; } +// static +EffectChainPresetPointer EffectChainPresetManager::createEmptyNamelessChainPreset() { + auto pPreset = EffectChainPresetPointer::create(EffectChainPreset()); + pPreset->setName(""); + return pPreset; +} + EffectManifestPointer EffectChainPresetManager::getDefaultEqEffect() { EffectManifestPointer pDefaultEqEffect = m_pBackendManager->getManifest( BiquadFullKillEQEffect::getId(), EffectBackendType::BuiltIn); @@ -705,6 +729,10 @@ EffectsXmlData EffectChainPresetManager::readEffectsXml( QDomElement chainElement = chainNode.toElement(); EffectChainPresetPointer pPreset = EffectChainPresetPointer::create(chainElement); + // Shouldn't happen, see EffectSlot::loadEffectInner + VERIFY_OR_DEBUG_ASSERT(pPreset->name() != kNoEffectString) { + pPreset->setName(""); + } standardEffectChainPresets.append(pPreset); } } @@ -782,8 +810,9 @@ EffectsXmlData EffectChainPresetManager::readEffectsXml( // It will not be visible in the effects preferences. // Note: we also need to take care of this preset in resetToDefaults() and // setQuickEffectPresetOrder(), both called from DlgPrefEffects - EffectChainPresetPointer pEmptyChainPreset = createEmptyChainPreset(); + EffectChainPresetPointer pEmptyChainPreset = createEmptyReadOnlyChainPreset(); m_effectChainPresets.insert(pEmptyChainPreset->name(), pEmptyChainPreset); + m_effectChainPresetsSorted.prepend(pEmptyChainPreset); m_quickEffectChainPresetsSorted.prepend(pEmptyChainPreset); emit effectChainPresetListUpdated(); @@ -896,6 +925,17 @@ void EffectChainPresetManager::saveEffectsXml(QDomDocument* pDoc, const EffectsX QDomElement chainsElement = pDoc->createElement(EffectXml::kChainsRoot); rackElement.appendChild(chainsElement); for (const auto& pPreset : std::as_const(data.standardEffectChainPresets)) { + // Don't store the empty '---' preset. + if (pPreset->name() == kNoEffectString) { + // It must not have any effects loaded. If it has, clear the name. + // See readEffectsXml() for explanation. + VERIFY_OR_DEBUG_ASSERT(pPreset->isEmpty()) { + pPreset->setName(""); + } + else { + continue; + } + } chainsElement.appendChild(pPreset->toXml(pDoc)); } @@ -909,6 +949,11 @@ void EffectChainPresetManager::saveEffectsXml(QDomDocument* pDoc, const EffectsX QDomElement chainPresetListElement = pDoc->createElement(EffectXml::kChainPresetList); for (const auto& pPreset : std::as_const(m_effectChainPresetsSorted)) { + // Don't store the empty '---' preset in the main preset list, + // it won't be exported anyway. + if (pPreset->name() == kNoEffectString) { + continue; + } XmlParse::addElement(*pDoc, chainPresetListElement, EffectXml::kChainPresetName, @@ -920,7 +965,7 @@ void EffectChainPresetManager::saveEffectsXml(QDomDocument* pDoc, const EffectsX QDomElement quickEffectChainPresetListElement = pDoc->createElement(EffectXml::kQuickEffectList); for (const auto& pPreset : std::as_const(m_quickEffectChainPresetsSorted)) { - // Don't store the empty '---' in the QuickEffect preset list + // Same here, don't store the empty '---' preset if (pPreset->name() == kNoEffectString) { continue; } diff --git a/src/effects/presets/effectchainpresetmanager.h b/src/effects/presets/effectchainpresetmanager.h index b1657950d07d..71d8fc48885e 100644 --- a/src/effects/presets/effectchainpresetmanager.h +++ b/src/effects/presets/effectchainpresetmanager.h @@ -76,6 +76,8 @@ class EffectChainPresetManager : public QObject { EffectManifestPointer getDefaultEqEffect(); EffectChainPresetPointer getDefaultQuickEffectPreset(); + static EffectChainPresetPointer createEmptyNamelessChainPreset(); + EffectsXmlData readEffectsXml(const QDomDocument& doc, const QStringList& deckStrings); EffectXmlDataSingleDeck readEffectsXmlSingleDeck( const QDomDocument& doc, const QString& deckString); diff --git a/src/preferences/dialog/dlgprefeffects.cpp b/src/preferences/dialog/dlgprefeffects.cpp index 5daaca2b51ed..4bb742a852e1 100644 --- a/src/preferences/dialog/dlgprefeffects.cpp +++ b/src/preferences/dialog/dlgprefeffects.cpp @@ -167,6 +167,12 @@ void DlgPrefEffects::clearChainInfoDisableButtons() { void DlgPrefEffects::loadChainPresetLists() { QStringList chainPresetNames; for (const auto& pChainPreset : m_pChainPresetManager->getPresetsSorted()) { + // Don't show the empty '---' preset. + // After pushing the changed preferences list back to the preset manager + // it is re-added to the base list. + if (pChainPreset->name() == kNoEffectString) { + continue; + } chainPresetNames << pChainPreset->name(); } auto* pModel = dynamic_cast(chainListView->model()); @@ -174,9 +180,7 @@ void DlgPrefEffects::loadChainPresetLists() { QStringList quickEffectChainPresetNames; for (const auto& pChainPreset : m_pChainPresetManager->getQuickEffectPresetsSorted()) { - // Don't show the empty '---' preset. - // After pushing the changed preferences list back to the preset manager - // it is re-added to the root list. + // Same here, don't show the empty '---' preset. if (pChainPreset->name() == kNoEffectString) { continue; }