commit b97af0a395a2a1eb5427535986a2e63d6480532b
parent 6984e7c9f169f2df8a7abe63379d4939b43b0c55
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date: Tue, 17 May 2022 21:59:21 +0200
implement generic multi dump parsing
Diffstat:
7 files changed, 205 insertions(+), 130 deletions(-)
diff --git a/source/jucePlugin/VirusController.cpp b/source/jucePlugin/VirusController.cpp
@@ -118,8 +118,8 @@ namespace Virus
void Controller::parseParamChange(const SysEx& msg)
{
- std::map<pluginLib::MidiDataType, uint8_t> params;
- std::map<uint32_t, uint8_t> parameterValues;
+ pluginLib::MidiPacket::Data params;
+ pluginLib::MidiPacket::ParamValues parameterValues;
if(!parseMidiPacket("parameterchange", params, parameterValues, msg))
return;
@@ -188,13 +188,6 @@ namespace Virus
bankNames.add(parseAsciiText(m_singles[bank][i].data, 128 + 112));
return bankNames;
}
- juce::StringArray Controller::getMultiPresetsName() const
- {
- juce::StringArray bankNames;
- for (const auto& m_multi : m_multis)
- bankNames.add(parseAsciiText(m_multi.data, 0));
- return bankNames;
- }
void Controller::setSinglePresetName(uint8_t part, juce::String _name) {
constexpr uint8_t asciiStart = 112;
_name = _name.substring(0,kNameLength).paddedRight(' ', kNameLength);
@@ -251,8 +244,8 @@ namespace Virus
uint8_t Controller::getCurrentPartProgram(uint8_t part) const { return m_currentProgram[part]; }
void Controller::parseSingle(const SysEx &msg)
{
- std::map<pluginLib::MidiDataType, uint8_t> data;
- std::map<uint32_t, uint8_t> parameterValues;
+ pluginLib::MidiPacket::Data data;
+ pluginLib::MidiPacket::ParamValues parameterValues;
if(!parseMidiPacket("singledump", data, parameterValues, msg))
return;
@@ -278,7 +271,7 @@ namespace Virus
for(auto it = parameterValues.begin(); it != parameterValues.end(); ++it)
{
- auto* p = getParameter(it->first, ch);
+ auto* p = getParameter(it->first.second, ch);
p->setValueFromSynth(it->second, true);
for (const auto& linkedParam : p->getLinkedParameters())
@@ -297,51 +290,40 @@ namespace Virus
DBG(progName);
}
- void Controller::parseMulti(const SysEx &msg)
+ void Controller::parseMulti(const SysEx& _msg)
{
- constexpr auto expectedDataSize = 2 + 256;
- constexpr auto checkSumSize = 1;
- const auto dataSize = msg.size() - kHeaderWithMsgCodeLen - 1;
- const auto hasChecksum = dataSize == expectedDataSize + checkSumSize;
- assert(hasChecksum || dataSize == expectedDataSize);
+ pluginLib::MidiPacket::Data data;
+ pluginLib::MidiPacket::ParamValues paramValues;
+
+ if(!parseMidiPacket("multidump", data, paramValues, _msg))
+ return;
constexpr auto startPos = kHeaderWithMsgCodeLen;
- MultiPatch patch;
- patch.bankNumber = msg[startPos];
- patch.progNumber = msg[startPos + 1];
- auto progName = parseAsciiText(msg, startPos + 2 + 3);
- [[maybe_unused]] auto dataSum = copyData(msg, startPos + 2, patch.data);
-
- /* If it's a multi edit buffer, set the part page C single parameters to their multi equivalents */
- if (patch.bankNumber == 0) {
- for (uint8_t pt = 0; pt < 16; pt++) {
- for(int i = 0; i < 8; i++) {
- const auto& params = findSynthParam(pt, virusLib::PAGE_C, virusLib::PART_MIDI_CHANNEL + i);
- for (const auto& p : params)
- p->setValueFromSynth(patch.data[virusLib::MD_PART_MIDI_CHANNEL + (i * 16) + pt], true);
- }
- const auto& params = findSynthParam(pt, virusLib::PAGE_B, virusLib::CLOCK_TEMPO);
- for (const auto& p : params)
- p->setValueFromSynth(patch.data[virusLib::MD_CLOCK_TEMPO], true);
-/* if (auto* p = findSynthParam(pt, virusLib::PAGE_A, virusLib::EFFECT_SEND)) {
- p->setValueFromSynth(patch.data[virusLib::MD_PART_EFFECT_SEND], true);
- }*/
- m_currentBank[pt] = static_cast<virusLib::BankNumber>(patch.data[virusLib::MD_PART_BANK_NUMBER + pt] + 1);
- m_currentProgram[pt] = patch.data[virusLib::MD_PART_PROGRAM_NUMBER + pt];
- }
- }
- if (hasChecksum)
+ const auto bankNumber = data[pluginLib::MidiDataType::Bank];
+
+ auto progName = parseAsciiText(_msg, 13);
+
+ /* If it's a multi edit buffer, set the part page C parameters to their multi equivalents */
+ if (bankNumber == 0)
{
- const int expectedChecksum = msg[msg.size() - 2];
- const auto msgDeviceId = msg[5];
- const int checksum = (msgDeviceId + 0x11 + patch.bankNumber + patch.progNumber + dataSum) & 0x7f;
- assert(checksum == expectedChecksum);
- }
- if (patch.bankNumber == 0) {
- m_currentMulti = patch;
- } else {
- m_multis[patch.progNumber] = patch;
+ for (const auto & paramValue : paramValues)
+ {
+ const auto part = paramValue.first.first;
+ const auto index = paramValue.first.second;
+ const auto value = paramValue.second;
+
+ auto* param = getParameter(index, part);
+ if(!param)
+ continue;
+
+ const auto& desc = param->getDescription();
+
+ if(desc.page != virusLib::PAGE_C)
+ continue;
+
+ param->setValueFromSynth(value);
+ }
}
}
diff --git a/source/jucePlugin/VirusController.h b/source/jucePlugin/VirusController.h
@@ -59,7 +59,6 @@ namespace Virus
return m_singles;
}
- juce::StringArray getMultiPresetsName() const;
void setSinglePresetName(uint8_t part, juce::String _name);
bool isMultiMode()
{
@@ -102,9 +101,7 @@ namespace Virus
private:
void timerCallback() override;
- Multis m_multis; // RAM has 128 Multi 'snapshots'
Singles m_singles;
- MultiPatch m_currentMulti;
// unchecked copy for patch data bytes
static inline uint8_t copyData(const SysEx &src, int startPos, std::array<uint8_t, kDataSizeInBytes>& dst);
diff --git a/source/jucePluginLib/controller.cpp b/source/jucePluginLib/controller.cpp
@@ -179,7 +179,7 @@ namespace pluginLib
return true;
}
- bool Controller::parseMidiPacket(const std::string& _name, std::map<pluginLib::MidiDataType, uint8_t>& _data, std::map<uint32_t, uint8_t>& _parameterValues, const std::vector<uint8_t>& _src) const
+ bool Controller::parseMidiPacket(const std::string& _name, MidiPacket::Data& _data, MidiPacket::ParamValues& _parameterValues, const std::vector<uint8_t>& _src) const
{
auto* m = getMidiPacket(_name);
assert(m);
diff --git a/source/jucePluginLib/controller.h b/source/jucePluginLib/controller.h
@@ -25,7 +25,7 @@ namespace pluginLib
const MidiPacket* getMidiPacket(const std::string& _name) const;
bool createMidiDataFromPacket(std::vector<uint8_t>& _sysex, const std::string& _packetName, const std::map<MidiDataType, uint8_t>& _params) const;
- bool parseMidiPacket(const std::string& _name, std::map<pluginLib::MidiDataType, uint8_t>& _data, std::map<uint32_t, uint8_t>& _parameterValues, const std::vector<uint8_t>& _src) const;
+ bool parseMidiPacket(const std::string& _name, MidiPacket::Data& _data, MidiPacket::ParamValues& _parameterValues, const std::vector<uint8_t>& _src) const;
protected:
virtual Parameter* createParameter(Controller& _controller, const Description& _desc, uint8_t _part, int _uid) = 0;
diff --git a/source/jucePluginLib/midipacket.cpp b/source/jucePluginLib/midipacket.cpp
@@ -8,95 +8,138 @@
namespace pluginLib
{
+ MidiPacket::MidiPacket(std::string _name, std::vector<MidiDataDefinition>&& _bytes) : m_name(std::move(_name)), m_definitions(std::move(_bytes))
+ {
+ uint8_t usedMask = 0;
+
+ uint32_t byteIndex = 0;
+
+ if(m_name == "multidump")
+ int d=0;
+ for(uint32_t i=0; i<m_definitions.size(); ++i)
+ {
+ const auto& d = m_definitions[i];
+
+ const uint8_t masked = (0xff & d.paramMask) << d.paramShift;
+
+ if(usedMask & masked)
+ {
+ // next byte starts if the current mask overlaps with an existing one
+ usedMask = 0;
+ ++byteIndex;
+ }
+
+ m_definitionToByteIndex.insert(std::make_pair(i, byteIndex));
+ m_byteToDefinitionIndex.insert(std::make_pair(byteIndex, i));
+
+ usedMask |= masked;
+ }
+
+ m_byteSize = byteIndex + 1;
+ }
+
bool MidiPacket::create(std::vector<uint8_t>& _dst, const std::map<MidiDataType, uint8_t>& _data) const
{
- _dst.reserve(size());
+ _dst.assign(size(), 0);
for(size_t i=0; i<size(); ++i)
{
- const auto& b = m_bytes[i];
- switch (b.type)
+ const auto range = m_byteToDefinitionIndex.equal_range(static_cast<uint32_t>(i));
+
+ for(auto itRange = range.first; itRange != range.second; ++itRange)
{
- case MidiDataType::Null:
- _dst.push_back(0);
- break;
- case MidiDataType::Byte:
- _dst.push_back(b.byte);
- break;
- default:
- {
- const auto it = _data.find(b.type);
+ const auto& d = m_definitions[itRange->second];
- if(it == _data.end())
+ switch (d.type)
+ {
+ case MidiDataType::Null:
+ _dst[i] = 0;
+ break;
+ case MidiDataType::Byte:
+ _dst[i] = d.byte;
+ break;
+ default:
{
- LOG("Failed to find data of type " << static_cast<int>(b.type) << " to fill byte " << i << " of midi packet");
- return false;
- }
+ const auto it = _data.find(d.type);
+
+ if(it == _data.end())
+ {
+ LOG("Failed to find data of type " << static_cast<int>(d.type) << " to fill byte " << i << " of midi packet");
+ return false;
+ }
- _dst.push_back(it->second);
+ _dst[i] |= (it->second & d.paramMask) << d.paramShift;
+ }
}
}
}
return true;
}
- bool MidiPacket::parse(std::map<MidiDataType, uint8_t>& _data, std::map<uint32_t, uint8_t>& _parameterValues, const ParameterDescriptions& _parameters, const std::vector<uint8_t>& _src) const
+ bool MidiPacket::parse(Data& _data, ParamValues& _parameterValues, const ParameterDescriptions& _parameters, const Sysex& _src) const
{
if(_src.size() != size())
return false;
for(size_t i=0; i<_src.size(); ++i)
{
- const auto& b = m_bytes[i];
const auto s = _src[i];
- switch (b.type)
+ const auto range = m_byteToDefinitionIndex.equal_range(static_cast<uint32_t>(i));
+
+ for(auto it = range.first; it != range.second; ++it)
{
- case MidiDataType::Null:
- continue;
- case MidiDataType::Byte:
- if(b.byte != s)
- return false;
- break;
- case MidiDataType::Checksum:
+ const auto& d = m_definitions[it->second];
+
+ switch (d.type)
{
- uint8_t checksum = 0;
+ case MidiDataType::Null:
+ continue;
+ case MidiDataType::Byte:
+ if(d.byte != s)
+ return false;
+ break;
+ case MidiDataType::Checksum:
+ {
+ uint8_t checksum = 0;
- for(uint32_t c = b.checksumFirstIndex; c <= b.checksumLastIndex; ++c)
- checksum += _src[c];
+ for(uint32_t c = d.checksumFirstIndex; c <= d.checksumLastIndex; ++c)
+ checksum += _src[c];
- checksum &= 0x7f;
+ checksum &= 0x7f;
- if(checksum != s)
- {
- LOG("Packet checksum error, calculated " << std::hex << checksum << " but data contains " << s);
- return false;
+ if(checksum != s)
+ {
+ LOG("Packet checksum error, calculated " << std::hex << checksum << " but data contains " << s);
+ return false;
+ }
}
- }
- continue;
- case MidiDataType::DeviceId:
- case MidiDataType::Bank:
- case MidiDataType::Program:
- case MidiDataType::ParameterIndex:
- case MidiDataType::ParameterValue:
- case MidiDataType::Page:
- case MidiDataType::Part:
- _data.insert(std::make_pair(b.type, s));
- break;
- case MidiDataType::Parameter:
- {
- uint32_t idx;
- if(!_parameters.getIndexByName(idx, b.name))
+ continue;
+ case MidiDataType::DeviceId:
+ case MidiDataType::Bank:
+ case MidiDataType::Program:
+ case MidiDataType::ParameterIndex:
+ case MidiDataType::ParameterValue:
+ case MidiDataType::Page:
+ case MidiDataType::Part:
+ _data.insert(std::make_pair(d.type, s));
+ break;
+ case MidiDataType::Parameter:
{
- LOG("Failed to find named parameter " << b.name << " while parsing midi packet, midi byte " << i);
- return false;
+ uint32_t idx;
+ if(!_parameters.getIndexByName(idx, d.paramName))
+ {
+ LOG("Failed to find named parameter " << d.paramName << " while parsing midi packet, midi byte " << i);
+ return false;
+ }
+ const auto sMasked = (s >> d.paramShift) & d.paramMask;
+ _parameterValues.insert(std::make_pair(std::make_pair(d.paramPart, idx), sMasked));
}
- _parameterValues.insert(std::make_pair(idx, s));
+ break;
+ default:
+ assert(false && "unknown data type");
+ return false;
}
- break;
- default:
- assert(false && "unknown data type");
- return false;
}
}
return true;
diff --git a/source/jucePluginLib/midipacket.h b/source/jucePluginLib/midipacket.h
@@ -27,26 +27,42 @@ namespace pluginLib
class MidiPacket
{
public:
- struct MidiByte
+ static constexpr uint8_t AnyPart = 0xff;
+
+ struct MidiDataDefinition
{
MidiDataType type = MidiDataType::Null;
+
uint8_t byte = 0;
- std::string name;
+
+ std::string paramName;
+ uint8_t paramMask = 0xff;
+ uint8_t paramShift = 0;
+ uint8_t paramPart = AnyPart;
+
uint32_t checksumFirstIndex = 0;
uint32_t checksumLastIndex = 0;
uint32_t checksumInitValue = 0;
};
+ using Data = std::map<MidiDataType, uint8_t>;
+ using ParamValues = std::map<std::pair<uint8_t,uint32_t>, uint8_t>; // part, index => value
+ using Sysex = const std::vector<uint8_t>;
+
MidiPacket() = default;
- explicit MidiPacket(std::vector<MidiByte>&& bytes) : m_bytes(std::move(bytes)) {}
+ explicit MidiPacket(std::string _name, std::vector<MidiDataDefinition>&& _bytes);
- const std::vector<MidiByte>& bytes() { return m_bytes; }
- size_t size() const { return m_bytes.size(); }
+ const std::vector<MidiDataDefinition>& definitions() { return m_definitions; }
+ uint32_t size() const { return m_byteSize; }
bool create(std::vector<uint8_t>& _dst, const std::map<MidiDataType, uint8_t>& _data) const;
- bool parse(std::map<MidiDataType, uint8_t>& _data, std::map<uint32_t, uint8_t>& _parameterValues, const ParameterDescriptions& _parameters, const std::vector<uint8_t>& _src) const;
+ bool parse(Data& _data, ParamValues& _parameterValues, const ParameterDescriptions& _parameters, Sysex& _src) const;
private:
- std::vector<MidiByte> m_bytes;
+ const std::string m_name;
+ std::vector<MidiDataDefinition> m_definitions;
+ std::map<uint32_t, uint32_t> m_definitionToByteIndex;
+ std::multimap<uint32_t, uint32_t> m_byteToDefinitionIndex;
+ uint32_t m_byteSize = 0;
};
}
diff --git a/source/jucePluginLib/parameterdescriptions.cpp b/source/jucePluginLib/parameterdescriptions.cpp
@@ -342,7 +342,7 @@ namespace pluginLib
return;
}
- std::vector<MidiPacket::MidiByte> bytes;
+ std::vector<MidiPacket::MidiDataDefinition> bytes;
for(auto i=0; i<arr->size(); ++i)
{
@@ -350,7 +350,7 @@ namespace pluginLib
auto type = entry["type"].toString().toStdString();
- MidiPacket::MidiByte byte;
+ MidiPacket::MidiDataDefinition byte;
if(type == "byte")
{
@@ -375,14 +375,51 @@ namespace pluginLib
}
else if(type == "param")
{
- byte.name = entry["name"].toString().toStdString();
+ byte.paramName = entry["name"].toString().toStdString();
- if(byte.name.empty())
+ if(byte.paramName.empty())
{
_errors << "no parameter name specified for type param, midi packet " << _key << ", index " << i << std::endl;
return;
}
+ const auto hasMask = entry.hasProperty("mask");
+ const auto hasShift = entry.hasProperty("shift");
+ const auto hasPart = entry.hasProperty("part");
+
+ if(hasMask)
+ {
+ const int mask = strtol(entry["mask"].toString().toStdString().c_str(), nullptr, 16);
+ if(mask < 0 || mask > 0xff)
+ {
+ _errors << "mask needs to be between 00 and ff but got " << std::hex << mask << std::endl;
+ return;
+ }
+ byte.paramMask = static_cast<uint8_t>(mask);
+ }
+
+ if(hasShift)
+ {
+ const int shift = entry["shift"];
+ if(shift < 0 || shift > 7)
+ {
+ _errors << "shift value needs to be between 0 and 7 but got " << shift << std::endl;
+ return;
+ }
+ byte.paramShift = static_cast<uint8_t>(shift);
+ }
+
+ if(hasPart)
+ {
+ const int part= entry["part"];
+ if(part < 0 || part > 15)
+ {
+ _errors << "part needs to be between 0 and 15 but got " << part << std::endl;
+ return;
+ }
+ byte.paramPart = static_cast<uint8_t>(part);
+ }
+
byte.type = MidiDataType::Parameter;
}
else if(type == "checksum")
@@ -419,12 +456,12 @@ namespace pluginLib
bytes.push_back(byte);
}
- MidiPacket packet(std::move(bytes));
+ MidiPacket packet(_key, std::move(bytes));
// post-read validation
- for(size_t i=0; i<packet.bytes().size(); ++i)
+ for(size_t i=0; i<packet.definitions().size(); ++i)
{
- const auto& p = packet.bytes()[i];
+ const auto& p = packet.definitions()[i];
if(p.type == MidiDataType::Checksum)
{
@@ -438,9 +475,9 @@ namespace pluginLib
{
uint32_t index;
- if(!getIndexByName(index, p.name))
+ if(!getIndexByName(index, p.paramName))
{
- _errors << "specified parameter " << p.name << " does not exist" << std::endl;
+ _errors << "specified parameter " << p.paramName << " does not exist" << std::endl;
return;
}
}