n2xstate.cpp (18335B)
1 #include "n2xstate.h" 2 3 #include <cassert> 4 5 #include "n2xhardware.h" 6 #include "synthLib/midiToSysex.h" 7 #include "synthLib/midiTranslator.h" 8 #include "synthLib/midiTypes.h" 9 10 namespace n2x 11 { 12 static constexpr uint8_t g_singleDefault[] = 13 { 14 72, // O2Pitch 15 67, // O2PitchFine 16 64, // Mix 17 100, // Cutoff 18 10, // Resonance 19 0, // FilterEnvAmount 20 0, // PW 21 0, // FmDepth 22 0, // FilterEnvA 23 10, // FilterEnvD 24 100, // FilterEnvS 25 10, // FilterEnvR 26 0, // AmpEnvA 27 10, // AmpEnvD 28 100, // AmpEnvS 29 0, // AmpEnvR 30 0, // Portamento 31 127, // Gain 32 0, // ModEnvA 33 0, // ModEnvD 34 0, // ModEnvLevel 35 10, // Lfo1Rate 36 0, // Lfo1Level 37 10, // Lfo2Rate 38 0, // ArpRange 39 0, // O2PitchSens 40 0, // O2PitchFineSens 41 0, // MixSens 42 0, // CutoffSens 43 0, // ResonanceSens 44 0, // FilterEnvAmountSens 45 0, // PWSens 46 0, // FmDepthSens 47 0, // FilterEnvASens 48 0, // FilterEnvDSens 49 0, // FilterEnvSSens 50 0, // FilterEnvRSens 51 0, // AmpEnvASens 52 0, // AmpEnvDSens 53 0, // AmpEnvSSens 54 0, // AmpEnvRSens 55 0, // PortamentoSens 56 0, // GainSens 57 0, // ModEnvASens 58 0, // ModEnvDSens 59 0, // ModEnvLevelSens 60 0, // Lfo1RateSens 61 0, // Lfo1LevelSens 62 0, // Lfo2RateSens 63 0, // ArpRangeSens 64 0, // O1Waveform 65 0, // O2Waveform 66 0, // Sync/RM/Dist 67 0, // FilterType 68 1, // O2Keytrack 69 0, // FilterKeytrack 70 0, // Lfo1Waveform 71 0, // Lfo1Dest 72 0, // VoiceMode 73 0, // ModWheelDest 74 0, // Unison 75 0, // ModEnvDest 76 0, // Auto 77 0, // FilterVelocity 78 2, // OctaveShift 79 0, // Lfo2Dest 80 }; 81 82 static_assert(std::size(g_singleDefault) == g_singleDataSize/2); 83 84 using MultiDefaultData = std::array<uint8_t, ((g_multiDataSize - g_singleDataSize * 4)>>1)>; 85 86 const std::unordered_map<ControlChange, SingleParam> g_controllerMap = 87 { 88 {ControlChange::CCGain , SingleParam::Gain }, 89 {ControlChange::CCOctaveShift , SingleParam::OctaveShift }, 90 {ControlChange::CCModWheelDest , SingleParam::ModWheelDest }, 91 {ControlChange::CCUnison , SingleParam::Unison }, 92 {ControlChange::CCVoiceMode , SingleParam::VoiceMode }, 93 {ControlChange::CCAuto , SingleParam::Auto }, 94 {ControlChange::CCPortamento , SingleParam::Portamento }, 95 {ControlChange::CCLfo1Rate , SingleParam::Lfo1Rate }, 96 {ControlChange::CCLfo1Waveform , SingleParam::Lfo1Waveform }, 97 {ControlChange::CCLfo1Dest , SingleParam::Lfo1Dest }, 98 {ControlChange::CCLfo1Level , SingleParam::Lfo1Level }, 99 {ControlChange::CCLfo2Rate , SingleParam::Lfo2Rate }, 100 {ControlChange::CCLfo2Dest , SingleParam::Lfo2Dest }, 101 {ControlChange::CCArpRange , SingleParam::ArpRange }, 102 {ControlChange::CCModEnvA , SingleParam::ModEnvA }, 103 {ControlChange::CCModEnvD , SingleParam::ModEnvD }, 104 {ControlChange::CCModEnvDest , SingleParam::ModEnvDest }, 105 {ControlChange::CCModEnvLevel , SingleParam::ModEnvLevel }, 106 {ControlChange::CCO1Waveform , SingleParam::O1Waveform }, 107 {ControlChange::CCO2Waveform , SingleParam::O2Waveform }, 108 {ControlChange::CCO2Pitch , SingleParam::O2Pitch }, 109 {ControlChange::CCO2PitchFine , SingleParam::O2PitchFine }, 110 {ControlChange::CCFmDepth , SingleParam::FmDepth }, 111 {ControlChange::CCO2Keytrack , SingleParam::O2Keytrack }, 112 {ControlChange::CCPW , SingleParam::PW }, 113 {ControlChange::CCSync , SingleParam::Sync }, 114 {ControlChange::CCMix , SingleParam::Mix }, 115 {ControlChange::CCAmpEnvA , SingleParam::AmpEnvA }, 116 {ControlChange::CCAmpEnvD , SingleParam::AmpEnvD }, 117 {ControlChange::CCAmpEnvS , SingleParam::AmpEnvS }, 118 {ControlChange::CCAmpEnvR , SingleParam::AmpEnvR }, 119 {ControlChange::CCFilterEnvA , SingleParam::FilterEnvA }, 120 {ControlChange::CCFilterEnvD , SingleParam::FilterEnvD }, 121 {ControlChange::CCFilterEnvS , SingleParam::FilterEnvS }, 122 {ControlChange::CCFilterEnvR , SingleParam::FilterEnvR }, 123 {ControlChange::CCFilterType , SingleParam::FilterType }, 124 {ControlChange::CCCutoff , SingleParam::Cutoff }, 125 {ControlChange::CCResonance , SingleParam::Resonance }, 126 {ControlChange::CCFilterEnvAmount , SingleParam::FilterEnvAmount }, 127 {ControlChange::CCFilterVelocity , SingleParam::FilterVelocity }, 128 {ControlChange::CCFilterKeytrack , SingleParam::FilterKeytrack }, 129 {ControlChange::CCDistortion , SingleParam::Distortion } 130 }; 131 132 MultiDefaultData createMultiDefaultData() 133 { 134 uint32_t i=0; 135 136 MultiDefaultData multi{}; 137 138 auto set4 = [&](const uint8_t _a, const uint8_t _b, const uint8_t _c, const uint8_t _d) 139 { 140 multi[i++] = _a; multi[i++] = _b; multi[i++] = _c; multi[i++] = _d; 141 }; 142 143 set4( 0, 1, 2, 3); // MIDI channel 144 set4( 0, 0, 0, 0); // LFO 1 Sync 145 set4( 0, 0, 0, 0); // LFO 2 Sync 146 set4( 0, 0, 0, 0); // Filter Env Trigger 147 set4( 0, 1, 2, 3); // Filter Env Trigger Midi Channel 148 set4(23,23,23,23); // Filter Env Trigger Note Number 149 set4( 0, 0, 0, 0); // Amp Env Trigger 150 set4( 0, 1, 2, 3); // Amp Env Trigger Midi Channel 151 set4(23,23,23,23); // Amp Env Trigger Note Number 152 set4( 0, 0, 0, 0); // Morph Trigger 153 set4( 0, 1, 2, 3); // Morph Trigger Midi Channel 154 set4(23,23,23,23); // Morph Trigger Note Number 155 multi[i++] = 2; // Bend Range 156 multi[i++] = 2; // Unison Detune 157 multi[i++] = 0; // Out Mode A&B (lower nibble) and C&D (upper nibble) 158 multi[i++] = 15; // Global Midi Channel 159 multi[i++] = 0; // Program Change Enable 160 multi[i++] = 1; // Midi Control 161 multi[i++] = 0; // Master Tune 162 multi[i++] = 0; // Pedal Type 163 multi[i++] = 1; // Local Control 164 multi[i++] = 2; // Keyboard Octave Shift 165 multi[i++] = 0; // Selected Channel 166 multi[i++] = 0; // Arp Midi Out 167 set4(1,0,0,0); // Channel Active 168 set4(0,0,0,0); // Program Select 169 set4(0,0,0,0); // Bank Select 170 set4(7,7,7,7); // Channel Pressure Amount 171 set4(0,0,0,0); // Channel Pressure Dest 172 set4(7,7,7,7); // Expression Pedal Amount 173 set4(0,0,0,0); // Expression Pedal Dest 174 multi[i++] = 0; // Keyboard Split 175 multi[i++] = 64; // Split Point 176 177 assert(i == multi.size()); 178 179 return multi; 180 } 181 182 static const MultiDefaultData g_multiDefault = createMultiDefaultData(); 183 184 State::State(Hardware* _hardware, synthLib::MidiTranslator* _midiTranslator) : m_hardware(_hardware), m_midiTranslator(_midiTranslator) 185 { 186 for(uint8_t i=0; i<static_cast<uint8_t>(m_singles.size()); ++i) 187 createDefaultSingle(m_singles[i], i); 188 createDefaultMulti(m_multi); 189 190 receive(m_multi); 191 for (const auto& single : m_singles) 192 receive(single); 193 } 194 195 bool State::getState(std::vector<uint8_t>& _state) 196 { 197 updateMultiFromSingles(); 198 if(m_multi[g_multiDumpSize - 1] == 0xf7) 199 _state.insert(_state.end(), m_multi.begin(), m_multi.end() - g_nameLength); 200 else 201 _state.insert(_state.end(), m_multi.begin(), m_multi.end()); 202 for (const auto it : m_knobStates) 203 { 204 auto knobSysex = createKnobSysex(it.first, it.second); 205 _state.insert(_state.end(), knobSysex.begin(), knobSysex.end()); 206 } 207 return true; 208 } 209 210 bool State::setState(const std::vector<uint8_t>& _state) 211 { 212 std::vector<std::vector<uint8_t>> msgs; 213 synthLib::MidiToSysex::splitMultipleSysex(msgs, _state); 214 215 for (auto& msg : msgs) 216 receive(msg, synthLib::MidiEventSource::Host); 217 218 return false; 219 } 220 221 bool State::receive(std::vector<synthLib::SMidiEvent>& _responses, const synthLib::SMidiEvent& _ev) 222 { 223 if(_ev.sysex.empty()) 224 { 225 return receiveNonSysex(_ev); 226 } 227 228 auto& sysex = _ev.sysex; 229 230 if(sysex.size() <= SysexIndex::IdxMsgSpec) 231 return false; 232 233 const auto bank = sysex[SysexIndex::IdxMsgType]; 234 const auto prog = sysex[SysexIndex::IdxMsgSpec]; 235 236 if(isSingleDump(sysex)) 237 { 238 if(bank != SysexByte::SingleDumpBankEditBuffer) 239 return false; 240 if(prog > m_singles.size()) 241 return false; 242 m_singles[prog].fill(0); 243 std::copy(sysex.begin(), sysex.end(), m_singles[prog].begin()); 244 send(_ev); 245 return true; 246 } 247 248 if(isMultiDump(sysex)) 249 { 250 if(bank != SysexByte::MultiDumpBankEditBuffer) 251 return false; 252 if(prog != 0) 253 return false; 254 m_multi.fill(0); 255 std::copy(sysex.begin(), sysex.end(), m_multi.begin()); 256 257 if (m_midiTranslator) 258 { 259 // As we need support for individual midi channels for the editor to adjus each part separately but 260 // knobs can only be modified via midi CC, we tell the device that parts 0-3 are on midi channels 0-3 even if 261 // they are not. The midi translator will translate regular midi messages and the editor uses messages that are 262 // not translated 263 264 m_midiTranslator->clear(); 265 266 MultiDump dump = m_multi; 267 for (uint8_t i = 0; i < 4; ++i) 268 { 269 const auto ch = getPartMidiChannel(dump, i); 270 setPartMidiChannel(dump, i, i); 271 m_midiTranslator->addTargetChannel(ch, i); 272 } 273 274 synthLib::SMidiEvent e; 275 e.sysex.assign(dump.begin(), dump.end()); 276 send(e); 277 } 278 else 279 { 280 send(_ev); 281 } 282 283 return true; 284 } 285 286 if (bank == SysexByte::MultiRequestBankEditBuffer) 287 { 288 _responses.emplace_back(synthLib::MidiEventSource::Internal); 289 _responses.back().sysex.assign(m_multi.begin(), m_multi.end()); 290 _responses.back().sysex = validateDump(_responses.back().sysex); 291 return true; 292 } 293 294 if(bank == n2x::SysexByte::EmuSetPotPosition) 295 { 296 KnobType knobType; 297 uint8_t knobValue; 298 299 if(parseKnobSysex(knobType, knobValue, sysex)) 300 { 301 if(m_hardware) 302 m_hardware->setKnobPosition(knobType, knobValue); 303 m_knobStates[knobType] = knobValue; 304 return true; 305 } 306 } 307 else if(bank == SysexByte::EmuGetPotsPosition) 308 { 309 for (const auto it : m_knobStates) 310 { 311 _responses.emplace_back(synthLib::MidiEventSource::Internal); 312 _responses.back().sysex = createKnobSysex(it.first, it.second); 313 } 314 return true; 315 } 316 else if (bank == SysexByte::EmuSetPartCC) 317 { 318 synthLib::SMidiEvent e; 319 auto part = sysex[5]; 320 e.a = sysex[6]; 321 e.b = sysex[7]; 322 e.c = sysex[8]; 323 e.source = _ev.source; 324 e.offset = _ev.offset; 325 changeSingleParameter(part, e); 326 } 327 328 return false; 329 } 330 331 bool State::receive(const std::vector<uint8_t>& _data, synthLib::MidiEventSource _source) 332 { 333 synthLib::SMidiEvent e; 334 e.sysex = _data; 335 e.source = _source; 336 return receive(e); 337 } 338 339 bool State::receiveNonSysex(const synthLib::SMidiEvent& _ev) 340 { 341 switch (_ev.a & 0xf0) 342 { 343 case synthLib::M_CONTROLCHANGE: 344 { 345 const auto parts = getPartsForMidiChannel(_ev); 346 if(parts.empty()) 347 return false; 348 for (const auto part : parts) 349 { 350 if (!changeSingleParameter(part, _ev)) 351 return false; 352 } 353 return true; 354 } 355 default: 356 return false; 357 } 358 } 359 360 bool State::changeSingleParameter(const uint8_t _part, const synthLib::SMidiEvent& _ev) 361 { 362 const auto cc = static_cast<ControlChange>(_ev.b); 363 const auto it = g_controllerMap.find(cc); 364 if(it == g_controllerMap.end()) 365 return false; 366 const SingleParam param = it->second; 367 const auto offset = getOffsetInSingleDump(param); 368 switch (param) 369 { 370 case SingleParam::Sync: 371 // this can either be sync or distortion, they end up in the same midi byte 372 switch(cc) 373 { 374 case ControlChange::CCSync: 375 { 376 auto v = unpackNibbles(m_singles[_part], offset); 377 v &= ~0x3; 378 v |= _ev.c & 0x3; 379 packNibbles(m_singles[_part], offset, v); 380 } 381 return true; 382 case ControlChange::CCDistortion: 383 { 384 auto v = unpackNibbles(m_singles[_part], offset); 385 v &= ~(1<<4); 386 v |= _ev.c << 4; 387 packNibbles(m_singles[_part], offset, v); 388 } 389 return true; 390 default: 391 assert(false && "unexpected control change type"); 392 return false; 393 } 394 break; 395 default: 396 packNibbles(m_singles[_part], offset, _ev.c); 397 return true; 398 } 399 } 400 401 bool State::changeSingleParameter(const uint8_t _part, const SingleParam _parameter, const uint8_t _value) 402 { 403 if(_part >= m_singles.size()) 404 return false; 405 return changeSingleParameter(m_singles[_part], _parameter, _value); 406 } 407 408 bool State::changeMultiParameter(const MultiParam _parameter, const uint8_t _value) 409 { 410 return changeDumpParameter(m_multi, getOffsetInMultiDump(_parameter), _value); 411 } 412 413 bool State::changeSingleParameter(SingleDump& _dump, const SingleParam _param, const uint8_t _value) 414 { 415 return changeDumpParameter(_dump, getOffsetInSingleDump(_param), _value); 416 } 417 418 void State::updateMultiFromSingles() 419 { 420 for(uint8_t i=0; i<static_cast<uint8_t>(m_singles.size()); ++i) 421 copySingleToMulti(m_multi, m_singles[i], i); 422 } 423 424 void State::createDefaultSingle(SingleDump& _single, const uint8_t _program, const uint8_t _bank/* = n2x::SingleDumpBankEditBuffer*/) 425 { 426 createHeader(_single, _bank, _program); 427 _single[g_singleDumpSize-1] = 0xf7; 428 429 uint32_t o = IdxMsgSpec + 1; 430 431 for (const auto b : g_singleDefault) 432 { 433 _single[o++] = b & 0xf; 434 _single[o++] = (b>>4) & 0xf; 435 } 436 } 437 438 // ReSharper disable once CppParameterMayBeConstPtrOrRef 439 void State::copySingleToMulti(MultiDump& _multi, const SingleDump& _single, const uint8_t _index) 440 { 441 uint32_t i = SysexIndex::IdxMsgSpec + 1; 442 i += g_singleDataSize * _index; 443 std::copy_n(_single.begin() + g_sysexHeaderSize, g_singleDataSize, _multi.begin() + i); 444 } 445 446 void State::extractSingleFromMulti(SingleDump& _single, const MultiDump& _multi, uint8_t _index) 447 { 448 uint32_t i = SysexIndex::IdxMsgSpec + 1; 449 i += g_singleDataSize * _index; 450 std::copy_n(_multi.begin() + i, g_singleDataSize, _single.begin() + g_sysexHeaderSize); 451 } 452 453 void State::createDefaultMulti(MultiDump& _multi, const uint8_t _bank/* = SysexByte::MultiDumpBankEditBuffer*/) 454 { 455 createHeader(_multi, _bank, 0); 456 _multi[g_multiDumpSize-1] = 0xf7; 457 458 SingleDump single; 459 createDefaultSingle(single, 0); 460 461 copySingleToMulti(_multi, single, 0); 462 copySingleToMulti(_multi, single, 1); 463 copySingleToMulti(_multi, single, 2); 464 copySingleToMulti(_multi, single, 3); 465 466 uint32_t i = SysexIndex::IdxMsgSpec + 1 + 4 * g_singleDataSize; 467 assert(i == 264 * 2 + g_sysexHeaderSize); 468 469 for (const auto b : g_multiDefault) 470 { 471 _multi[i++] = b & 0xf; 472 _multi[i++] = (b>>4) & 0xf; 473 } 474 assert(i + g_sysexFooterSize == g_multiDumpSize); 475 } 476 477 uint32_t State::getOffsetInSingleDump(const SingleParam _param) 478 { 479 return g_sysexHeaderSize + (_param<<1); 480 } 481 482 uint32_t State::getOffsetInMultiDump(const MultiParam _param) 483 { 484 return g_sysexHeaderSize + (_param<<1); 485 } 486 487 std::vector<uint8_t> State::getPartsForMidiChannel(const synthLib::SMidiEvent& _ev) const 488 { 489 const auto ch = _ev.a & 0xf; 490 return getPartsForMidiChannel(ch); 491 } 492 493 std::vector<uint8_t> State::getPartsForMidiChannel(const uint8_t _channel) const 494 { 495 std::vector<uint8_t> res; 496 497 for(uint8_t i=0; i<static_cast<uint8_t>(m_singles.size()); ++i) 498 { 499 if(getPartMidiChannel(i) == _channel) 500 res.push_back(i); 501 } 502 return res; 503 } 504 505 std::vector<uint8_t> State::createKnobSysex(KnobType _type, uint8_t _value) 506 { 507 return {0xf0, IdClavia, DefaultDeviceId, IdN2X, 508 EmuSetPotPosition, 509 static_cast<uint8_t>(_type), 510 static_cast<uint8_t>(_value >> 4), 511 static_cast<uint8_t>(_value & 0x0f), 512 0xf7 513 }; 514 } 515 516 bool State::parseKnobSysex(KnobType& _type, uint8_t& _value, const std::vector<uint8_t>& _sysex) 517 { 518 if(_sysex.size() <= SysexIndex::IdxKnobPosL) 519 return false; 520 if(_sysex[SysexIndex::IdxMsgType] != SysexByte::EmuSetPotPosition) 521 return false; 522 523 _type = static_cast<KnobType>(_sysex[SysexIndex::IdxMsgSpec]); 524 _value = static_cast<uint8_t>((_sysex[SysexIndex::IdxKnobPosH] << 4) | _sysex[SysexIndex::IdxKnobPosL]); 525 526 return true; 527 } 528 529 bool State::getKnobState(uint8_t& _result, const KnobType _type) const 530 { 531 const auto it = m_knobStates.find(_type); 532 if(it == m_knobStates.end()) 533 return false; 534 _result = it->second; 535 return true; 536 } 537 538 bool State::isSingleDump(const std::vector<uint8_t>& _dump) 539 { 540 return _dump.size() == g_singleDumpSize || _dump.size() == g_singleDumpWithNameSize; 541 } 542 543 bool State::isMultiDump(const std::vector<uint8_t>& _dump) 544 { 545 return _dump.size() == g_multiDumpSize || _dump.size() == g_multiDumpWithNameSize; 546 } 547 548 std::string State::extractPatchName(const std::vector<uint8_t>& _dump) 549 { 550 if(!isDumpWithPatchName(_dump)) 551 return {}; 552 auto* begin = &_dump[_dump.size() - g_nameLength - 1]; 553 if(*begin == 0xf7) 554 return {}; 555 std::string name(reinterpret_cast<const char*>(begin), g_nameLength); 556 return name; 557 } 558 559 bool State::isDumpWithPatchName(const std::vector<uint8_t>& _dump) 560 { 561 return _dump.size() == g_singleDumpWithNameSize || _dump.size() == g_multiDumpWithNameSize; 562 } 563 564 std::vector<uint8_t> State::stripPatchName(const std::vector<uint8_t>& _dump) 565 { 566 if(!isDumpWithPatchName(_dump)) 567 return _dump; 568 auto d = _dump; 569 d.erase(d.end() - g_nameLength - 1, d.end() - 1); 570 assert(d.size() == g_singleDumpSize || d.size() == g_multiDumpSize); 571 d.back() = 0xf7; 572 return d; 573 } 574 575 bool State::isValidPatchName(const std::vector<uint8_t>& _dump) 576 { 577 if(!isDumpWithPatchName(_dump)) 578 return false; 579 580 if(_dump.back() != 0xf7) 581 return false; 582 583 const auto nameStart = _dump.size() - g_nameLength - 1; 584 585 for(size_t i=nameStart; i<nameStart+g_nameLength; ++i) 586 { 587 if(_dump[i] < 32 || _dump[i] >= 128) 588 return false; 589 } 590 return true; 591 } 592 593 std::vector<uint8_t> State::validateDump(const std::vector<uint8_t>& _dump) 594 { 595 if(!isValidPatchName(_dump)) 596 return stripPatchName(_dump); 597 return _dump; 598 } 599 600 synthLib::SMidiEvent& State::createPartCC(uint8_t _part, synthLib::SMidiEvent& _ccEvent) 601 { 602 _ccEvent.sysex = { 0xf0, IdClavia, SysexByte::DefaultDeviceId, IdN2X, 603 SysexByte::EmuSetPartCC, 604 _part, 605 _ccEvent.a, 606 _ccEvent.b, 607 _ccEvent.c, 608 0xf7 609 }; 610 return _ccEvent; 611 } 612 613 void State::send(const synthLib::SMidiEvent& _e) const 614 { 615 if(_e.source == synthLib::MidiEventSource::Plugin) 616 return; 617 618 if(m_hardware) 619 { 620 const auto& sysex = _e.sysex; 621 622 if(isDumpWithPatchName(sysex)) 623 { 624 auto e = _e; 625 e.sysex = stripPatchName(sysex); 626 m_hardware->sendMidi(e); 627 } 628 else 629 { 630 m_hardware->sendMidi(_e); 631 } 632 } 633 } 634 635 template<size_t Size> 636 void State::createHeader(std::array<uint8_t, Size>& _buffer, uint8_t _msgType, uint8_t _msgSpec) 637 { 638 _buffer.fill(0); 639 640 _buffer[0] = 0xf0; 641 642 _buffer[SysexIndex::IdxClavia] = SysexByte::IdClavia; 643 _buffer[SysexIndex::IdxDevice] = SysexByte::DefaultDeviceId; 644 _buffer[SysexIndex::IdxN2x] = SysexByte::IdN2X; 645 _buffer[SysexIndex::IdxMsgType] = _msgType; 646 _buffer[SysexIndex::IdxMsgSpec] = _msgSpec; 647 648 _buffer.back() = 0xf7; 649 } 650 }