commit a03ae96337bbe2332924b3856613089e176644e7
parent b8094452200625521ac9526e5d89361d8615a024
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date: Wed, 4 Dec 2024 00:06:19 +0100
do not use midi packets to parse & build singles before export, corrupts Q patches that should be exported as-is
Diffstat:
8 files changed, 159 insertions(+), 39 deletions(-)
diff --git a/source/mqJucePlugin/mqPatchManager.cpp b/source/mqJucePlugin/mqPatchManager.cpp
@@ -3,6 +3,7 @@
#include "mqController.h"
#include "mqEditor.h"
#include "jucePluginEditorLib/pluginProcessor.h"
+#include "mqLib/mqstate.h"
namespace mqJucePlugin
{
@@ -64,15 +65,25 @@ namespace mqJucePlugin
pluginLib::patchDB::Data PatchManager::applyModifications(const pluginLib::patchDB::PatchPtr& _patch) const
{
- pluginLib::MidiPacket::Data data;
- pluginLib::MidiPacket::AnyPartParamValues parameterValues;
+ auto result = _patch->sysex;
- if (!m_controller.parseSingle(data, parameterValues, _patch->sysex))
- return _patch->sysex;
+ if (_patch->sysex.size() != std::tuple_size_v<mqLib::State::Single> &&
+ _patch->sysex.size() != std::tuple_size_v<mqLib::State::SingleQ>)
+ return result;
- // apply name
if (!_patch->getName().empty())
- m_controller.setSingleName(parameterValues, _patch->getName());
+ mqLib::State::setSingleName(result, _patch->getName());
+
+ // first set tag is category
+ const auto& tags = _patch->getTags(pluginLib::patchDB::TagType::Category).getAdded();
+
+ std::string category;
+
+ if(!tags.empty())
+ category = *tags.begin();
+
+ if (!category.empty())
+ mqLib::State::setCategory(result, category);
// apply program
uint32_t program = 0;
@@ -85,16 +96,10 @@ namespace mqJucePlugin
program -= bank * 100;
}
- // apply category
- const auto& tags = _patch->getTags(pluginLib::patchDB::TagType::Category).getAdded();
-
- if(!tags.empty())
- m_controller.setCategory(parameterValues, *tags.begin());
+ result[mqLib::IdxSingleBank] = static_cast<uint8_t>(bank);
+ result[mqLib::IdxSingleProgram] = static_cast<uint8_t>(program);
- return m_controller.createSingleDump(
- static_cast<mqLib::MidiBufferNum>(static_cast<uint8_t>(mqLib::MidiBufferNum::DeprecatedSingleBankA) + bank),
- static_cast<mqLib::MidiSoundLocation>(static_cast<uint8_t>(mqLib::MidiSoundLocation::AllSinglesBankA) + bank),
- static_cast<uint8_t>(program), parameterValues);
+ return result;
}
uint32_t PatchManager::getCurrentPart() const
diff --git a/source/mqLib/mqmiditypes.h b/source/mqLib/mqmiditypes.h
@@ -69,7 +69,25 @@ namespace mqLib
EditBufferFirstSingleLayer = 0x00,
EditBufferFirstDrumMapInstrument = 0x10,
};
-
+
+ namespace mq
+ {
+ constexpr uint32_t g_singleNameLength = 16;
+ constexpr uint32_t g_singleNameOffset = 370;
+
+ constexpr uint32_t g_categoryLength = 4;
+ constexpr uint32_t g_categoryOffset = 386;
+ };
+
+ namespace q
+ {
+ constexpr uint32_t g_singleNameLength = 16;
+ constexpr uint32_t g_singleNameOffset = 371;
+
+ constexpr uint32_t g_categoryLength = 4;
+ constexpr uint32_t g_categoryOffset = 387;
+ };
+
enum class GlobalParameter : uint8_t
{
// Global data
@@ -175,6 +193,8 @@ namespace mqLib
enum SysexIndex
{
// first parameter of a dump
+ IdxSingleBank = 5,
+ IdxSingleProgram = 6,
IdxSingleParamFirst = 7,
IdxMultiParamFirst = IdxSingleParamFirst,
IdxDrumParamFirst = IdxSingleParamFirst,
diff --git a/source/mqLib/mqstate.cpp b/source/mqLib/mqstate.cpp
@@ -235,6 +235,46 @@ namespace mqLib
return loadState(_state);
}
+ bool State::setSingleName(std::vector<uint8_t>& _sysex, const std::string& _name)
+ {
+ if (getCommand(_sysex) != SysexCommand::SingleDump)
+ return false;
+
+ if (_sysex.size() == std::tuple_size_v<Single>)
+ {
+ for (size_t i=0; i<mq::g_singleNameLength; ++i)
+ _sysex[i + mq::g_singleNameOffset] = i >= _name.size() ? ' ' : _name[i];
+ return true;
+ }
+ if (_sysex.size() == std::tuple_size_v<SingleQ>)
+ {
+ for (size_t i=0; i<q::g_singleNameLength; ++i)
+ _sysex[i + q::g_singleNameOffset] = i >= _name.size() ? ' ' : _name[i];
+ return true;
+ }
+ return false;
+ }
+
+ bool State::setCategory(std::vector<uint8_t>& _sysex, const std::string& _name)
+ {
+ if (getCommand(_sysex) != SysexCommand::SingleDump)
+ return false;
+
+ if (_sysex.size() == std::tuple_size_v<Single>)
+ {
+ for (size_t i=0; i<mq::g_categoryLength; ++i)
+ _sysex[i + mq::g_categoryOffset] = i >= _name.size() ? ' ' : _name[i];
+ return true;
+ }
+ if (_sysex.size() == std::tuple_size_v<SingleQ>)
+ {
+ for (size_t i=0; i<q::g_categoryLength; ++i)
+ _sysex[i + q::g_categoryOffset] = i >= _name.size() ? ' ' : _name[i];
+ return true;
+ }
+ return false;
+ }
+
bool State::parseSingleDump(const SysEx& _data)
{
Single single;
diff --git a/source/mqLib/mqstate.h b/source/mqLib/mqstate.h
@@ -4,6 +4,7 @@
#include <vector>
#include <cstddef>
#include <cstdint>
+#include <string>
#include "mqmiditypes.h"
@@ -89,6 +90,9 @@ namespace mqLib
bool getState(std::vector<uint8_t>& _state, synthLib::StateType _type) const;
bool setState(const std::vector<uint8_t>& _state, synthLib::StateType _type);
+ static bool setSingleName(std::vector<uint8_t>& _sysex, const std::string& _name);
+ static bool setCategory(std::vector<uint8_t>& _sysex, const std::string& _name);
+
static void createSequencerMultiData(std::vector<uint8_t>& _data);
private:
diff --git a/source/xtJucePlugin/xtPatchManager.cpp b/source/xtJucePlugin/xtPatchManager.cpp
@@ -108,29 +108,54 @@ namespace xtJucePlugin
pluginLib::patchDB::Data PatchManager::applyModifications(const pluginLib::patchDB::PatchPtr& _patch) const
{
- pluginLib::MidiPacket::Data data;
- pluginLib::MidiPacket::AnyPartParamValues parameterValues;
+ auto applyModifications = [&_patch](pluginLib::patchDB::Data& _result) -> bool
+ {
+ if (xt::State::getCommand(_patch->sysex) != xt::SysexCommand::SingleDump)
+ return false;
+
+ const auto dumpSize = _patch->sysex.size();
+
+ if (dumpSize != std::tuple_size_v<xt::State::Single>)
+ return false;
+
+ // apply name
+ if (!_patch->getName().empty())
+ xt::State::setSingleName(_result, _patch->getName());
+
+ // apply program
+ uint32_t program = 0;
+ uint32_t bank = 0;
+ if(_patch->program != pluginLib::patchDB::g_invalidProgram)
+ {
+ program = std::clamp(_patch->program, 0u, 299u);
- if (!m_controller.parseSingle(data, parameterValues, _patch->sysex))
- return _patch->sysex;
+ bank = program / 128;
+ program -= bank * 128;
+ }
+
+ _result[xt::SysexIndex::IdxSingleBank ] = static_cast<uint8_t>(bank);
+ _result[xt::SysexIndex::IdxSingleProgram] = static_cast<uint8_t>(program);
- // apply name
- if (!_patch->getName().empty())
- m_controller.setSingleName(parameterValues, _patch->getName());
+ return true;
+ };
- // apply program
- uint32_t program = 0;
- uint32_t bank = 0;
- if(_patch->program != pluginLib::patchDB::g_invalidProgram)
+ if (xt::State::getCommand(_patch->sysex) == xt::SysexCommand::SingleDump)
{
- program = std::clamp(_patch->program, 0u, 299u);
+ auto result = _patch->sysex;
- bank = program / 128;
- program -= bank * 128;
+ if (applyModifications(result))
+ return result;
+
+ std::vector<xt::SysEx> dumps;
+
+ if (xt::State::splitCombinedPatch(dumps, _patch->sysex))
+ {
+ if (applyModifications(dumps[0]))
+ return xt::State::createCombinedPatch(dumps);
+ }
}
- const auto sysex = m_controller.createSingleDump(static_cast<xt::LocationH>(static_cast<uint8_t>(xt::LocationH::SingleBankA) + bank), static_cast<uint8_t>(program), parameterValues);
- return createCombinedDump(sysex);
+ return _patch->sysex;
}
uint32_t PatchManager::getCurrentPart() const
diff --git a/source/xtLib/xtMidiTypes.h b/source/xtLib/xtMidiTypes.h
@@ -59,6 +59,8 @@ namespace xt
enum SysexIndex
{
// first parameter of a dump
+ IdxSingleBank = 5,
+ IdxSingleProgram = 6,
IdxSingleParamFirst = 7,
IdxSingleChecksumStart = IdxSingleParamFirst,
IdxMultiParamFirst = IdxSingleParamFirst,
@@ -194,6 +196,12 @@ namespace xt
static constexpr uint32_t g_idmPreset = 0x42;
};
+ namespace mw2
+ {
+ static constexpr uint32_t g_singleNameLength = 16;
+ static constexpr uint32_t g_singleNamePosition = 247; // in a dump including sysex header
+ }
+
namespace wave
{
static constexpr uint16_t g_romWaveCount = 506;
diff --git a/source/xtLib/xtState.cpp b/source/xtLib/xtState.cpp
@@ -327,6 +327,19 @@ namespace xt
}
}
+ bool State::setSingleName(std::vector<uint8_t>& _sysex, const std::string& _name)
+ {
+ if (_sysex.size() != std::tuple_size_v<Single>)
+ return false;
+
+ if (getCommand(_sysex) != SysexCommand::SingleDump)
+ return false;
+
+ for (size_t i=0; i<mw2::g_singleNameLength; ++i)
+ _sysex[i + mw2::g_singleNamePosition] = i >= _name.size() ? ' ' : _name[i];
+ return true;
+ }
+
TableId State::getWavetableFromSingleDump(const SysEx& _single)
{
constexpr auto wavetableIndex = IdxSingleParamFirst + static_cast<uint32_t>(SingleParameter::Wavetable);
@@ -1104,20 +1117,20 @@ namespace xt
return sysex;
}
- void State::splitCombinedPatch(std::vector<SysEx>& _dumps, const SysEx& _combinedSingle)
+ bool State::splitCombinedPatch(std::vector<SysEx>& _dumps, const SysEx& _combinedSingle)
{
if(getCommand(_combinedSingle) != SysexCommand::SingleDump)
- return;
+ return false;
constexpr auto singleSize = std::tuple_size_v<Single>;
constexpr auto tableSize = std::tuple_size_v<Table>;
constexpr auto waveSize = std::tuple_size_v<Wave>;
if(_combinedSingle.size() == singleSize)
- return;
+ return false;
if(_combinedSingle.size() < singleSize + tableSize - 2)
- return;
+ return false;
auto& single = _dumps.emplace_back();
size_t offBegin = 0;
@@ -1143,6 +1156,8 @@ namespace xt
wave.insert(wave.end(), _combinedSingle.begin() + static_cast<ptrdiff_t>(offBegin), _combinedSingle.begin() + static_cast<ptrdiff_t>(offEnd));
wave.push_back(0xf7);
}
+
+ return true;
}
SysEx State::createCombinedPatch(const std::vector<SysEx>& _dumps)
diff --git a/source/xtLib/xtState.h b/source/xtLib/xtState.h
@@ -5,6 +5,7 @@
#include <cstddef>
#include <cstdint>
#include <functional>
+#include <string>
#include "xtMidiTypes.h"
#include "xtTypes.h"
@@ -91,6 +92,8 @@ namespace xt
void process(uint32_t _numSamples);
+ static bool setSingleName(std::vector<uint8_t>& _sysex, const std::string& _name);
+
static TableId getWavetableFromSingleDump(const SysEx& _single);
static void createSequencerMultiData(std::vector<uint8_t>& _data);
@@ -103,7 +106,9 @@ namespace xt
static SysEx createTableData(const TableData& _table, uint32_t _tableIndex, bool _preview);
static SysEx createCombinedPatch(const std::vector<SysEx>& _dumps);
- static void splitCombinedPatch(std::vector<SysEx>& _dumps, const SysEx& _combinedSingle);
+ static bool splitCombinedPatch(std::vector<SysEx>& _dumps, const SysEx& _combinedSingle);
+
+ static SysexCommand getCommand(const SysEx& _data);
private:
@@ -199,8 +204,6 @@ namespace xt
return _mode.front() == 0xf0;
}
- static SysexCommand getCommand(const SysEx& _data);
-
void forwardToDevice(const SysEx& _data);
void requestGlobal() const;