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 }