commit 21561e3556aa444df6f818e019315751b654bcfc
parent 21b2f2337006678e3f7aa421606d6a09b1b861a8
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date: Sat, 14 May 2022 19:48:37 +0200
midi packet parsing
Diffstat:
3 files changed, 215 insertions(+), 1 deletion(-)
diff --git a/source/jucePluginLib/midipacket.h b/source/jucePluginLib/midipacket.h
@@ -1,5 +1,39 @@
#pragma once
+#include <cstdint>
+#include <string>
+#include <vector>
+
namespace pluginLib
{
+ enum class MidiDataType
+ {
+ Null,
+ Byte,
+ DeviceId,
+ Checksum,
+ Bank,
+ Program,
+ Parameter,
+ ParameterIndex,
+ ParameterValue,
+ Page,
+ Part
+ };
+
+ class MidiPacket
+ {
+ public:
+ struct MidiByte
+ {
+ MidiDataType type = MidiDataType::Null;
+ uint8_t byte = 0;
+ std::string name;
+ uint32_t checksumFirstIndex = 0;
+ uint32_t checksumLastIndex = 0;
+ uint32_t checksumInitValue = 0;
+ };
+
+ std::vector<MidiByte> bytes;
+ };
}
diff --git a/source/jucePluginLib/parameterdescriptions.cpp b/source/jucePluginLib/parameterdescriptions.cpp
@@ -36,6 +36,21 @@ namespace pluginLib
return _json;
}
+ bool ParameterDescriptions::getIndexByName(uint32_t& _index, const std::string& _name) const
+ {
+ for (uint32_t i=0; i<m_descriptions.size(); ++i)
+ {
+ const auto& description = m_descriptions[i];
+
+ if(description.name == _name)
+ {
+ _index = i;
+ return true;
+ }
+ }
+ return false;
+ }
+
std::string ParameterDescriptions::loadJson(const std::string& _jsonString)
{
// juce' JSON parser doesn't like JSON5-style comments
@@ -265,7 +280,166 @@ namespace pluginLib
m_descriptions.push_back(d);
}
+ const auto midipackets = json["midipackets"].getDynamicObject();
+
+ parseMidiPackets(errors, midipackets);
+
+ const auto res = errors.str();
+ assert(res.empty());
+ return res;
+ }
+
+ void ParameterDescriptions::parseMidiPackets(std::stringstream& _errors, juce::DynamicObject* _packets)
+ {
+ if(!_packets)
+ return;
+
+ const auto entryProps = _packets->getProperties();
+
+ for(int i=0; i<entryProps.size(); ++i)
+ {
+ const auto key = std::string(entryProps.getName(i).toString().toUTF8());
+ const auto& value = entryProps.getValueAt(i);
+
+ parseMidiPacket(_errors, key, value);
+ }
+ }
+
+ void ParameterDescriptions::parseMidiPacket(std::stringstream& _errors, const std::string& _key, const juce::var& _value)
+ {
+ if(_key.empty())
+ {
+ _errors << "midi packet name must not be empty" << std::endl;
+ return;
+ }
+
+ if(m_midiPackets.find(_key) != m_midiPackets.end())
+ {
+ _errors << "midi packet with name " << _key << " is already defined" << std::endl;
+ return;
+ }
+
+ const auto arr = _value.getArray();
+
+ if(!arr)
+ {
+ _errors << "midi packet " << _key << " is empty" << std::endl;
+ return;
+ }
+
+ std::vector<MidiPacket::MidiByte> bytes;
+
+ for(auto i=0; i<arr->size(); ++i)
+ {
+ auto entry = (*arr)[i];
+
+ auto type = entry["type"].toString().toStdString();
+
+ MidiPacket::MidiByte byte;
+
+ if(type == "byte")
+ {
+ auto value = entry["value"].toString().toStdString();
+
+ if(value.empty())
+ {
+ _errors << "no value specified for type byte, midi packet " << _key << ", index " << i << std::endl;
+ return;
+ }
+
+ const auto v = ::strtol(value.c_str(), nullptr, 16);
+
+ if(v < 0 || v > 0xff)
+ {
+ _errors << "Midi byte must be in range 0-255" << std::endl;
+ return;
+ }
+
+ byte.type = MidiDataType::Byte;
+ byte.byte = static_cast<uint8_t>(v);
+ }
+ else if(type == "param")
+ {
+ byte.name = entry["name"].toString().toStdString();
+
+ if(byte.name.empty())
+ {
+ _errors << "no parameter name specified for type param, midi packet " << _key << ", index " << i << std::endl;
+ return;
+ }
+
+ byte.type = MidiDataType::Parameter;
+ }
+ else if(type == "checksum")
+ {
+ const int first = entry["first"];
+ const int last = entry["last"];
+ const int init = entry["init"];
+
+ if(first < 0 || last < 0 || last <= first)
+ {
+ _errors << "specified checksum range " << first << "-" << last << " is not valid, midi packet " << _key << ", index " << i << std::endl;
+ return;
+ }
+
+ byte.type = MidiDataType::Checksum;
+ byte.checksumFirstIndex = first;
+ byte.checksumLastIndex = last;
+ byte.checksumInitValue = init;
+ }
+ else if(type == "bank")
+ byte.type = MidiDataType::Bank;
+ else if(type == "program")
+ byte.type = MidiDataType::Program;
+ else if(type == "deviceid")
+ byte.type = MidiDataType::DeviceId;
+ else if(type == "page")
+ byte.type = MidiDataType::Page;
+ else if(type == "part")
+ byte.type = MidiDataType::Part;
+ else if(type == "paramindex")
+ byte.type = MidiDataType::ParameterIndex;
+ else if(type == "paramvalue")
+ byte.type = MidiDataType::ParameterValue;
+ else if(type == "null")
+ byte.type = MidiDataType::Null;
+ else
+ {
+ _errors << "Unknown midi packet data type " << type << ", midi packet " << _key << ", index " << i << std::endl;
+ return;
+ }
+
+ bytes.push_back(byte);
+ }
+
+ MidiPacket packet;
+ packet.bytes = bytes;
+
+ // post-read validation
+ for(size_t i=0; i<packet.bytes.size(); ++i)
+ {
+ const auto& p = packet.bytes[i];
+
+ if(p.type == MidiDataType::Checksum)
+ {
+ if(p.checksumFirstIndex >= (packet.bytes.size()-1) || p.checksumLastIndex >= (packet.bytes.size()-1))
+ {
+ _errors << "specified checksum range " << p.checksumFirstIndex << "-" << p.checksumLastIndex << " is out of range 0-" << packet.bytes.size() << i << std::endl;
+ return;
+ }
+ }
+ else if(p.type == MidiDataType::Parameter)
+ {
+ uint32_t index;
+
+ if(!getIndexByName(index, p.name))
+ {
+ _errors << "specified parameter " << p.name << " does not exist" << std::endl;
+ return;
+ }
+ }
+ }
- return errors.str();
+ m_midiPackets.insert(std::make_pair(_key, packet));
}
}
diff --git a/source/jucePluginLib/parameterdescriptions.h b/source/jucePluginLib/parameterdescriptions.h
@@ -4,6 +4,7 @@
#include <string>
#include <vector>
+#include "midipacket.h"
#include "parameterdescription.h"
namespace pluginLib
@@ -20,10 +21,15 @@ namespace pluginLib
static std::string removeComments(std::string _json);
+ bool getIndexByName(uint32_t& _index, const std::string& _name) const;
+
private:
std::string loadJson(const std::string& _jsonString);
+ void parseMidiPackets(std::stringstream& _errors, juce::DynamicObject* _packets);
+ void parseMidiPacket(std::stringstream& _errors, const std::string& _key, const juce::var& _value);
std::map<std::string, ValueList> m_valueLists;
std::vector<Description> m_descriptions;
+ std::map<std::string, MidiPacket> m_midiPackets;
};
}