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 aa41a7d1a4b0fafafb706ad58c284d88ba91cbd2
parent 24b9a0ef4f200713c00df6879b0cc187513c541f
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Thu, 12 Sep 2024 20:07:07 +0200

transfer front panel state via custom midi sysex to prevent direct access to device

Diffstat:
Msource/virusJucePlugin/Leds.cpp | 60+++++++++++++++++++++++++++++++++---------------------------
Msource/virusJucePlugin/Leds.h | 11+++++++++--
Msource/virusJucePlugin/Parts.cpp | 13+++++++------
Msource/virusJucePlugin/Parts.h | 5+++++
Msource/virusJucePlugin/VirusController.cpp | 8++++++++
Msource/virusJucePlugin/VirusController.h | 6++++++
Msource/virusLib/device.cpp | 10++++------
Msource/virusLib/device.h | 4+---
Msource/virusLib/frontpanelState.cpp | 77++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msource/virusLib/frontpanelState.h | 10++++++++++
Msource/virusLib/microcontrollerTypes.h | 1+
11 files changed, 160 insertions(+), 45 deletions(-)

diff --git a/source/virusJucePlugin/Leds.cpp b/source/virusJucePlugin/Leds.cpp @@ -10,23 +10,18 @@ namespace genericVirusUI constexpr const char* g_lfoNames[3] = {"Lfo1LedOn", "Lfo2LedOn", "Lfo3LedOn"}; - Leds::Leds(const genericUI::Editor& _editor, virus::VirusProcessor& _processor) : m_processor(_processor), m_logoAnimationEnabled(_processor.getConfig().getBoolValue(g_logoAnimKey, true)) + Leds::Leds(const VirusEditor& _editor, virus::VirusProcessor& _processor) : m_processor(_processor), m_logoAnimationEnabled(_processor.getConfig().getBoolValue(g_logoAnimKey, true)) { + m_onFrontPanelStateChanged.set(_editor.getController().onFrontPanelStateChanged, [this](const virusLib::FrontpanelState& _frontpanelState) + { + onFrontPanelStateChanged(_frontpanelState); + }); + for(size_t i=0; i<m_lfos.size(); ++i) { if(auto* comp = _editor.findComponentT<juce::Component>(g_lfoNames[i], false)) { m_lfos[i].reset(new jucePluginEditorLib::Led(comp)); - m_lfos[i]->setSourceCallback([i, &_processor] - { - auto* d = dynamic_cast<virusLib::Device*>(_processor.getPlugin().getDevice()); - - if(!d) - return 0.0f; - - const auto v = std::clamp(d->getFrontpanelState().m_lfoPhases[i], 0.0f, 1.0f); - return std::pow(1.0f - v, 0.2f); - }); } } @@ -36,22 +31,6 @@ namespace genericVirusUI m_logoLed.reset(new jucePluginEditorLib::Led(logoAnim)); - m_logoLed->setSourceCallback([this, &_processor] - { - if(!m_logoAnimationEnabled) - return 0.0f; - auto* d = dynamic_cast<virusLib::Device*>(_processor.getPlugin().getDevice()); - - if(!d) - return 0.0f; - - const auto& s = d->getFrontpanelState(); - - const auto v = std::clamp(_processor.getModel() == virusLib::DeviceModel::Snow ? s.m_bpm : s.m_logo, 0.0f, 1.0f); - - return std::pow(1.0f - v, 0.2f); - }); - m_logoClickListener.reset(new LogoMouseListener(*this)); m_logoAnim->addMouseListener(m_logoClickListener.get(), false); @@ -65,6 +44,8 @@ namespace genericVirusUI m_logo->setInterceptsMouseClicks(true, true); } } + + onFrontPanelStateChanged(_editor.getController().getFrontpanelState()); } Leds::~Leds() @@ -87,4 +68,29 @@ namespace genericVirusUI m_processor.getConfig().setValue(g_logoAnimKey, m_logoAnimationEnabled); m_processor.getConfig().saveIfNeeded(); } + + void Leds::onFrontPanelStateChanged(const virusLib::FrontpanelState& _frontpanelState) const + { + for(size_t i=0; i<_frontpanelState.m_lfoPhases.size(); ++i) + { + const auto v = std::clamp(_frontpanelState.m_lfoPhases[i], 0.0f, 1.0f); + if(!m_lfos[i]) + continue; + m_lfos[i]->setValue(std::pow(1.0f - v, 0.2f)); + } + + if(m_logoLed) + { + if(!m_logoAnimationEnabled) + { + m_logoLed->setValue(0.0f); + } + else + { + const auto v = std::clamp(m_processor.getModel() == virusLib::DeviceModel::Snow ? _frontpanelState.m_bpm : _frontpanelState.m_logo, 0.0f, 1.0f); + + m_logoLed->setValue(std::pow(1.0f - v, 0.2f)); + } + } + } } diff --git a/source/virusJucePlugin/Leds.h b/source/virusJucePlugin/Leds.h @@ -4,8 +4,9 @@ #include <memory> #include "jucePluginEditorLib/led.h" - +#include "jucePluginLib/event.h" #include "juce_gui_basics/juce_gui_basics.h" +#include "virusLib/frontpanelState.h" namespace juce { @@ -24,6 +25,8 @@ namespace genericUI namespace genericVirusUI { + class VirusEditor; + class Leds { public: @@ -44,7 +47,7 @@ namespace genericVirusUI Leds& m_leds; }; - Leds(const genericUI::Editor& _editor, virus::VirusProcessor& _processor); + Leds(const VirusEditor& _editor, virus::VirusProcessor& _processor); ~Leds(); void toggleLogoAnimation(); @@ -53,6 +56,8 @@ namespace genericVirusUI bool isLogoAnimationEnabled() const { return m_logoAnimationEnabled; } private: + void onFrontPanelStateChanged(const virusLib::FrontpanelState& _frontpanelState) const; + virus::VirusProcessor& m_processor; bool m_logoAnimationEnabled = true; @@ -64,5 +69,7 @@ namespace genericVirusUI juce::Component* m_logoAnim = nullptr; std::unique_ptr<LogoMouseListener> m_logoClickListener; + + pluginLib::EventListener<virusLib::FrontpanelState> m_onFrontPanelStateChanged; }; } diff --git a/source/virusJucePlugin/Parts.cpp b/source/virusJucePlugin/Parts.cpp @@ -71,6 +71,12 @@ namespace genericVirusUI startTimer(1000/20); } + + m_onFrontpanelStateChanged.set(_editor.getController().onFrontPanelStateChanged, [this](const virusLib::FrontpanelState& _frontpanelState) + { + for(size_t i=0; i<m_frontpanelState.m_midiEventReceived.size(); ++i) + m_frontpanelState.m_midiEventReceived[i] |= _frontpanelState.m_midiEventReceived[i]; + }); } Parts::~Parts() = default; @@ -196,12 +202,7 @@ namespace genericVirusUI void Parts::timerCallback() { - auto* device = dynamic_cast<virusLib::Device*>(m_editor.getProcessor().getPlugin().getDevice()); - - if(!device) - return; - - auto& fpState = device->getFrontpanelState(); + auto& fpState = m_frontpanelState; const uint32_t maxPart = m_editor.getController().isMultiMode() ? 16 : 1; diff --git a/source/virusJucePlugin/Parts.h b/source/virusJucePlugin/Parts.h @@ -2,7 +2,9 @@ #include <vector> +#include "jucePluginLib/event.h" #include "juceUiLib/button.h" +#include "virusLib/frontpanelState.h" namespace genericVirusUI { @@ -60,5 +62,8 @@ namespace genericVirusUI std::vector<juce::Component*> m_partActive; std::vector<PartButton*> m_presetName; + + virusLib::FrontpanelState m_frontpanelState; + pluginLib::EventListener<virusLib::FrontpanelState> m_onFrontpanelStateChanged; }; } diff --git a/source/virusJucePlugin/VirusController.cpp b/source/virusJucePlugin/VirusController.cpp @@ -89,6 +89,14 @@ namespace virus pluginLib::MidiPacket::Data data; pluginLib::MidiPacket::ParamValues parameterValues; + if(_msg.size() > 6 && _msg[6] == virusLib::DUMP_EMU_SYNTHSTATE) + { + if(!m_frontpanelState.fromMidiEvent(_msg)) + return false; + onFrontPanelStateChanged(m_frontpanelState); + return true; + } + if(parseMidiPacket(name, data, parameterValues, _msg)) { const auto deviceId = data[pluginLib::MidiDataType::DeviceId]; diff --git a/source/virusJucePlugin/VirusController.h b/source/virusJucePlugin/VirusController.h @@ -4,6 +4,7 @@ #include "jucePluginLib/controller.h" #include "jucePluginLib/event.h" +#include "virusLib/frontpanelState.h" #include "virusLib/microcontrollerTypes.h" #include "virusLib/romfile.h" @@ -14,6 +15,8 @@ namespace virus class Controller : public pluginLib::Controller { public: + pluginLib::Event<virusLib::FrontpanelState> onFrontPanelStateChanged; + struct Patch { std::string name; @@ -155,6 +158,8 @@ namespace virus bool parseSingle(pluginLib::MidiPacket::Data& _data, pluginLib::MidiPacket::AnyPartParamValues& _parameterValues, const pluginLib::SysEx& _msg) const; bool parseSingle(pluginLib::MidiPacket::Data& _data, pluginLib::MidiPacket::AnyPartParamValues& _parameterValues, const pluginLib::SysEx& _msg, MidiPacketType& usedPacketType) const; + const auto& getFrontpanelState() const { return m_frontpanelState; } + private: Singles m_singles; SinglePatch m_singleEditBuffer; // single mode @@ -177,5 +182,6 @@ namespace virus uint8_t m_currentProgram[16]{}; PresetSource m_currentPresetSource[16]{PresetSource::Unknown}; pluginLib::EventListener<const virusLib::ROMFile*> m_onRomChanged; + virusLib::FrontpanelState m_frontpanelState; }; }; // namespace Virus diff --git a/source/virusLib/device.cpp b/source/virusLib/device.cpp @@ -18,6 +18,8 @@ namespace virusLib : m_rom(std::move(_rom)) , m_samplerate(getDeviceSamplerate(_preferredDeviceSamplerate, _hostSamplerate)) { + m_frontpanelStateMidiEvent.source = synthLib::MidiEventSource::Internal; + DspSingle* dsp1; createDspInstances(dsp1, m_dsp2, m_rom, m_samplerate); m_dsp.reset(dsp1); @@ -163,12 +165,8 @@ namespace virusLib m_numSamplesProcessed += static_cast<uint32_t>(_size); - m_frontpanelStateGui.m_lfoPhases = m_frontpanelStateDSP.m_lfoPhases; - m_frontpanelStateGui.m_bpm = m_frontpanelStateDSP.m_bpm; - m_frontpanelStateGui.m_logo = m_frontpanelStateDSP.m_logo; - - for(size_t i=0; i<m_frontpanelStateDSP.m_midiEventReceived.size(); ++i) - m_frontpanelStateGui.m_midiEventReceived[i] |= m_frontpanelStateDSP.m_midiEventReceived[i]; + m_frontpanelStateDSP.toMidiEvent(m_frontpanelStateMidiEvent); + _midiOut.push_back(m_frontpanelStateMidiEvent); } #if !SYNTHLIB_DEMO_MODE diff --git a/source/virusLib/device.h b/source/virusLib/device.h @@ -58,8 +58,6 @@ namespace virusLib static void applyDspMemoryPatches(const DspSingle* _dspA, const DspSingle* _dspB, const ROMFile& _rom); void applyDspMemoryPatches() const; - FrontpanelState& getFrontpanelState() { return m_frontpanelStateGui; } - private: bool sendMidi(const synthLib::SMidiEvent& _ev, std::vector<synthLib::SMidiEvent>& _response) override; void readMidiOut(std::vector<synthLib::SMidiEvent>& _midiOut) override; @@ -76,6 +74,6 @@ namespace virusLib uint32_t m_numSamplesProcessed = 0; float m_samplerate; FrontpanelState m_frontpanelStateDSP; - FrontpanelState m_frontpanelStateGui; + synthLib::SMidiEvent m_frontpanelStateMidiEvent; }; } diff --git a/source/virusLib/frontpanelState.cpp b/source/virusLib/frontpanelState.cpp @@ -1,16 +1,22 @@ #include "frontpanelState.h" +#include "microcontrollerTypes.h" + +#include "synthLib/midiTypes.h" + #include "dsp56kEmu/dsp.h" #include "dsp56kEmu/peripherals.h" namespace virusLib { + static constexpr std::initializer_list<uint8_t> g_midiDumpHeader = {0xf0, 0x00, 0x20, 0x33, 0x01, OMNI_DEVICE_ID, DUMP_EMU_SYNTHSTATE}; + void FrontpanelState::updateLfoPhaseFromTimer(dsp56k::DSP& _dsp, const uint32_t _lfo, const uint32_t _timer, const float _minimumValue/* = 0.0f*/, float _maximumValue/* = 1.0f*/) { updatePhaseFromTimer(m_lfoPhases[_lfo], _dsp, _timer, _minimumValue, _maximumValue); } - void FrontpanelState::updatePhaseFromTimer(float& _target, dsp56k::DSP& _dsp, uint32_t _timer, float _minimumValue, float _maximumValue) + void FrontpanelState::updatePhaseFromTimer(float& _target, dsp56k::DSP& _dsp, const uint32_t _timer, float _minimumValue, float _maximumValue) { const auto* peripherals = _dsp.getPeriph(0); @@ -41,4 +47,73 @@ namespace virusLib _target = (normalized - _minimumValue) * floatRangeInv; } + + void writeFloat(std::vector<uint8_t>& _sysex, const float _v) + { + const auto* ptr = reinterpret_cast<const uint8_t*>(&_v); + for(size_t i=0; i<sizeof(_v); ++i) + _sysex.push_back(ptr[i]); + } + + size_t readFloat(float& _dst, const uint8_t* _s) + { + auto* ptr = reinterpret_cast<uint8_t*>(&_dst); + for(size_t i=0; i<sizeof(_dst); ++i) + ptr[i] = _s[i]; + return sizeof(_dst); + } + + void FrontpanelState::toMidiEvent(synthLib::SMidiEvent& _e) const + { + _e.sysex.assign(g_midiDumpHeader.begin(), g_midiDumpHeader.end()); + + uint32_t midiEvents = 0; + + for(size_t i=0; i<m_midiEventReceived.size(); ++i) + { + if(m_midiEventReceived[i]) + midiEvents |= (1<<i); + } + + _e.sysex.push_back((midiEvents >> 14) & 0x7f); + _e.sysex.push_back((midiEvents >> 7) & 0x7f); + _e.sysex.push_back((midiEvents) & 0x7f); + + for (const auto lfoPhase : m_lfoPhases) + writeFloat(_e.sysex, lfoPhase); + + writeFloat(_e.sysex, m_logo); + writeFloat(_e.sysex, m_bpm); + + _e.sysex.push_back(0xf7); + } + + bool FrontpanelState::fromMidiEvent(const synthLib::SMidiEvent& _e) + { + return fromMidiEvent(_e.sysex); + } + + bool FrontpanelState::fromMidiEvent(const std::vector<uint8_t>& _sysex) + { + if(_sysex.size() < g_midiDumpHeader.size()) + return false; + + const auto* s = &_sysex[g_midiDumpHeader.size()]; + + uint32_t midiEvents = 0; + midiEvents |= static_cast<uint32_t>(*s) << 14; ++s; + midiEvents |= static_cast<uint32_t>(*s) << 7; ++s; + midiEvents |= static_cast<uint32_t>(*s); ++s; + + for(size_t i=0; i<m_midiEventReceived.size(); ++i) + m_midiEventReceived[i] = (midiEvents & (1<<i)) ? true : false; + + for (auto& lfoPhase : m_lfoPhases) + s += readFloat(lfoPhase, s); + + s += readFloat(m_logo, s); + readFloat(m_bpm, s); + + return true; + } } diff --git a/source/virusLib/frontpanelState.h b/source/virusLib/frontpanelState.h @@ -2,6 +2,12 @@ #include <array> #include <cstdint> +#include <vector> + +namespace synthLib +{ + struct SMidiEvent; +} namespace dsp56k { @@ -30,6 +36,10 @@ namespace virusLib static void updatePhaseFromTimer(float& _target, dsp56k::DSP& _dsp, uint32_t _timer, float _minimumValue = 0.0f, float _maximumValue = 1.0f); static void updatePhaseFromTimer(float& _target, const dsp56k::Timers& _timers, uint32_t _timer, float _minimumValue = 0.0f, float _maximumValue = 1.0f); + void toMidiEvent(synthLib::SMidiEvent& _e) const; + bool fromMidiEvent(const synthLib::SMidiEvent& _e); + bool fromMidiEvent(const std::vector<uint8_t>& _sysex); + std::array<bool, 16> m_midiEventReceived; std::array<float, 3> m_lfoPhases; diff --git a/source/virusLib/microcontrollerTypes.h b/source/virusLib/microcontrollerTypes.h @@ -6,6 +6,7 @@ namespace virusLib { enum SysexMessageType : uint8_t { + DUMP_EMU_SYNTHSTATE = 0x09, DUMP_SINGLE = 0x10, DUMP_MULTI = 0x11, REQUEST_SINGLE = 0x30,