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 1b2018955518f1d6e98a224578641e4fa32b49b3
parent 224c0d8d72c970e500d8d7d04fdb8950b232c6f5
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Mon,  5 Aug 2024 22:04:14 +0200

master volume now saved in plugin state

Diffstat:
Msource/nord/n2x/n2xJucePlugin/n2xController.cpp | 30+++++++++++++++++++++++++++++-
Msource/nord/n2x/n2xJucePlugin/n2xController.h | 7++++---
Msource/nord/n2x/n2xJucePlugin/n2xMasterVolume.cpp | 15++++++++++++++-
Msource/nord/n2x/n2xJucePlugin/n2xMasterVolume.h | 9+++++++++
Msource/nord/n2x/n2xLib/n2xdevice.cpp | 4++--
Msource/nord/n2x/n2xLib/n2xmiditypes.h | 7++++---
Msource/nord/n2x/n2xLib/n2xstate.cpp | 36+++++++++++++++++++++++++++++-------
Msource/nord/n2x/n2xLib/n2xstate.h | 11++++++++++-
8 files changed, 101 insertions(+), 18 deletions(-)

diff --git a/source/nord/n2x/n2xJucePlugin/n2xController.cpp b/source/nord/n2x/n2xJucePlugin/n2xController.cpp @@ -43,6 +43,8 @@ namespace n2xJucePlugin requestDump(n2x::SysexByte::MultiRequestBankEditBuffer, 0); // performance edit buffer + requestDump(n2x::SysexByte::EmuGetPotsPosition, 0); + m_currentPartChanged.set(onCurrentPartChanged, [this](const uint8_t& _part) { setMultiParameter(n2x::SelectedChannel, _part); @@ -86,12 +88,33 @@ namespace n2xJucePlugin return {}; } - bool Controller::parseSysexMessage(const pluginLib::SysEx& _msg, synthLib::MidiEventSource) + void Controller::onStateLoaded() + { + requestDump(n2x::SysexByte::EmuGetPotsPosition, 0); + } + + bool Controller::parseSysexMessage(const pluginLib::SysEx& _msg, synthLib::MidiEventSource _source) { if(_msg.size() == n2x::g_singleDumpSize) + { return parseSingleDump(_msg); + } if(_msg.size() == n2x::g_multiDumpSize) + { return parseMultiDump(_msg); + } + + n2x::KnobType knobType; + uint8_t knobValue; + + if(n2x::State::parseKnobSysex(knobType, knobValue, _msg)) + { + if(m_state.receive(_msg, _source)) + { + onKnobChanged(knobType, knobValue); + return true; + } + } return false; } @@ -389,4 +412,9 @@ namespace n2xJucePlugin return PatchManager::getPatchName({multi.begin(), multi.end()}); return getSingleName(_part); } + + bool Controller::getKnobState(uint8_t& _result, const n2x::KnobType _type) const + { + return m_state.getKnobState(_result, _type); + } } diff --git a/source/nord/n2x/n2xJucePlugin/n2xController.h b/source/nord/n2x/n2xJucePlugin/n2xController.h @@ -20,15 +20,14 @@ namespace n2xJucePlugin }; pluginLib::Event<> onProgramChanged; + pluginLib::Event<n2x::KnobType, uint8_t> onKnobChanged; Controller(AudioPluginAudioProcessor&); ~Controller() override; static std::string loadParameterDescriptions(); - void onStateLoaded() override - { - } + void onStateLoaded() override; uint8_t getPartCount() const override { @@ -63,6 +62,8 @@ namespace n2xJucePlugin using pluginLib::Controller::sendSysEx; + bool getKnobState(uint8_t& _result, n2x::KnobType _type) const; + private: n2x::State m_state; pluginLib::EventListener<uint8_t> m_currentPartChanged; diff --git a/source/nord/n2x/n2xJucePlugin/n2xMasterVolume.cpp b/source/nord/n2x/n2xJucePlugin/n2xMasterVolume.cpp @@ -8,7 +8,13 @@ namespace n2xJucePlugin MasterVolume::MasterVolume(const Editor& _editor) : m_editor(_editor), m_volume(_editor.findComponentT<juce::Slider>("MasterVolume")) { m_volume->setRange(0.0f, 255.0f); - m_volume->setValue(255.0f); + + uint8_t currentValue; + + if(_editor.getN2xController().getKnobState(currentValue, n2x::KnobType::MasterVol)) + m_volume->setValue(currentValue, juce::dontSendNotification); + else + m_volume->setValue(255.0f, juce::dontSendNotification); m_volume->onValueChange = [this] { @@ -16,5 +22,12 @@ namespace n2xJucePlugin m_editor.getN2xController().sendSysEx(sysex); }; + + m_onKnobChanged.set(_editor.getN2xController().onKnobChanged, [this](const n2x::KnobType& _type, const unsigned char& _value) + { + if(_type != n2x::KnobType::MasterVol) + return; + m_volume->setValue(_value, juce::dontSendNotification); + }); } } diff --git a/source/nord/n2x/n2xJucePlugin/n2xMasterVolume.h b/source/nord/n2x/n2xJucePlugin/n2xMasterVolume.h @@ -1,5 +1,12 @@ #pragma once +#include "jucePluginLib/event.h" + +namespace n2x +{ + enum class KnobType; +} + namespace juce { class Slider; @@ -18,5 +25,7 @@ namespace n2xJucePlugin const Editor& m_editor; juce::Slider* m_volume; + + pluginLib::EventListener<n2x::KnobType, uint8_t> m_onKnobChanged; }; } diff --git a/source/nord/n2x/n2xLib/n2xdevice.cpp b/source/nord/n2x/n2xLib/n2xdevice.cpp @@ -90,14 +90,14 @@ namespace n2x { if(_ev.sysex.empty()) { - m_state.receive(_ev); + m_state.receive(_response, _ev); auto e = _ev; e.offset += m_numSamplesProcessed + getExtraLatencySamples(); m_hardware.sendMidi(e); } else { - if(m_state.receive(_ev)) + if(m_state.receive(_response, _ev)) return true; m_hardware.sendMidi(_ev); diff --git a/source/nord/n2x/n2xLib/n2xmiditypes.h b/source/nord/n2x/n2xLib/n2xmiditypes.h @@ -14,7 +14,8 @@ namespace n2x MultiDumpBankEditBuffer = 30, MultiDumpBankA, MultiRequestBankEditBuffer = 40, - EmuSetPotPosition = 90, // total dump is: f0, IdClavia, IdDevice, IdN2x, EmuSetPotPosition, KnobType / nibble high, nibble low / f7 + EmuSetPotPosition = 90, // total dump is: f0, IdClavia, IdDevice, IdN2x, EmuSetPotPosition, KnobType / nibble high, nibble low / f7 + EmuGetPotsPosition = 91, }; enum SysexIndex @@ -24,8 +25,8 @@ namespace n2x IdxN2x, IdxMsgType, IdxMsgSpec, - IdxPotPosH, - IdxPotPosL + IdxKnobPosH, + IdxKnobPosL }; enum SingleParam diff --git a/source/nord/n2x/n2xLib/n2xstate.cpp b/source/nord/n2x/n2xLib/n2xstate.cpp @@ -195,6 +195,11 @@ namespace n2x { updateMultiFromSingles(); _state.insert(_state.end(), m_multi.begin(), m_multi.end()); + for (const auto it : m_knobStates) + { + auto knobSysex = createKnobSysex(it.first, it.second); + _state.insert(_state.end(), knobSysex.begin(), knobSysex.end()); + } return true; } @@ -209,7 +214,7 @@ namespace n2x return false; } - bool State::receive(const synthLib::SMidiEvent& _ev) + bool State::receive(std::vector<synthLib::SMidiEvent>& _responses, const synthLib::SMidiEvent& _ev) { if(_ev.sysex.empty()) { @@ -246,9 +251,6 @@ namespace n2x return true; } - if(sysex.size() == g_patchRequestSize) - return false; - if(bank == n2x::SysexByte::EmuSetPotPosition) { KnobType knobType; @@ -256,10 +258,21 @@ namespace n2x if(parseKnobSysex(knobType, knobValue, sysex)) { - m_hardware->setKnobPosition(knobType, knobValue); + if(m_hardware) + m_hardware->setKnobPosition(knobType, knobValue); + m_knobStates[knobType] = knobValue; return true; } } + else if(bank == SysexByte::EmuGetPotsPosition) + { + for (const auto it : m_knobStates) + { + _responses.emplace_back(synthLib::MidiEventSource::Internal); + _responses.back().sysex = createKnobSysex(it.first, it.second); + } + return true; + } return false; } @@ -439,17 +452,26 @@ namespace n2x bool State::parseKnobSysex(KnobType& _type, uint8_t& _value, const std::vector<uint8_t>& _sysex) { - if(_sysex.size() <= SysexIndex::IdxPotPosL) + if(_sysex.size() <= SysexIndex::IdxKnobPosL) return false; if(_sysex[SysexIndex::IdxMsgType] != SysexByte::EmuSetPotPosition) return false; _type = static_cast<KnobType>(_sysex[SysexIndex::IdxMsgSpec]); - _value = static_cast<uint8_t>((_sysex[SysexIndex::IdxPotPosH] << 4) | _sysex[SysexIndex::IdxPotPosL]); + _value = static_cast<uint8_t>((_sysex[SysexIndex::IdxKnobPosH] << 4) | _sysex[SysexIndex::IdxKnobPosL]); return true; } + bool State::getKnobState(uint8_t& _result, const KnobType _type) const + { + const auto it = m_knobStates.find(_type); + if(it == m_knobStates.end()) + return false; + _result = it->second; + return true; + } + void State::send(const synthLib::SMidiEvent& _e) const { if(_e.source == synthLib::MidiEventSource::Plugin) diff --git a/source/nord/n2x/n2xLib/n2xstate.h b/source/nord/n2x/n2xLib/n2xstate.h @@ -3,6 +3,7 @@ #include <array> #include <vector> #include <cstddef> +#include <unordered_map> #include "n2xmiditypes.h" #include "n2xtypes.h" @@ -24,7 +25,12 @@ namespace n2x bool getState(std::vector<uint8_t>& _state); bool setState(const std::vector<uint8_t>& _state); - bool receive(const synthLib::SMidiEvent& _ev); + bool receive(const synthLib::SMidiEvent& _ev) + { + std::vector<synthLib::SMidiEvent> responses; + return receive(responses, _ev); + } + bool receive(std::vector<synthLib::SMidiEvent>& _responses, const synthLib::SMidiEvent& _ev); bool receive(const std::vector<uint8_t>& _data, synthLib::MidiEventSource _source); bool receiveNonSysex(const synthLib::SMidiEvent& _ev); @@ -110,6 +116,8 @@ namespace n2x static std::vector<uint8_t> createKnobSysex(KnobType _type, uint8_t _value); static bool parseKnobSysex(KnobType& _type, uint8_t& _value, const std::vector<uint8_t>& _sysex); + bool getKnobState(uint8_t& _result, KnobType _type) const; + private: template<size_t Size> bool receive(const std::array<uint8_t, Size>& _data) { @@ -124,5 +132,6 @@ namespace n2x Hardware* m_hardware; std::array<SingleDump, 4> m_singles; MultiDump m_multi; + std::unordered_map<KnobType, uint8_t> m_knobStates; }; }