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 b6d130b97f9c2f349a56f174197cdb03d67bd5f6
parent 8bd1093698cf4fcd4f2198e1357ff64d38f1f091
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Thu, 26 May 2022 13:12:21 +0200

add support for generic condition system to show/hide components based on parameter values

Diffstat:
Msource/jucePlugin/ui3/FxPage.cpp | 45+++++++++------------------------------------
Msource/jucePlugin/ui3/FxPage.h | 22++++++----------------
Msource/jucePlugin/ui3/VirusEditor.cpp | 30++++++++++++------------------
Msource/jucePlugin/ui3/VirusEditor.h | 6+++---
Msource/juceUiLib/CMakeLists.txt | 1+
Asource/juceUiLib/condition.cpp | 26++++++++++++++++++++++++++
Asource/juceUiLib/condition.h | 28++++++++++++++++++++++++++++
Msource/juceUiLib/editor.cpp | 20++++++++++++++++++++
Msource/juceUiLib/editor.h | 4++++
Msource/juceUiLib/editorInterface.h | 1+
Msource/juceUiLib/uiObject.cpp | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msource/juceUiLib/uiObject.h | 10++++++++--
12 files changed, 172 insertions(+), 77 deletions(-)

diff --git a/source/jucePlugin/ui3/FxPage.cpp b/source/jucePlugin/ui3/FxPage.cpp @@ -7,48 +7,21 @@ namespace genericVirusUI { - FxPage::FxPage(VirusEditor& _editor) : m_editor(_editor) + FxPage::FxPage(const VirusEditor& _editor) { - m_reverbContainer = _editor.findComponent("ContainerReverb"); - m_delayContainer = _editor.findComponent("ContainerDelay"); + const auto delayReverbMode = _editor.getController().getParameterIndexByName(Virus::g_paramDelayReverbMode); + const auto p = _editor.getController().getParamValueObject(delayReverbMode); - const auto delayReverbMode = m_editor.getController().getParameterIndexByName(Virus::g_paramDelayReverbMode); - const auto p = m_editor.getController().getParameter(delayReverbMode, 0); + const auto containerReverb = _editor.findComponent("ContainerReverb"); + const auto containerDelay = _editor.findComponent("ContainerDelay"); - if (p) - { - p->getValueObject().addListener(this); - } - - updateReverbDelay(); + m_conditionReverb.reset(new genericUI::Condition(*containerReverb, *p, {2,3,4})); + m_conditionDelay.reset(new genericUI::Condition(*containerDelay, *p, {0,1,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26})); } FxPage::~FxPage() { - const auto delayReverbMode = m_editor.getController().getParameterIndexByName(Virus::g_paramDelayReverbMode); - const auto p = m_editor.getController().getParameter(delayReverbMode, 0); - if(p) - p->getValueObject().removeListener(this); - } - - void FxPage::valueChanged(juce::Value& value) - { - updateReverbDelay(); - } - - void FxPage::updateReverbDelay() const - { - const auto delayReverbMode = m_editor.getController().getParameterIndexByName(Virus::g_paramDelayReverbMode); - const auto p = m_editor.getController().getParameter(delayReverbMode, 0); - - if (!p) - return; - - const auto value = static_cast<int>(p->getValueObject().getValueSource().getValue()); - - const bool isReverb = (value > 1 && value < 5); - - VirusEditor::setEnabled(*m_delayContainer, !isReverb); - VirusEditor::setEnabled(*m_reverbContainer, isReverb); + m_conditionReverb.reset(); + m_conditionDelay.reset(); } } diff --git a/source/jucePlugin/ui3/FxPage.h b/source/jucePlugin/ui3/FxPage.h @@ -1,28 +1,18 @@ #pragma once -#include <juce_audio_processors/juce_audio_processors.h> - -namespace juce -{ - class Component; -} +#include "../juceUiLib/condition.h" namespace genericVirusUI { class VirusEditor; - class FxPage : public juce::Value::Listener + class FxPage { public: - explicit FxPage(VirusEditor& _editor); - ~FxPage() override; - - void valueChanged(juce::Value& value) override; + explicit FxPage(const VirusEditor& _editor); + ~FxPage(); private: - void updateReverbDelay() const; - - VirusEditor& m_editor; - juce::Component* m_reverbContainer = nullptr; - juce::Component* m_delayContainer = nullptr; + std::unique_ptr<genericUI::Condition> m_conditionReverb; + std::unique_ptr<genericUI::Condition> m_conditionDelay; }; } diff --git a/source/jucePlugin/ui3/VirusEditor.cpp b/source/jucePlugin/ui3/VirusEditor.cpp @@ -12,11 +12,11 @@ namespace genericVirusUI { - VirusEditor::VirusEditor(VirusParameterBinding& _binding, AudioPluginAudioProcessor &_processorRef, const std::string& _jsonFilename, const std::string& _skinFolder, std::function<void()> _openMenuCallback) : + VirusEditor::VirusEditor(VirusParameterBinding& _binding, AudioPluginAudioProcessor &_processorRef, const std::string& _jsonFilename, std::string _skinFolder, std::function<void()> _openMenuCallback) : Editor(static_cast<EditorInterface&>(*this)), m_processor(_processorRef), m_parameterBinding(_binding), - m_skinFolder(_skinFolder), + m_skinFolder(std::move(_skinFolder)), m_openMenuCallback(std::move(_openMenuCallback)) { create(_jsonFilename); @@ -28,7 +28,11 @@ namespace genericVirusUI m_tabs.reset(new Tabs(*this)); m_midiPorts.reset(new MidiPorts(*this)); - m_fxPage.reset(new FxPage(*this)); + + // be backwards compatible with old skins + if(!getConditionCountRecursive()) + m_fxPage.reset(new FxPage(*this)); + m_patchBrowser.reset(new PatchBrowser(*this)); m_presetName = findComponentT<juce::Label>("PatchName"); @@ -128,21 +132,6 @@ namespace genericVirusUI return m_processor.getController(); } - void VirusEditor::setEnabled(juce::Component& _component, bool _enable) - { - if(_component.getProperties().contains("disabledAlpha")) - { - const float a = _component.getProperties()["disabledAlpha"]; - - _component.setAlpha(_enable ? 1.0f : a); - _component.setEnabled(_enable); - } - else - { - _component.setVisible(_enable); - } - } - const char* VirusEditor::findNamedResourceByFilename(const std::string& _filename, uint32_t& _size) { for(size_t i=0; i<BinaryData::namedResourceListSize; ++i) @@ -236,6 +225,11 @@ namespace genericVirusUI return true; } + juce::Value* VirusEditor::getParameterValue(int _parameterIndex) + { + return getController().getParamValueObject(_parameterIndex); + } + void VirusEditor::onProgramChange() { m_parts->onProgramChange(); diff --git a/source/jucePlugin/ui3/VirusEditor.h b/source/jucePlugin/ui3/VirusEditor.h @@ -16,7 +16,8 @@ namespace genericVirusUI class VirusEditor : public genericUI::EditorInterface, public genericUI::Editor { public: - VirusEditor(VirusParameterBinding& _binding, AudioPluginAudioProcessor &_processorRef, const std::string& _jsonFilename, const std::string& _skinFolder, std::function<void()> _openMenuCallback); + VirusEditor(VirusParameterBinding& _binding, AudioPluginAudioProcessor &_processorRef, const std::string& _jsonFilename, + std::string _skinFolder, std::function<void()> _openMenuCallback); ~VirusEditor() override; void setPart(size_t _part); @@ -26,8 +27,6 @@ namespace genericVirusUI Virus::Controller& getController() const; - static void setEnabled(juce::Component& _component, bool _enable); - static const char* findNamedResourceByFilename(const std::string& _filename, uint32_t& _size); private: @@ -36,6 +35,7 @@ namespace genericVirusUI bool bindParameter(juce::Button& _target, int _parameterIndex) override; bool bindParameter(juce::ComboBox& _target, int _parameterIndex) override; bool bindParameter(juce::Slider& _target, int _parameterIndex) override; + juce::Value* getParameterValue(int _parameterIndex) override; void onProgramChange(); void onPlayModeChanged(); diff --git a/source/juceUiLib/CMakeLists.txt b/source/juceUiLib/CMakeLists.txt @@ -4,6 +4,7 @@ project(juceUiLib VERSION ${CMAKE_PROJECT_VERSION}) set(SOURCES editor.cpp editor.h editorInterface.h + condition.cpp condition.h image.cpp image.h rotaryStyle.cpp rotaryStyle.h comboboxStyle.cpp comboboxStyle.h diff --git a/source/juceUiLib/condition.cpp b/source/juceUiLib/condition.cpp @@ -0,0 +1,26 @@ +#include "condition.h" + +#include "editor.h" + +namespace genericUI +{ + Condition::Condition(juce::Component& _target, juce::Value& _value, std::set<uint8_t> _values) : m_target(_target), m_value(_value), m_values(std::move(_values)) + { + m_value.addListener(this); + valueChanged(m_value); + } + + Condition::~Condition() + { + m_value.removeListener(this); + } + + void Condition::valueChanged(juce::Value& _value) + { + const auto v = roundToInt(_value.getValueSource().getValue()); + + const auto enable = m_values.find(static_cast<uint8_t>(v)) != m_values.end(); + + Editor::setEnabled(m_target, enable); + } +} diff --git a/source/juceUiLib/condition.h b/source/juceUiLib/condition.h @@ -0,0 +1,28 @@ +#pragma once + +#include <juce_audio_processors/juce_audio_processors.h> + +#include <set> + +#include "condition.h" + +namespace juce +{ + class Value; + class Component; +} + +namespace genericUI +{ + class Condition : juce::Value::Listener + { + public: + Condition(juce::Component& _target, juce::Value& _value, std::set<uint8_t> _values); + ~Condition() override; + void valueChanged(juce::Value& _value) override; + private: + juce::Component& m_target; + juce::Value& m_value; + std::set<uint8_t> m_values; + }; +} diff --git a/source/juceUiLib/editor.cpp b/source/juceUiLib/editor.cpp @@ -201,4 +201,24 @@ namespace genericUI throw std::runtime_error("Failed to find component named " + _name); return comps.empty() ? nullptr : comps.front(); } + + size_t Editor::getConditionCountRecursive() const + { + return m_rootObject ? m_rootObject->getConditionCountRecursive() : 0; + } + + void Editor::setEnabled(juce::Component& _component, const bool _enable) + { + if(_component.getProperties().contains("disabledAlpha")) + { + const float a = _component.getProperties()["disabledAlpha"]; + + _component.setAlpha(_enable ? 1.0f : a); + _component.setEnabled(_enable); + } + else + { + _component.setVisible(_enable); + } + } } diff --git a/source/juceUiLib/editor.h b/source/juceUiLib/editor.h @@ -72,6 +72,10 @@ namespace genericUI return m_tabGroupsByName.size(); } + size_t getConditionCountRecursive() const; + + static void setEnabled(juce::Component& _component, bool _enable); + private: EditorInterface& m_interface; diff --git a/source/juceUiLib/editorInterface.h b/source/juceUiLib/editorInterface.h @@ -10,6 +10,7 @@ namespace genericUI virtual const char* getResourceByFilename(const std::string& _name, uint32_t& _dataSize) = 0; virtual int getParameterIndexByName(const std::string& _name) = 0; + virtual juce::Value* getParameterValue(int _parameterIndex) = 0; virtual bool bindParameter(juce::Slider& _target, int _parameterIndex) = 0; virtual bool bindParameter(juce::Button& _target, int _parameterIndex) = 0; diff --git a/source/juceUiLib/uiObject.cpp b/source/juceUiLib/uiObject.cpp @@ -28,7 +28,7 @@ namespace genericUI m_style.reset(); } - void UiObject::createJuceTree(Editor& _editor) const + void UiObject::createJuceTree(Editor& _editor) { apply(_editor, _editor); @@ -64,7 +64,7 @@ namespace genericUI } } - void UiObject::apply(Editor& _editor, juce::Component& _target) const + void UiObject::apply(Editor& _editor, juce::Component& _target) { const auto x = getPropertyInt("x"); const auto y = getPropertyInt("y"); @@ -80,6 +80,8 @@ namespace genericUI _target.setTopLeftPosition(x, y); _target.setSize(w, h); + + createCondition(_editor, _target); } void UiObject::apply(Editor& _editor, juce::Slider& _target) @@ -240,6 +242,56 @@ namespace genericUI return _default; } + size_t UiObject::getConditionCountRecursive() const + { + size_t count = m_condition ? 1 : 0; + + for (const auto & c : m_children) + count += c->getConditionCountRecursive(); + + return count; + } + + void UiObject::createCondition(Editor& _editor, juce::Component& _target) + { + if(!hasComponent("condition")) + return; + + const auto paramName = getProperty("enableOnParameter"); + + const auto index = _editor.getInterface().getParameterIndexByName(paramName); + + if(index < 0) + throw std::runtime_error("Parameter named " + paramName + " not found"); + + const auto v = _editor.getInterface().getParameterValue(index); + + if(!v) + throw std::runtime_error("Parameter named " + paramName + " not found"); + + const auto conditionValues = getProperty("enableOnValues"); + + size_t start = 0; + + std::set<uint8_t> values; + + for(size_t i=0; i<=conditionValues.size(); ++i) + { + const auto isEnd = i == conditionValues.size() || conditionValues[i] == ',' || conditionValues[i] == ';'; + + if(!isEnd) + continue; + + const auto valueString = conditionValues.substr(start, i - start); + const int val = strtol(valueString.c_str(), nullptr, 10); + values.insert(static_cast<uint8_t>(val)); + + start = i + 1; + } + + m_condition.reset(new Condition(_target, *v, values)); + } + bool UiObject::parse(juce::DynamicObject* _obj) { if (!_obj) diff --git a/source/juceUiLib/uiObject.h b/source/juceUiLib/uiObject.h @@ -6,6 +6,7 @@ #include <string> #include <vector> +#include "condition.h" #include "tabgroup.h" namespace juce @@ -34,11 +35,11 @@ namespace genericUI explicit UiObject(const juce::var& _json); ~UiObject(); - void createJuceTree(Editor& _editor) const; + void createJuceTree(Editor& _editor); void createChildObjects(Editor& _editor, juce::Component& _parent) const; void createTabGroups(Editor& _editor); - void apply(Editor& _editor, juce::Component& _target) const; + void apply(Editor& _editor, juce::Component& _target); void apply(Editor& _editor, juce::Slider& _target); void apply(Editor& _editor, juce::ComboBox& _target); void apply(Editor& _editor, juce::DrawableButton& _target); @@ -54,10 +55,13 @@ namespace genericUI float getPropertyFloat(const std::string& _key, float _default = 0.0f) const; std::string getProperty(const std::string& _key, const std::string& _default = std::string()) const; + size_t getConditionCountRecursive() const; + private: bool hasComponent(const std::string& _component) const; template<typename T> T* createJuceObject(Editor& _editor); template<typename T> T* createJuceObject(Editor& _editor, T* _object); + void createCondition(Editor& _editor, juce::Component& _target); bool parse(juce::DynamicObject* _obj); @@ -75,6 +79,8 @@ namespace genericUI std::vector<std::unique_ptr<juce::Component>> m_juceObjects; std::unique_ptr<UiObjectStyle> m_style; + std::unique_ptr<Condition> m_condition; + TabGroup m_tabGroup; };