gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

commit d60227ee878cba8e3d4398c10ffbfe63f209b411
parent a7c089b874a8d0dea67074413e0619e0a41d059c
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Tue, 19 Nov 2024 22:12:36 +0100

rework vm map indicators to be relative to the base parameter value

Diffstat:
Msource/jucePluginEditorLib/focusedParameter.cpp | 9++++++---
Msource/jucePluginEditorLib/focusedParameter.h | 2++
Msource/nord/n2x/n2xJucePlugin/n2xEditor.h | 6++++++
Msource/nord/n2x/n2xJucePlugin/n2xVmMap.cpp | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Msource/nord/n2x/n2xJucePlugin/n2xVmMap.h | 43+++++++++++++++++++++++++++++++++++++++----
5 files changed, 189 insertions(+), 40 deletions(-)

diff --git a/source/jucePluginEditorLib/focusedParameter.cpp b/source/jucePluginEditorLib/focusedParameter.cpp @@ -83,10 +83,13 @@ namespace jucePluginEditorLib void FocusedParameter::onMouseEnter(const juce::MouseEvent& _event) { - auto* component = _event.eventComponent; + updateByComponent(_event.eventComponent); + } - if(component && component->getProperties().contains("parameter")) - updateControlLabel(component, Priority::High); + void FocusedParameter::updateByComponent(juce::Component* _comp) + { + if(_comp && _comp->getProperties().contains("parameter")) + updateControlLabel(_comp, Priority::High); } void FocusedParameter::updateParameter(const std::string& _name, const std::string& _value) diff --git a/source/jucePluginEditorLib/focusedParameter.h b/source/jucePluginEditorLib/focusedParameter.h @@ -41,6 +41,8 @@ namespace jucePluginEditorLib void onMouseEnter(const juce::MouseEvent& _event); + void updateByComponent(juce::Component* _comp); + virtual void updateParameter(const std::string& _name, const std::string& _value); private: diff --git a/source/nord/n2x/n2xJucePlugin/n2xEditor.h b/source/nord/n2x/n2xJucePlugin/n2xEditor.h @@ -59,6 +59,12 @@ namespace n2xJucePlugin return *m_lcd.get(); } + FocusedParameter& getFocusedParameter() const + { + assert(m_focusedParameter); + return *m_focusedParameter; + } + private: void mouseEnter(const juce::MouseEvent& _ev) override; void onBtSave() const; diff --git a/source/nord/n2x/n2xJucePlugin/n2xVmMap.cpp b/source/nord/n2x/n2xJucePlugin/n2xVmMap.cpp @@ -6,28 +6,96 @@ namespace n2xJucePlugin { constexpr const char* g_postfix = "Sens"; - constexpr float g_enabledAlpha = 0.5f; + + class SliderListener : public juce::Slider::Listener + { + public: + SliderListener() = delete; + + SliderListener(const SliderListener&) = delete; + SliderListener(SliderListener&&) = default; + + SliderListener& operator=(const SliderListener&) = delete; + SliderListener& operator=(SliderListener&&) = default; + + SliderListener(juce::Slider* _slider, const std::function<void(juce::Slider*)>& _onValueChanged) : m_slider(_slider), m_onValueChanged(_onValueChanged) + { + _slider->addListener(this); + } + ~SliderListener() override + { + m_slider->removeListener(this); + } + void sliderValueChanged(juce::Slider* _slider) override + { + if (m_onValueChanged) + m_onValueChanged(_slider); + } + private: + juce::Slider* m_slider; + std::function<void(juce::Slider*)> m_onValueChanged; + }; VmMap::VmMap(Editor& _editor, pluginLib::ParameterBinding& _binding) : m_editor(_editor) - , m_binding(_binding) , m_btVmMap(_editor.findComponentT<juce::Button>("VMMAP")) { - const auto& c = _editor.getN2xController(); + auto& c = _editor.getN2xController(); + const auto& descs = c.getParameterDescriptions().getDescriptions(); for (const auto& desc : descs) { - uint32_t idx; + uint32_t idxBase, idxVm; + + const auto& nameBase = desc.name; + const auto& nameVm = desc.name + g_postfix; + + if(c.getParameterDescriptions().getIndexByName(idxBase, nameBase) && + c.getParameterDescriptions().getIndexByName(idxVm, nameVm)) + { + auto* compBase = _editor.findComponentByParamT<juce::Slider>(nameBase); + auto* compVm = _editor.findComponentByParamT<juce::Slider>(nameVm); + + // we do not want vm params to be bound at all, we do this manually. Remove any binding that might still be present in a skin + _binding.unbind(compVm); + + auto vmParam = std::make_unique<VmParam>(); + + vmParam->paramNameBase = nameBase; + vmParam->paramNameVm = nameVm; + vmParam->compBase = compBase; + vmParam->compVm = compVm; - if(c.getParameterDescriptions().getIndexByName(idx, desc.name + g_postfix)) - m_paramNames.insert(desc.name); + vmParam->sliderListenerVm = new SliderListener(compVm, [this, vmParam = vmParam.get()](juce::Slider* _slider) + { + onVmSliderChanged(*vmParam); + }); + + m_vmParams.push_back(std::move(vmParam)); + } } m_btVmMap->onClick = [this] { toggleVmMap(m_btVmMap->getToggleState()); }; + m_onCurrentPartChanged.set(c.onCurrentPartChanged, [this](const uint8_t&/* _part*/) + { + onCurrentPartChanged(); + }); + + bindAll(); + } + + void VmMap::setEnabled(bool _enabled) + { + toggleVmMap(_enabled); + } + + VmMap::VmParam::~VmParam() + { + delete sliderListenerVm; } void VmMap::toggleVmMap(const bool _enabled) @@ -35,48 +103,83 @@ namespace n2xJucePlugin if(m_enabled == _enabled) return; - const auto wasEnabled = m_enabled; - m_enabled = _enabled; - const auto& controller = m_editor.getN2xController(); + for (auto& vmParam : m_vmParams) + { + vmParam->compVm->setInterceptsMouseClicks(_enabled, _enabled); + vmParam->compBase->setInterceptsMouseClicks(!_enabled, !_enabled); + vmParam->compVm->setEnabled(_enabled); + vmParam->compBase->setEnabled(!_enabled); + } + + m_btVmMap->setToggleState(_enabled, juce::dontSendNotification); + } + + void VmMap::onCurrentPartChanged() const + { + bindAll(); + } + + void VmMap::bindAll() const + { + const auto part = m_editor.getN2xController().getCurrentPart(); - // initial setup, collect all default-bound components. Only executed once and delayed - // because in our constructor the components might not be bound yet - if(m_boundComponents.empty()) + for (auto& vmParamPtr : m_vmParams) { - for(const auto& name : m_paramNames) + auto& vmParam = *vmParamPtr; + + auto* paramBase = m_editor.getN2xController().getParameter(vmParam.paramNameBase, part); + auto* paramVm = m_editor.getN2xController().getParameter(vmParam.paramNameVm, part); + + vmParam.paramBase = paramBase; + vmParam.paramVm = paramVm; + + vmParam.parameterListenerBase.set(paramBase, [&vmParam, this](pluginLib::Parameter* _parameter) { - const auto paramIdxDefault = controller.getParameterIndexByName(name); - const auto paramDefault = controller.getParameter(paramIdxDefault); + onBaseParamChanged(vmParam, _parameter); + }); - auto* comp = m_binding.getBoundComponent(paramDefault); + vmParam.parameterListenerVm.set(paramVm, [&vmParam, this](pluginLib::Parameter* _parameter) + { + onVmParamChanged(vmParam, _parameter); + }); - if(comp) - m_boundComponents.insert({name, comp}); - } + updateVmSlider(vmParam); } + } - for (const auto& paramName : m_paramNames) - { - const auto paramIdxDefault = controller.getParameterIndexByName(paramName); - const auto paramIdxVm = controller.getParameterIndexByName(paramName + g_postfix); + void VmMap::onVmSliderChanged(const VmParam& _param) const + { + if (!_param.paramBase || !_param.paramVm) + return; - auto it = m_boundComponents.find(paramName); + int value = juce::roundToInt(_param.compVm->getValue()); + value -= _param.paramBase->getUnnormalizedValue(); - if(it == m_boundComponents.end()) - continue; + _param.paramVm->setUnnormalizedValue(value, pluginLib::Parameter::Origin::Ui); - auto* comp = it->second; + m_editor.getFocusedParameter().updateByComponent(_param.compVm); + } - m_binding.unbind(comp); - m_boundComponents.erase(it); + void VmMap::onBaseParamChanged(const VmParam& _vmParam, pluginLib::Parameter*) + { + updateVmSlider(_vmParam); + } + + void VmMap::onVmParamChanged(const VmParam& _vmParam, pluginLib::Parameter*) + { + updateVmSlider(_vmParam); + } - m_binding.bind(*comp, _enabled ? paramIdxVm : paramIdxDefault, pluginLib::ParameterBinding::CurrentPart); + void VmMap::updateVmSlider(const VmParam& _vmParam) + { + const auto& range = _vmParam.compBase->getRange(); + _vmParam.compVm->setRange(range.getStart(), range.getEnd()); - m_boundComponents.insert({paramName, comp}); + const auto baseValue = _vmParam.paramBase->getUnnormalizedValue(); - comp->setAlpha(_enabled ? g_enabledAlpha : 1.0f); - } + _vmParam.compVm->setValue(baseValue + _vmParam.paramVm->getUnnormalizedValue(), juce::dontSendNotification); + _vmParam.compVm->setDoubleClickReturnValue(true, baseValue); } } diff --git a/source/nord/n2x/n2xJucePlugin/n2xVmMap.h b/source/nord/n2x/n2xJucePlugin/n2xVmMap.h @@ -2,7 +2,10 @@ #include <map> #include <string> -#include <set> +#include <vector> + +#include "n2xFocusedParameter.h" +#include "jucePluginLib/event.h" namespace pluginLib { @@ -11,6 +14,7 @@ namespace pluginLib namespace juce { + class Slider; class Component; class Button; } @@ -19,20 +23,51 @@ namespace n2xJucePlugin { class Editor; + class SliderListener; + class VmMap { public: explicit VmMap(Editor& _editor, pluginLib::ParameterBinding& _binding); + void setEnabled(bool _enabled); + private: + struct VmParam + { + ~VmParam(); + + std::string paramNameBase; + std::string paramNameVm; + + pluginLib::Parameter* paramBase = nullptr; + pluginLib::Parameter* paramVm = nullptr; + + juce::Slider* compBase = nullptr; + juce::Slider* compVm = nullptr; + + pluginLib::ParameterListener parameterListenerBase; + pluginLib::ParameterListener parameterListenerVm; + + SliderListener* sliderListenerVm = nullptr; + }; + void toggleVmMap(bool _enabled); + void onCurrentPartChanged() const; + void bindAll() const; + + void onVmSliderChanged(const VmParam& _param) const; + + static void onBaseParamChanged(const VmParam& _vmParam, pluginLib::Parameter* _baseParam); + static void onVmParamChanged(const VmParam& _vmParam, pluginLib::Parameter* _baseParam); + + static void updateVmSlider(const VmParam& _vmParam); Editor& m_editor; - pluginLib::ParameterBinding& m_binding; - std::set<std::string> m_paramNames; - std::map<std::string, juce::Component*> m_boundComponents; + std::vector<std::unique_ptr<VmParam>> m_vmParams; juce::Button* m_btVmMap; bool m_enabled = false; + pluginLib::EventListener<uint8_t> m_onCurrentPartChanged; }; }