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 248abc25366c8d7703ea4c815396c488b0cd1ca7
parent 70ef5508291afbfbe36b9055b8d982128ebb6ea5
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Sat, 10 Aug 2024 17:12:33 +0200

implement LFO sync

Diffstat:
Msource/nord/n2x/n2xJucePlugin/CMakeLists.txt | 1+
Msource/nord/n2x/n2xJucePlugin/n2xEditor.cpp | 5+++++
Msource/nord/n2x/n2xJucePlugin/n2xEditor.h | 2++
Asource/nord/n2x/n2xJucePlugin/n2xLfo.cpp | 44++++++++++++++++++++++++++++++++++++++++++++
Asource/nord/n2x/n2xJucePlugin/n2xLfo.h | 31+++++++++++++++++++++++++++++++
Msource/nord/n2x/n2xJucePlugin/n2xPart.cpp | 40++++++++++++++++++++++++++++++++++++++++
Msource/nord/n2x/n2xJucePlugin/skins/n2xTrancy/n2xTrancy.json | 6+++---
7 files changed, 126 insertions(+), 3 deletions(-)

diff --git a/source/nord/n2x/n2xJucePlugin/CMakeLists.txt b/source/nord/n2x/n2xJucePlugin/CMakeLists.txt @@ -11,6 +11,7 @@ set(SOURCES n2xOctLed.cpp n2xOctLed.h n2xParameterDrivenLed.cpp n2xParameterDrivenLed.h n2xLcd.cpp n2xLcd.h + n2xLfo.cpp n2xLfo.h n2xPart.cpp n2xPart.h n2xPartLed.cpp n2xPartLed.h n2xParts.cpp n2xParts.h diff --git a/source/nord/n2x/n2xJucePlugin/n2xEditor.cpp b/source/nord/n2x/n2xJucePlugin/n2xEditor.cpp @@ -6,6 +6,7 @@ #include "n2xController.h" #include "n2xFocusedParameter.h" #include "n2xLcd.h" +#include "n2xLfo.h" #include "n2xMasterVolume.h" #include "n2xOctLed.h" #include "n2xPart.h" @@ -74,6 +75,8 @@ namespace n2xJucePlugin m_arp.reset(new Arp(*this)); m_focusedParameter.reset(new FocusedParameter(*this)); m_lcd.reset(new Lcd(*this)); + for(uint8_t i=0; i<m_lfos.size(); ++i) + m_lfos[i].reset(new Lfo(*this, i)); m_masterVolume.reset(new MasterVolume(*this)); m_octLed.reset(new OctLed(*this)); m_parts.reset(new Parts(*this)); @@ -120,6 +123,8 @@ namespace n2xJucePlugin m_arp.reset(); m_focusedParameter.reset(); m_lcd.reset(); + for (auto& lfo : m_lfos) + lfo.reset(); m_masterVolume.reset(); m_octLed.reset(); m_parts.reset(); diff --git a/source/nord/n2x/n2xJucePlugin/n2xEditor.h b/source/nord/n2x/n2xJucePlugin/n2xEditor.h @@ -16,6 +16,7 @@ namespace pluginLib namespace n2xJucePlugin { + class Lfo; class FocusedParameter; class PatchManager; class Controller; @@ -72,6 +73,7 @@ namespace n2xJucePlugin std::unique_ptr<Arp> m_arp; std::unique_ptr<FocusedParameter> m_focusedParameter; std::unique_ptr<Lcd> m_lcd; + std::array<std::unique_ptr<Lfo>, 2> m_lfos; std::unique_ptr<MasterVolume> m_masterVolume; std::unique_ptr<OctLed> m_octLed; std::unique_ptr<Parts> m_parts; diff --git a/source/nord/n2x/n2xJucePlugin/n2xLfo.cpp b/source/nord/n2x/n2xJucePlugin/n2xLfo.cpp @@ -0,0 +1,44 @@ +#include "n2xLfo.h" + +#include "n2xController.h" +#include "n2xEditor.h" + +namespace n2xJucePlugin +{ + Lfo::Lfo(Editor& _editor, const uint8_t _index) + : m_editor(_editor) + , m_index(_index) + , m_button(_editor.findComponentT<juce::Button>(_index ? "PerfLfo2SyncA" : "PerfLfo1SyncA")) + { + m_onCurrentPartChanged.set(_editor.getN2xController().onCurrentPartChanged, [this](const uint8_t&) + { + bind(); + }); + + bind(); + } + + std::string Lfo::getSyncMultiParamName(const uint8_t _part, const uint8_t _lfoIndex) + { + return std::string("PerfLfo") + std::to_string(_lfoIndex+1) + "Sync" + static_cast<char>('A' + _part); + } + + void Lfo::bind() + { + const auto& controller = m_editor.getN2xController(); + + auto* syncMultiParam = controller.getParameter(getSyncMultiParamName(controller.getCurrentPart(), m_index), 0); + + m_onSyncMultiParamChanged.set(syncMultiParam, [this](pluginLib::Parameter* const& _parameter) + { + updateState(_parameter); + }); + + updateState(syncMultiParam); + } + + void Lfo::updateState(const pluginLib::Parameter* _param) const + { + m_button->setToggleState(_param->getUnnormalizedValue() > 0, juce::dontSendNotification); + } +} diff --git a/source/nord/n2x/n2xJucePlugin/n2xLfo.h b/source/nord/n2x/n2xJucePlugin/n2xLfo.h @@ -0,0 +1,31 @@ +#pragma once + +#include "n2xParameterDrivenLed.h" + +namespace juce +{ + class Slider; +} + +namespace n2xJucePlugin +{ + class Lfo + { + public: + Lfo(Editor& _editor, uint8_t _index); + + static std::string getSyncMultiParamName(const uint8_t _part, const uint8_t _lfoIndex); + + private: + void bind(); + void updateState(const pluginLib::Parameter* _param) const; + + Editor& m_editor; + const uint8_t m_index; + juce::Button* m_button; + + pluginLib::ParameterListener m_onSyncMultiParamChanged; + pluginLib::ParameterListener m_onSyncRateParamChanged; + pluginLib::EventListener<uint8_t> m_onCurrentPartChanged; + }; +} diff --git a/source/nord/n2x/n2xJucePlugin/n2xPart.cpp b/source/nord/n2x/n2xJucePlugin/n2xPart.cpp @@ -2,6 +2,7 @@ #include "n2xController.h" #include "n2xEditor.h" +#include "n2xLfo.h" namespace n2xJucePlugin { @@ -26,6 +27,7 @@ namespace n2xJucePlugin auto& controller = m_editor.getN2xController(); + // Midi Channel { juce::PopupMenu menuChannel; @@ -50,6 +52,8 @@ namespace n2xJucePlugin menu.addSubMenu("Midi Channel", menuChannel); } + + // Output Mode { juce::PopupMenu menuOut; @@ -97,6 +101,42 @@ namespace n2xJucePlugin menu.addSubMenu("Output Mode", menuOut); } + // LFO Sync + { + juce::PopupMenu lfoA; + juce::PopupMenu lfoB; + + auto createSyncMenu = [this](juce::PopupMenu& _menu, uint8_t _lfoIndex) + { + const auto paramName = Lfo::getSyncMultiParamName(getPart(), _lfoIndex); + auto* param = m_editor.getN2xController().getParameter(paramName, 0); + const auto v = param->getUnnormalizedValue(); + + auto createEntry = [&_menu, param, v](const char* _name, const uint8_t _v) + { + _menu.addItem(_name, true, _v == v, [param, _v] + { + param->setUnnormalizedValueNotifyingHost(_v, pluginLib::Parameter::Origin::Ui); + }); + }; + + createEntry("Off", 0); + createEntry("2/1", 1); + createEntry("1/1", 2); + createEntry("1/2", 3); + createEntry("1/4", 4); + createEntry("1/8", 5); + createEntry("1/8.", 6); + createEntry("1/16", 7); + }; + + createSyncMenu(lfoA, 0); + createSyncMenu(lfoB, 1); + + menu.addSubMenu("LFO 1 Sync", lfoA); + menu.addSubMenu("LFO 2 Sync", lfoB); + } + menu.showMenuAsync({}); } } diff --git a/source/nord/n2x/n2xJucePlugin/skins/n2xTrancy/n2xTrancy.json b/source/nord/n2x/n2xJucePlugin/skins/n2xTrancy/n2xTrancy.json @@ -114,7 +114,7 @@ { "name" : "Distortion", "parameterAttachment" : { "parameter" : "Distortion"}, "button" : { "isToggle" : "1", "normalImage" : "0", "overImage" : "0", "downImage" : "0", "normalImageOn" : "1", "overImageOn" : "1", "downImageOn" : "1", "x" : "1954", "y" : "705", "width" : "70", "height" : "70", "texture" : "button_round", "tileSizeX" : "70", "tileSizeY" : "70" , "hitOffsetT":20, "hitOffsetB":-20, "hitOffsetR":150} }, - { "name" : "PerfLfo1SyncA", "parameterAttachment" : { "parameter" : "PerfLfo1SyncA"}, "button" : { "isToggle" : "1", "normalImage" : "0", "overImage" : "0", "downImage" : "0", "normalImageOn" : "1", "overImageOn" : "1", "downImageOn" : "1", "x" : "485", "y" : "-13", "width" : "70", "height" : "70", "texture" : "button_round", "tileSizeX" : "70", "tileSizeY" : "70", "hitOffsetT":20, "hitOffsetB":-20, "hitOffsetR":130 } }, + { "name" : "PerfLfo1SyncA", "button" : { "isToggle" : "1", "normalImage" : "0", "overImage" : "0", "downImage" : "0", "normalImageOn" : "1", "overImageOn" : "1", "downImageOn" : "1", "x" : "485", "y" : "-13", "width" : "70", "height" : "70", "texture" : "button_round", "tileSizeX" : "70", "tileSizeY" : "70", "hitOffsetT":20, "hitOffsetB":-20, "hitOffsetR":130 } }, { "name" : "Lfo1Rate", "parameterAttachment" : { "parameter" : "Lfo1Rate" }, "rotary" : { }, "spritesheet" : { "x" : "36", "y" : "88", "width" : "113", "height" : "113", "texture" : "knob_big", "tileSizeX" : "113", "tileSizeY" : "113" } }, { "name" : "Lfo1Level", "parameterAttachment" : { "parameter" : "Lfo1Level" }, "rotary" : { }, "spritesheet" : { "x" : "508", "y" : "88", "width" : "113", "height" : "113", "texture" : "knob_big", "tileSizeX" : "113", "tileSizeY" : "113" } }, @@ -138,7 +138,6 @@ { "name" : "Lfo2Dest_LFO2_OSC1+2", "parameterAttachment" : { "parameter" : "Lfo2Dest", "value":4}, "button" : { "isToggle" : "1", "normalImage" : "0", "overImage" : "0", "downImage" : "0", "normalImageOn" : "1", "overImageOn" : "1", "downImageOn" : "1", "x" : "162", "y" : "45", "width" : "70", "height" : "70", "texture" : "button_round", "tileSizeX" : "70", "tileSizeY" : "70", "hitOffsetT":20, "hitOffsetB":-20, "hitOffsetR":100 } }, { "name" : "Lfo2Dest_LFO2_AMP", "parameterAttachment" : { "parameter" : "Lfo2Dest", "value":3}, "button" : { "isToggle" : "1", "normalImage" : "0", "overImage" : "0", "downImage" : "0", "normalImageOn" : "1", "overImageOn" : "1", "downImageOn" : "1", "x" : "162", "y" : "82", "width" : "70", "height" : "70", "texture" : "button_round", "tileSizeX" : "70", "tileSizeY" : "70", "hitOffsetT":20, "hitOffsetB":-20, "hitOffsetR":100 } }, { "name" : "Lfo2Dest_LFO2_FILTER", "parameterAttachment" : { "parameter" : "Lfo2Dest", "value":7}, "button" : { "isToggle" : "1", "normalImage" : "0", "overImage" : "0", "downImage" : "0", "normalImageOn" : "1", "overImageOn" : "1", "downImageOn" : "1", "x" : "162", "y" : "119", "width" : "70", "height" : "70", "texture" : "button_round", "tileSizeX" : "70", "tileSizeY" : "70", "hitOffsetT":20, "hitOffsetB":-20, "hitOffsetR":100 } }, - { "name" : "PerfLfo2SyncA", "parameterAttachment" : { "parameter" : "PerfLfo2SyncA"}, "button" : { "isToggle" : "1", "normalImage" : "0", "overImage" : "0", "downImage" : "0", "normalImageOn" : "1", "overImageOn" : "1", "downImageOn" : "1", "x" : "350", "y" : "-13", "width" : "70", "height" : "70", "texture" : "button_round", "tileSizeX" : "70", "tileSizeY" : "70", "hitOffsetT":20, "hitOffsetB":-20, "hitOffsetR":130 } }, ] }, @@ -160,7 +159,8 @@ ] }, - { "name" : "ArpEnabled", "button" : { "isToggle" : "1", "normalImage" : "0", "overImage" : "0", "downImage" : "0", "normalImageOn" : "1", "overImageOn" : "1", "downImageOn" : "1", "x" : "485", "y" : "254", "width" : "70", "height" : "70", "texture" : "button_round", "tileSizeX" : "70", "tileSizeY" : "70" , "hitOffsetT":20, "hitOffsetB":-20, "hitOffsetR":90} }, + { "name" : "PerfLfo2SyncA", "button" : { "isToggle" : "1", "normalImage" : "0", "overImage" : "0", "downImage" : "0", "normalImageOn" : "1", "overImageOn" : "1", "downImageOn" : "1", "x" : "350", "y" : "254", "width" : "70", "height" : "70", "texture" : "button_round", "tileSizeX" : "70", "tileSizeY" : "70", "hitOffsetT":20, "hitOffsetB":-20, "hitOffsetR":130 } }, + { "name" : "ArpEnabled", "button" : { "isToggle" : "1", "normalImage" : "0", "overImage" : "0", "downImage" : "0", "normalImageOn" : "1", "overImageOn" : "1", "downImageOn" : "1", "x" : "485", "y" : "254", "width" : "70", "height" : "70", "texture" : "button_round", "tileSizeX" : "70", "tileSizeY" : "70" , "hitOffsetT":20, "hitOffsetB":-20, "hitOffsetR":90} }, { "name" : "Lfo2Rate", "parameterAttachment" : { "parameter" : "Lfo2Rate" }, "rotary" : { }, "spritesheet" : { "x" : "36", "y" : "350", "width" : "113", "height" : "113", "texture" : "knob_big", "tileSizeX" : "113", "tileSizeY" : "113" } },