diff --git a/Source/cg_ControlGrisAudioProcessor.cpp b/Source/cg_ControlGrisAudioProcessor.cpp index b403179..f7f8242 100644 --- a/Source/cg_ControlGrisAudioProcessor.cpp +++ b/Source/cg_ControlGrisAudioProcessor.cpp @@ -359,8 +359,6 @@ void ControlGrisAudioProcessor::setNumberOfSources(int const numOfSources, bool mPositionSourceLinkEnforcer.numberOfSourcesChanged(); mElevationSourceLinkEnforcer.numberOfSourcesChanged(); - mPresetManager.numberOfSourcesChanged(); - auto const positionSourceLink{ mPositionTrajectoryManager.getSourceLink() }; auto const isSymmetricLink{ positionSourceLink == PositionSourceLink::symmetricX || positionSourceLink == PositionSourceLink::symmetricY }; diff --git a/Source/cg_ControlGrisAudioProcessor.hpp b/Source/cg_ControlGrisAudioProcessor.hpp index 3c14ee7..02fe454 100644 --- a/Source/cg_ControlGrisAudioProcessor.hpp +++ b/Source/cg_ControlGrisAudioProcessor.hpp @@ -96,7 +96,8 @@ class ControlGrisAudioProcessor final PresetsManager mPresetManager{ mFixPositionData, mSources, mPositionSourceLinkEnforcer, - mElevationSourceLinkEnforcer }; + mElevationSourceLinkEnforcer, + mFirstSourceId }; PositionTrajectoryManager mPositionTrajectoryManager{ *this, mSources.getPrimarySource() }; ElevationTrajectoryManager mElevationTrajectoryManager{ *this, mSources.getPrimarySource() }; diff --git a/Source/cg_ControlGrisAudioProcessorEditor.cpp b/Source/cg_ControlGrisAudioProcessorEditor.cpp index b266539..c71775c 100644 --- a/Source/cg_ControlGrisAudioProcessorEditor.cpp +++ b/Source/cg_ControlGrisAudioProcessorEditor.cpp @@ -364,33 +364,29 @@ void ControlGrisAudioProcessorEditor::oscStateChangedCallback(bool const state) //============================================================================== void ControlGrisAudioProcessorEditor::numberOfSourcesChangedCallback(int const numOfSources) { - if (mProcessor.getSources().size() != numOfSources || mIsInsideSetPluginState) { - auto const initSourcePlacement{ mProcessor.getSources().size() != numOfSources }; - auto const currentPositionSourceLink{ mPositionTrajectoryManager.getSourceLink() }; - auto const symmetricLinkAllowed{ numOfSources == 2 }; - mSectionTrajectory.setSymmetricLinkComboState(symmetricLinkAllowed); - if (!symmetricLinkAllowed) { - auto const isCurrentPositionSourceLinkSymmetric{ currentPositionSourceLink == PositionSourceLink::symmetricX - || currentPositionSourceLink - == PositionSourceLink::symmetricY }; - if (isCurrentPositionSourceLinkSymmetric) { - mProcessor.setPositionSourceLink(PositionSourceLink::independent, - SourceLinkEnforcer::OriginOfChange::user); - } - } - - mSelectedSource = {}; - mProcessor.setNumberOfSources(numOfSources); - mSectionGeneralSettings.setNumberOfSources(numOfSources); - mSectionTrajectory.setNumberOfSources(numOfSources); - mSectionSourceSpan.setSelectedSource(&mProcessor.getSources()[mSelectedSource]); - mPositionField.refreshSources(); - mElevationField.refreshSources(); - mSectionSourcePosition.setNumberOfSources(numOfSources, mProcessor.getFirstSourceId()); - if (initSourcePlacement) { - sourcesPlacementChangedCallback(SourcePlacement::leftAlternate); + auto const initSourcePlacement{ mProcessor.getSources().size() != numOfSources }; + auto const currentPositionSourceLink{ mPositionTrajectoryManager.getSourceLink() }; + auto const symmetricLinkAllowed{ numOfSources == 2 }; + mSectionTrajectory.setSymmetricLinkComboState(symmetricLinkAllowed); + if (!symmetricLinkAllowed) { + auto const isCurrentPositionSourceLinkSymmetric{ currentPositionSourceLink == PositionSourceLink::symmetricX + || currentPositionSourceLink + == PositionSourceLink::symmetricY }; + if (isCurrentPositionSourceLinkSymmetric) { + mProcessor.setPositionSourceLink(PositionSourceLink::independent, SourceLinkEnforcer::OriginOfChange::user); } } + mSelectedSource = {}; + mProcessor.setNumberOfSources(numOfSources); + mSectionGeneralSettings.setNumberOfSources(numOfSources); + mSectionTrajectory.setNumberOfSources(numOfSources); + mSectionSourceSpan.setSelectedSource(&mProcessor.getSources()[mSelectedSource]); + mPositionField.refreshSources(); + mElevationField.refreshSources(); + mSectionSourcePosition.setNumberOfSources(numOfSources, mProcessor.getFirstSourceId()); + if (initSourcePlacement) { + sourcesPlacementChangedCallback(SourcePlacement::leftAlternate); + } } //============================================================================== @@ -719,6 +715,10 @@ void ControlGrisAudioProcessorEditor::fieldSourcePositionChangedCallback(SourceI void ControlGrisAudioProcessorEditor::positionPresetChangedCallback(int const presetNumber) { mProcessor.getPresetsManager().forceLoad(presetNumber); + numberOfSourcesChangedCallback(mProcessor.getSources().size()); + + if (auto const presetSourceId {mProcessor.getPresetsManager().getPresetSourceId(presetNumber)}) + firstSourceIdChangedCallback(SourceId{*presetSourceId}); auto * parameter{ mAudioProcessorValueTreeState.getParameter(Automation::Ids::POSITION_PRESET) }; auto const newValue{ static_cast(presetNumber) / static_cast(NUMBER_OF_POSITION_PRESETS) }; diff --git a/Source/cg_LinkStrategies.hpp b/Source/cg_LinkStrategies.hpp index 8e1235b..1cd82f4 100644 --- a/Source/cg_LinkStrategies.hpp +++ b/Source/cg_LinkStrategies.hpp @@ -82,7 +82,7 @@ class Base }; // class Base //============================================================================== -// only use full to recall saved positions +// only useful to recall saved positions class PositionIndependent final : public Base { void computeParameters_implementation(Sources const &, SourcesSnapshots const &) override {} @@ -240,7 +240,7 @@ class PositionDeltaLock final : public Base }; //============================================================================== -// only usefuLl to recall saved positions +// only useful to recall saved positions class ElevationIndependent final : public Base { void computeParameters_implementation(Sources const &, SourcesSnapshots const &) override {} diff --git a/Source/cg_PresetsManager.cpp b/Source/cg_PresetsManager.cpp index d137113..1fc8327 100644 --- a/Source/cg_PresetsManager.cpp +++ b/Source/cg_PresetsManager.cpp @@ -50,11 +50,13 @@ juce::String PresetsManager::PresetsManager(juce::XmlElement & data, Sources & sources, SourceLinkEnforcer & positionLinkEnforcer, - SourceLinkEnforcer & elevationLinkEnforcer) + SourceLinkEnforcer & elevationLinkEnforcer, + SourceId & firstSourceId) : mData(data) , mSources(sources) , mPositionLinkEnforcer(positionLinkEnforcer) , mElevationLinkEnforcer(elevationLinkEnforcer) + , mFirstSourceId(firstSourceId) { } @@ -64,6 +66,15 @@ int PresetsManager::getCurrentPreset() const return mLastLoadedPreset; } +std::optional PresetsManager::getPresetSourceId(int presetNumber) +{ + auto const presetData { getPresetData(presetNumber) }; + if (!presetData || ! (*presetData)->hasAttribute("firstSourceId")) + return {}; + + return (*presetData)->getIntAttribute("firstSourceId"); +} + //============================================================================== bool PresetsManager::loadIfPresetChanged(int const presetNumber) { @@ -91,6 +102,13 @@ bool PresetsManager::load(int const presetNumber) auto const * presetData{ *maybe_presetData }; + if (presetData->hasAttribute ("numberOfSources")) + mSources.setSize(presetData->getIntAttribute("numberOfSources")); + + if (presetData->hasAttribute("firstSourceId")) + mFirstSourceId = SourceId{ presetData->getIntAttribute("firstSourceId") }; + + // Store the preset's source positions in a new SourcesSnapshots SourcesSnapshots snapshots{}; for (auto & source : mSources) { SourceSnapshot snapshot{}; @@ -107,8 +125,8 @@ bool PresetsManager::load(int const presetNumber) snapshot.position = position; auto const zPosId{ getFixedPosSourceName(FixedPositionType::initial, index, 2) }; if (presetData->hasAttribute(zPosId)) { - auto const inversedNormalizedElevation{ static_cast( - presetData->getDoubleAttribute(getFixedPosSourceName(FixedPositionType::initial, index, 2))) }; + auto const elevation {getFixedPosSourceName(FixedPositionType::initial, index, 2)}; + auto const inversedNormalizedElevation{ static_cast(presetData->getDoubleAttribute(elevation)) }; snapshot.z = MAX_ELEVATION * (1.0f - inversedNormalizedElevation); } } @@ -119,6 +137,7 @@ bool PresetsManager::load(int const presetNumber) } } + //load the snapshots into the enforcers, which are references to the ControlGrisAudioProcessor members mPositionLinkEnforcer.loadSnapshots(snapshots); if (mSources.getPrimarySource().getSpatMode() == SpatMode::cube) { mElevationLinkEnforcer.loadSnapshots(snapshots); @@ -128,6 +147,7 @@ bool PresetsManager::load(int const presetNumber) auto const yTerminalPositionId{ getFixedPosSourceName(FixedPositionType::terminal, SourceIndex{ 0 }, 1) }; auto const zTerminalPositionId{ getFixedPosSourceName(FixedPositionType::terminal, SourceIndex{ 0 }, 2) }; + // position the first source juce::Point terminalPosition; if (presetData->hasAttribute(xTerminalPositionId) && presetData->hasAttribute(yTerminalPositionId)) { juce::Point const inversedNormalizedTerminalPosition{ @@ -157,6 +177,9 @@ bool PresetsManager::load(int const presetNumber) } } mLastLoadedPreset = presetNumber; + + // send a change message, which will end up calling SourceLinkEnforcer::enforceSourceLink() + // to position the secondary sources sendChangeMessage(); return true; @@ -185,17 +208,21 @@ std::optional PresetsManager::getPresetData(int const preset std::unique_ptr PresetsManager::createPresetData(int const presetNumber) const { // Build a new fixed position element. - auto result{ std::make_unique("ITEM") }; - result->setAttribute("ID", presetNumber); + auto preset{ std::make_unique("ITEM") }; + preset->setAttribute("ID", presetNumber); auto const & positionSnapshots{ mPositionLinkEnforcer.getSnapshots() }; auto const & elevationsSnapshots{ mElevationLinkEnforcer.getSnapshots() }; + //save the number and initial position of all sources SourceIndex const numberOfSources{ mSources.size() }; + preset->setAttribute("numberOfSources", numberOfSources.get()); + preset->setAttribute("firstSourceId", mFirstSourceId.get()); + for (SourceIndex sourceIndex{}; sourceIndex < numberOfSources; ++sourceIndex) { - auto const xName{ getFixedPosSourceName(FixedPositionType::initial, sourceIndex, 0) }; - auto const yName{ getFixedPosSourceName(FixedPositionType::initial, sourceIndex, 1) }; - auto const zName{ getFixedPosSourceName(FixedPositionType::initial, sourceIndex, 2) }; + auto const curSourceX{ getFixedPosSourceName(FixedPositionType::initial, sourceIndex, 0) }; + auto const curSourceY{ getFixedPosSourceName(FixedPositionType::initial, sourceIndex, 1) }; + auto const curSourceZ{ getFixedPosSourceName(FixedPositionType::initial, sourceIndex, 2) }; auto const position{ positionSnapshots[sourceIndex].position }; auto const elevation{ elevationsSnapshots[sourceIndex].z }; @@ -206,30 +233,27 @@ std::unique_ptr PresetsManager::createPresetData(int const pre auto const mirroredNormalizedPosition{ (mirroredPosition + juce::Point{ 1.0f, 1.0f }) / 2.0f }; auto const inversedNormalizedElevation{ 1.0f - normalizedElevation }; - result->setAttribute(xName, mirroredNormalizedPosition.getX()); - result->setAttribute(yName, mirroredNormalizedPosition.getY()); - result->setAttribute(zName, inversedNormalizedElevation); + preset->setAttribute(curSourceX, mirroredNormalizedPosition.getX()); + preset->setAttribute(curSourceY, mirroredNormalizedPosition.getY()); + preset->setAttribute(curSourceZ, inversedNormalizedElevation); } - auto const xName{ getFixedPosSourceName(FixedPositionType::terminal, SourceIndex{ 0 }, 0) }; - auto const yName{ getFixedPosSourceName(FixedPositionType::terminal, SourceIndex{ 0 }, 1) }; - auto const zName{ getFixedPosSourceName(FixedPositionType::terminal, SourceIndex{ 0 }, 2) }; + //save the terminal position of only the first source + auto const firstSourceX{ getFixedPosSourceName(FixedPositionType::terminal, SourceIndex{ 0 }, 0) }; + auto const firstSourceY{ getFixedPosSourceName(FixedPositionType::terminal, SourceIndex{ 0 }, 1) }; + auto const firstSourceZ{ getFixedPosSourceName(FixedPositionType::terminal, SourceIndex{ 0 }, 2) }; + //The position is stored normalized with inversed Y and elevation auto const position{ mSources.getPrimarySource().getPos() }; - juce::Point const mirroredPosition{ - position.getX(), - position.getY() * -1.0f - }; // For some legacy reason, we store a normalized value with inversed Y. + juce::Point const mirroredPosition{ position.getX(), position.getY() * -1.0f }; auto const inversedNormalizedPosition{ (mirroredPosition + juce::Point{ 1.0f, 1.0f }) / 2.0f }; - auto const inversedNormalizedElevation{ - 1.0f - mSources.getPrimarySource().getNormalizedElevation().get() - }; // Same this happens with elevation. + auto const inversedNormalizedElevation{ 1.0f - mSources.getPrimarySource().getNormalizedElevation().get() }; - result->setAttribute(xName, inversedNormalizedPosition.getX()); - result->setAttribute(yName, inversedNormalizedPosition.getY()); - result->setAttribute(zName, inversedNormalizedElevation); + preset->setAttribute(firstSourceX, inversedNormalizedPosition.getX()); + preset->setAttribute(firstSourceY, inversedNormalizedPosition.getY()); + preset->setAttribute(firstSourceZ, inversedNormalizedElevation); - return result; + return preset; } //============================================================================== @@ -265,12 +289,6 @@ bool PresetsManager::deletePreset(int const presetNumber) const return true; } -//============================================================================== -void PresetsManager::numberOfSourcesChanged() -{ - // TODO -} - //============================================================================== std::array PresetsManager::getSavedPresets() const { diff --git a/Source/cg_PresetsManager.hpp b/Source/cg_PresetsManager.hpp index e638c24..d6c4253 100644 --- a/Source/cg_PresetsManager.hpp +++ b/Source/cg_PresetsManager.hpp @@ -42,6 +42,7 @@ class PresetsManager final : public juce::ChangeBroadcaster Sources & mSources; SourceLinkEnforcer & mPositionLinkEnforcer; SourceLinkEnforcer & mElevationLinkEnforcer; + SourceId & mFirstSourceId; public: //============================================================================== @@ -57,16 +58,17 @@ class PresetsManager final : public juce::ChangeBroadcaster PresetsManager(juce::XmlElement & data, Sources & sources, SourceLinkEnforcer & positionLinkEnforcer, - SourceLinkEnforcer & elevationLinkEnforcer); + SourceLinkEnforcer & elevationLinkEnforcer, + SourceId & firstSourceId); //============================================================================== [[nodiscard]] int getCurrentPreset() const; [[nodiscard]] std::array getSavedPresets() const; + std::optional getPresetSourceId(int presetNumber); bool loadIfPresetChanged(int presetNumber); bool forceLoad(int presetNumber); void save(int presetNumber) const; [[nodiscard]] bool deletePreset(int presetNumber) const; - void numberOfSourcesChanged(); private: //============================================================================== diff --git a/Source/cg_SectionPositionPresets.cpp b/Source/cg_SectionPositionPresets.cpp index d729e82..fc84817 100644 --- a/Source/cg_SectionPositionPresets.cpp +++ b/Source/cg_SectionPositionPresets.cpp @@ -74,7 +74,7 @@ void PresetButton::internalClickCallback(juce::ModifierKeys const & mods) setToggleState(false, juce::NotificationType::dontSendNotification); mLoaded = mSaved = false; } else if (mSaved) { - TextButton::internalClickCallback(mods); + TextButton::internalClickCallback(mods); //this will cause PresetButton::clicked() to be called right above } refresh(); } diff --git a/Source/cg_SourceSnapshot.hpp b/Source/cg_SourceSnapshot.hpp index a94804d..b6cd8f5 100644 --- a/Source/cg_SourceSnapshot.hpp +++ b/Source/cg_SourceSnapshot.hpp @@ -56,6 +56,15 @@ struct SourcesSnapshots { SourceSnapshot & operator[](SourceIndex const index); int size() const { return secondaries.size() + 1; } + juce::String toString () const + { + juce::String ret; + ret += primary.position.toString() + ", " + primary.z.toString() + "; "; + for (auto const & snapshot : secondaries) + ret += snapshot.position.toString() + ", " + snapshot.z.toString() + "; "; + return ret; + } + private: JUCE_LEAK_DETECTOR(SourceSnapshot) }; // class SourcesSnapshots