commit 9efae1c73459eaa91a293a6c4591487b3f0fcaf9
parent a579a7ee30785aed79dc260fdc6e1adc8f5d1559
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date: Thu, 1 Aug 2024 21:47:54 +0200
support performance parameter editing
Diffstat:
5 files changed, 121 insertions(+), 38 deletions(-)
diff --git a/source/nord/n2x/n2xJucePlugin/n2xController.cpp b/source/nord/n2x/n2xJucePlugin/n2xController.cpp
@@ -29,7 +29,7 @@ namespace
namespace n2xJucePlugin
{
- Controller::Controller(AudioPluginAudioProcessor& _p) : pluginLib::Controller(_p, loadParameterDescriptions())
+ Controller::Controller(AudioPluginAudioProcessor& _p) : pluginLib::Controller(_p, loadParameterDescriptions()), m_state(nullptr)
{
registerParams(_p);
@@ -100,7 +100,7 @@ namespace n2xJucePlugin
if(bank == n2x::SysexByte::SingleDumpBankEditBuffer && program < getPartCount())
{
- std::copy(_msg.begin(), _msg.end(), m_singles[program].begin());
+ m_state.receive(_msg, synthLib::MidiEventSource::Plugin);
applyPatchParameters(params, program);
return true;
}
@@ -122,7 +122,7 @@ namespace n2xJucePlugin
if(bank != n2x::SysexByte::MultiDumpBankEditBuffer)
return false;
- std::copy(_msg.begin(), _msg.end(), m_multi.begin());
+ m_state.receive(_msg, synthLib::MidiEventSource::Plugin);
applyPatchParameters(params, 0);
@@ -139,24 +139,31 @@ namespace n2xJucePlugin
const auto origin = midiEventSourceToParameterOrigin(_e.source);
- if(_e.b == n2x::ControlChange::CCSync)
- {
- // this controls both Sync and RingMod
- // Sync = bit 0
- // RingMod = bit 1
- auto* paramSync = getParameter("Sync", _e.a & 0xf);
- auto* paramRingMod = getParameter("RingMod", _e.a & 0xf);
- paramSync->setValueFromSynth(_e.c & 1, origin);
- paramRingMod->setValueFromSynth((_e.c>>1) & 1, origin);
- }
- else
+ m_state.receive(_e);
+
+ const auto parts = m_state.getPartsForMidiChannel(_e);
+
+ for (const uint8_t part : parts)
{
- for (const auto paramIndex : paramIndices)
+ if(_e.b == n2x::ControlChange::CCSync)
{
- auto* param = getParameter(paramIndex);
- assert(param && "parameter not found for control change");
- // TODO: part
- param->setValueFromSynth(_e.c, origin);
+ // this controls both Sync and RingMod
+ // Sync = bit 0
+ // RingMod = bit 1
+ auto* paramSync = getParameter("Sync", part);
+ auto* paramRingMod = getParameter("RingMod", part);
+ paramSync->setValueFromSynth(_e.c & 1, origin);
+ paramRingMod->setValueFromSynth((_e.c>>1) & 1, origin);
+ }
+ else
+ {
+ for (const auto paramIndex : paramIndices)
+ {
+ auto* param = getParameter(paramIndex, part);
+ assert(param && "parameter not found for control change");
+ // TODO: part
+ param->setValueFromSynth(_e.c, origin);
+ }
}
}
@@ -165,6 +172,12 @@ namespace n2xJucePlugin
void Controller::sendParameterChange(const pluginLib::Parameter& _parameter, uint8_t _value)
{
+ if(_parameter.getDescription().page >= 10)
+ {
+ sendPerformanceParameter(_parameter, _value);
+ return;
+ }
+
const auto& controllerMap = getParameterDescriptions().getControllerMap();
const auto& ccs = controllerMap.getControlChanges(synthLib::M_CONTROLCHANGE, _parameter.getParameterIndex());
@@ -182,9 +195,22 @@ namespace n2xJucePlugin
_value = static_cast<uint8_t>(paramSync->getUnnormalizedValue() | (paramRingMod->getUnnormalizedValue() << 1));
}
+ m_state.receive(synthLib::SMidiEvent{synthLib::MidiEventSource::Editor, synthLib::M_CONTROLCHANGE, cc, _value});
sendMidiEvent(synthLib::M_CONTROLCHANGE, cc, _value);
}
+ void Controller::sendPerformanceParameter(const pluginLib::Parameter& _parameter, const uint8_t _value)
+ {
+ const auto& desc = _parameter.getDescription();
+
+ const auto mp = static_cast<n2x::MultiParam>(desc.index + (desc.page - 10) * 128);
+
+ if(!m_state.changeMultiParameter(mp, _value))
+ return;
+ const auto& multi = m_state.updateAndGetMulti();
+ pluginLib::Controller::sendSysEx(pluginLib::SysEx{multi.begin(), multi.end()});
+ }
+
bool Controller::sendSysEx(MidiPacketType _packet, const std::map<pluginLib::MidiDataType, uint8_t>& _params) const
{
return pluginLib::Controller::sendSysEx(midiPacketName(_packet), _params);
diff --git a/source/nord/n2x/n2xJucePlugin/n2xController.h b/source/nord/n2x/n2xJucePlugin/n2xController.h
@@ -30,7 +30,7 @@ namespace n2xJucePlugin
uint8_t getPartCount() override
{
- return static_cast<uint8_t>(m_singles.size());
+ return 4;
}
bool parseSysexMessage(const pluginLib::SysEx&, synthLib::MidiEventSource) override;
@@ -39,6 +39,7 @@ namespace n2xJucePlugin
bool parseControllerMessage(const synthLib::SMidiEvent&) override;
void sendParameterChange(const pluginLib::Parameter& _parameter, uint8_t _value) override;
+ void sendPerformanceParameter(const pluginLib::Parameter& _parameter, uint8_t _value);
bool sendSysEx(MidiPacketType _packet, const std::map<pluginLib::MidiDataType, uint8_t>& _params) const;
void requestDump(uint8_t _bank, uint8_t _patch) const;
@@ -49,7 +50,6 @@ namespace n2xJucePlugin
bool isDerivedParameter(pluginLib::Parameter& _derived, pluginLib::Parameter& _base) const override;
private:
- std::array<n2x::State::SingleDump, 4> m_singles;
- n2x::State::MultiDump m_multi;
+ n2x::State m_state;
};
}
diff --git a/source/nord/n2x/n2xLib/n2xdevice.cpp b/source/nord/n2x/n2xLib/n2xdevice.cpp
@@ -5,7 +5,7 @@
namespace n2x
{
- Device::Device() : m_state(m_hardware)
+ Device::Device() : m_state(&m_hardware)
{
}
diff --git a/source/nord/n2x/n2xLib/n2xstate.cpp b/source/nord/n2x/n2xLib/n2xstate.cpp
@@ -180,7 +180,7 @@ namespace n2x
static const MultiDefaultData g_multiDefault = createMultiDefaultData();
- State::State(Hardware& _hardware) : m_hardware(_hardware)
+ State::State(Hardware* _hardware) : m_hardware(_hardware)
{
for(uint8_t i=0; i<static_cast<uint8_t>(m_singles.size()); ++i)
createDefaultSingle(m_singles[i], i);
@@ -193,9 +193,8 @@ namespace n2x
bool State::getState(std::vector<uint8_t>& _state)
{
+ updateMultiFromSingles();
_state.insert(_state.end(), m_multi.begin(), m_multi.end());
- for (const auto& single : m_singles)
- _state.insert(_state.end(), single.begin(), single.end());
return true;
}
@@ -205,12 +204,7 @@ namespace n2x
synthLib::MidiToSysex::splitMultipleSysex(msgs, _state);
for (auto& msg : msgs)
- {
- synthLib::SMidiEvent e;
- e.source = synthLib::MidiEventSource::Host;
- e.sysex = std::move(msg);
- receive(e);
- }
+ receive(msg, synthLib::MidiEventSource::Host);
return false;
}
@@ -258,6 +252,14 @@ namespace n2x
return false;
}
+ bool State::receive(const std::vector<uint8_t>& _data, synthLib::MidiEventSource _source)
+ {
+ synthLib::SMidiEvent e;
+ e.sysex = _data;
+ e.source = _source;
+ return receive(e);
+ }
+
bool State::receiveNonSysex(const synthLib::SMidiEvent& _ev)
{
switch (_ev.a & 0xf0)
@@ -306,11 +308,41 @@ namespace n2x
default:
for (const auto part : parts)
packNibbles(m_singles[part], offset, _ev.c);
+ return true;
}
}
- break;
+ return false;
+ default:
+ return false;
}
- return false;
+ }
+
+ bool State::changeSingleParameter(const uint8_t _part, const SingleParam _parameter, uint8_t _value)
+ {
+ if(_part >= m_singles.size())
+ return false;
+ const auto off = getOffsetInSingleDump(_parameter);
+ const auto current = unpackNibbles(m_singles[_part], _parameter);
+ if(current == _value)
+ return false;
+ packNibbles(m_singles[_part], off, _value);
+ return true;
+ }
+
+ bool State::changeMultiParameter(const MultiParam _parameter, const uint8_t _value)
+ {
+ const auto off = getOffsetInMultiDump(_parameter);
+ const auto current = unpackNibbles(m_multi, _parameter);
+ if(current == _value)
+ return false;
+ packNibbles(m_multi, off, _value);
+ return true;
+ }
+
+ void State::updateMultiFromSingles()
+ {
+ for(uint8_t i=0; i<static_cast<uint8_t>(m_singles.size()); ++i)
+ copySingleToMulti(m_multi, m_singles[i], i);
}
void State::createDefaultSingle(SingleDump& _single, const uint8_t _program, const uint8_t _bank/* = n2x::SingleDumpBankEditBuffer*/)
@@ -331,7 +363,14 @@ namespace n2x
{
uint32_t i = SysexIndex::IdxMsgSpec + 1;
i += g_singleDataSize * _index;
- std::copy(_single.begin() + g_sysexHeaderSize, _single.end() - g_sysexFooterSize, _multi.begin() + i);
+ std::copy_n(_single.begin() + g_sysexHeaderSize, g_singleDataSize, _multi.begin() + i);
+ }
+
+ void State::extractSingleFromMulti(SingleDump& _single, const MultiDump& _multi, uint8_t _index)
+ {
+ uint32_t i = SysexIndex::IdxMsgSpec + 1;
+ i += g_singleDataSize * _index;
+ std::copy_n(_multi.begin() + i, g_singleDataSize, _single.begin() + g_sysexHeaderSize);
}
void State::createDefaultMulti(MultiDump& _multi, const uint8_t _bank/* = SysexByte::MultiDumpBankEditBuffer*/)
@@ -385,7 +424,8 @@ namespace n2x
{
if(_e.source == synthLib::MidiEventSource::Plugin)
return;
- m_hardware.sendMidi(_e);
+ if(m_hardware)
+ m_hardware->sendMidi(_e);
}
template<size_t Size>
diff --git a/source/nord/n2x/n2xLib/n2xstate.h b/source/nord/n2x/n2xLib/n2xstate.h
@@ -17,16 +17,33 @@ namespace n2x
using SingleDump = std::array<uint8_t, g_singleDumpSize>;
using MultiDump = std::array<uint8_t, g_multiDumpSize>;
- State(Hardware& _hardware);
+ explicit State(Hardware* _hardware);
bool getState(std::vector<uint8_t>& _state);
bool setState(const std::vector<uint8_t>& _state);
bool receive(const synthLib::SMidiEvent& _ev);
+ bool receive(const std::vector<uint8_t>& _data, synthLib::MidiEventSource _source);
+
bool receiveNonSysex(const synthLib::SMidiEvent& _ev);
+ bool changeSingleParameter(uint8_t _part, SingleParam _parameter, uint8_t _value);
+ bool changeMultiParameter(MultiParam _parameter, uint8_t _value);
+
+ void updateMultiFromSingles();
+
+ const auto& getMulti() const { return m_multi; }
+ const auto& getSingle(uint8_t _part) const { return m_singles[_part]; }
+
+ const auto& updateAndGetMulti()
+ {
+ updateMultiFromSingles();
+ return getMulti();
+ }
+
static void createDefaultSingle(SingleDump& _single, uint8_t _program, uint8_t _bank = n2x::SingleDumpBankEditBuffer);
static void copySingleToMulti(MultiDump& _multi, const SingleDump& _single, uint8_t _index);
+ static void extractSingleFromMulti(SingleDump& _single, const MultiDump& _multi, uint8_t _index);
static void createDefaultMulti(MultiDump& _multi, uint8_t _bank = SysexByte::MultiDumpBankEditBuffer);
template<size_t Size>
@@ -88,7 +105,7 @@ namespace n2x
void send(const synthLib::SMidiEvent& _e) const;
- Hardware& m_hardware;
+ Hardware* m_hardware;
std::array<SingleDump, 4> m_singles;
MultiDump m_multi;
};