commit b2b50555805ac8393eb2c1c9be713744d3ceef3b
parent a4b0b8f737ff68d3f151337a3f70cf9f210a1b4f
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date: Thu, 19 May 2022 18:25:58 +0200
midi packet parsing & preset management is now completely generic
Diffstat:
5 files changed, 287 insertions(+), 236 deletions(-)
diff --git a/source/jucePlugin/VirusController.cpp b/source/jucePlugin/VirusController.cpp
@@ -11,13 +11,30 @@ using MessageType = virusLib::SysexMessageType;
namespace Virus
{
- static constexpr uint8_t kSysExStart[] = {0xf0, 0x00, 0x20, 0x33, 0x01};
- static constexpr auto kHeaderWithMsgCodeLen = 7;
+ constexpr const char* g_midiPacketNames[] =
+ {
+ "requestsingle",
+ "requestmulti",
+ "requestsinglebank",
+ "requestmultibank",
+ "requestarrangement",
+ "requestglobal",
+ "requesttotal",
+ "requestcontrollerdump",
+ "parameterchange",
+ "singledump",
+ "multidump"
+ };
+
+ static_assert(std::size(g_midiPacketNames) == static_cast<size_t>(Controller::MidiPacketType::Count));
+
+ const char* midiPacketName(Controller::MidiPacketType _type)
+ {
+ return g_midiPacketNames[static_cast<uint32_t>(_type)];
+ }
Controller::Controller(AudioPluginAudioProcessor &p, unsigned char deviceId) : pluginLib::Controller(BinaryData::parameterDescriptions_C_json), m_processor(p), m_deviceId(deviceId)
{
-// assert(m_descriptions.getDescriptions().size() == Param_Count && "size of enum must match size of parameter descriptions");
-
registerParams(p);
juce::PropertiesFile::Options opts;
@@ -57,54 +74,37 @@ namespace Virus
delete m_config;
}
- void Controller::parseMessage(const SysEx &msg)
+ void Controller::parseMessage(const SysEx& _msg)
{
- if (msg.size() < 8)
- return; // shorter than expected!
-
- if (msg[msg.size() - 1] != 0xf7)
- return; // invalid end?!?
+ std::string name;
+ pluginLib::MidiPacket::Data data;
+ pluginLib::MidiPacket::ParamValues parameterValues;
- for (size_t i = 0; i < msg.size(); ++i)
+ if(parseMidiPacket(name, data, parameterValues, _msg))
{
- if (i < 5)
- {
- if (msg[i] != kSysExStart[i])
- return; // invalid header
- }
- else if (i == 5)
- {
- if (msg[i] != m_deviceId && msg[i] != 0x10)
- return; // not intended to this device!
- }
- else if (i == 6)
+ const auto deviceId = data[pluginLib::MidiDataType::DeviceId];
+
+ if(deviceId != m_deviceId && deviceId != virusLib::OMNI_DEVICE_ID)
+ return; // not intended to this device!
+
+ if(name == midiPacketName(MidiPacketType::SingleDump))
+ parseSingle(_msg, data, parameterValues);
+ else if(name == midiPacketName(MidiPacketType::MultiDump))
+ parseMulti(data, parameterValues);
+ else if(name == midiPacketName(MidiPacketType::ParameterChange))
+ parseParamChange(data);
+ else
{
- switch (msg[i])
- {
- case MessageType::DUMP_SINGLE:
- parseSingle(msg);
- break;
- case MessageType::DUMP_MULTI:
- parseMulti(msg);
- break;
- case MessageType::PARAM_CHANGE_A:
- case MessageType::PARAM_CHANGE_B:
- case MessageType::PARAM_CHANGE_C:
- parseParamChange(msg);
- break;
- case MessageType::REQUEST_SINGLE:
- case MessageType::REQUEST_MULTI:
- case MessageType::REQUEST_GLOBAL:
- case MessageType::REQUEST_TOTAL:
- sendSysEx(msg);
- break;
- default:
- LOG("Controller: Begin Unhandled SysEx! --");
- printMessage(msg);
- LOG("Controller: End Unhandled SysEx! --");
- }
+ LOG("Controller: Begin unhandled SysEx! --");
+ printMessage(_msg);
+ LOG("Controller: End unhandled SysEx! --");
}
+ return;
}
+
+ LOG("Controller: Begin unknown SysEx! --");
+ printMessage(_msg);
+ LOG("Controller: End unknown SysEx! --");
}
juce::Value* Controller::getParamValue(uint8_t ch, uint8_t bank, uint8_t paramIndex)
@@ -118,18 +118,12 @@ namespace Virus
return ¶ms.front()->getValueObject();
}
- void Controller::parseParamChange(const SysEx& msg)
+ void Controller::parseParamChange(const pluginLib::MidiPacket::Data& _data)
{
- pluginLib::MidiPacket::Data params;
- pluginLib::MidiPacket::ParamValues parameterValues;
-
- if(!parseMidiPacket("parameterchange", params, parameterValues, msg))
- return;
-
- const auto page = params[pluginLib::MidiDataType::Page];
- const auto part = params[pluginLib::MidiDataType::Part];
- const auto index = params[pluginLib::MidiDataType::ParameterIndex];
- const auto value = params[pluginLib::MidiDataType::ParameterValue];
+ const auto page = _data.find(pluginLib::MidiDataType::Page)->second;
+ const auto part = _data.find(pluginLib::MidiDataType::Part)->second;
+ const auto index = _data.find(pluginLib::MidiDataType::ParameterIndex)->second;
+ const auto value = _data.find(pluginLib::MidiDataType::ParameterValue)->second;
const auto& partParams = findSynthParam(part, page, index);
@@ -190,7 +184,27 @@ namespace Virus
bankNames.add(m_singles[bank][i].name);
return bankNames;
}
- void Controller::setSinglePresetName(uint8_t _part, const juce::String& _name)
+
+ std::string Controller::getSinglePresetName(const pluginLib::MidiPacket::ParamValues& _values) const
+ {
+ std::string name;
+ for(uint32_t i=0; i<kNameLength; ++i)
+ {
+ const std::string paramName = "SingleName" + std::to_string(i);
+ const auto idx = getParameterIndexByName(paramName);
+ if(idx == InvalidParameterIndex)
+ break;
+
+ const auto it = _values.find(std::make_pair(pluginLib::MidiPacket::AnyPart, idx));
+ if(it == _values.end())
+ break;
+
+ name += static_cast<char>(it->second);
+ }
+ return name;
+ }
+
+ void Controller::setSinglePresetName(uint8_t _part, const juce::String& _name)
{
for (int i=0; i<kNameLength; i++)
{
@@ -257,7 +271,7 @@ namespace Virus
sendParameterChange(MessageType::PARAM_CHANGE_C, pt, virusLib::PART_BANK_SELECT, virusLib::toMidiByte(_bank));
sendParameterChange(MessageType::PARAM_CHANGE_C, pt, virusLib::PART_PROGRAM_CHANGE, _prg);
- requestSingle(0x0, pt);
+ requestSingle(virusLib::toMidiByte(virusLib::BankNumber::EditBuffer), pt);
m_currentBank[_part] = _bank;
m_currentProgram[_part] = _prg;
@@ -271,34 +285,48 @@ namespace Virus
{
return m_currentProgram[_part];
}
+
+ bool Controller::parseSingle(pluginLib::MidiPacket::Data& _data, pluginLib::MidiPacket::ParamValues& _parameterValues, const SysEx& _msg) const
+ {
+ const auto packetName = midiPacketName(MidiPacketType::SingleDump);
+
+ auto* m = getMidiPacket(packetName);
+
+ if(!m)
+ return false;
+
+ if(_msg.size() > m->size())
+ {
+ SysEx temp;
+ temp.insert(temp.begin(), _msg.begin(), _msg.begin() + (m->size()-1));
+ temp.push_back(0xf7);
+ return parseMidiPacket(*m, _data, _parameterValues, temp);
+ }
+
+ return parseMidiPacket(*m, _data, _parameterValues, _msg);
+ }
+
void Controller::parseSingle(const SysEx& msg)
{
pluginLib::MidiPacket::Data data;
pluginLib::MidiPacket::ParamValues parameterValues;
- if(!parseMidiPacket("singledump", data, parameterValues, msg))
+ if(!parseSingle(data, parameterValues, msg))
return;
- SinglePatch patch;
+ parseSingle(msg, data, parameterValues);
+ }
- patch.bankNumber = virusLib::fromMidiByte(data[pluginLib::MidiDataType::Bank]);
- patch.progNumber = data[pluginLib::MidiDataType::Program];
-
- for(uint32_t i=0; i<kNameLength; ++i)
- {
- const std::string paramName = "SingleName" + std::to_string(i);
- const auto idx = getParameterIndexByName(paramName);
- if(idx == InvalidParameterIndex)
- break;
+ void Controller::parseSingle(const SysEx& _msg, const pluginLib::MidiPacket::Data& _data, const pluginLib::MidiPacket::ParamValues& _parameterValues)
+ {
+ SinglePatch patch;
- const auto it = parameterValues.find(std::make_pair(pluginLib::MidiPacket::AnyPart, idx));
- if(it == parameterValues.end())
- break;
+ patch.bankNumber = virusLib::fromMidiByte(_data.find(pluginLib::MidiDataType::Bank)->second);
+ patch.progNumber = _data.find(pluginLib::MidiDataType::Program)->second;
- patch.name += static_cast<char>(it->second);
- }
+ patch.name = getSinglePresetName(_parameterValues);
- copyData(msg, kHeaderWithMsgCodeLen + 2, patch.data);
+ patch.data = _msg;
if (patch.bankNumber == virusLib::BankNumber::EditBuffer)
{
@@ -312,7 +340,7 @@ namespace Virus
const uint8_t ch = patch.progNumber == virusLib::SINGLE ? 0 : patch.progNumber;
- for(auto it = parameterValues.begin(); it != parameterValues.end(); ++it)
+ for(auto it = _parameterValues.begin(); it != _parameterValues.end(); ++it)
{
auto* p = getParameter(it->first.second, ch);
p->setValueFromSynth(it->second, true);
@@ -326,22 +354,16 @@ namespace Virus
}
else
m_singles[virusLib::toArrayIndex(patch.bankNumber)][patch.progNumber] = patch;
- }
+ }
- void Controller::parseMulti(const SysEx& _msg)
+ void Controller::parseMulti(const pluginLib::MidiPacket::Data& _data, const pluginLib::MidiPacket::ParamValues& _parameterValues)
{
- pluginLib::MidiPacket::Data data;
- pluginLib::MidiPacket::ParamValues paramValues;
-
- if(!parseMidiPacket("multidump", data, paramValues, _msg))
- return;
-
- const auto bankNumber = data[pluginLib::MidiDataType::Bank];
+ const auto bankNumber = _data.find(pluginLib::MidiDataType::Bank)->second;
/* If it's a multi edit buffer, set the part page C parameters to their multi equivalents */
if (bankNumber == 0)
{
- for (const auto & paramValue : paramValues)
+ for (const auto & paramValue : _parameterValues)
{
const auto part = paramValue.first.first;
const auto index = paramValue.first.second;
@@ -381,20 +403,6 @@ namespace Virus
p->setValueFromSynth(m.c, true);
}
- uint8_t Controller::copyData(const SysEx &src, int startPos, std::array<uint8_t, kDataSizeInBytes>& dst)
- {
- uint8_t sum = 0;
-
- size_t iSrc = startPos;
-
- for (size_t iDst = 0; iSrc < src.size() && iDst < dst.size(); ++iSrc, ++iDst)
- {
- dst[iDst] = src[iSrc];
- sum += dst[iDst];
- }
- return sum;
- }
-
void Controller::printMessage(const SysEx &msg)
{
std::stringstream ss;
@@ -426,7 +434,7 @@ namespace Virus
data.insert(std::make_pair(pluginLib::MidiDataType::Bank, _bank));
data.insert(std::make_pair(pluginLib::MidiDataType::Program, _program));
- return sendSysEx(_multi ? "requestmulti" : "requestsingle", data);
+ return sendSysEx(_multi ? MidiPacketType::RequestMulti : MidiPacketType::RequestSingle, data);
}
bool Controller::requestSingle(uint8_t _bank, uint8_t _program) const
@@ -444,32 +452,32 @@ namespace Virus
std::map<pluginLib::MidiDataType, uint8_t> data;
data.insert(std::make_pair(pluginLib::MidiDataType::Bank, _bank));
- return sendSysEx("requestsinglebank", data);
+ return sendSysEx(MidiPacketType::RequestSingleBank, data);
}
bool Controller::requestTotal() const
{
- return sendSysEx("requesttotal");
+ return sendSysEx(MidiPacketType::RequestTotal);
}
bool Controller::requestArrangement() const
{
- return sendSysEx("requestarrangement");
+ return sendSysEx(MidiPacketType::RequestArrangement);
}
- bool Controller::sendSysEx(const std::string& _packetType) const
+ bool Controller::sendSysEx(MidiPacketType _type) const
{
std::map<pluginLib::MidiDataType, uint8_t> params;
- return sendSysEx(_packetType, params);
+ return sendSysEx(_type, params);
}
- bool Controller::sendSysEx(const std::string& _packetType, std::map<pluginLib::MidiDataType, uint8_t>& _params) const
+ bool Controller::sendSysEx(MidiPacketType _type, std::map<pluginLib::MidiDataType, uint8_t>& _params) const
{
std::vector<uint8_t> sysex;
_params.insert(std::make_pair(pluginLib::MidiDataType::DeviceId, m_deviceId));
- if(!createMidiDataFromPacket(sysex, _packetType, _params, 0))
+ if(!createMidiDataFromPacket(sysex, midiPacketName(_type), _params, 0))
return false;
sendSysEx(sysex);
@@ -517,7 +525,7 @@ namespace Virus
data.insert(std::make_pair(pluginLib::MidiDataType::ParameterIndex, _index));
data.insert(std::make_pair(pluginLib::MidiDataType::ParameterValue, _value));
- return sendSysEx("parameterchange", data);
+ return sendSysEx(MidiPacketType::ParameterChange, data);
}
pluginLib::Parameter* Controller::createParameter(pluginLib::Controller& _controller, const pluginLib::Description& _desc, uint8_t _part, int _uid)
@@ -527,7 +535,7 @@ namespace Virus
std::vector<uint8_t> Controller::createSingleDump(uint8_t _part, uint8_t _bank, uint8_t _program)
{
- std::map<pluginLib::MidiDataType, uint8_t> data;
+ pluginLib::MidiPacket::Data data;
data.insert(std::make_pair(pluginLib::MidiDataType::DeviceId, m_deviceId));
data.insert(std::make_pair(pluginLib::MidiDataType::Bank, _bank));
@@ -535,9 +543,51 @@ namespace Virus
std::vector<uint8_t> dst;
- if(!createMidiDataFromPacket(dst, "singledump", data, _part))
+ if(!createMidiDataFromPacket(dst, midiPacketName(MidiPacketType::SingleDump), data, _part))
return {};
return dst;
}
+
+ std::vector<uint8_t> Controller::createSingleDump(uint8_t _bank, uint8_t _program, const pluginLib::MidiPacket::ParamValues& _paramValues)
+ {
+ const auto* m = getMidiPacket(midiPacketName(MidiPacketType::SingleDump));
+ assert(m && "midi packet not found");
+
+ if(!m)
+ return {};
+
+ pluginLib::MidiPacket::Data data;
+ pluginLib::MidiPacket::NamedParamValues paramValues;
+
+ 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));
+
+ for (const auto& it : _paramValues)
+ {
+ const auto* p = getParameter(it.first.second);
+ assert(p);
+ if(!p)
+ return {};
+ const auto key = std::make_pair(it.first.first, p->getDescription().name);
+ paramValues.insert(std::make_pair(key, it.second));
+ }
+
+ pluginLib::MidiPacket::Sysex dst;
+ if(!m->create(dst, data, paramValues))
+ return {};
+ return dst;
+ }
+
+ std::vector<uint8_t> Controller::modifySingleDump(const std::vector<uint8_t>& _sysex, const virusLib::BankNumber _newBank, const uint8_t _newProgram, const bool _modifyBank, const bool _modifyProgram)
+ {
+ pluginLib::MidiPacket::Data data;
+ pluginLib::MidiPacket::ParamValues parameterValues;
+
+ if(!parseSingle(data, parameterValues, _sysex))
+ return {};
+
+ return createSingleDump(_modifyBank ? toMidiByte(_newBank) : data[pluginLib::MidiDataType::Bank], _modifyProgram ? _newProgram : data[pluginLib::MidiDataType::Program], parameterValues);
+ }
}; // namespace Virus
diff --git a/source/jucePlugin/VirusController.h b/source/jucePlugin/VirusController.h
@@ -22,14 +22,31 @@ namespace Virus
virusLib::BankNumber bankNumber = static_cast<virusLib::BankNumber>(0);
uint8_t progNumber = 0;
std::string name;
- std::array<uint8_t, kDataSizeInBytes> data{};
+ std::vector<uint8_t> data;
};
using Singles = std::array<std::array<SinglePatch, 128>, 8>;
static constexpr auto kNameLength = 10;
- Controller(AudioPluginAudioProcessor &, unsigned char deviceId = 0x00);
+ enum class MidiPacketType
+ {
+ RequestSingle,
+ RequestMulti,
+ RequestSingleBank,
+ RequestMultiBank,
+ RequestArrangement,
+ RequestGlobal,
+ RequestTotal,
+ RequestControllerDump,
+ ParameterChange,
+ SingleDump,
+ MultiDump,
+
+ Count
+ };
+
+ Controller(AudioPluginAudioProcessor &, unsigned char deviceId = 0x00);
~Controller() override;
// this is called by the plug-in on audio thread!
@@ -37,14 +54,17 @@ namespace Virus
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);
+ std::vector<uint8_t> createSingleDump(uint8_t _bank, uint8_t _program, const pluginLib::MidiPacket::ParamValues& _paramValues);
+ std::vector<uint8_t> modifySingleDump(const std::vector<uint8_t>& _sysex, virusLib::BankNumber _newBank, uint8_t _newProgram, bool _modifyBank, bool _modifyProgram);
static void printMessage(const SysEx &);
juce::Value* getParamValue(uint8_t ch, uint8_t bank, uint8_t paramIndex);
virusLib::VirusModel getVirusModel() const;
- // bank - 0-1 (AB)
+
juce::StringArray getSinglePresetNames(virusLib::BankNumber bank) const;
+ std::string getSinglePresetName(const pluginLib::MidiPacket::ParamValues& _values) const;
const Singles& getSinglePresets() const
{
@@ -80,23 +100,23 @@ namespace Virus
void sendParameterChange(const pluginLib::Parameter& _parameter, uint8_t _value) override;
bool sendParameterChange(uint8_t _page, uint8_t _part, uint8_t _index, uint8_t _value) const;
- bool sendSysEx(const std::string& _packetType) const;
- bool sendSysEx(const std::string& _packetType, std::map<pluginLib::MidiDataType, uint8_t>& _params) const;
+ bool sendSysEx(MidiPacketType _type) const;
+ bool sendSysEx(MidiPacketType _type, std::map<pluginLib::MidiDataType, uint8_t>& _params) const;
uint8_t getDeviceId() const { return m_deviceId; }
+ bool parseSingle(pluginLib::MidiPacket::Data& _data, pluginLib::MidiPacket::ParamValues& _parameterValues, const SysEx& _msg) const;
+
private:
void timerCallback() override;
Singles m_singles;
- // unchecked copy for patch data bytes
- static inline uint8_t copyData(const SysEx &src, int startPos, std::array<uint8_t, kDataSizeInBytes>& dst);
+ void parseSingle(const SysEx& _msg);
+ void parseSingle(const SysEx& _msg, const pluginLib::MidiPacket::Data& _data, const pluginLib::MidiPacket::ParamValues& _parameterValues);
- template <typename T> static juce::String parseAsciiText(const T &, int startPos);
- void parseSingle(const SysEx &);
- void parseMulti(const SysEx &);
- void parseParamChange(const SysEx &);
+ void parseMulti(const pluginLib::MidiPacket::Data& _data, const pluginLib::MidiPacket::ParamValues& _parameterValues);
+ void parseParamChange(const pluginLib::MidiPacket::Data& _data);
void parseControllerDump(synthLib::SMidiEvent &);
AudioPluginAudioProcessor& m_processor;
diff --git a/source/jucePlugin/ui3/PatchBrowser.cpp b/source/jucePlugin/ui3/PatchBrowser.cpp
@@ -13,16 +13,10 @@ using namespace juce;
const juce::Array<juce::String> ModelList = {"A","B","C","TI"};
-const Array<String> g_categories = { "", "Lead", "Bass", "Pad", "Decay", "Pluck",
- "Acid", "Classic", "Arpeggiator", "Effects", "Drums", "Percussion",
- "Input", "Vocoder", "Favourite 1", "Favourite 2", "Favourite 3" };
-
namespace genericVirusUI
{
- virusLib::VirusModel guessVersion(const uint8_t* _data)
+ virusLib::VirusModel guessVersion(const uint8_t v)
{
- const auto v = _data[0];
-
if (v < 5)
return virusLib::A;
if (v == 6)
@@ -32,25 +26,6 @@ namespace genericVirusUI
return virusLib::TI;
}
- static juce::String parseAsciiText(const std::vector<uint8_t>& msg, const int start)
- {
- char text[Virus::Controller::kNameLength + 1];
- text[Virus::Controller::kNameLength] = 0; // termination
- for (int pos = 0; pos < Virus::Controller::kNameLength; ++pos)
- text[pos] = static_cast<char>(msg[start + pos]);
- return {text};
- }
-
- static void initializePatch(Patch& _patch)
- {
- _patch.name = parseAsciiText(_patch.data, 128 + 112);
- _patch.category1 = _patch.data[251];
- _patch.category2 = _patch.data[252];
- _patch.unison = _patch.data[97];
- _patch.transpose = _patch.data[93];
- _patch.model = guessVersion(&_patch.data[0]);
- }
-
PatchBrowser::PatchBrowser(const VirusEditor& _editor) : m_editor(_editor), m_controller(_editor.getController()),
m_fileFilter("*.syx;*.mid;*.midi", "*", "Virus Patch Dumps"),
m_bankList(FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles, File::getSpecialLocation(File::SpecialLocationType::currentApplicationFile), &m_fileFilter, nullptr),
@@ -104,7 +79,7 @@ namespace genericVirusUI
m_romBankSelect->addItem("-", 1);
- for(uint32_t i=0; i<m_editor.getController().getBankCount(); ++i)
+ for(uint32_t i=0; i<m_controller.getBankCount(); ++i)
{
std::stringstream ss;
ss << "Bank " << static_cast<char>('A' + i);
@@ -137,58 +112,48 @@ namespace genericVirusUI
void PatchBrowser::selectionChanged() {}
- uint32_t PatchBrowser::load(std::vector<Patch>& _result, std::set<std::string>* _dedupeChecksums, const std::vector<std::vector<uint8_t>>& _packets)
+ uint32_t PatchBrowser::load(const Virus::Controller& _controller, std::vector<Patch>& _result, std::set<std::string>* _dedupeChecksums, const std::vector<std::vector<uint8_t>>& _packets)
{
uint32_t count = 0;
for (const auto& packet : _packets)
{
- if (load(_result, _dedupeChecksums, packet))
+ if (load(_controller, _result, _dedupeChecksums, packet))
++count;
}
return count;
}
- bool PatchBrowser::load(std::vector<Patch>& _result, std::set<std::string>* _dedupeChecksums, const std::vector<uint8_t>& _data)
+ bool PatchBrowser::load(const Virus::Controller& _controller, std::vector<Patch>& _result, std::set<std::string>* _dedupeChecksums, const std::vector<uint8_t>& _data)
{
if (_data.size() < 267)
return false;
- auto* it = &_data.front();
+ Patch patch;
+ patch.sysex = _data;
+ if(!initializePatch(_controller, patch))
+ return false;
+
+ patch.progNumber = static_cast<int>(_result.size());
- if (*it == (uint8_t)0xf0
- && *(it + 1) == (uint8_t)0x00
- && *(it + 2) == (uint8_t)0x20
- && *(it + 3) == (uint8_t)0x33
- && *(it + 4) == (uint8_t)0x01
- && *(it + 6) == (uint8_t)virusLib::DUMP_SINGLE)
+ if (!_dedupeChecksums)
{
- Patch patch;
- patch.progNumber = static_cast<int>(_result.size());
- patch.sysex = _data;
- patch.data.insert(patch.data.begin(), _data.begin() + 9, _data.end());
- initializePatch(patch);
+ _result.push_back(patch);
+ }
+ else
+ {
+ const auto md5 = std::string(MD5(&_data.front() + 9 + 17, 256 - 17 - 3).toHexString().toRawUTF8());
- if (!_dedupeChecksums)
+ if (_dedupeChecksums->find(md5) == _dedupeChecksums->end())
{
+ _dedupeChecksums->insert(md5);
_result.push_back(patch);
}
- else
- {
- const auto md5 = std::string(MD5(it + 9 + 17, 256 - 17 - 3).toHexString().toRawUTF8());
-
- if (_dedupeChecksums->find(md5) == _dedupeChecksums->end())
- {
- _dedupeChecksums->insert(md5);
- _result.push_back(patch);
- }
- }
-
- return true;
}
- return false;
+
+ return true;
}
- uint32_t PatchBrowser::loadBankFile(std::vector<Patch>& _result, std::set<std::string>* _dedupeChecksums, const File& file)
+ uint32_t PatchBrowser::loadBankFile(const Virus::Controller& _controller, std::vector<Patch>& _result, std::set<std::string>* _dedupeChecksums, const File& file)
{
const auto ext = file.getFileExtension().toLowerCase();
const auto path = file.getParentDirectory().getFullPathName();
@@ -207,7 +172,7 @@ namespace genericVirusUI
std::vector<std::vector<uint8_t>> packets;
synthLib::MidiToSysex::splitMultipleSysex(packets, d);
- return load(_result, _dedupeChecksums, packets);
+ return load(_controller, _result, _dedupeChecksums, packets);
}
if (ext == ".mid" || ext == ".midi")
@@ -222,7 +187,7 @@ namespace genericVirusUI
std::vector<std::vector<uint8_t>> packets;
synthLib::MidiToSysex::splitMultipleSysex(packets, data);
- return load(_result, _dedupeChecksums, packets);
+ return load(_controller, _result, _dedupeChecksums, packets);
}
return 0;
@@ -244,7 +209,7 @@ namespace genericVirusUI
std::vector<Patch> patches;
for (const auto& f : RangedDirectoryIterator(file, false, "*.syx;*.mid;*.midi", File::findFiles))
- loadBankFile(patches, &dedupeChecksums, f.getFile());
+ loadBankFile(m_controller, patches, &dedupeChecksums, f.getFile());
m_filteredPatches.clear();
@@ -271,7 +236,7 @@ namespace genericVirusUI
{
m_patches.clear();
std::vector<Patch> patches;
- loadBankFile(patches, nullptr, file);
+ loadBankFile(m_controller, patches, nullptr, file);
m_filteredPatches.clear();
for (const auto& patch : patches)
{
@@ -317,11 +282,11 @@ namespace genericVirusUI
else if (columnId == Columns::NAME)
text = rowElement.name;
else if (columnId == Columns::CAT1)
- text = g_categories[rowElement.category1];
+ text = rowElement.category1;
else if (columnId == Columns::CAT2)
- text = g_categories[rowElement.category2];
+ text = rowElement.category2;
else if (columnId == Columns::ARP)
- text = rowElement.data[129] != 0 ? "Y" : " ";
+ text = rowElement.arpMode != 0 ? "Y" : " ";
else if (columnId == Columns::UNI)
text = rowElement.unison == 0 ? " " : String(rowElement.unison + 1);
else if (columnId == Columns::ST)
@@ -342,14 +307,15 @@ namespace genericVirusUI
if (idx == -1)
return;
- // force to edit buffer
+ // re-pack single, force to edit buffer
const auto program = m_controller.isMultiMode() ? m_controller.getCurrentPart() : static_cast<uint8_t>(virusLib::ProgramType::SINGLE);
- auto sysex = m_filteredPatches[idx].sysex;
- sysex[7] = toMidiByte(virusLib::BankNumber::EditBuffer);
- sysex[8] = program;
+ const auto msg = m_controller.modifySingleDump(m_filteredPatches[idx].sysex, virusLib::BankNumber::EditBuffer, program, true, true);
+
+ if(msg.empty())
+ return;
- m_controller.sendSysEx(sysex);
+ m_controller.sendSysEx(msg);
m_controller.requestSingle(0x0, program);
}
@@ -374,11 +340,11 @@ namespace genericVirusUI
if (m_attributeToSort == Columns::NAME)
return m_direction * first.name.compareIgnoreCase(second.name);
if (m_attributeToSort == Columns::CAT1)
- return m_direction * (first.category1 - second.category1);
+ return m_direction * first.category1.compare(second.category1);
if (m_attributeToSort == Columns::CAT2)
- return m_direction * (first.category2 - second.category2);
+ return m_direction * first.category2.compare(second.category2);
if (m_attributeToSort == Columns::ARP)
- return m_direction * (first.data[129] - second.data[129]);
+ return m_direction * (first.arpMode - second.arpMode);
if (m_attributeToSort == Columns::UNI)
return m_direction * (first.unison - second.unison);
if (m_attributeToSort == Columns::VER)
@@ -405,7 +371,7 @@ namespace genericVirusUI
void PatchBrowser::loadRomBank(uint32_t _bankIndex)
{
- const auto& singles = m_editor.getController().getSinglePresets();
+ const auto& singles = m_controller.getSinglePresets();
if(_bankIndex >= singles.size())
return;
@@ -421,32 +387,12 @@ namespace genericVirusUI
{
Patch patch;
- patch.progNumber = static_cast<int>(s);
- patch.data.insert(patch.data.begin(), bank[s].data.begin(), bank[s].data.end());
-
- initializePatch(patch);
+ patch.sysex = bank[s].data;
- // build sysex message
+ if(!initializePatch(m_controller, patch))
+ continue;
- patch.sysex.push_back(0xf0);
- patch.sysex.push_back(0x00); // Manufacturer
- patch.sysex.push_back(0x20);
- patch.sysex.push_back(0x33);
- patch.sysex.push_back(0x01); // Product Id = Virus
- patch.sysex.push_back(m_editor.getController().getDeviceId());
- patch.sysex.push_back(virusLib::DUMP_SINGLE);
- patch.sysex.push_back(static_cast<uint8_t>(_bankIndex)); // bank
- patch.sysex.push_back(static_cast<uint8_t>(patch.progNumber));
- patch.sysex.insert(patch.sysex.end(), patch.data.begin(), patch.data.end());
-
- // checksum
- uint8_t cs = 0;
-
- for(size_t i=5; i<patch.sysex.size(); ++i)
- cs += patch.sysex[i];
-
- patch.sysex.push_back(cs & 0x7f);
- patch.sysex.push_back(0xf7);
+ patch.progNumber = static_cast<int>(s);
m_patches.add(patch);
@@ -458,4 +404,40 @@ namespace genericVirusUI
m_patchList.deselectAllRows();
m_patchList.repaint();
}
+
+ bool PatchBrowser::initializePatch(const Virus::Controller& _controller, Patch& _patch)
+ {
+ const auto& c = _controller;
+
+ pluginLib::MidiPacket::Data data;
+ pluginLib::MidiPacket::ParamValues parameterValues;
+
+ if(!c.parseSingle(data, parameterValues, _patch.sysex))
+ return false;
+
+ const auto idxVersion = c.getParameterIndexByName("Version");
+ const auto idxCategory1 = c.getParameterIndexByName("Category1");
+ const auto idxCategory2 = c.getParameterIndexByName("Category2");
+ const auto idxUnison = c.getParameterIndexByName("Unison Mode");
+ const auto idxTranspose = c.getParameterIndexByName("Transpose");
+ const auto idxArpMode = c.getParameterIndexByName("Arp Mode");
+
+ _patch.name = c.getSinglePresetName(parameterValues);
+ _patch.model = guessVersion(parameterValues.find(std::make_pair(pluginLib::MidiPacket::AnyPart, idxVersion))->second);
+ _patch.unison = parameterValues.find(std::make_pair(pluginLib::MidiPacket::AnyPart, idxUnison))->second;
+ _patch.transpose = parameterValues.find(std::make_pair(pluginLib::MidiPacket::AnyPart, idxTranspose))->second;
+ _patch.arpMode = parameterValues.find(std::make_pair(pluginLib::MidiPacket::AnyPart, idxArpMode))->second;
+
+ const auto category1 = parameterValues.find(std::make_pair(pluginLib::MidiPacket::AnyPart, idxCategory1))->second;
+ const auto category2 = parameterValues.find(std::make_pair(pluginLib::MidiPacket::AnyPart, idxCategory2))->second;
+
+ const auto* paramCategory1 = c.getParameter(idxCategory1, 0);
+ const auto* paramCategory2 = c.getParameter(idxCategory2, 0);
+
+ _patch.category1 = paramCategory1->getDescription().valueList.valueToText(category1);
+ _patch.category2 = paramCategory2->getDescription().valueList.valueToText(category2);
+
+ return true;
+ }
+
}
diff --git a/source/jucePlugin/ui3/PatchBrowser.h b/source/jucePlugin/ui3/PatchBrowser.h
@@ -22,13 +22,13 @@ namespace genericVirusUI
{
int progNumber = 0;
juce::String name;
- uint8_t category1 = 0;
- uint8_t category2 = 0;
- std::vector<uint8_t> data;
+ std::string category1;
+ std::string category2;
std::vector<uint8_t> sysex;
virusLib::VirusModel model;
uint8_t unison = 0;
uint8_t transpose = 0;
+ uint8_t arpMode = 0;
};
class PatchBrowser : public juce::FileBrowserListener, juce::TableListBoxModel
@@ -36,11 +36,13 @@ namespace genericVirusUI
public:
explicit PatchBrowser(const VirusEditor& _editor);
- static uint32_t load(std::vector<Patch>& _result, std::set<std::string>* _dedupeChecksums, const std::vector<std::vector<uint8_t>>& _packets);
- static bool load(std::vector<Patch>& _result, std::set<std::string>* _dedupeChecksums, const std::vector<uint8_t>& _data);
- static uint32_t loadBankFile(std::vector<Patch>& _result, std::set<std::string>* _dedupeChecksums, const juce::File& file);
+ static uint32_t load(const Virus::Controller& _controller, std::vector<Patch>& _result, std::set<std::string>* _dedupeChecksums, const std::vector<std::vector<uint8_t>>& _packets);
+ static bool load(const Virus::Controller& _controller, std::vector<Patch>& _result, std::set<std::string>* _dedupeChecksums, const std::vector<uint8_t>& _data);
+ static uint32_t loadBankFile(const Virus::Controller& _controller, std::vector<Patch>& _result, std::set<std::string>* _dedupeChecksums, const juce::File& file);
private:
+ static bool initializePatch(const Virus::Controller& _controller, Patch& _patch);
+
juce::FileBrowserComponent& getBankList() {return m_bankList; }
juce::TableListBox& getPatchList() {return m_patchList; }
juce::TextEditor& getSearchBox() {return m_search; }
diff --git a/source/jucePlugin/ui3/VirusEditor.cpp b/source/jucePlugin/ui3/VirusEditor.cpp
@@ -432,7 +432,7 @@ namespace genericVirusUI
const auto ext = result.getFileExtension().toLowerCase();
std::vector<Patch> patches;
- PatchBrowser::loadBankFile(patches, nullptr, result);
+ PatchBrowser::loadBankFile(getController(), patches, nullptr, result);
if (patches.empty())
return;
@@ -440,12 +440,8 @@ namespace genericVirusUI
if (patches.size() == 1)
{
// load to edit buffer of current part
- auto data = patches.front().sysex;
- data[7] = virusLib::toMidiByte(virusLib::BankNumber::EditBuffer);
- if (getController().isMultiMode())
- data[8] = getController().getCurrentPart();
- else
- data[8] = virusLib::SINGLE;
+ const auto data = getController().modifySingleDump(patches.front().sysex, virusLib::BankNumber::EditBuffer,
+ getController().isMultiMode() ? getController().getCurrentPart() : virusLib::SINGLE, true, true);
getController().sendSysEx(data);
}
else
@@ -453,8 +449,7 @@ namespace genericVirusUI
// load to bank A
for (const auto& p : patches)
{
- auto data = p.sysex;
- data[7] = virusLib::toMidiByte(virusLib::BankNumber::A);
+ const auto data = getController().modifySingleDump(p.sysex, virusLib::BankNumber::A, 0, true, false);
getController().sendSysEx(data);
}
}
@@ -467,7 +462,9 @@ namespace genericVirusUI
void VirusEditor::setPlayMode(uint8_t _playMode)
{
const auto playMode = getController().getParameterIndexByName(Virus::g_paramPlayMode);
- getController().getParameter(playMode)->setValue(_playMode);
+
+ getController().getParameter(playMode)->setValue(_playMode);
+
if (_playMode == virusLib::PlayModeSingle && getController().getCurrentPart() != 0)
m_parameterBinding.setPart(0);