commit c0482300576fca41e02bf5ec99ef32f963149e2d
parent dece68e462e7532f528f577866ce171cce8fef23
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date: Sat, 12 Feb 2022 18:15:25 +0100
fix some midi file couldn't be parsed in patch browser
fix virus model not always correctly detected
larger patch browser refactoring / code-deduplication and cleanup
Diffstat:
3 files changed, 143 insertions(+), 183 deletions(-)
diff --git a/source/jucePlugin/VirusController.h b/source/jucePlugin/VirusController.h
@@ -62,6 +62,7 @@ namespace Virus
juce::PropertiesFile* getConfig() { return m_config; }
std::function<void()> onProgramChange = {};
std::function<void()> onMsgDone = {};
+ std::vector<uint8_t> constructMessage(SysEx msg) const;
private:
void timerCallback() override;
@@ -121,8 +122,6 @@ namespace Virus
void parseParamChange(const SysEx &);
void parseControllerDump(synthLib::SMidiEvent &);
- std::vector<uint8_t> constructMessage(SysEx msg) const;
-
AudioPluginAudioProcessor &m_processor;
juce::CriticalSection m_eventQueueLock;
std::vector<synthLib::SMidiEvent> m_virusOut;
diff --git a/source/jucePlugin/ui/Virus_PatchBrowser.cpp b/source/jucePlugin/ui/Virus_PatchBrowser.cpp
@@ -1,10 +1,11 @@
#include "../VirusParameterBinding.h"
-#include "BinaryData.h"
-#include "Ui_Utils.h"
#include "Virus_PatchBrowser.h"
#include "VirusEditor.h"
#include <juce_gui_extra/juce_gui_extra.h>
#include <juce_cryptography/juce_cryptography.h>
+
+#include "../synthLib/midiToSysex.h"
+
using namespace juce;
using namespace virusLib;
constexpr auto comboBoxWidth = 98;
@@ -15,14 +16,13 @@ const Array<String> categories = {"", "Lead", "Bass", "Pad", "Decay", "
PatchBrowser::PatchBrowser(VirusParameterBinding & _parameterBinding, Virus::Controller& _controller) :
m_parameterBinding(_parameterBinding),
m_controller(_controller),
- m_patchList("Patch browser"),
m_fileFilter("*.syx;*.mid;*.midi", "*", "virus patch dumps"),
- m_bankList(FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles, File::getSpecialLocation(File::SpecialLocationType::currentApplicationFile), &m_fileFilter, NULL),
- m_search("Search Box")
+ m_bankList(FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles, File::getSpecialLocation(File::SpecialLocationType::currentApplicationFile), &m_fileFilter, NULL),
+ m_search("Search Box"),
+ m_patchList("Patch browser"),
+ m_properties(m_controller.getConfig())
{
- m_properties = m_controller.getConfig();
-
- auto bankDir = m_properties->getValue("virus_bank_dir", "");
+ const auto bankDir = m_properties->getValue("virus_bank_dir", "");
if (bankDir != "" && File(bankDir).isDirectory())
{
m_bankList.setRoot(bankDir);
@@ -70,159 +70,105 @@ PatchBrowser::PatchBrowser(VirusParameterBinding & _parameterBinding, Virus::Con
void PatchBrowser::selectionChanged() {}
-VirusModel guessVersion(uint8_t *data) {
- if (data[51] > 3) {
- // check extra filter modes
- return VirusModel::C;
- }
- if(data[179] == 0x40 && data[180] == 0x40) // soft knobs don't exist on B so they have fixed value
- return VirusModel::B;
- /*if (data[232] != 0x03 || data[235] != 0x6c || data[238] != 0x01) { // extra mod slots
- return VirusModel::C;
- }*/
- /*if(data[173] != 0x00 || data[174] != 0x00) // EQ
- return VirusModel::C;*/
- /*if (data[220] != 0x40 || data[221] != 0x54 || data[222] != 0x20 || data[223] != 0x40 || data[224] != 0x40) {
- // eq controls
- return VirusModel::C;
- }*/
- return VirusModel::C;
+VirusModel guessVersion(const uint8_t* _data)
+{
+ const auto v = _data[0];
+ if (v < 5)
+ return VirusModel::A;
+ if (v == 6)
+ return VirusModel::B;
+ if (v == 7)
+ return VirusModel::C;
+ return VirusModel::TI;
}
-int PatchBrowser::loadBankFile(const File& file, const int _startIndex = 0, const bool dedupe = false) {
+
+uint32_t PatchBrowser::load(const std::vector<std::vector<uint8_t>>& _packets, bool dedupe)
+{
+ uint32_t count = 0;
+ for (const auto& packet : _packets)
+ {
+ if (load(packet, dedupe))
+ ++count;
+ }
+ return count;
+}
+
+bool PatchBrowser::load(const std::vector<uint8_t>& _data, bool dedupe)
+{
+ if (_data.size() < 267)
+ return false;
+
+ auto* it = &_data.front();
+
+ 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)
+ {
+ Patch patch;
+ patch.progNumber = m_patches.size();
+ patch.sysex = _data;
+ patch.data.insert(patch.data.begin(), _data.begin() + 9, _data.end());
+ 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];
+ if ((uint8_t)*(it + 266) != (uint8_t)0xf7 && (uint8_t)*(it + 266) != (uint8_t)0xf8) {
+ patch.model = VirusModel::TI;
+ }
+ else {
+ patch.model = guessVersion(&patch.data[0]);
+ }
+ auto md5 = MD5(it + 9 + 17, 256 - 17 - 3).toHexString();
+ if (!dedupe || !m_checksums.contains(md5)) {
+ m_checksums.set(md5, true);
+ m_patches.add(patch);
+ }
+
+ return true;
+ }
+ return false;
+}
+
+uint32_t PatchBrowser::loadBankFile(const File& file, const int _startIndex = 0, const bool dedupe = false)
+{
auto ext = file.getFileExtension().toLowerCase();
auto path = file.getParentDirectory().getFullPathName();
- int loadedCount = 0;
- int index = _startIndex;
- if (ext == ".syx")
+
+ if (ext == ".syx")
{
MemoryBlock data;
if (!file.loadFileAsData(data)) {
return 0;
}
- for (auto it = data.begin(); it != data.end(); it += 267)
- {
- if ((uint8_t)*it == (uint8_t)0xf0
- && (uint8_t)*(it+1) == (uint8_t)0x00
- && (uint8_t)*(it+2) == (uint8_t)0x20
- && (uint8_t)*(it+3) == (uint8_t)0x33
- && (uint8_t)*(it+4) == (uint8_t)0x01
- && (uint8_t)*(it+6) == (uint8_t)virusLib::DUMP_SINGLE)
- {
- Patch patch;
- patch.progNumber = index;
- data.copyTo(patch.data, 267*index + 9, 256);
- 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];
- if ((uint8_t)*(it + 266) != (uint8_t)0xf7 && (uint8_t)*(it + 266) != (uint8_t)0xf8) {
- patch.model = VirusModel::TI;
- }
- else {
- patch.model = guessVersion(patch.data);
- }
- auto md5 = MD5(it+9 + 17, 256-17-3).toHexString();
- if(!dedupe || !m_checksums.contains(md5)) {
- m_checksums.set(md5, true);
- m_patches.add(patch);
- index++;
- }
- }
- }
+
+ std::vector<uint8_t> d;
+ d.resize(data.getSize());
+ memcpy(&d[0], data.getData(), data.getSize());
+
+ std::vector<std::vector<uint8_t>> packets;
+ splitMultipleSysex(packets, d);
+
+ return load(packets, dedupe);
}
- else if (ext == ".mid" || ext == ".midi")
+
+ if (ext == ".mid" || ext == ".midi")
{
- MemoryBlock data;
- if (!file.loadFileAsData(data))
- {
- return 0;
- }
+ std::vector<uint8_t> data;
- const uint8_t *ptr = (uint8_t *)data.getData();
- const auto end = ptr + data.getSize();
-
- for (auto it = ptr; it < end; it += 1)
- {
- if ((uint8_t)*it == (uint8_t)0xf0 && (it + 267) < end) // we don't check for sysex eof so we can load TI banks
- {
- if ((uint8_t) *(it+1) == (uint8_t)0x00
- && (uint8_t)*(it+2) == 0x20
- && (uint8_t)*(it+3) == 0x33
- && (uint8_t)*(it+4) == 0x01
- && (uint8_t)*(it+6) == virusLib::DUMP_SINGLE)
- {
- auto syx = Virus::SysEx(it, it + 267);
- syx[7] = 0x01; // force to bank a
- syx[266] = 0xf7;
-
- Patch patch;
- patch.progNumber = index;
- std::copy(syx.begin() + 9, syx.end() - 2, patch.data);
- 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];
- if ((uint8_t)*(it + 266) != (uint8_t)0xf7 && (uint8_t)*(it + 266) != (uint8_t)0xf8) {
- patch.model = VirusModel::TI;
- }
- else {
- patch.model = guessVersion(patch.data);
- }
- auto md5 = MD5(it+9 + 17, 256-17-3).toHexString();
- if(!dedupe || !m_checksums.contains(md5)) {
- m_checksums.set(md5, true);
- m_patches.add(patch);
- index++;
- }
-
- it += 266;
- }
- else if((uint8_t)*(it+3) == 0x00 // some midi files have two bytes after the 0xf0
- && (uint8_t)*(it+4) == 0x20
- && (uint8_t)*(it+5) == 0x33
- && (uint8_t)*(it+6) == 0x01
- && (uint8_t)*(it+8) == virusLib::DUMP_SINGLE)
- {
- auto syx = Virus::SysEx();
- syx.push_back(0xf0);
- for (auto i = it + 3; i < it + 3 + 266; i++)
- {
- syx.push_back((uint8_t)*i);
- }
- syx[7] = 0x01; // force to bank a
- syx[266] = 0xf7;
-
- Patch patch;
- std::memcpy(patch.data, syx.data()+9, 256);
- patch.progNumber = index;
- 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];
- if ((uint8_t)*(it + 2 + 266) != (uint8_t)0xf7 && (uint8_t)*(it + 2 + 266) != (uint8_t)0xf8) {
- patch.model = VirusModel::TI;
- }
- else {
- patch.model = guessVersion(patch.data);
- }
- auto md5 = MD5(it+2+9 + 17, 256-17-3).toHexString();
- if(!dedupe || !m_checksums.contains(md5)) {
- m_checksums.set(md5, true);
- m_patches.add(patch);
- index++;
- }
- loadedCount++;
-
- it += 266;
- }
- }
- }
+ if (!synthLib::MidiToSysex::readFile(data, file.getFullPathName().getCharPointer()))
+ return 0;
+
+ std::vector<std::vector<uint8_t>> packets;
+ splitMultipleSysex(packets, data);
+
+ return load(packets, dedupe);
}
- return index;
+ return 0;
}
void PatchBrowser::fileClicked(const File &file, const MouseEvent &e)
@@ -234,9 +180,9 @@ void PatchBrowser::fileClicked(const File &file, const MouseEvent &e)
p.addItem("Add directory contents to patch list", [this, file]() {
m_patches.clear();
m_checksums.clear();
- int lastIndex = 0;
+
for (auto f : RangedDirectoryIterator(file, false, "*.syx;*.mid;*.midi", File::findFiles)) {
- lastIndex = loadBankFile(f.getFile(), lastIndex, true);
+ loadBankFile(f.getFile(), 0, true);
}
m_filteredPatches.clear();
for(auto patch : m_patches) {
@@ -323,36 +269,23 @@ void PatchBrowser::paintCell(Graphics &g, int rowNumber, int columnId, int width
g.fillRect(width - 1, 0, 1, height); // [7]
}
-void PatchBrowser::selectedRowsChanged(int lastRowSelected) {
- auto idx = m_patchList.getSelectedRow();
- if (idx == -1) {
+void PatchBrowser::selectedRowsChanged(int lastRowSelected)
+{
+ const auto idx = m_patchList.getSelectedRow();
+
+ if (idx == -1)
return;
- }
- uint8_t syxHeader[9] = {0xF0, 0x00, 0x20, 0x33, 0x01, 0x00, 0x10, 0x00, 0x00};
- syxHeader[8] = m_controller.isMultiMode() ? m_controller.getCurrentPart() : virusLib::ProgramType::SINGLE; // set edit buffer
- const uint8_t syxEof = 0xF7;
- uint8_t cs = syxHeader[5] + syxHeader[6] + syxHeader[7] + syxHeader[8];
- uint8_t data[256];
- for (int i = 0; i < 256; i++)
- {
- data[i] = m_filteredPatches[idx].data[i];
- cs += data[i];
- }
- cs = cs & 0x7f;
- Virus::SysEx syx;
- for (auto i : syxHeader)
- {
- syx.push_back(i);
- }
- for (auto i : data)
- {
- syx.push_back(i);
- }
- syx.push_back(cs);
- syx.push_back(syxEof);
- m_controller.sendSysEx(syx); // send to edit buffer
- m_controller.parseMessage(syx); // update ui
- getParentComponent()->postCommandMessage(VirusEditor::Commands::UpdateParts);
+
+ // force to edit buffer
+ const auto part = m_controller.isMultiMode() ? m_controller.getCurrentPart() : static_cast<uint8_t>(virusLib::ProgramType::SINGLE);
+
+ auto sysex = m_filteredPatches[idx].sysex;
+ sysex[7] = 0;
+ sysex[8] = part;
+
+ m_controller.sendSysEx(sysex);
+
+ m_controller.sendSysEx(m_controller.constructMessage({ virusLib::REQUEST_SINGLE, 0x0, part }));
}
void PatchBrowser::cellDoubleClicked(int rowNumber, int columnId, const MouseEvent &)
@@ -413,3 +346,26 @@ void PatchBrowser::sortOrderChanged(int newSortColumnId, bool isForwards)
m_patchList.updateContent();
}
}
+
+void PatchBrowser::splitMultipleSysex(std::vector<std::vector<uint8_t>>& _dst, const std::vector<uint8_t>& _src)
+{
+ for(size_t i=0; i<_src.size(); ++i)
+ {
+ if(_src[i] != 0xf0)
+ continue;
+
+ for(size_t j=i+1; j < _src.size(); ++j)
+ {
+ if(_src[j] == 0xf7)
+ {
+ std::vector<uint8_t> entry;
+ entry.insert(entry.begin(), _src.begin() + i, _src.begin() + j + 1);
+
+ _dst.emplace_back(entry);
+
+ i = j;
+ break;
+ }
+ }
+ }
+}
diff --git a/source/jucePlugin/ui/Virus_PatchBrowser.h b/source/jucePlugin/ui/Virus_PatchBrowser.h
@@ -13,7 +13,8 @@ struct Patch
juce::String name;
uint8_t category1;
uint8_t category2;
- uint8_t data[256];
+ std::vector<uint8_t> data;
+ std::vector<uint8_t> sysex;
virusLib::VirusModel model;
uint8_t unison;
uint8_t transpose;
@@ -44,7 +45,9 @@ private:
juce::Array<Patch> m_filteredPatches;
juce::PropertiesFile *m_properties;
juce::HashMap<juce::String, bool> m_checksums;
- int loadBankFile(const juce::File &file, const int _startIndex, const bool dedupe);
+ uint32_t load(const std::vector<std::vector<uint8_t>>& _packets, bool dedupe);
+ bool load(const std::vector<uint8_t>& _data, bool dedupe);
+ uint32_t loadBankFile(const juce::File &file, const int _startIndex, const bool dedupe);
// Inherited via FileBrowserListener
void selectionChanged() override;
void fileClicked(const juce::File &file, const juce::MouseEvent &e) override;
@@ -72,4 +75,6 @@ private:
ST = 7,
VER = 8,
};
+
+ static void splitMultipleSysex(std::vector<std::vector<uint8_t>>& _dst, const std::vector<uint8_t>& _src);
};