commit 635ee4c2342be5c8dc8b8d8c0fec566eec8f340a
parent e5bfaa4d02148f0afaae32f0067f95397436326f
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date: Wed, 31 Jul 2024 12:09:41 +0200
begin state saving
Diffstat:
6 files changed, 356 insertions(+), 17 deletions(-)
diff --git a/source/nord/n2x/n2xLib/CMakeLists.txt b/source/nord/n2x/n2xLib/CMakeLists.txt
@@ -15,6 +15,7 @@ set(SOURCES
n2xmiditypes.h
n2xrom.cpp n2xrom.h
n2xromdata.cpp n2xromdata.h
+ n2xstate.cpp n2xstate.h
n2xtypes.h
)
diff --git a/source/nord/n2x/n2xLib/n2xdevice.cpp b/source/nord/n2x/n2xLib/n2xdevice.cpp
@@ -5,9 +5,8 @@
namespace n2x
{
- Device::Device()
+ Device::Device() : m_state(m_hardware)
{
- m_hardware.reset(new Hardware());
}
float Device::getSamplerate() const
@@ -17,19 +16,17 @@ namespace n2x
bool Device::isValid() const
{
- return m_hardware->isValid();
+ return m_hardware.isValid();
}
bool Device::getState(std::vector<uint8_t>& _state, synthLib::StateType _type)
{
- // TODO
- return false;
+ return m_state.getState(_state);
}
bool Device::setState(const std::vector<uint8_t>& _state, synthLib::StateType _type)
{
- // TODO
- return false;
+ return m_state.setState(_state);
}
uint32_t Device::getChannelCountIn()
@@ -44,33 +41,36 @@ namespace n2x
bool Device::setDspClockPercent(const uint32_t _percent)
{
- bool res = m_hardware->getDSPA().getPeriph().getEsaiClock().setSpeedPercent(_percent);
- res &= m_hardware->getDSPB().getPeriph().getEsaiClock().setSpeedPercent(_percent);
+ bool res = m_hardware.getDSPA().getPeriph().getEsaiClock().setSpeedPercent(_percent);
+ res &= m_hardware.getDSPB().getPeriph().getEsaiClock().setSpeedPercent(_percent);
return res;
}
uint32_t Device::getDspClockPercent() const
{
- return m_hardware->getDSPA().getPeriph().getEsaiClock().getSpeedPercent();
+ return const_cast<Hardware&>(m_hardware).getDSPA().getPeriph().getEsaiClock().getSpeedPercent();
}
uint64_t Device::getDspClockHz() const
{
- return m_hardware->getDSPA().getPeriph().getEsaiClock().getSpeedInHz();
+ return const_cast<Hardware&>(m_hardware).getDSPA().getPeriph().getEsaiClock().getSpeedInHz();
}
void Device::readMidiOut(std::vector<synthLib::SMidiEvent>& _midiOut)
{
- m_hardware->getMidi().read(m_midiOutBuffer);
+ m_hardware.getMidi().read(m_midiOutBuffer);
m_midiParser.write(m_midiOutBuffer);
m_midiOutBuffer.clear();
m_midiParser.getEvents(_midiOut);
+
+ for (const auto& midiOut : _midiOut)
+ m_state.receive(midiOut);
}
void Device::processAudio(const synthLib::TAudioInputs& _inputs, const synthLib::TAudioOutputs& _outputs, size_t _samples)
{
- m_hardware->processAudio(_outputs, static_cast<uint32_t>(_samples), getExtraLatencySamples());
- m_numSamplesProcessed += _samples;
+ m_hardware.processAudio(_outputs, static_cast<uint32_t>(_samples), getExtraLatencySamples());
+ m_numSamplesProcessed += static_cast<uint32_t>(_samples);
}
bool Device::sendMidi(const synthLib::SMidiEvent& _ev, std::vector<synthLib::SMidiEvent>& _response)
@@ -79,11 +79,14 @@ namespace n2x
{
auto e = _ev;
e.offset += m_numSamplesProcessed + getExtraLatencySamples();
- m_hardware->sendMidi(e);
+ m_hardware.sendMidi(e);
}
else
{
- m_hardware->sendMidi(_ev);
+ if(m_state.receive(_ev))
+ return true;
+
+ m_hardware.sendMidi(_ev);
}
return true;
}
diff --git a/source/nord/n2x/n2xLib/n2xdevice.h b/source/nord/n2x/n2xLib/n2xdevice.h
@@ -2,6 +2,9 @@
#include <memory>
+#include "n2xhardware.h"
+#include "n2xstate.h"
+
#include "wLib/wDevice.h"
namespace n2x
@@ -29,7 +32,8 @@ namespace n2x
bool sendMidi(const synthLib::SMidiEvent& _ev, std::vector<synthLib::SMidiEvent>& _response) override;
private:
- std::unique_ptr<Hardware> m_hardware = nullptr;
+ Hardware m_hardware;
+ State m_state;
std::vector<uint8_t> m_midiOutBuffer;
synthLib::MidiBufferParser m_midiParser;
uint32_t m_numSamplesProcessed = 0;
diff --git a/source/nord/n2x/n2xLib/n2xmiditypes.h b/source/nord/n2x/n2xLib/n2xmiditypes.h
@@ -33,4 +33,5 @@ namespace n2x
static constexpr uint32_t g_singleDumpSize = g_singleDataSize + g_sysexContainerSize;
static constexpr uint32_t g_multiDumpSize = g_multiDataSize + g_sysexContainerSize;
+ static constexpr uint32_t g_patchRequestSize = g_sysexContainerSize;
}
diff --git a/source/nord/n2x/n2xLib/n2xstate.cpp b/source/nord/n2x/n2xLib/n2xstate.cpp
@@ -0,0 +1,277 @@
+#include "n2xstate.h"
+
+#include <cassert>
+
+#include "n2xhardware.h"
+#include "synthLib/midiToSysex.h"
+#include "synthLib/midiTypes.h"
+
+namespace n2x
+{
+ static constexpr uint8_t g_singleDefault[] =
+ {
+ 72, // O2Pitch
+ 67, // O2PitchFine
+ 64, // Mix
+ 100, // Cutoff
+ 10, // Resonance
+ 0, // FilterEnvAmount
+ 0, // PW
+ 0, // FmDepth
+ 0, // FilterEnvA
+ 10, // FilterEnvD
+ 100, // FilterEnvS
+ 10, // FilterEnvR
+ 0, // AmpEnvA
+ 10, // AmpEnvD
+ 100, // AmpEnvS
+ 0, // AmpEnvR
+ 0, // Portamento
+ 127, // Gain
+ 0, // ModEnvA
+ 0, // ModEnvD
+ 0, // ModEnvLevel
+ 10, // Lfo1Rate
+ 0, // Lfo1Level
+ 10, // Lfo2Rate
+ 0, // ArpRange
+ 0, // O2PitchSens
+ 0, // O2PitchFineSens
+ 0, // MixSens
+ 0, // CutoffSens
+ 0, // ResonanceSens
+ 0, // FilterEnvAmountSens
+ 0, // PWSens
+ 0, // FmDepthSens
+ 0, // FilterEnvASens
+ 0, // FilterEnvDSens
+ 0, // FilterEnvSSens
+ 0, // FilterEnvRSens
+ 0, // AmpEnvASens
+ 0, // AmpEnvDSens
+ 0, // AmpEnvSSens
+ 0, // AmpEnvRSens
+ 0, // PortamentoSens
+ 0, // GainSens
+ 0, // ModEnvASens
+ 0, // ModEnvDSens
+ 0, // ModEnvLevelSens
+ 0, // Lfo1RateSens
+ 0, // Lfo1LevelSens
+ 0, // Lfo2RateSens
+ 0, // ArpRangeSens
+ 0, // O1Waveform
+ 0, // O2Waveform
+ 0, // Sync/RM/Dist
+ 0, // FilterType
+ 1, // O2Keytrack
+ 0, // FilterKeytrack
+ 0, // Lfo1Waveform
+ 0, // Lfo1Dest
+ 0, // VoiceMode
+ 0, // ModWheelDest
+ 0, // Unison
+ 0, // ModEnvDest
+ 0, // Auto
+ 0, // FilterVelocity
+ 0, // OctaveShift
+ 0, // Lfo2Dest
+ };
+
+ static_assert(std::size(g_singleDefault) == g_singleDataSize/2);
+
+ using MultiDefaultData = std::array<uint8_t, ((g_multiDataSize - g_singleDataSize * 4)>>1)>;
+
+ MultiDefaultData createMultiDefaultData()
+ {
+ uint32_t i=0;
+
+ MultiDefaultData multi{};
+
+ auto set4 = [&](const uint8_t _a, const uint8_t _b, const uint8_t _c, const uint8_t _d)
+ {
+ multi[i++] = _a; multi[i++] = _b; multi[i++] = _c; multi[i++] = _d;
+ };
+
+ set4( 0, 1, 2, 3); // MIDI channel
+ set4( 0, 0, 0, 0); // LFO 1 Sync
+ set4( 0, 0, 0, 0); // LFO 2 Sync
+ set4( 0, 0, 0, 0); // Filter Env Trigger
+ set4( 0, 1, 2, 3); // Filter Env Trigger Midi Channel
+ set4(23,23,23,23); // Filter Env Trigger Note Number
+ set4( 0, 0, 0, 0); // Amp Env Trigger
+ set4( 0, 1, 2, 3); // Amp Env Trigger Midi Channel
+ set4(23,23,23,23); // Amp Env Trigger Note Number
+ set4( 0, 0, 0, 0); // Morph Trigger
+ set4( 0, 1, 2, 3); // Morph Trigger Midi Channel
+ set4(23,23,23,23); // Morph Trigger Note Number
+ multi[i++] = 2; // Bend Range
+ multi[i++] = 2; // Unison Detune
+ multi[i++] = 0; // Out Mode A&B (lower nibble) and C&D (upper nibble)
+ multi[i++] = 15; // Global Midi Channel
+ multi[i++] = 0; // Program Change Enable
+ multi[i++] = 1; // Midi Control
+ multi[i++] = 0; // Master Tune
+ multi[i++] = 0; // Pedal Type
+ multi[i++] = 1; // Local Control
+ multi[i++] = 0; // Keyboard Octave Shift
+ multi[i++] = 0; // Selected Channel
+ multi[i++] = 0; // Arp Midi Out
+ set4(1,0,0,0); // Channel Active
+ set4(0,0,0,0); // Program Select
+ set4(0,0,0,0); // Bank Select
+ set4(7,7,7,7); // Channel Pressure Amount
+ set4(0,0,0,0); // Channel Pressure Dest
+ set4(7,7,7,7); // Expression Pedal Amount
+ set4(0,0,0,0); // Expression Pedal Dest
+ multi[i++] = 0; // Keyboard Split
+ multi[i++] = 64; // Split Point
+
+ assert(i == multi.size());
+
+ return multi;
+ }
+
+ static const MultiDefaultData g_multiDefault = createMultiDefaultData();
+
+ 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);
+ createDefaultMulti(m_multi);
+
+ receive(m_multi);
+ for (const auto& single : m_singles)
+ receive(single);
+ }
+
+ bool State::getState(std::vector<uint8_t>& _state)
+ {
+ _state.reserve(sizeof(m_multi) + sizeof(m_singles));
+ _state.insert(_state.begin(), m_multi.begin(), m_multi.end());
+ for (const auto& single : m_singles)
+ _state.insert(_state.begin(), single.begin(), single.end());
+ return true;
+ }
+
+ bool State::setState(const std::vector<uint8_t>& _state)
+ {
+ std::vector<std::vector<uint8_t>> msgs;
+ synthLib::MidiToSysex::splitMultipleSysex(msgs, _state);
+
+ for (auto& msg : msgs)
+ {
+ synthLib::SMidiEvent e;
+ e.source = synthLib::MidiEventSource::Host;
+ e.sysex = std::move(msg);
+ receive(e);
+ }
+
+ return false;
+ }
+
+ bool State::receive(const synthLib::SMidiEvent& _ev)
+ {
+ auto& sysex = _ev.sysex;
+
+ if(sysex.size() <= SysexIndex::IdxMsgSpec)
+ return false;
+
+ const auto bank = sysex[SysexIndex::IdxMsgType];
+ const auto prog = sysex[SysexIndex::IdxMsgSpec];
+
+ if(sysex.size() == g_singleDumpSize)
+ {
+ if(bank != SysexByte::SingleDumpBankEditBuffer)
+ return false;
+ if(prog > m_singles.size())
+ return false;
+ std::copy(sysex.begin(), sysex.end(), m_singles[prog].begin());
+ send(_ev);
+ return true;
+ }
+
+ if(sysex.size() == g_multiDumpSize)
+ {
+ if(bank != SysexByte::MultiDumpBankEditBuffer)
+ return false;
+ if(prog != 0)
+ return false;
+ std::copy(sysex.begin(), sysex.end(), m_multi.begin());
+ send(_ev);
+ return true;
+ }
+
+ if(sysex.size() == g_patchRequestSize)
+ return false;
+
+ return false;
+ }
+
+ void State::createDefaultSingle(SingleDump& _single, uint8_t _program)
+ {
+ createHeader(_single, SysexByte::SingleDumpBankEditBuffer, _program);
+
+ uint32_t o = IdxMsgSpec + 1;
+
+ for (const auto b : g_singleDefault)
+ {
+ _single[o++] = b & 0xf;
+ _single[o++] = (b>>4) & 0xf;
+ }
+ }
+
+ // ReSharper disable once CppParameterMayBeConstPtrOrRef
+ void State::copySingleToMulti(MultiDump& _multi, const SingleDump& _single, const uint8_t _index)
+ {
+ uint32_t i = SysexIndex::IdxMsgSpec + 1;
+ i += g_singleDataSize * _index;
+ std::copy(_single.begin() + g_sysexHeaderSize, _single.end() - g_sysexFooterSize, _multi.begin() + i);
+ }
+
+ void State::createDefaultMulti(MultiDump& _multi)
+ {
+ createHeader(_multi, SysexByte::MultiDumpBankEditBuffer, 0);
+
+ SingleDump single;
+ createDefaultSingle(single, 0);
+
+ copySingleToMulti(_multi, single, 0);
+ copySingleToMulti(_multi, single, 1);
+ copySingleToMulti(_multi, single, 2);
+ copySingleToMulti(_multi, single, 3);
+
+ uint32_t i = SysexIndex::IdxMsgSpec + 1 + 4 * g_singleDataSize;
+ assert(i == 264 * 2 + g_sysexHeaderSize);
+
+ for (const auto b : g_multiDefault)
+ {
+ _multi[i++] = b & 0xf;
+ _multi[i++] = (b>>4) & 0xf;
+ }
+ assert(i + g_sysexFooterSize == g_multiDumpSize);
+ }
+
+ void State::send(const synthLib::SMidiEvent& _e) const
+ {
+ if(_e.source == synthLib::MidiEventSource::Plugin)
+ return;
+ m_hardware.sendMidi(_e);
+ }
+
+ template<size_t Size>
+ void State::createHeader(std::array<uint8_t, Size>& _buffer, uint8_t _msgType, uint8_t _msgSpec)
+ {
+ _buffer.fill(0);
+
+ _buffer[0] = 0xf0;
+
+ _buffer[SysexIndex::IdxClavia] = SysexByte::IdClavia;
+ _buffer[SysexIndex::IdxDevice] = SysexByte::DefaultDeviceId;
+ _buffer[SysexIndex::IdxN2x] = SysexByte::IdN2X;
+ _buffer[SysexIndex::IdxMsgType] = _msgType;
+ _buffer[SysexIndex::IdxMsgSpec] = _msgSpec;
+
+ _buffer.back() = 0xf7;
+ }
+}
diff --git a/source/nord/n2x/n2xLib/n2xstate.h b/source/nord/n2x/n2xLib/n2xstate.h
@@ -0,0 +1,53 @@
+#pragma once
+
+#include <array>
+#include <vector>
+
+#include "n2xmiditypes.h"
+#include "synthLib/midiTypes.h"
+
+namespace n2x
+{
+ class Hardware;
+
+ class State
+ {
+ public:
+ enum class ReceiveOrder
+ {
+ SingleA, SingleB, SingleC, SingleD, Multi, Count
+ };
+
+ using SingleDump = std::array<uint8_t, g_singleDumpSize>;
+ using MultiDump = std::array<uint8_t, g_multiDumpSize>;
+
+ State(Hardware& _hardware);
+
+ bool getState(std::vector<uint8_t>& _state);
+ bool setState(const std::vector<uint8_t>& _state);
+
+ bool receive(const synthLib::SMidiEvent& _ev);
+
+ static void createDefaultSingle(SingleDump& _single, uint8_t _program);
+ static void copySingleToMulti(MultiDump& _multi, const SingleDump& _single, uint8_t _index);
+ static void createDefaultMulti(MultiDump& _multi);
+
+ template<size_t Size>
+ static void createHeader(std::array<uint8_t, Size>& _buffer, uint8_t _msgType, uint8_t _msgSpec);
+
+ private:
+ template<size_t Size> bool receive(const std::array<uint8_t, Size>& _data)
+ {
+ synthLib::SMidiEvent e;
+ e.sysex.insert(e.sysex.begin(), _data.begin(), _data.end());
+ e.source = synthLib::MidiEventSource::Host;
+ return receive(e);
+ }
+ void send(const synthLib::SMidiEvent& _e) const;
+
+ Hardware& m_hardware;
+ std::array<SingleDump, 4> m_singles;
+ MultiDump m_multi;
+ std::vector<ReceiveOrder> m_receiveOrder;
+ };
+}