VirusController.cpp (27618B)
1 #include "VirusController.h" 2 3 #include <fstream> 4 5 #include "ParameterNames.h" 6 #include "VirusProcessor.h" 7 8 #include "virusLib/microcontrollerTypes.h" 9 10 #include "VirusEditor.h" 11 12 using MessageType = virusLib::SysexMessageType; 13 14 namespace virus 15 { 16 constexpr const char* g_midiPacketNames[] = 17 { 18 "requestsingle", 19 "requestmulti", 20 "requestsinglebank", 21 "requestmultibank", 22 "requestarrangement", 23 "requestglobal", 24 "requesttotal", 25 "requestcontrollerdump", 26 "parameterchange", 27 "singledump", 28 "multidump", 29 "singledump_C", 30 }; 31 32 static_assert(std::size(g_midiPacketNames) == static_cast<size_t>(Controller::MidiPacketType::Count)); 33 34 const char* midiPacketName(Controller::MidiPacketType _type) 35 { 36 return g_midiPacketNames[static_cast<uint32_t>(_type)]; 37 } 38 39 Controller::Controller(VirusProcessor& p, const virusLib::DeviceModel _defaultModel, unsigned char deviceId) 40 : pluginLib::Controller(p, virusLib::isTIFamily(_defaultModel) ? "parameterDescriptions_TI.json" : "parameterDescriptions_C.json") 41 , m_processor(p) 42 , m_defaultModel(_defaultModel) 43 , m_deviceId(deviceId) 44 , m_onRomChanged(p.evRomChanged) 45 { 46 registerParams(p); 47 48 // add lambda to enforce updating patches when virus switches from/to multi/single. 49 const auto paramIdx = getParameterIndexByName(g_paramPlayMode); 50 auto* parameter = getParameter(paramIdx); 51 if(parameter) 52 { 53 parameter->onValueChanged.addListener([this](pluginLib::Parameter*) 54 { 55 const uint8_t prg = isMultiMode() ? 0x0 : virusLib::SINGLE; 56 requestSingle(0, prg); 57 requestMulti(0, prg); 58 59 if (onMsgDone) 60 { 61 onMsgDone(); 62 } 63 }); 64 } 65 66 requestTotal(); 67 requestArrangement(); 68 69 // ABC models have different factory presets depending on the used ROM, but for the TI we have all presets from all models loaded anyway so no need to replace them at runtime 70 if(isTIFamily(m_processor.getModel())) 71 { 72 requestRomBanks(); 73 } 74 else 75 { 76 m_onRomChanged = [this](const virusLib::ROMFile*) 77 { 78 requestRomBanks(); 79 }; 80 } 81 } 82 83 Controller::~Controller() = default; 84 85 bool Controller::parseSysexMessage(const pluginLib::SysEx& _msg, synthLib::MidiEventSource _source) 86 { 87 std::string name; 88 pluginLib::MidiPacket::Data data; 89 pluginLib::MidiPacket::ParamValues parameterValues; 90 91 if(_msg.size() > 6 && _msg[6] == virusLib::DUMP_EMU_SYNTHSTATE) 92 { 93 if(!m_frontpanelState.fromMidiEvent(_msg)) 94 return false; 95 onFrontPanelStateChanged(m_frontpanelState); 96 return true; 97 } 98 99 if(parseMidiPacket(name, data, parameterValues, _msg)) 100 { 101 const auto deviceId = data[pluginLib::MidiDataType::DeviceId]; 102 103 if(deviceId != m_deviceId && deviceId != virusLib::OMNI_DEVICE_ID) 104 return false; // not intended to this device! 105 106 if(name == midiPacketName(MidiPacketType::SingleDump) || name == midiPacketName(MidiPacketType::SingleDump_C)) 107 parseSingle(_msg, data, parameterValues); 108 else if(name == midiPacketName(MidiPacketType::MultiDump)) 109 parseMulti(_msg, data, parameterValues); 110 else if(name == midiPacketName(MidiPacketType::ParameterChange)) 111 { 112 // TI DSP sends parameter changes back as sysex, unsure why. Ignore them as it stops 113 // host automation because the host thinks we "edit" the parameter 114 if(_source != synthLib::MidiEventSource::Plugin) 115 parseParamChange(data); 116 } 117 else 118 { 119 LOG("Controller: Begin unhandled SysEx! --"); 120 printMessage(_msg); 121 LOG("Controller: End unhandled SysEx! --"); 122 return false; 123 } 124 return true; 125 } 126 127 LOG("Controller: Begin unknown SysEx! --"); 128 printMessage(_msg); 129 LOG("Controller: End unknown SysEx! --"); 130 return false; 131 } 132 133 bool Controller::parseControllerMessage(const synthLib::SMidiEvent& e) 134 { 135 return parseControllerDump(e); 136 } 137 138 void Controller::parseParamChange(const pluginLib::MidiPacket::Data& _data) 139 { 140 const auto page = _data.find(pluginLib::MidiDataType::Page)->second; 141 const auto part = _data.find(pluginLib::MidiDataType::Part)->second; 142 const auto index = _data.find(pluginLib::MidiDataType::ParameterIndex)->second; 143 const auto value = _data.find(pluginLib::MidiDataType::ParameterValue)->second; 144 145 const auto& partParams = findSynthParam(part == virusLib::SINGLE ? 0 : part, page, index); 146 147 if (partParams.empty() && part != 0 && part != virusLib::SINGLE) 148 { 149 // ensure it's not global 150 const auto& globalParams = findSynthParam(0, page, index); 151 if (globalParams.empty()) 152 { 153 jassertfalse; 154 return; 155 } 156 for (const auto& param : globalParams) 157 { 158 if (!param->getDescription().isNonPartSensitive()) 159 { 160 jassertfalse; 161 return; 162 } 163 } 164 for (const auto& param : globalParams) 165 param->setValueFromSynth(value, pluginLib::Parameter::Origin::Midi); 166 } 167 for (const auto& param : partParams) 168 param->setValueFromSynth(value, pluginLib::Parameter::Origin::Midi); 169 // TODO: 170 /** 171 If a 172 global parameter or a Multi parameter is ac- 173 cessed, which is not part-sensitive (e.g. Input 174 Boost or Multi Delay Time), the part number is 175 ignored 176 */ 177 } 178 179 juce::StringArray Controller::getSinglePresetNames(virusLib::BankNumber _bank) const 180 { 181 if (_bank == virusLib::BankNumber::EditBuffer) 182 { 183 jassertfalse; 184 return {}; 185 } 186 187 const auto bank = virusLib::toArrayIndex(_bank); 188 189 if (bank >= m_singles.size() || bank < 0) 190 { 191 jassertfalse; 192 return {}; 193 } 194 195 juce::StringArray bankNames; 196 for (auto i = 0; i < 128; i++) 197 bankNames.add(m_singles[bank][i].name); 198 return bankNames; 199 } 200 201 std::string Controller::getSinglePresetName(const pluginLib::MidiPacket::ParamValues& _values) const 202 { 203 return getPresetName("SingleName", _values); 204 } 205 206 std::string Controller::getSinglePresetName(const pluginLib::MidiPacket::AnyPartParamValues& _values) const 207 { 208 return getPresetName("SingleName", _values); 209 } 210 211 std::string Controller::getMultiPresetName(const pluginLib::MidiPacket::ParamValues& _values) const 212 { 213 return getPresetName("MultiName", _values); 214 } 215 216 std::string Controller::getPresetName(const std::string& _paramNamePrefix, const pluginLib::MidiPacket::ParamValues& _values) const 217 { 218 std::string name; 219 for(uint32_t i=0; i<kNameLength; ++i) 220 { 221 const std::string paramName = _paramNamePrefix + std::to_string(i); 222 const auto idx = getParameterIndexByName(paramName); 223 if(idx == InvalidParameterIndex) 224 break; 225 226 const auto it = _values.find(std::make_pair(pluginLib::MidiPacket::AnyPart, idx)); 227 if(it == _values.end()) 228 break; 229 230 name += static_cast<char>(it->second); 231 } 232 return name; 233 } 234 235 std::string Controller::getPresetName(const std::string& _paramNamePrefix, const pluginLib::MidiPacket::AnyPartParamValues& _values) const 236 { 237 std::string name; 238 for(uint32_t i=0; i<kNameLength; ++i) 239 { 240 const std::string paramName = _paramNamePrefix + std::to_string(i); 241 const auto idx = getParameterIndexByName(paramName); 242 if(idx == InvalidParameterIndex) 243 break; 244 245 const auto it = _values[idx]; 246 if(!it) 247 break; 248 249 name += static_cast<char>(*it); 250 } 251 return name; 252 } 253 254 void Controller::setSinglePresetName(const uint8_t _part, const juce::String& _name) const 255 { 256 for (int i=0; i<kNameLength; i++) 257 { 258 const std::string paramName = "SingleName" + std::to_string(i); 259 const auto idx = getParameterIndexByName(paramName); 260 if(idx == InvalidParameterIndex) 261 break; 262 263 auto* param = getParameter(idx, _part); 264 if(!param) 265 break; 266 auto& v = param->getValueObject(); 267 if(i >= _name.length()) 268 v.setValue(static_cast<uint8_t>(' ')); 269 else 270 v.setValue(static_cast<uint8_t>(_name[i])); 271 } 272 } 273 274 void Controller::setSinglePresetName(pluginLib::MidiPacket::AnyPartParamValues& _values, const std::string& _name) const 275 { 276 for(uint32_t i=0; i<kNameLength; ++i) 277 { 278 const std::string paramName = "SingleName" + std::to_string(i); 279 const auto idx = getParameterIndexByName(paramName); 280 if(idx == InvalidParameterIndex) 281 break; 282 283 _values[idx] = (i < _name.size()) ? _name[i] : ' '; 284 } 285 } 286 287 bool Controller::isMultiMode() const 288 { 289 return getParameter(g_paramPlayMode, 0)->getUnnormalizedValue(); 290 } 291 292 juce::String Controller::getCurrentPartPresetName(const uint8_t _part) const 293 { 294 std::string name; 295 for (int i=0; i<kNameLength; i++) 296 { 297 const std::string paramName = "SingleName" + std::to_string(i); 298 const auto idx = getParameterIndexByName(paramName); 299 if(idx == InvalidParameterIndex) 300 break; 301 302 auto* param = getParameter(idx, _part); 303 if(!param) 304 break; 305 const auto v = param->getUnnormalizedValue(); 306 name += static_cast<char>(v); 307 } 308 return name; 309 } 310 311 void Controller::setCurrentPartPreset(uint8_t _part, const virusLib::BankNumber _bank, uint8_t _prg) 312 { 313 if(_bank == virusLib::BankNumber::EditBuffer || _prg > m_singles[0].size()) 314 { 315 jassertfalse; 316 return; 317 } 318 319 const auto bank = virusLib::toArrayIndex(_bank); 320 321 if (bank >= m_singles.size()) 322 { 323 jassertfalse; 324 return; 325 } 326 327 const uint8_t pt = isMultiMode() ? _part : virusLib::SINGLE; 328 329 sendParameterChange(MessageType::PARAM_CHANGE_C, pt, virusLib::PART_BANK_SELECT, virusLib::toMidiByte(_bank)); 330 sendParameterChange(MessageType::PARAM_CHANGE_C, pt, virusLib::PART_PROGRAM_CHANGE, _prg); 331 332 requestSingle(toMidiByte(virusLib::BankNumber::EditBuffer), pt); 333 334 m_currentBank[_part] = _bank; 335 m_currentProgram[_part] = _prg; 336 m_currentPresetSource[_part] = PresetSource::Rom; 337 } 338 339 void Controller::setCurrentPartPresetSource(uint8_t _part, PresetSource _source) 340 { 341 m_currentPresetSource[_part] = _source; 342 } 343 344 virusLib::BankNumber Controller::getCurrentPartBank(const uint8_t _part) const 345 { 346 return m_currentBank[_part]; 347 } 348 349 uint8_t Controller::getCurrentPartProgram(const uint8_t _part) const 350 { 351 return m_currentProgram[_part]; 352 } 353 354 Controller::PresetSource Controller::getCurrentPartPresetSource(uint8_t _part) const 355 { 356 return m_currentPresetSource[_part]; 357 } 358 359 bool Controller::parseSingle(pluginLib::MidiPacket::Data& _data, pluginLib::MidiPacket::AnyPartParamValues& _parameterValues, const pluginLib::SysEx& _msg) const 360 { 361 MidiPacketType unused; 362 return parseSingle(_data, _parameterValues, _msg, unused); 363 } 364 365 bool Controller::parseSingle(pluginLib::MidiPacket::Data& _data, pluginLib::MidiPacket::AnyPartParamValues& _parameterValues, const pluginLib::SysEx& _msg, MidiPacketType& usedPacketType) const 366 { 367 const auto packetName = midiPacketName(MidiPacketType::SingleDump); 368 369 auto* m = getMidiPacket(packetName); 370 371 if(!m) 372 return false; 373 374 usedPacketType = MidiPacketType::SingleDump; 375 376 if(_msg.size() > m->size()) 377 { 378 pluginLib::SysEx temp; 379 temp.insert(temp.begin(), _msg.begin(), _msg.begin() + (m->size()-1)); 380 temp.push_back(0xf7); 381 return parseMidiPacket(*m, _data, _parameterValues, temp); 382 } 383 384 if(_msg.size() < m->size()) 385 { 386 const auto* mc = getMidiPacket(midiPacketName(MidiPacketType::SingleDump_C)); 387 if(!mc) 388 return false; 389 usedPacketType = MidiPacketType::SingleDump_C; 390 return parseMidiPacket(*mc, _data, _parameterValues, _msg); 391 } 392 393 return parseMidiPacket(*m, _data, _parameterValues, _msg); 394 } 395 396 void Controller::parseSingle(const pluginLib::SysEx& _msg, const pluginLib::MidiPacket::Data& _data, const pluginLib::MidiPacket::ParamValues& _parameterValues) 397 { 398 SinglePatch patch; 399 400 patch.bankNumber = virusLib::fromMidiByte(_data.find(pluginLib::MidiDataType::Bank)->second); 401 patch.progNumber = _data.find(pluginLib::MidiDataType::Program)->second; 402 403 patch.name = getSinglePresetName(_parameterValues); 404 405 patch.data = _msg; 406 407 if (patch.bankNumber == virusLib::BankNumber::EditBuffer) 408 { 409 if(patch.progNumber == virusLib::SINGLE) 410 m_singleEditBuffer = patch; 411 else 412 m_singleEditBuffers[patch.progNumber] = patch; 413 414 // virus sends also the single buffer not matter what's the mode. (?? no, both is requested, so both is sent) 415 // instead of keeping both, we 'encapsulate' this into first channel. 416 // the logic to maintain this is done by listening the global single/multi param. 417 if (isMultiMode() && patch.progNumber == virusLib::SINGLE) 418 return; 419 if (!isMultiMode() && patch.progNumber == 0x0) 420 return; 421 422 const uint8_t ch = patch.progNumber == virusLib::SINGLE ? 0 : patch.progNumber; 423 424 const auto locked = m_locking.getLockedParameterNames(ch); 425 426 // if there are locked parameters and the current values in the received preset do not match 427 // the values of the parameters that are locked, create a new preset that contains all 428 // locked parameter values and send it back to the device 429 std::unordered_set<const pluginLib::Parameter*> lockedParamsToApply; 430 431 for (const auto& parameterValue : _parameterValues) 432 { 433 auto* p = getParameter(parameterValue.first.second, ch); 434 435 // if a parameter is not locked, apply it 436 if(locked.find(p->getDescription().name) == locked.end()) 437 p->setValueFromSynth(parameterValue.second, pluginLib::Parameter::Origin::PresetChange); 438 // otherwise, remember the locked parameter if the locked value doesn't match the preset value 439 else if (parameterValue.second != p->getUnnormalizedValue()) 440 lockedParamsToApply.insert(p); 441 } 442 443 // if we have any locked parameters that need to be applied, create a new preset and send it to the device 444 if (!lockedParamsToApply.empty()) 445 { 446 auto newDump = createSingleDump(patch.progNumber == virusLib::SINGLE ? 0 : patch.progNumber, virusLib::toMidiByte(virusLib::BankNumber::EditBuffer), patch.progNumber); 447 sendSysEx(newDump); 448 } 449 450 getProcessor().updateHostDisplay(juce::AudioProcessorListener::ChangeDetails().withProgramChanged(true)); 451 452 if(m_currentPresetSource[ch] != PresetSource::Browser) 453 { 454 bool found = false; 455 for(size_t b=0; b<m_singles.size() && !found; ++b) 456 { 457 const auto& singlePatches = m_singles[b]; 458 459 for(size_t s=0; s<singlePatches.size(); ++s) 460 { 461 const auto& singlePatch = singlePatches[s]; 462 463 if(singlePatch.name == patch.name) 464 { 465 m_currentBank[ch] = virusLib::fromArrayIndex(static_cast<uint8_t>(b)); 466 m_currentProgram[ch] = static_cast<uint8_t>(s); 467 m_currentPresetSource[ch] = PresetSource::Rom; 468 found = true; 469 break; 470 } 471 } 472 } 473 474 if(!found) 475 { 476 m_currentProgram[ch] = 0; 477 m_currentBank[ch] = virusLib::BankNumber::EditBuffer; 478 m_currentPresetSource[ch] = PresetSource::Unknown; 479 } 480 } 481 else 482 { 483 m_currentProgram[ch] = 0; 484 m_currentBank[ch] = virusLib::BankNumber::EditBuffer; 485 } 486 487 if (onProgramChange) 488 onProgramChange(patch.progNumber); 489 } 490 else 491 { 492 const auto bank = toArrayIndex(patch.bankNumber); 493 const auto program = patch.progNumber; 494 495 m_singles[bank][program] = patch; 496 497 if(onRomPatchReceived) 498 onRomPatchReceived(patch.bankNumber, program); 499 } 500 } 501 502 void Controller::parseMulti(const pluginLib::SysEx& _msg, const pluginLib::MidiPacket::Data& _data, const pluginLib::MidiPacket::ParamValues& _parameterValues) 503 { 504 const auto bankNumber = _data.find(pluginLib::MidiDataType::Bank)->second; 505 506 /* If it's a multi edit buffer, set the part page C parameters to their multi equivalents */ 507 if (bankNumber == 0) 508 { 509 m_multiEditBuffer.progNumber = _data.find(pluginLib::MidiDataType::Program)->second; 510 m_multiEditBuffer.name = getMultiPresetName(_parameterValues); 511 m_multiEditBuffer.data = _msg; 512 513 for (const auto & paramValue : _parameterValues) 514 { 515 const auto part = paramValue.first.first; 516 const auto index = paramValue.first.second; 517 const auto value = paramValue.second; 518 519 auto* param = getParameter(index, part); 520 if(!param) 521 continue; 522 523 const auto& desc = param->getDescription(); 524 525 if(desc.page != virusLib::PAGE_C) 526 continue; 527 528 param->setValueFromSynth(value, pluginLib::Parameter::Origin::PresetChange); 529 } 530 531 getProcessor().updateHostDisplay(juce::AudioProcessorListener::ChangeDetails().withProgramChanged(true)); 532 533 if(onMultiReceived) 534 onMultiReceived(); 535 } 536 } 537 538 bool Controller::parseControllerDump(const synthLib::SMidiEvent& m) const 539 { 540 const uint8_t status = m.a & 0xf0; 541 const uint8_t part = m.a & 0x0f; 542 543 uint8_t page; 544 545 if (status == synthLib::M_CONTROLCHANGE) 546 { 547 page = virusLib::PAGE_A; 548 } 549 else if (status == synthLib::M_POLYPRESSURE) 550 { 551 // device decides if PP is enabled and will echo any parameter change to us. Reject any other source 552 if(m.source != synthLib::MidiEventSource::Plugin) 553 return false; 554 page = virusLib::PAGE_B; 555 } 556 else if(status == synthLib::M_PROGRAMCHANGE) 557 { 558 if(isMultiMode()) 559 { 560 for(uint8_t p=0; p<getPartCount(); ++p) 561 { 562 const auto idx = getParameterIndexByName("Part Midi Channel"); 563 if(idx == pluginLib::Controller::InvalidParameterIndex) 564 continue; 565 566 const auto v = getParameter(idx, p); 567 if(v->getUnnormalizedValue() == part) 568 requestSingle(toMidiByte(virusLib::BankNumber::EditBuffer), p); 569 } 570 } 571 else 572 { 573 requestSingle(toMidiByte(virusLib::BankNumber::EditBuffer), virusLib::SINGLE); 574 } 575 return true; 576 } 577 else 578 { 579 return false; 580 } 581 582 const auto& params = findSynthParam(part, page, m.b); 583 for (const auto & p : params) 584 p->setValueFromSynth(m.c, pluginLib::Parameter::Origin::Midi); 585 586 return true; 587 } 588 589 void Controller::printMessage(const pluginLib::SysEx &msg) 590 { 591 std::stringstream ss; 592 ss << "[size " << msg.size() << "] "; 593 for(size_t i=0; i<msg.size(); ++i) 594 { 595 ss << HEXN(static_cast<int>(msg[i]), 2); 596 if(i < msg.size()-1) 597 ss << ','; 598 } 599 const auto s(ss.str()); 600 LOG(s); 601 } 602 603 void Controller::onStateLoaded() 604 { 605 requestTotal(); 606 requestArrangement(); 607 } 608 609 uint8_t Controller::getPartCount() const 610 { 611 return m_processor.getModel() == virusLib::DeviceModel::Snow ? 4 : 16; 612 } 613 614 bool Controller::requestProgram(uint8_t _bank, uint8_t _program, bool _multi) const 615 { 616 std::map<pluginLib::MidiDataType, uint8_t> data; 617 618 data.insert(std::make_pair(pluginLib::MidiDataType::Bank, _bank)); 619 data.insert(std::make_pair(pluginLib::MidiDataType::Program, _program)); 620 621 return sendSysEx(_multi ? MidiPacketType::RequestMulti : MidiPacketType::RequestSingle, data); 622 } 623 624 bool Controller::requestSingle(uint8_t _bank, uint8_t _program) const 625 { 626 return requestProgram(_bank, _program, false); 627 } 628 629 bool Controller::requestMulti(uint8_t _bank, uint8_t _program) const 630 { 631 return requestProgram(_bank, _program, true); 632 } 633 634 bool Controller::requestSingleBank(uint8_t _bank) const 635 { 636 std::map<pluginLib::MidiDataType, uint8_t> data; 637 data.insert(std::make_pair(pluginLib::MidiDataType::Bank, _bank)); 638 639 return sendSysEx(MidiPacketType::RequestSingleBank, data); 640 } 641 642 bool Controller::requestTotal() const 643 { 644 return sendSysEx(MidiPacketType::RequestTotal); 645 } 646 647 bool Controller::requestArrangement() const 648 { 649 return sendSysEx(MidiPacketType::RequestArrangement); 650 } 651 652 void Controller::requestRomBanks() 653 { 654 switch(m_processor.getModel()) 655 { 656 default: 657 case virusLib::DeviceModel::A: 658 case virusLib::DeviceModel::B: 659 case virusLib::DeviceModel::C: 660 m_singles.resize(8); 661 break; 662 case virusLib::DeviceModel::Snow: 663 case virusLib::DeviceModel::TI: 664 case virusLib::DeviceModel::TI2: 665 m_singles.resize( 666 virusLib::ROMFile::getRomBankCount(virusLib::DeviceModel::TI) + 667 virusLib::ROMFile::getRomBankCount(virusLib::DeviceModel::TI2) + 668 virusLib::ROMFile::getRomBankCount(virusLib::DeviceModel::Snow) + 669 2 670 ); 671 break; 672 } 673 674 for(uint8_t i=3; i<=getBankCount(); ++i) 675 requestSingleBank(i); 676 } 677 678 bool Controller::sendSysEx(MidiPacketType _type) const 679 { 680 std::map<pluginLib::MidiDataType, uint8_t> params; 681 return sendSysEx(_type, params); 682 } 683 684 bool Controller::sendSysEx(MidiPacketType _type, std::map<pluginLib::MidiDataType, uint8_t>& _params) const 685 { 686 _params.insert(std::make_pair(pluginLib::MidiDataType::DeviceId, m_deviceId)); 687 return pluginLib::Controller::sendSysEx(midiPacketName(_type), _params); 688 } 689 690 void Controller::sendParameterChange(const pluginLib::Parameter& _parameter, const pluginLib::ParamValue _value) 691 { 692 const auto& desc = _parameter.getDescription(); 693 694 sendParameterChange(desc.page, _parameter.getPart(), desc.index, static_cast<uint8_t>(_value)); 695 } 696 697 bool Controller::sendParameterChange(uint8_t _page, uint8_t _part, uint8_t _index, uint8_t _value) const 698 { 699 std::map<pluginLib::MidiDataType, uint8_t> data; 700 701 data.insert(std::make_pair(pluginLib::MidiDataType::Page, _page)); 702 data.insert(std::make_pair(pluginLib::MidiDataType::Part, _part)); 703 data.insert(std::make_pair(pluginLib::MidiDataType::ParameterIndex, _index)); 704 data.insert(std::make_pair(pluginLib::MidiDataType::ParameterValue, _value)); 705 706 return sendSysEx(MidiPacketType::ParameterChange, data); 707 } 708 709 std::vector<uint8_t> Controller::createSingleDump(uint8_t _part, uint8_t _bank, uint8_t _program) 710 { 711 pluginLib::MidiPacket::Data data; 712 713 data.insert(std::make_pair(pluginLib::MidiDataType::DeviceId, m_deviceId)); 714 data.insert(std::make_pair(pluginLib::MidiDataType::Bank, _bank)); 715 data.insert(std::make_pair(pluginLib::MidiDataType::Program, _program)); 716 717 std::vector<uint8_t> dst; 718 719 if(!createMidiDataFromPacket(dst, midiPacketName(MidiPacketType::SingleDump), data, _part)) 720 return {}; 721 722 return dst; 723 } 724 725 std::vector<uint8_t> Controller::createSingleDump(MidiPacketType _packet, uint8_t _bank, uint8_t _program, const pluginLib::MidiPacket::AnyPartParamValues& _paramValues) 726 { 727 const auto* m = getMidiPacket(midiPacketName(_packet)); 728 assert(m && "midi packet not found"); 729 730 if(!m) 731 return {}; 732 733 pluginLib::MidiPacket::Data data; 734 pluginLib::MidiPacket::NamedParamValues paramValues; 735 736 data.insert(std::make_pair(pluginLib::MidiDataType::DeviceId, m_deviceId)); 737 data.insert(std::make_pair(pluginLib::MidiDataType::Bank, _bank)); 738 data.insert(std::make_pair(pluginLib::MidiDataType::Program, _program)); 739 740 if(!createNamedParamValues(paramValues, _paramValues)) 741 return {}; 742 743 pluginLib::MidiPacket::Sysex dst; 744 if(!m->create(dst, data, paramValues)) 745 return {}; 746 return dst; 747 } 748 749 std::vector<uint8_t> Controller::modifySingleDump(const std::vector<uint8_t>& _sysex, const virusLib::BankNumber _newBank, const uint8_t _newProgram) const 750 { 751 auto* m = getMidiPacket(midiPacketName(MidiPacketType::SingleDump)); 752 assert(m); 753 754 const auto idxBank = m->getByteIndexForType(pluginLib::MidiDataType::Bank); 755 const auto idxProgram = m->getByteIndexForType(pluginLib::MidiDataType::Program); 756 757 assert(idxBank != pluginLib::MidiPacket::InvalidIndex); 758 assert(idxProgram != pluginLib::MidiPacket::InvalidIndex); 759 760 auto data = _sysex; 761 762 data[idxBank] = toMidiByte(_newBank); 763 data[idxProgram] = _newProgram; 764 765 return data; 766 } 767 768 void Controller::selectPrevPreset(const uint8_t _part) 769 { 770 if(getCurrentPartProgram(_part) > 0) 771 { 772 setCurrentPartPreset(_part, getCurrentPartBank(_part), getCurrentPartProgram(_part) - 1); 773 } 774 } 775 776 void Controller::selectNextPreset(uint8_t _part) 777 { 778 if(getCurrentPartProgram(_part) < m_singles[0].size()) 779 { 780 setCurrentPartPreset(_part, getCurrentPartBank(_part), getCurrentPartProgram(_part) + 1); 781 } 782 } 783 784 std::string Controller::getBankName(uint32_t _index) const 785 { 786 char temp[32]{0}; 787 788 if(getBankCount() <= 26) 789 { 790 snprintf(temp, sizeof(temp), "Bank %c", 'A' + _index); 791 } 792 else if(_index < 2) 793 { 794 snprintf(temp, sizeof(temp), "RAM Bank %c", 'A' + _index); 795 } 796 else 797 { 798 _index -= 2; 799 800 const auto countTI = virusLib::ROMFile::getRomBankCount(virusLib::DeviceModel::TI); 801 const auto countTI2 = virusLib::ROMFile::getRomBankCount(virusLib::DeviceModel::TI2); 802 803 if(_index < countTI) sprintf(temp, "TI Rom %c", 'A' + _index); 804 else if(_index < countTI + countTI2) sprintf(temp, "TI2 Rom %c", 'A' + (_index - countTI)); 805 else sprintf(temp, "Snow Rom %c", 'A' + (_index - countTI - countTI2)); 806 } 807 return temp; 808 } 809 810 bool Controller::activatePatch(const std::vector<unsigned char>& _sysex) 811 { 812 return activatePatch(_sysex, isMultiMode() ? getCurrentPart() : static_cast<uint8_t>(virusLib::ProgramType::SINGLE)); 813 } 814 815 bool Controller::activatePatch(const std::vector<unsigned char>& _sysex, uint32_t _part) 816 { 817 if(_part == virusLib::ProgramType::SINGLE) 818 { 819 if(isMultiMode()) 820 _part = 0; 821 } 822 else if(_part >= 16) 823 { 824 return false; 825 } 826 else if(!isMultiMode() && _part == 0) 827 { 828 _part = virusLib::ProgramType::SINGLE; 829 } 830 831 const auto program = static_cast<uint8_t>(_part); 832 833 // re-pack, force to edit buffer 834 const auto msg = modifySingleDump(_sysex, virusLib::BankNumber::EditBuffer, program); 835 836 if(msg.empty()) 837 return false; 838 839 sendSysEx(msg); 840 841 requestSingle(toMidiByte(virusLib::BankNumber::EditBuffer), program); 842 843 setCurrentPartPresetSource(program == virusLib::ProgramType::SINGLE ? 0 : program, PresetSource::Browser); 844 845 return true; 846 } 847 }; // namespace Virus