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

xtController.cpp (23437B)


      1 #include "xtController.h"
      2 
      3 #include <fstream>
      4 
      5 #include "PluginProcessor.h"
      6 
      7 #include "xtLib/xtState.h"
      8 
      9 #include "dsp56kEmu/logging.h"
     10 
     11 #include "xtFrontPanel.h"
     12 #include "xtWaveEditor.h"
     13 
     14 namespace
     15 {
     16 	constexpr const char* g_midiPacketNames[] =
     17 	{
     18 	    "requestsingle",
     19 	    "requestmulti",
     20 	    "requestsinglebank",
     21 	    "requestmultibank",
     22 	    "requestglobal",
     23 	    "requestmode",
     24 	    "requestallsingles",
     25 	    "singleparameterchange",
     26 	    "multiparameterchange",
     27 	    "globalparameterchange",
     28 	    "singledump",
     29 	    "multidump",
     30 	    "globaldump",
     31 	    "modedump",
     32 	    "emuRequestLcd",
     33 	    "emuRequestLeds",
     34 	    "emuSendButton",
     35 	    "emuSendRotary",
     36 	    "requestWave",
     37 	    "waveDump",
     38 		"requestTable",
     39 		"tableDump"
     40 	};
     41 
     42 	static_assert(std::size(g_midiPacketNames) == static_cast<size_t>(xtJucePlugin::Controller::MidiPacketType::Count));
     43 
     44 	const char* midiPacketName(xtJucePlugin::Controller::MidiPacketType _type)
     45 	{
     46 		return g_midiPacketNames[static_cast<uint32_t>(_type)];
     47 	}
     48 
     49 	constexpr uint32_t g_pageMulti = 10;
     50 	constexpr uint32_t g_pageMultiInst0 = 11;
     51 	constexpr uint32_t g_pageMultiInst1 = 12;
     52 	constexpr uint32_t g_pageMultiInst2 = 13;
     53 	constexpr uint32_t g_pageMultiInst3 = 14;
     54 	constexpr uint32_t g_pageMultiInst4 = 15;
     55 	constexpr uint32_t g_pageMultiInst5 = 16;
     56 	constexpr uint32_t g_pageMultiInst6 = 17;
     57 	constexpr uint32_t g_pageMultiInst7 = 18;
     58 	constexpr uint32_t g_pageGlobal = 20;
     59 	constexpr uint32_t g_pageSoftKnobs = 30;
     60 	constexpr uint32_t g_pageControllers = 40;
     61 }
     62 
     63 namespace xtJucePlugin
     64 {
     65 	Controller::Controller(AudioPluginAudioProcessor& p, const unsigned char _deviceId) : pluginLib::Controller(p, "parameterDescriptions_xt.json"), m_deviceId(_deviceId)
     66 	{
     67 	    registerParams(p);
     68 
     69 		sendSysEx(RequestGlobal);
     70 		sendSysEx(RequestMode);
     71 
     72 		onPlayModeChanged.addListener([this](bool multiMode)
     73 		{
     74 			requestAllPatches();
     75 		});
     76 
     77 		// slow down edits of the wavetable, device gets overloaded quickly if we send too many changes
     78 		uint32_t idx;
     79 		getParameterDescriptions().getIndexByName(idx, "Wave");
     80 		for(uint8_t i=0; i<m_singleEditBuffers.size(); ++i)
     81 		{
     82 			auto* p = getParameter(idx, i);
     83 			p->setRateLimitMilliseconds(250);
     84 		}
     85 	}
     86 
     87 	Controller::~Controller() = default;
     88 
     89 	bool Controller::sendSingle(const std::vector<uint8_t>& _sysex)
     90 	{
     91 		return sendSingle(_sysex, getCurrentPart());
     92 	}
     93 
     94 	bool Controller::sendSingle(const std::vector<uint8_t>& _sysex, const uint8_t _part)
     95 	{
     96 		if(_sysex.size() == xt::Mw1::g_singleDumpLength)
     97 		{
     98 			// No program/bank bytes are part of the dump, send as-is and request the result
     99 
    100 			if(_part > 0)
    101 				return false;	// we cannot support this as the hardware loads a MW1 to the "current" instrument, which is always the first one
    102 
    103 			pluginLib::Controller::sendSysEx(_sysex);
    104 			requestSingle(isMultiMode() ? xt::LocationH::SingleEditBufferMultiMode : xt::LocationH::SingleEditBufferSingleMode, 0);
    105 			return true;
    106 		}
    107 
    108 		auto data = _sysex;
    109 
    110 		if(_sysex.size() > std::tuple_size_v<xt::State::Single>)
    111 		{
    112 			// split a combined single into single, table, waves and send all of them
    113 			std::vector<xt::SysEx> splitResults;
    114 			xt::State::splitCombinedPatch(splitResults, _sysex);
    115 
    116 			if(!splitResults.empty())
    117 			{
    118 				const auto& single = splitResults.front();
    119 
    120 				if(m_waveEditor)
    121 				{
    122 					const auto& table = splitResults[1];
    123 					m_waveEditor->getData().onReceiveTable(table, true);
    124 
    125 					for(size_t i=2; i<splitResults.size(); ++i)
    126 					{
    127 						const auto& wave = splitResults[i];
    128 						m_waveEditor->getData().onReceiveWave(wave, true);
    129 					}
    130 				}
    131 
    132 				data = single;
    133 			}
    134 		}
    135 
    136 		data[wLib::IdxBuffer] = static_cast<uint8_t>(isMultiMode() ? xt::LocationH::SingleEditBufferMultiMode : xt::LocationH::SingleEditBufferSingleMode);
    137 		data[wLib::IdxLocation] = isMultiMode() ? _part : 0;
    138 		data[wLib::IdxDeviceId] = m_deviceId;
    139 
    140 		const auto* p = getMidiPacket(g_midiPacketNames[SingleDump]);
    141 
    142 		if (!p->updateChecksums(data))
    143 			return false;
    144 
    145 		pluginLib::Controller::sendSysEx(data);
    146 
    147 		sendLockedParameters(_part);
    148 
    149 		requestSingle(isMultiMode() ? xt::LocationH::SingleEditBufferMultiMode : xt::LocationH::SingleEditBufferSingleMode, 0);
    150 
    151 		return true;
    152 	}
    153 
    154 	void Controller::onStateLoaded()
    155 	{
    156 		sendSysEx(RequestGlobal);
    157 		sendSysEx(RequestMode);
    158 	}
    159 
    160 	uint8_t Controller::getPartCount() const
    161 	{
    162 		return 8;
    163 	}
    164 
    165 	std::vector<uint8_t> Controller::getPartsForMidiChannel(const uint8_t _channel)
    166 	{
    167 		if (!isMultiMode())
    168 			return {0};
    169 
    170 		std::vector<uint8_t> parts;
    171 
    172 		for (uint8_t p=0; p<getPartCount(); ++p)
    173 		{
    174 			char paramName[16];
    175 			(void)snprintf(paramName, std::size(paramName), "MI%dMidiChannel", static_cast<int>(p));
    176 			auto* param = getParameter(paramName, 0);
    177 			assert(param && "parameter not found");
    178 			const auto v = param->getUnnormalizedValue();
    179 			if (v < 2 || v - 2 == _channel)	// omni, global, 0, 1, ....
    180 				parts.push_back(p);
    181 		}
    182 		return parts;
    183 	}
    184 
    185 	std::string Controller::getSingleName(const pluginLib::MidiPacket::ParamValues& _values) const
    186 	{
    187 	    std::string name;
    188 	    for(uint32_t i=0; i<16; ++i)
    189 	    {
    190 	        char paramName[16];
    191 	        (void)snprintf(paramName, sizeof(paramName), "Name%02u", i);
    192 	        const auto idx = getParameterIndexByName(paramName);
    193 	        if(idx == InvalidParameterIndex)
    194 	            break;
    195 
    196 	        const auto it = _values.find(std::make_pair(pluginLib::MidiPacket::AnyPart, idx));
    197 	        if(it == _values.end())
    198 	            break;
    199 
    200 	        name += static_cast<char>(it->second);
    201 	    }
    202 	    return name;
    203 	}
    204 
    205 	std::string Controller::getSingleName(const uint8_t _part) const
    206 	{
    207 	    std::string name;
    208 	    for(uint32_t i=0; i<16; ++i)
    209 	    {
    210 	        char paramName[16];
    211 	        (void)snprintf(paramName, sizeof(paramName), "Name%02u", i);
    212 	        const auto idx = getParameterIndexByName(paramName);
    213 	        if(idx == InvalidParameterIndex)
    214 	            break;
    215 
    216 			const auto* p = getParameter(idx, _part);
    217 	        if(!p)
    218 	            break;
    219 
    220 	        name += static_cast<char>(p->getUnnormalizedValue());
    221 	    }
    222 	    return name;
    223 	}
    224 
    225 	std::string Controller::getSingleName(const pluginLib::MidiPacket::AnyPartParamValues& _values) const
    226 	{
    227 	    return getString(_values, "Name", 16);
    228 	}
    229 
    230 	std::string Controller::getString(const pluginLib::MidiPacket::AnyPartParamValues& _values, const std::string& _prefix,	const size_t _len) const
    231 	{
    232 	    std::string name;
    233 	    for(uint32_t i=0; i<_len; ++i)
    234 	    {
    235 	        char paramName[64];
    236 	        (void)snprintf(paramName, sizeof(paramName), "%s%02u", _prefix.c_str(), i);
    237 
    238 	        const auto idx = getParameterIndexByName(paramName);
    239 	        if(idx == InvalidParameterIndex)
    240 	            break;
    241 
    242 	        const auto it = _values[idx];
    243 	        if(!it)
    244 	            break;
    245 
    246 	        name += static_cast<char>(*it);
    247 	    }
    248 	    return name;
    249 	}
    250 
    251 	bool Controller::setSingleName(pluginLib::MidiPacket::AnyPartParamValues& _values, const std::string& _value) const
    252 	{
    253 	    return setString(_values, "Name", 16, _value);
    254 	}
    255 
    256 	void Controller::parseSingle(const pluginLib::SysEx& _msg, const pluginLib::MidiPacket::Data& _data, const pluginLib::MidiPacket::ParamValues& _params)
    257 	{
    258 	    Patch patch;
    259 	    patch.data = _msg;
    260 	    patch.name = getSingleName(_params);
    261 
    262 	    const auto bank = _data.at(pluginLib::MidiDataType::Bank);
    263 	    const auto prog = _data.at(pluginLib::MidiDataType::Program);
    264 
    265 	    if(bank == static_cast<uint8_t>(xt::LocationH::SingleEditBufferSingleMode) && prog == 0)
    266 	    {
    267 		    m_singleEditBuffer = patch;
    268 
    269 			if(!isMultiMode())
    270 				applyPatchParameters(_params, 0);
    271 	    }
    272 	    else if(bank == static_cast<uint8_t>(xt::LocationH::SingleEditBufferMultiMode))
    273 	    {
    274 		    m_singleEditBuffers[prog] = patch;
    275 
    276     		if (isMultiMode())
    277 				applyPatchParameters(_params, prog);
    278 
    279 			// if we switched to multi, all singles have to be requested. However, we cannot send all requests at once (device will miss some)
    280 			// so we chain them one after the other
    281 			if(prog + 1 < m_singleEditBuffers.size())
    282 				requestSingle(xt::LocationH::SingleEditBufferMultiMode, prog + 1);
    283 	    }
    284 		else
    285 			return;
    286 
    287 		// if the single that was received contains a user table, request it from the device as it might not match what we have in the editor
    288 		if (m_waveEditor)
    289 		{
    290 			const auto tableId = xt::TableId(xt::State::getWavetableFromSingleDump(patch.data));
    291 
    292 			if (!xt::wave::isReadOnly(tableId))
    293 			{
    294 				if (requestTable(tableId.rawId()))
    295 					m_requestWavesForTables.insert(tableId);
    296 			}
    297 		}
    298 
    299 		onProgramChanged(prog);
    300 	}
    301 
    302 	void Controller::parseMulti(const pluginLib::SysEx& _msg, const pluginLib::MidiPacket::Data& _data,	const pluginLib::MidiPacket::ParamValues& _params) const
    303 	{
    304 		Patch patch;
    305 		patch.data = _msg;
    306 		patch.name = getSingleName(_params);
    307 
    308 		const auto bank = _data.at(pluginLib::MidiDataType::Bank);
    309 	//	const auto prog = _data.at(pluginLib::MidiDataType::Program);
    310 
    311 		if(bank == static_cast<uint8_t>(xt::LocationH::MultiDumpMultiEditBuffer))
    312 		{
    313 			applyPatchParameters(_params, 0);
    314 
    315 			if(isMultiMode())
    316 			{
    317 				// requst first single. The other singles 1-7 are requested one after the other after a single has been received
    318 				requestSingle(xt::LocationH::SingleEditBufferMultiMode, 0);
    319 			}
    320 		}
    321 	}
    322 
    323 	void Controller::parseGlobal(const pluginLib::SysEx& _msg, const pluginLib::MidiPacket::Data& _data, const pluginLib::MidiPacket::ParamValues& _params)
    324 	{
    325 		memcpy(m_globalData.data(), &_msg[xt::IdxGlobalParamFirst], sizeof(m_globalData));
    326 
    327 		applyPatchParameters(_params, 0);
    328 	}
    329 
    330 	bool Controller::parseSysexMessage(const pluginLib::SysEx& _msg, synthLib::MidiEventSource)
    331 	{
    332 	    if(_msg.size() >= 5)
    333 	    {
    334 		    switch (const auto cmd = static_cast<xt::SysexCommand>(_msg[4]))
    335 	        {
    336 	        case xt::SysexCommand::EmuRotaries:
    337 	        case xt::SysexCommand::EmuButtons:
    338 	        case xt::SysexCommand::EmuLCD:
    339 	        case xt::SysexCommand::EmuLEDs:
    340 				if(m_frontPanel)
    341 					m_frontPanel->processSysex(_msg);
    342 	            return true;
    343 	        default:
    344 	            break;
    345 	        }
    346 	    }
    347 
    348 		LOG("Got sysex of size " << _msg.size());
    349 
    350 		std::string name;
    351 	    pluginLib::MidiPacket::Data data;
    352 	    pluginLib::MidiPacket::ParamValues parameterValues;
    353 
    354 	    if(!pluginLib::Controller::parseMidiPacket(name,  data, parameterValues, _msg))
    355 			return false;
    356 
    357 	    if(name == midiPacketName(SingleDump))
    358 	    {
    359 		    parseSingle(_msg, data, parameterValues);
    360 	    }
    361 	    else if (name == midiPacketName(MultiDump))
    362 	    {
    363 		    parseMulti(_msg, data, parameterValues);
    364 	    }
    365 	    else if(name == midiPacketName(GlobalDump))
    366 	    {
    367 		    parseGlobal(_msg, data, parameterValues);
    368 	    }
    369 	    else if(name == midiPacketName(ModeDump))
    370 	    {
    371 		    const auto lastPlayMode = isMultiMode();
    372 		    memcpy(m_modeData.data(), &_msg[xt::IdxModeParamFirst], sizeof(m_modeData));
    373 		    const auto newPlayMode = isMultiMode();
    374 
    375 		    if(lastPlayMode != newPlayMode)
    376 			    onPlayModeChanged(newPlayMode);
    377 		    else
    378 			    requestAllPatches();
    379 	    }
    380 	    else if(name == midiPacketName(SingleParameterChange))
    381 	    {
    382 		    const auto page = data[pluginLib::MidiDataType::Page];
    383 		    const auto index = data[pluginLib::MidiDataType::ParameterIndex];
    384 		    const auto part = data[pluginLib::MidiDataType::Part];
    385 		    const auto value = data[pluginLib::MidiDataType::ParameterValue];
    386 
    387 		    auto& params = findSynthParam(part, page, index);
    388 
    389 		    for (auto& param : params)
    390 			    param->setValueFromSynth(value, pluginLib::Parameter::Origin::Midi);
    391 
    392 		    LOG("Single parameter " << static_cast<int>(index) << ", page " << static_cast<int>(page) << " for part " << static_cast<int>(part) << " changed to value " << static_cast<int>(value));
    393 	    }
    394 	    else if(name == midiPacketName(GlobalParameterChange))
    395 	    {
    396 		    const auto index = (static_cast<uint32_t>(data[pluginLib::MidiDataType::Page]) << 7) + static_cast<uint32_t>(data[pluginLib::MidiDataType::ParameterIndex]);
    397 		    const auto value = data[pluginLib::MidiDataType::ParameterValue];
    398 
    399 		    if(m_globalData[index] != value)
    400 		    {
    401 			    LOG("Global parameter " << index << " changed to value " << static_cast<int>(value));
    402 			    m_globalData[index] = value;
    403 		    }
    404 	    }
    405 		else if(name == midiPacketName(WaveDump))
    406 		{
    407 			if(m_waveEditor)
    408 				m_waveEditor->onReceiveWave(data, _msg);
    409 		}
    410 		else if(name == midiPacketName(TableDump))
    411 		{
    412 			if(m_waveEditor)
    413 				m_waveEditor->onReceiveTable(data, _msg);
    414 
    415 			const auto tableId = xt::TableId(static_cast<uint16_t>(_msg[xt::SysexIndex::IdxWaveIndexH] << 7 | _msg[xt::SysexIndex::IdxWaveIndexL]));
    416 
    417 			if (m_requestWavesForTables.find(tableId) != m_requestWavesForTables.end())
    418 			{
    419 				xt::TableData table;
    420 
    421 				if (xt::State::parseTableData(table, _msg))
    422 				{
    423 					auto waves = xt::State::getWavesForTable(table);
    424 					for (const auto& wave : waves)
    425 					{
    426 						if (!xt::wave::isReadOnly(wave))
    427 							requestWave(wave.rawId());
    428 					}
    429 				}
    430 				m_requestWavesForTables.erase(tableId);
    431 			}
    432 		}
    433 	    else
    434 	    {
    435 		    LOG("Received unknown sysex of size " << _msg.size());
    436 			return false;
    437 	    }
    438 		return true;
    439 	}
    440 
    441 	bool Controller::parseControllerMessage(const synthLib::SMidiEvent& _e)
    442 	{
    443 		const auto& cm = getParameterDescriptions().getControllerMap();
    444 		const auto paramIndices = cm.getControlledParameters(_e);
    445 
    446 		if(paramIndices.empty())
    447 			return false;
    448 
    449 		const auto origin = midiEventSourceToParameterOrigin(_e.source);
    450 
    451 		const auto parts = isMultiMode() ? getPartsForMidiEvent(_e) : std::vector<uint8_t>{0};
    452 
    453 		if (parts.empty())
    454 			return false;
    455 
    456 		for (const uint8_t part : parts)
    457 		{
    458 			for (const auto paramIndex : paramIndices)
    459 			{
    460 				auto* param = getParameter(paramIndex, part);
    461 				assert(param && "parameter not found for control change");
    462 				param->setValueFromSynth(_e.c, origin);
    463 			}
    464 		}
    465 
    466 		return true;
    467 	}
    468 
    469 	bool Controller::parseMidiPacket(MidiPacketType _type, pluginLib::MidiPacket::Data& _data, pluginLib::MidiPacket::AnyPartParamValues& _params, const pluginLib::SysEx& _sysex) const
    470 	{
    471 		const auto* p = getMidiPacket(g_midiPacketNames[_type]);
    472 		assert(p && "midi packet not found");
    473 		return pluginLib::Controller::parseMidiPacket(*p, _data, _params, _sysex);
    474 	}
    475 
    476 	bool Controller::sendSysEx(MidiPacketType _type) const
    477 	{
    478 	    std::map<pluginLib::MidiDataType, uint8_t> params;
    479 	    return sendSysEx(_type, params);
    480 	}
    481 
    482 	bool Controller::sendSysEx(const MidiPacketType _type, std::map<pluginLib::MidiDataType, uint8_t>& _params) const
    483 	{
    484 	    _params.insert(std::make_pair(pluginLib::MidiDataType::DeviceId, m_deviceId));
    485 	    return pluginLib::Controller::sendSysEx(midiPacketName(_type), _params);
    486 	}
    487 
    488 	bool Controller::isMultiMode() const
    489 	{
    490 	    return m_modeData.front() != 0;
    491 	}
    492 
    493 	void Controller::setPlayMode(const bool _multiMode)
    494 	{
    495 		if(isMultiMode() == _multiMode)
    496 			return;
    497 
    498 		m_modeData[0] = _multiMode ? 1 : 0;
    499 
    500 		sendModeDump();
    501 
    502 		onPlayModeChanged(_multiMode);
    503 	}
    504 
    505 	void Controller::selectNextPreset()
    506 	{
    507 		selectPreset(+1);
    508 	}
    509 
    510 	void Controller::selectPrevPreset()
    511 	{
    512 		selectPreset(-1);
    513 	}
    514 
    515 	std::vector<uint8_t> Controller::createSingleDump(const xt::LocationH _buffer, const uint8_t _location, const uint8_t _part) const
    516 	{
    517 		pluginLib::MidiPacket::Data data;
    518 
    519 		data.insert(std::make_pair(pluginLib::MidiDataType::DeviceId, m_deviceId));
    520 		data.insert(std::make_pair(pluginLib::MidiDataType::Bank, static_cast<uint8_t>(_buffer)));
    521 		data.insert(std::make_pair(pluginLib::MidiDataType::Program, _location));
    522 
    523 		std::vector<uint8_t> dst;
    524 
    525 		if (!createMidiDataFromPacket(dst, midiPacketName(SingleDump), data, _part))
    526 			return {};
    527 
    528 		return dst;
    529 	}
    530 
    531 	std::vector<uint8_t> Controller::createSingleDump(xt::LocationH _buffer, const uint8_t _location, const pluginLib::MidiPacket::AnyPartParamValues& _values) const
    532 	{
    533 		pluginLib::MidiPacket::Data data;
    534 
    535 		data.insert(std::make_pair(pluginLib::MidiDataType::DeviceId, m_deviceId));
    536 		data.insert(std::make_pair(pluginLib::MidiDataType::Bank, static_cast<uint8_t>(_buffer)));
    537 		data.insert(std::make_pair(pluginLib::MidiDataType::Program, _location));
    538 
    539 		std::vector<uint8_t> dst;
    540 
    541 		if (!createMidiDataFromPacket(dst, midiPacketName(SingleDump), data, _values))
    542 			return {};
    543 
    544 		return dst;
    545 	}
    546 
    547 	bool Controller::parseSingle(pluginLib::MidiPacket::Data& _data, pluginLib::MidiPacket::AnyPartParamValues& _paramValues, const std::vector<uint8_t>& _sysex) const
    548 	{
    549 		return parseMidiPacket(SingleDump, _data, _paramValues, _sysex);
    550 	}
    551 
    552 	bool Controller::setString(pluginLib::MidiPacket::AnyPartParamValues& _values, const std::string& _prefix, size_t _len, const std::string& _value) const
    553 	{
    554 	    for(uint32_t i=0; i<_len && i <_value.size(); ++i)
    555 	    {
    556 	        char paramName[64];
    557 	        (void)snprintf(paramName, sizeof(paramName), "%s%02u", _prefix.c_str(), i);
    558 
    559 	        const auto idx = getParameterIndexByName(paramName);
    560 	        if(idx == InvalidParameterIndex)
    561 	            break;
    562 
    563 	        _values[idx] = static_cast<uint8_t>(_value[i]);
    564 	    }
    565 	    return true;
    566 	}
    567 
    568 	void Controller::setFrontPanel(xtJucePlugin::FrontPanel* _frontPanel)
    569 	{
    570 		m_frontPanel = _frontPanel;
    571 	}
    572 
    573 	void Controller::setWaveEditor(xtJucePlugin::WaveEditor* _waveEditor)
    574 	{
    575 		m_waveEditor = _waveEditor;
    576 	}
    577 
    578 	void Controller::selectPreset(const int _offset)
    579 	{
    580 	    auto& current = isMultiMode() ? m_currentSingles[getCurrentPart()] : m_currentSingle;
    581 
    582 		int index = static_cast<int>(current) + _offset;
    583 
    584 		if (index < 0)
    585 	        index += 300;
    586 
    587 		if (index >= 300)
    588 	        index -= 300;
    589 
    590 	    current = static_cast<uint32_t>(index);
    591 
    592 	    const int single = index % 100;
    593 	    const int bank = index / 100;
    594 
    595 	    if (isMultiMode())
    596 	    {
    597 		    // TODO: modify multi
    598 	    }
    599 		else
    600 	    {
    601 			sendMidiEvent(synthLib::M_CONTROLCHANGE, synthLib::MC_BANKSELECTMSB, m_deviceId);
    602 	        sendMidiEvent(synthLib::M_CONTROLCHANGE, synthLib::MC_BANKSELECTLSB, static_cast<uint8_t>(xt::LocationH::SingleBankA) + bank);
    603 	        sendMidiEvent(synthLib::M_PROGRAMCHANGE, static_cast<uint8_t>(single), 0);
    604 	/*
    605 			sendGlobalParameterChange(xt::GlobalParameter::InstrumentABankNumber, static_cast<uint8_t>(bank));
    606 		    sendGlobalParameterChange(xt::GlobalParameter::InstrumentASingleNumber, static_cast<uint8_t>(single));
    607 	*/  }
    608 	}
    609 
    610 	void Controller::sendParameterChange(const pluginLib::Parameter& _parameter, const pluginLib::ParamValue _value)
    611 	{
    612 		const auto &desc = _parameter.getDescription();
    613 
    614 		std::map<pluginLib::MidiDataType, uint8_t> data;
    615 
    616 		switch (desc.page)
    617 		{
    618 		case g_pageGlobal:
    619 			{
    620 				data.insert(std::make_pair(pluginLib::MidiDataType::ParameterIndex, _parameter.getDescription().index & 0x7f));
    621 				data.insert(std::make_pair(pluginLib::MidiDataType::ParameterValue, _value));
    622 
    623 				sendSysEx(GlobalParameterChange, data);
    624 			}
    625 			return;
    626 		case g_pageMulti:
    627 		case g_pageMultiInst0:
    628 		case g_pageMultiInst1:
    629 		case g_pageMultiInst2:
    630 		case g_pageMultiInst3:
    631 		case g_pageMultiInst4:
    632 		case g_pageMultiInst5:
    633 		case g_pageMultiInst6:
    634 		case g_pageMultiInst7:
    635 			{
    636 				uint8_t v;
    637 
    638 				if (!combineParameterChange(v, g_midiPacketNames[MultiDump], _parameter, _value))
    639 					return;
    640 
    641 				uint8_t page;
    642 
    643 				if(desc.page > g_pageMulti)
    644 					page = desc.page - g_pageMultiInst0;
    645 				else
    646 					page = static_cast<uint8_t>(xt::LocationH::MultiDumpMultiEditBuffer);
    647 
    648 				data.insert(std::make_pair(pluginLib::MidiDataType::Part, _parameter.getPart()));
    649 				data.insert(std::make_pair(pluginLib::MidiDataType::Page, page));
    650 				data.insert(std::make_pair(pluginLib::MidiDataType::ParameterIndex, desc.index));
    651 				data.insert(std::make_pair(pluginLib::MidiDataType::ParameterValue, v));
    652 
    653 				sendSysEx(MultiParameterChange, data);
    654 			}
    655 			return;
    656 		case g_pageSoftKnobs:
    657 			break;
    658 		case g_pageControllers:
    659 			break;
    660 		default:
    661 			{
    662 				uint8_t v;
    663 				if (!combineParameterChange(v, g_midiPacketNames[SingleDump], _parameter, _value))
    664 					return;
    665 
    666 				data.insert(std::make_pair(pluginLib::MidiDataType::Part, _parameter.getPart()));
    667 				data.insert(std::make_pair(pluginLib::MidiDataType::Page, desc.page));
    668 				data.insert(std::make_pair(pluginLib::MidiDataType::ParameterIndex, desc.index));
    669 				data.insert(std::make_pair(pluginLib::MidiDataType::ParameterValue, v));
    670 
    671 				sendSysEx(SingleParameterChange, data);
    672 			}
    673 			break;
    674 		}
    675 	}
    676 
    677 	bool Controller::sendGlobalParameterChange(xt::GlobalParameter _param, uint8_t _value)
    678 	{
    679 		const auto index = static_cast<uint32_t>(_param);
    680 
    681 		if(m_globalData[index] == _value)
    682 			return true;
    683 
    684 	    std::map<pluginLib::MidiDataType, uint8_t> data;
    685 
    686 	    data.insert(std::make_pair(pluginLib::MidiDataType::Page, index >> 7 ));
    687 	    data.insert(std::make_pair(pluginLib::MidiDataType::ParameterIndex, index & 0x7f ));
    688 	    data.insert(std::make_pair(pluginLib::MidiDataType::ParameterValue, _value));
    689 
    690 	    m_globalData[index] = _value;
    691 
    692 	    return sendSysEx(GlobalParameterChange, data);
    693 	}
    694 
    695 	bool Controller::sendModeDump() const
    696 	{
    697 		std::vector<uint8_t> sysex;
    698 		std::map<pluginLib::MidiDataType, uint8_t> data;
    699 		data.insert({pluginLib::MidiDataType::DeviceId, m_deviceId});
    700 		if(!createMidiDataFromPacket(sysex, midiPacketName(ModeDump), data, 0))
    701 			return false;
    702 		sysex[xt::IdxModeParamFirst] = m_modeData.front();
    703 		pluginLib::Controller::sendSysEx(sysex);
    704 		return true;
    705 	}
    706 
    707 	void Controller::requestSingle(xt::LocationH _buf, uint8_t _location) const
    708 	{
    709 		std::map<pluginLib::MidiDataType, uint8_t> params;
    710 	    params[pluginLib::MidiDataType::Bank] = static_cast<uint8_t>(_buf);
    711 	    params[pluginLib::MidiDataType::Program] = _location;
    712 	    sendSysEx(RequestSingle, params);
    713 	}
    714 
    715 	void Controller::requestMulti(xt::LocationH _buf, uint8_t _location) const
    716 	{
    717 		std::map<pluginLib::MidiDataType, uint8_t> params;
    718 		params[pluginLib::MidiDataType::Bank] = static_cast<uint8_t>(_buf);
    719 		params[pluginLib::MidiDataType::Program] = _location;
    720 		sendSysEx(RequestMulti, params);
    721 	}
    722 
    723 	bool Controller::requestWave(const uint32_t _number) const
    724 	{
    725 		if(!xt::wave::isValidWaveIndex(_number))
    726 			return false;
    727 
    728 		std::map<pluginLib::MidiDataType, uint8_t> params;
    729 
    730 		params[pluginLib::MidiDataType::Bank] = static_cast<uint8_t>(_number >> 7);
    731 		params[pluginLib::MidiDataType::Program] = _number & 0x7f;
    732 
    733 		return sendSysEx(RequestWave, params);
    734 	}
    735 
    736 	bool Controller::requestTable(const uint32_t _number) const
    737 	{
    738 		if(!xt::wave::isValidTableIndex(_number))
    739 			return false;
    740 
    741 		std::map<pluginLib::MidiDataType, uint8_t> params;
    742 
    743 		params[pluginLib::MidiDataType::Bank] = static_cast<uint8_t>(_number >> 7);
    744 		params[pluginLib::MidiDataType::Program] = _number & 0x7f;
    745 
    746 		return sendSysEx(RequestTable, params);
    747 	}
    748 
    749 	uint8_t Controller::getGlobalParam(xt::GlobalParameter _type) const
    750 	{
    751 	    return m_globalData[static_cast<uint32_t>(_type)];
    752 	}
    753 
    754 	bool Controller::isDerivedParameter(pluginLib::Parameter& _derived, pluginLib::Parameter& _base) const
    755 	{
    756 		if(_derived.getDescription().page >= 100)
    757 			return false;
    758 
    759 		const auto& packetName = g_midiPacketNames[SingleDump];
    760 		const auto* packet = getMidiPacket(packetName);
    761 
    762 		if (!packet)
    763 		{
    764 			LOG("Failed to find midi packet " << packetName);
    765 			return true;
    766 		}
    767 		
    768 		const auto* defA = packet->getDefinitionByParameterName(_derived.getDescription().name);
    769 		const auto* defB = packet->getDefinitionByParameterName(_base.getDescription().name);
    770 
    771 		if (!defA || !defB)
    772 			return true;
    773 
    774 		return defA->doMasksOverlap(*defB);
    775 	}
    776 
    777 	void Controller::requestAllPatches() const
    778 	{
    779 		if (isMultiMode())
    780 		{
    781 			requestMulti(xt::LocationH::MultiDumpMultiEditBuffer, 0);
    782 		}
    783 		else
    784 		{
    785 			requestSingle(xt::LocationH::SingleEditBufferSingleMode, 0);
    786 		}
    787 	}
    788 }