commit 6d9873f5d0576582725217618d326a503452aeb6
parent de86df75f8e665ea291898fc56893a095e6b8bf3
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date: Tue, 17 May 2022 23:54:23 +0200
implement generic version of single dump creation and use it in editor to save singles to disk
Diffstat:
8 files changed, 145 insertions(+), 46 deletions(-)
diff --git a/source/jucePlugin/VirusController.cpp b/source/jucePlugin/VirusController.cpp
@@ -207,6 +207,14 @@ namespace Virus
v.setValue(static_cast<uint8_t>(_name[i]));
}
}
+
+ bool Controller::isMultiMode()
+ {
+ auto* value = getParamValue(0, 2, 0x7a);
+ jassert(value);
+ return value->getValue();
+ }
+
juce::String Controller::getCurrentPartPresetName(const uint8_t _part) const
{
std::string name;
@@ -459,7 +467,7 @@ namespace Virus
_params.insert(std::make_pair(pluginLib::MidiDataType::DeviceId, m_deviceId));
- if(!createMidiDataFromPacket(sysex, _packetType, _params))
+ if(!createMidiDataFromPacket(sysex, _packetType, _params, 0))
return false;
sendSysEx(sysex);
@@ -514,4 +522,20 @@ namespace Virus
{
return new Parameter(_controller, _desc, _part, _uid);
}
+
+ std::vector<uint8_t> Controller::createSingleDump(uint8_t _part, uint8_t _bank, uint8_t _program)
+ {
+ std::map<pluginLib::MidiDataType, uint8_t> data;
+
+ data.insert(std::make_pair(pluginLib::MidiDataType::DeviceId, m_deviceId));
+ data.insert(std::make_pair(pluginLib::MidiDataType::Bank, _bank));
+ data.insert(std::make_pair(pluginLib::MidiDataType::Program, _program));
+
+ std::vector<uint8_t> dst;
+
+ if(!createMidiDataFromPacket(dst, "singledump", data, _part))
+ return {};
+
+ return dst;
+ }
}; // namespace Virus
diff --git a/source/jucePlugin/VirusController.h b/source/jucePlugin/VirusController.h
@@ -38,6 +38,7 @@ namespace Virus
void dispatchVirusOut(const std::vector<synthLib::SMidiEvent> &);
pluginLib::Parameter* createParameter(pluginLib::Controller& _controller, const pluginLib::Description& _desc, uint8_t _part, int _uid) override;
+ std::vector<uint8_t> createSingleDump(uint8_t _part, uint8_t _bank, uint8_t _program);
static void printMessage(const SysEx &);
@@ -53,13 +54,8 @@ namespace Virus
}
void setSinglePresetName(uint8_t _part, const juce::String& _name);
- bool isMultiMode()
- {
- auto* value = getParamValue(0, 2, 0x7a);
- jassert(value);
- return value->getValue();
- }
- // part 0 - 15 (ignored when single! 0x40...)
+ bool isMultiMode();
+ // part 0 - 15 (ignored when single! 0x40...)
void setCurrentPartPreset(uint8_t _part, virusLib::BankNumber _bank, uint8_t _prg);
virusLib::BankNumber getCurrentPartBank(uint8_t _part) const;
uint8_t getCurrentPartProgram(uint8_t _part) const;
diff --git a/source/jucePlugin/ui3/VirusEditor.cpp b/source/jucePlugin/ui3/VirusEditor.cpp
@@ -398,25 +398,15 @@ namespace genericVirusUI
const auto result = chooser.getResult();
m_previousPath = result.getParentDirectory().getFullPathName();
const auto ext = result.getFileExtension().toLowerCase();
- const uint8_t syxHeader[9] = { 0xF0, 0x00, 0x20, 0x33, 0x01, 0x00, 0x10, 0x01, 0x00 };
- constexpr uint8_t syxEof[1] = { 0xF7 };
- uint8_t cs = syxHeader[5] + syxHeader[6] + syxHeader[7] + syxHeader[8];
- uint8_t data[256];
- for (int i = 0; i < 256; i++)
- {
- const auto param = getController().getParamValue(getController().getCurrentPart(), i < 128 ? 0 : 1, i & 127);
- data[i] = param ? static_cast<int>(param->getValue()) : 0;
- cs += data[i];
+ const auto data = getController().createSingleDump(getController().getCurrentPart(), virusLib::toMidiByte(virusLib::BankNumber::A), 0);
+
+ if(!data.empty())
+ {
+ result.deleteFile();
+ result.create();
+ result.appendData(&data[0], data.size());
}
- cs = cs & 0x7f;
-
- result.deleteFile();
- result.create();
- result.appendData(syxHeader, 9);
- result.appendData(data, 256);
- result.appendData(&cs, 1);
- result.appendData(syxEof, 1);
};
m_fileChooser->launchAsync(flags, onFileChooser);
}
diff --git a/source/jucePluginLib/controller.cpp b/source/jucePluginLib/controller.cpp
@@ -163,20 +163,38 @@ namespace pluginLib
return m_descriptions.getMidiPacket(_name);
}
- bool Controller::createMidiDataFromPacket(std::vector<uint8_t>& _sysex, const std::string& _packetName, const std::map<MidiDataType, uint8_t>& _params) const
+ bool Controller::createMidiDataFromPacket(std::vector<uint8_t>& _sysex, const std::string& _packetName, const std::map<MidiDataType, uint8_t>& _params, uint8_t _part) const
{
const auto* m = getMidiPacket(_packetName);
assert(m && "midi packet not found");
if(!m)
return false;
- if(!m->create(_sysex, _params))
- {
+ MidiPacket::NamedParamValues paramValues;
+
+ MidiPacket::ParamIndices indices;
+ m->getParameterIndices(indices, m_descriptions);
+
+ if(!indices.empty())
+ {
+ for (const auto& index : indices)
+ {
+ auto* p = getParameter(index.second, _part);
+ if(!p)
+ return false;
+
+ auto v = roundToInt(p->getValueObject().getValue());
+ paramValues.insert(std::make_pair(std::make_pair(index.first, p->getDescription().name), v));
+ }
+ }
+
+ if(!m->create(_sysex, _params, paramValues))
+ {
assert(false && "failed to create midi packet");
- _sysex.clear();
- return false;
- }
- return true;
+ _sysex.clear();
+ return false;
+ }
+ return true;
}
bool Controller::parseMidiPacket(const std::string& _name, MidiPacket::Data& _data, MidiPacket::ParamValues& _parameterValues, const std::vector<uint8_t>& _src) const
diff --git a/source/jucePluginLib/controller.h b/source/jucePluginLib/controller.h
@@ -23,7 +23,8 @@ namespace pluginLib
uint32_t getParameterIndexByName(const std::string& _name) const;
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 createMidiDataFromPacket(std::vector<uint8_t>& _sysex, const std::string& _packetName, const std::map<MidiDataType, uint8_t>& _params, uint8_t _part) const;
bool parseMidiPacket(const std::string& _name, MidiPacket::Data& _data, MidiPacket::ParamValues& _parameterValues, const std::vector<uint8_t>& _src) const;
diff --git a/source/jucePluginLib/midipacket.cpp b/source/jucePluginLib/midipacket.cpp
@@ -16,11 +16,15 @@ namespace pluginLib
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(d.type == MidiDataType::Parameter)
+ m_hasParameters = true;
+
+ const auto masked = static_cast<uint8_t>((0xff & d.paramMask) << d.paramShift);
if(usedMask & masked)
{
@@ -38,10 +42,12 @@ namespace pluginLib
m_byteSize = byteIndex + 1;
}
- bool MidiPacket::create(std::vector<uint8_t>& _dst, const std::map<MidiDataType, uint8_t>& _data) const
+ bool MidiPacket::create(std::vector<uint8_t>& _dst, const Data& _data, const NamedParamValues& _paramValues) const
{
_dst.assign(size(), 0);
+ std::map<uint32_t, uint32_t> pendingChecksums; // byte index => description index
+
for(size_t i=0; i<size(); ++i)
{
const auto range = m_byteToDefinitionIndex.equal_range(static_cast<uint32_t>(i));
@@ -58,6 +64,20 @@ namespace pluginLib
case MidiDataType::Byte:
_dst[i] = d.byte;
break;
+ case MidiDataType::Parameter:
+ {
+ const auto it = _paramValues.find(std::make_pair(d.paramPart, d.paramName));
+ if(it == _paramValues.end())
+ {
+ LOG("Failed to find value for parameter " << d.paramName << ", part " << d.paramPart);
+ return false;
+ }
+ _dst[i] |= (it->second & d.paramMask) << d.paramShift;
+ }
+ break;
+ case MidiDataType::Checksum:
+ pendingChecksums.insert(std::make_pair(static_cast<uint32_t>(i), itRange->second));
+ break;
default:
{
const auto it = _data.find(d.type);
@@ -68,14 +88,28 @@ namespace pluginLib
return false;
}
- _dst[i] |= (it->second & d.paramMask) << d.paramShift;
+ _dst[i] = it->second;
}
}
}
}
+
+ for (auto& pendingChecksum : pendingChecksums)
+ {
+ const auto byteIndex = pendingChecksum.first;
+ const auto descIndex = pendingChecksum.second;
+
+ _dst[byteIndex] = calcChecksum(m_definitions[descIndex], _dst);
+ }
+
return true;
}
+ bool MidiPacket::create(std::vector<uint8_t>& _dst, const Data& _data) const
+ {
+ return create(_dst, _data, {});
+ }
+
bool MidiPacket::parse(Data& _data, ParamValues& _parameterValues, const ParameterDescriptions& _parameters, const Sysex& _src) const
{
if(_src.size() != size())
@@ -101,12 +135,7 @@ namespace pluginLib
break;
case MidiDataType::Checksum:
{
- uint8_t checksum = 0;
-
- for(uint32_t c = d.checksumFirstIndex; c <= d.checksumLastIndex; ++c)
- checksum += _src[c];
-
- checksum &= 0x7f;
+ const uint8_t checksum = calcChecksum(d, _src);
if(checksum != s)
{
@@ -144,4 +173,36 @@ namespace pluginLib
}
return true;
}
+
+ bool MidiPacket::getParameterIndices(ParamIndices& _indices, const ParameterDescriptions& _parameters) const
+ {
+ if(!m_hasParameters)
+ return true;
+
+ for (const auto & d : m_definitions)
+ {
+ if(d.type != MidiDataType::Parameter)
+ continue;
+
+ uint32_t index;
+ if(!_parameters.getIndexByName(index, d.paramName))
+ {
+ LOG("Failed to retrieve index for parameter " << d.paramName);
+ return false;
+ }
+
+ _indices.insert(std::make_pair(d.paramPart, index));
+ }
+ return true;
+ }
+
+ uint8_t MidiPacket::calcChecksum(const MidiDataDefinition& _d, Sysex& _src)
+ {
+ auto checksum = _d.checksumInitValue;
+
+ for(uint32_t c = _d.checksumFirstIndex; c <= _d.checksumLastIndex; ++c)
+ checksum += _src[c];
+
+ return checksum & 0x7f;
+ }
}
diff --git a/source/jucePluginLib/midipacket.h b/source/jucePluginLib/midipacket.h
@@ -2,6 +2,7 @@
#include <cstdint>
#include <map>
+#include <set>
#include <string>
#include <vector>
@@ -42,11 +43,14 @@ namespace pluginLib
uint32_t checksumFirstIndex = 0;
uint32_t checksumLastIndex = 0;
- uint32_t checksumInitValue = 0;
+ uint8_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 ParamIndex = std::pair<uint8_t,uint32_t>;
+ using ParamIndices = std::set<ParamIndex>;
+ using ParamValues = std::map<ParamIndex, uint8_t>; // part, index => value
+ using NamedParamValues = std::map<std::pair<uint8_t,std::string>, uint8_t>; // part, name => value
using Sysex = const std::vector<uint8_t>;
MidiPacket() = default;
@@ -55,14 +59,19 @@ namespace pluginLib
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 create(std::vector<uint8_t>& _dst, const Data& _data, const NamedParamValues& _paramValues) const;
+ bool create(std::vector<uint8_t>& _dst, const Data& _data) const;
bool parse(Data& _data, ParamValues& _parameterValues, const ParameterDescriptions& _parameters, Sysex& _src) const;
+ bool getParameterIndices(ParamIndices& _indices, const ParameterDescriptions& _parameters) const;
private:
+ static uint8_t calcChecksum(const MidiDataDefinition& _d, const Sysex& _src);
+
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;
+ bool m_hasParameters = false;
};
}
diff --git a/source/jucePluginLib/parameterdescriptions.cpp b/source/jucePluginLib/parameterdescriptions.cpp
@@ -437,7 +437,7 @@ namespace pluginLib
byte.type = MidiDataType::Checksum;
byte.checksumFirstIndex = first;
byte.checksumLastIndex = last;
- byte.checksumInitValue = init;
+ byte.checksumInitValue = static_cast<uint8_t>(init);
}
else if(type == "bank") byte.type = MidiDataType::Bank;
else if(type == "program") byte.type = MidiDataType::Program;