gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

PatchManager.cpp (12999B)


      1 #include "PatchManager.h"
      2 
      3 #include "VirusEditor.h"
      4 #include "VirusController.h"
      5 
      6 #include "jucePluginLib/filetype.h"
      7 #include "jucePluginLib/patchdb/datasource.h"
      8 
      9 #include "jucePluginEditorLib/pluginEditor.h"
     10 
     11 #include "virusLib/microcontroller.h"
     12 #include "virusLib/device.h"
     13 #include "virusLib/midiFileToRomData.h"
     14 
     15 #include "synthLib/midiToSysex.h"
     16 
     17 #include "juce_cryptography/hashing/juce_MD5.h"
     18 
     19 namespace virus
     20 {
     21 	class Controller;
     22 }
     23 
     24 namespace genericVirusUI
     25 {
     26 	PatchManager::PatchManager(VirusEditor& _editor, juce::Component* _root) : jucePluginEditorLib::patchManager::PatchManager(_editor, _root), m_controller(_editor.getController())
     27 	{
     28 		setTagTypeName(pluginLib::patchDB::TagType::CustomA, "Virus Model");
     29 		setTagTypeName(pluginLib::patchDB::TagType::CustomB, "Virus Features");
     30 		
     31 		addGroupTreeItemForTag(pluginLib::patchDB::TagType::CustomA);
     32 		addGroupTreeItemForTag(pluginLib::patchDB::TagType::CustomB);
     33 
     34 		startLoaderThread();
     35 
     36 		// rom patches are received via midi, make sure we add all remaining ones, too
     37 		m_controller.onRomPatchReceived = [this](const virusLib::BankNumber _bank, const uint32_t _program)
     38 		{
     39 			if (_bank == virusLib::BankNumber::EditBuffer)
     40 				return;
     41 
     42 			const auto index = virusLib::toArrayIndex(_bank);
     43 
     44 			const auto& banks = m_controller.getSinglePresets();
     45 
     46 			if(index < banks.size())
     47 			{
     48 				const auto& bank = banks[index];
     49 
     50 				if(_program == bank.size() - 1)
     51 				{
     52 					const auto romDS = createRomDataSource(index);
     53 					removeDataSource(romDS);
     54 					addDataSource(romDS);
     55 				}
     56 			}
     57 		};
     58 
     59 		addRomPatches();
     60 	}
     61 
     62 	PatchManager::~PatchManager()
     63 	{
     64 		stopLoaderThread();
     65 		m_controller.onRomPatchReceived = {};
     66 	}
     67 
     68 	bool PatchManager::loadRomData(pluginLib::patchDB::DataList& _results, const uint32_t _bank, const uint32_t _program)
     69 	{
     70 		const auto bankIndex = _bank;
     71 
     72 		const auto& singles = m_controller.getSinglePresets();
     73 
     74 		if (bankIndex >= singles.size())
     75 			return false;
     76 
     77 		const auto& bank = singles[bankIndex];
     78 
     79 		if(_program != pluginLib::patchDB::g_invalidProgram)
     80 		{
     81 			if (_program >= bank.size())
     82 				return false;
     83 			const auto& s = bank[_program];
     84 			if (s.data.empty())
     85 				return false;
     86 			_results.push_back(s.data);
     87 		}
     88 		else
     89 		{
     90 			_results.reserve(bank.size());
     91 			for (const auto& patch : bank)
     92 				_results.push_back(patch.data);
     93 		}
     94 		return true;
     95 	}
     96 
     97 	std::shared_ptr<pluginLib::patchDB::Patch> PatchManager::initializePatch(std::vector<uint8_t>&& _sysex, const std::string& _defaultPatchName)
     98 	{
     99 		if (_sysex.size() < 267)
    100 			return nullptr;
    101 
    102 		const auto& c = static_cast<const virus::Controller&>(m_controller);
    103 
    104 		pluginLib::MidiPacket::Data data;
    105 		pluginLib::MidiPacket::AnyPartParamValues parameterValues;
    106 
    107 		if (!c.parseSingle(data, parameterValues, _sysex))
    108 			return nullptr;
    109 
    110 		const auto idxVersion = c.getParameterIndexByName("Version");
    111 		const auto idxCategory1 = c.getParameterIndexByName("Category1");
    112 		const auto idxCategory2 = c.getParameterIndexByName("Category2");
    113 		const auto idxUnison = c.getParameterIndexByName("Unison Mode");
    114 //		const auto idxTranspose = c.getParameterIndexByName("Transpose");
    115 		const auto idxArpMode = c.getParameterIndexByName("Arp Mode");
    116 		const auto idxPhaserMix = c.getParameterIndexByName("Phaser Mix");
    117 		const auto idxChorusMix = c.getParameterIndexByName("Chorus Mix");
    118 
    119 		auto patch = std::make_shared<pluginLib::patchDB::Patch>();
    120 
    121 		{
    122 			const auto it = data.find(pluginLib::MidiDataType::Bank);
    123 			if (it != data.end())
    124 				patch->bank = it->second;
    125 		}
    126 		{
    127 			const auto it = data.find(pluginLib::MidiDataType::Program);
    128 			if (it != data.end())
    129 				patch->program = it->second;
    130 		}
    131 
    132 		{
    133 			constexpr auto frontOffset = 9;			// remove bank number, program number and other stuff that we don't need, first index is the patch version
    134 			constexpr auto backOffset = 2;			// remove f7 and checksum
    135 			const juce::MD5 md5(_sysex.data() + frontOffset, _sysex.size() - frontOffset - backOffset);
    136 
    137 			static_assert(sizeof(juce::MD5) >= sizeof(pluginLib::patchDB::PatchHash));
    138 			memcpy(patch->hash.data(), md5.getChecksumDataArray(), std::size(patch->hash));
    139 		}
    140 
    141 		patch->sysex = std::move(_sysex);
    142 
    143 		patch->name = m_controller.getSinglePresetName(parameterValues);
    144 
    145 		const auto version = virusLib::Microcontroller::getPresetVersion(*parameterValues[idxVersion]);
    146 		const auto unison = *parameterValues[idxUnison];
    147 //		const auto transpose = parameterValues.find(std::make_pair(pluginLib::MidiPacket::AnyPart, idxTranspose))->second;
    148 		const auto arpMode = *parameterValues[idxArpMode];
    149 
    150 		const auto category1 = *parameterValues[idxCategory1];
    151 		const auto category2 = *parameterValues[idxCategory2];
    152 
    153 		const auto* paramCategory1 = c.getParameter(idxCategory1, 0);
    154 		const auto* paramCategory2 = c.getParameter(idxCategory2, 0);
    155 
    156 		auto addCategory = [&patch, version](const uint8_t _value, const pluginLib::Parameter* _param)
    157 		{
    158 			if(!_value)
    159 				return;
    160 			const auto& values = _param->getDescription().valueList;
    161 			if(_value >= values.texts.size())
    162 				return;
    163 
    164 			// Virus < TI had less categories
    165 			if(version < virusLib::D && _value > 16)
    166 				return;
    167 
    168 			const auto t = _param->getDescription().valueList.valueToText(_value);
    169 			patch->tags.add(pluginLib::patchDB::TagType::Category, t);
    170 		};
    171 
    172 		addCategory(category1, paramCategory1);
    173 		addCategory(category2, paramCategory2);
    174 
    175 		switch (version)
    176 		{
    177 		case virusLib::A:	patch->tags.add(pluginLib::patchDB::TagType::CustomA, "A");		break;
    178 		case virusLib::B:	patch->tags.add(pluginLib::patchDB::TagType::CustomA, "B");		break;
    179 		case virusLib::C:	patch->tags.add(pluginLib::patchDB::TagType::CustomA, "C");		break;
    180 		case virusLib::D:	patch->tags.add(pluginLib::patchDB::TagType::CustomA, "TI");		break;
    181 		case virusLib::D2:	patch->tags.add(pluginLib::patchDB::TagType::CustomA, "TI2");		break;
    182 		}
    183 
    184 		if(arpMode)
    185 			patch->tags.add(pluginLib::patchDB::TagType::CustomB, "Arp");
    186 		if(unison)
    187 			patch->tags.add(pluginLib::patchDB::TagType::CustomB, "Unison");
    188 		if(*parameterValues[idxPhaserMix] > 0)
    189 			patch->tags.add(pluginLib::patchDB::TagType::CustomB, "Phaser");
    190 		if(*parameterValues[idxChorusMix] > 0)
    191 			patch->tags.add(pluginLib::patchDB::TagType::CustomB, "Chorus");
    192 		return patch;
    193 	}
    194 
    195 	pluginLib::patchDB::Data PatchManager::applyModifications(const pluginLib::patchDB::PatchPtr& _patch, const pluginLib::FileType& _fileType, pluginLib::ExportType _exportType) const
    196 	{
    197 		if (_patch->sysex.size() < 267)
    198 			return _patch->sysex;
    199 
    200 		if (_patch->sysex[6] != virusLib::SysexMessageType::DUMP_SINGLE)
    201 			return _patch->sysex;
    202 
    203 		auto result = _patch->sysex;
    204 
    205 		// apply name
    206 		if (!_patch->getName().empty())
    207 		{
    208 			for (size_t i=0; i<10; ++i)
    209 				result[i + 249] = i >= _patch->getName().size() ? ' ' : _patch->getName()[i];
    210 		}
    211 
    212 		auto& bank = result[7];
    213 		auto& program = result[8];
    214 
    215 		// apply program
    216 		if (_patch->program != pluginLib::patchDB::g_invalidProgram)
    217 		{
    218 			const auto bankOffset = _patch->program / 128;
    219 			program = static_cast<uint8_t>(_patch->program - bankOffset * 128);
    220 			bank += static_cast<uint8_t>(bankOffset);
    221 		}
    222 
    223 		// apply categories
    224 		const uint32_t indicesCategory[] = {
    225 			m_controller.getParameterIndexByName("Category1"),
    226 			m_controller.getParameterIndexByName("Category2")
    227 		};
    228 
    229 		const pluginLib::Parameter* paramsCategory[] = {
    230 			m_controller.getParameter(indicesCategory[0], 0),
    231 			m_controller.getParameter(indicesCategory[1], 0)
    232 		};
    233 
    234 		uint8_t val0 = 0;
    235 		uint8_t val1 = 0;
    236 
    237 		const auto& tags = _patch->getTags(pluginLib::patchDB::TagType::Category);
    238 
    239 		size_t i = 0;
    240 		for (const auto& tag : tags.getAdded())
    241 		{
    242 			const auto categoryValue = paramsCategory[i]->getDescription().valueList.textToValue(tag);
    243 			if(categoryValue != 0)
    244 			{
    245 				auto& v = i ? val1 : val0;
    246 				v = static_cast<uint8_t>(categoryValue);
    247 				++i;
    248 				if (i == 2)
    249 					break;
    250 			}
    251 		}
    252 
    253 		result[249 + 11] = val0;
    254 		result[249 + 12] = val1;
    255 
    256 		result[265] = virusLib::Microcontroller::calcChecksum(result, 267 - 2);
    257 
    258 		if (result.size() > 267)
    259 			result[result.size() - 2] = virusLib::Microcontroller::calcChecksum(result, result.size() - 2);
    260 
    261 		return result;
    262 	}
    263 
    264 	bool PatchManager::parseFileData(pluginLib::patchDB::DataList& _results, const pluginLib::patchDB::Data& _data)
    265 	{
    266 		{
    267 			std::vector<synthLib::SMidiEvent> events;
    268 			virusLib::Device::parseTIcontrolPreset(events, _data);
    269 
    270 			for (const auto& e : events)
    271 			{
    272 				if (!e.sysex.empty())
    273 					_results.push_back(e.sysex);
    274 			}
    275 
    276 			if (!_results.empty())
    277 				return true;
    278 		}
    279 
    280 		if (virusLib::Device::parseVTIBackup(_results, _data))
    281 			return true;
    282 
    283 		bool res = virusLib::Device::parsePowercorePreset(_results, _data);
    284 		res |= synthLib::MidiToSysex::extractSysexFromData(_results, _data);
    285 
    286 		if(!res)
    287 			return false;
    288 
    289 		if(!_results.empty())
    290 		{
    291 			if(_data.size() > 500000)
    292 			{
    293 				virusLib::MidiFileToRomData romLoader;
    294 
    295 				for (const auto& result : _results)
    296 				{
    297 					if(!romLoader.add(result))
    298 						break;
    299 				}
    300 				if(romLoader.isComplete())
    301 				{
    302 					const auto& data = romLoader.getData();
    303 
    304 					if(data.size() > 0x10000)
    305 					{
    306 						// presets are written to ROM address 0x50000, the second half of an OS update is therefore at 0x10000
    307 						constexpr ptrdiff_t startAddr = 0x10000;
    308 						ptrdiff_t addr = startAddr;
    309 						uint32_t index = 0;
    310 
    311 						while(addr + 0x100 <= static_cast<ptrdiff_t>(data.size()))
    312 						{
    313 							std::vector<uint8_t> chunk(data.begin() + addr, data.begin() + addr + 0x100);
    314 
    315 							// validate
    316 //							const auto idxH = chunk[2];
    317 							const auto idxL = chunk[3];
    318 
    319 							if(/*idxH != (index >> 7) || */idxL != (index & 0x7f))
    320 								break;
    321 
    322 							bool validName = true;
    323 							for(size_t i=240; i<240+10; ++i)
    324 							{
    325 								if(chunk[i] < 32 || chunk[i] > 128)
    326 								{
    327 									validName = false;
    328 									break;
    329 								}
    330 							}
    331 
    332 							if(!validName)
    333 								continue;
    334 
    335 							addr += 0x100;
    336 							++index;
    337 						}
    338 
    339 						if(index > 0)
    340 						{
    341 							_results.clear();
    342 
    343 							for(uint32_t i=0; i<index; ++i)
    344 							{
    345 								// pack into sysex
    346 								std::vector<uint8_t>& sysex = _results.emplace_back(std::vector<uint8_t>
    347 									{0xf0, 0x00, 0x20, 0x33, 0x01, virusLib::OMNI_DEVICE_ID, 0x10, static_cast<uint8_t>(0x01 + (i >> 7)), static_cast<uint8_t>(i & 0x7f)}
    348 								);
    349 								sysex.insert(sysex.end(), data.begin() + i * 0x100 + startAddr, data.begin() + i * 0x100 + 0x100 + startAddr);
    350 								sysex.push_back(virusLib::Microcontroller::calcChecksum(sysex));
    351 								sysex.push_back(0xf7);
    352 							}
    353 						}
    354 					}
    355 				}
    356 			}
    357 
    358 		}
    359 
    360 		return !_results.empty();
    361 	}
    362 
    363 	bool PatchManager::requestPatchForPart(pluginLib::patchDB::Data& _data, const uint32_t _part, uint64_t)
    364 	{
    365 		_data = m_controller.createSingleDump(static_cast<uint8_t>(_part), toMidiByte(virusLib::BankNumber::A), 0);
    366 		return !_data.empty();
    367 	}
    368 
    369 	uint32_t PatchManager::getCurrentPart() const
    370 	{
    371 		return m_controller.getCurrentPart();
    372 	}
    373 
    374 	bool PatchManager::equals(const pluginLib::patchDB::PatchPtr& _a, const pluginLib::patchDB::PatchPtr& _b) const
    375 	{
    376 		pluginLib::MidiPacket::Data dataA, dataB;
    377 		pluginLib::MidiPacket::AnyPartParamValues parameterValuesA, parameterValuesB;
    378 
    379 		if (!m_controller.parseSingle(dataA, parameterValuesA, _a->sysex) || !m_controller.parseSingle(dataB, parameterValuesB, _b->sysex))
    380 			return false;
    381 
    382 		if(parameterValuesA.size() != parameterValuesB.size())
    383 			return false;
    384 
    385 		for(uint32_t i=0; i<parameterValuesA.size(); ++i)
    386 		{
    387 			const auto& itA = parameterValuesA[i];
    388 			const auto& itB = parameterValuesB[i];
    389 
    390 			if(!itA)
    391 			{
    392 				if(itB)
    393 					return false;
    394 				continue;
    395 			}
    396 
    397 			if(!itB)
    398 				return false;
    399 
    400 			auto vA = *itA;
    401 			auto vB = *itB;
    402 
    403 			if(vA != vB)
    404 			{
    405 				// parameters might be out of range because some dumps have values that are out of range indeed, clamp to valid range and compare again
    406 				const auto* param = m_controller.getParameter(i);
    407 				if(!param)
    408 					return false;
    409 
    410 				if(param->getDescription().isNonPartSensitive())
    411 					continue;
    412 
    413 				vA = static_cast<uint8_t>(param->getDescription().range.clipValue(vA));
    414 				vB = static_cast<uint8_t>(param->getDescription().range.clipValue(vB));
    415 
    416 				if(vA != vB)
    417 					return false;
    418 			}
    419 		}
    420 		return true;
    421 	}
    422 
    423 	bool PatchManager::activatePatch(const pluginLib::patchDB::PatchPtr& _patch, const uint32_t _part)
    424 	{
    425 		return m_controller.activatePatch(applyModifications(_patch, pluginLib::FileType::Empty, pluginLib::ExportType::EmuHardware), _part);
    426 	}
    427 
    428 	void PatchManager::addRomPatches()
    429 	{
    430 		const auto& singles = m_controller.getSinglePresets();
    431 
    432 		for (uint32_t b = 0; b < singles.size(); ++b)
    433 		{
    434 			const auto& bank = singles[b];
    435 
    436 			const auto& single = bank[bank.size()-1];
    437 
    438 			if (single.data.empty())
    439 				continue;
    440 
    441 			addDataSource(createRomDataSource(b));
    442 		}
    443 	}
    444 
    445 	pluginLib::patchDB::DataSource PatchManager::createRomDataSource(const uint32_t _bank) const
    446 	{
    447 		pluginLib::patchDB::DataSource ds;
    448 		ds.type = pluginLib::patchDB::SourceType::Rom;
    449 		ds.bank = _bank;
    450 		ds.name = m_controller.getBankName(_bank);
    451 		return ds;
    452 	}
    453 
    454 }