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

microcontroller.cpp (44905B)


      1 #include <vector>
      2 #include <thread>
      3 #include <cstring>	// memcpy
      4 #include <cmath>	// floor/abs
      5 
      6 #include "microcontroller.h"
      7 
      8 #include "dspSingle.h"
      9 #include "frontpanelState.h"
     10 #include "synthLib/midiTypes.h"
     11 
     12 using namespace dsp56k;
     13 using namespace synthLib;
     14 
     15 namespace virusLib
     16 {
     17 
     18 constexpr virusLib::PlayMode g_defaultPlayMode = virusLib::PlayModeSingle;
     19 
     20 constexpr uint32_t g_sysexPresetHeaderSize = 9;
     21 constexpr uint32_t g_sysexPresetFooterSize = 2;	// checksum, f7
     22 
     23 constexpr uint32_t g_singleRamBankCount = 2;
     24 
     25 const std::set<uint8_t> g_pageA = {0x05, 0x0A, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
     26                                    0x1E, 0x1F, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D,
     27                                    0x2E, 0x2F, 0x30, 0x31, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D,
     28                                    0x3E, 0x3F, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
     29                                    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5D, 0x5E, 0x61,
     30                                    0x62, 0x63, 0x64, 0x65, 0x66, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x7B};
     31 
     32 const std::set<uint8_t> g_pageB = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x11,
     33                                    0x12, 0x13, 0x15, 0x19, 0x1A, 0x1B, 0x1C, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24,
     34                                    0x26, 0x27, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x36, 0x37,
     35                                    0x38, 0x39, 0x3A, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
     36                                    0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x54, 0x55,
     37                                    0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63,
     38                                    0x64, 0x65, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x7B, 0x7C};
     39 
     40 constexpr uint8_t g_pageC_global[] = {45,  63,  64,  65,  66,  67,  68,  69,  70,  85,  86,  87,  90,  91,
     41                                       92,  93,  94,  95,  96,  97,  98,  99, 105, 106, 110, 111, 112, 113,
     42                                      114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126, 127};
     43 
     44 Microcontroller::Microcontroller(DspSingle& _dsp, const ROMFile& _romFile, bool _useEsaiBasedMidiTiming) : m_rom(_romFile)
     45 {
     46 	if(!_romFile.isValid())
     47 		return;
     48 
     49 	m_hdi08TxParsers.reserve(2);
     50 	m_midiQueues.reserve(2);
     51 
     52 	addDSP(_dsp, _useEsaiBasedMidiTiming);
     53 
     54 	m_globalSettings.fill(0xffffffff);
     55 
     56 	for(size_t i=0; i<m_multis.size(); ++i)
     57 		m_rom.getMulti(0, m_multis[i]);
     58 
     59 	m_multiEditBuffer = m_multis.front();
     60 	
     61 	bool failed = false;
     62 
     63 	// read all singles from ROM and copy first ROM banks to RAM banks
     64 	for(uint32_t b=0; b<128 && !failed; ++b)
     65 	{
     66 		std::vector<TPreset> singles;
     67 
     68 		const auto bank = b >= g_singleRamBankCount ? b - g_singleRamBankCount : b;
     69 
     70 		for(uint32_t p=0; p<m_rom.getPresetsPerBank(); ++p)
     71 		{
     72 			TPreset single;
     73 			if(!m_rom.getSingle(bank, p, single))
     74 				break;
     75 
     76 			if(ROMFile::getSingleName(single).size() != 10)
     77 			{
     78 				failed = true;
     79 				break;
     80 			}
     81 
     82 			singles.emplace_back(single);
     83 		}
     84 
     85 		if(!singles.empty())
     86 			m_singles.emplace_back(std::move(singles));
     87 	}
     88 
     89 	if(!m_singles.empty())
     90 	{
     91 		const auto& singles = m_singles[0];
     92 
     93 		if(!singles.empty())
     94 		{
     95 			m_singleEditBuffer = singles[0];
     96 
     97 			for(auto i=0; i<static_cast<int>(std::min(singles.size(), m_singleEditBuffers.size())); ++i)
     98 				m_singleEditBuffers[i] = singles[i];
     99 		}
    100 	}
    101 
    102 	m_pendingSysexInput.reserve(64);
    103 }
    104 
    105 void Microcontroller::sendInitControlCommands(uint8_t _masterVolume)
    106 {
    107 	writeHostBitsWithWait(0, 1);
    108 //	const std::vector<TWord> magic = { 0xf4f473, 0xf4f46e, 0xf4f46f, 0xf4f477 };	// snow
    109 //	const std::vector<TWord> magic = { 0xf4f453, 0xf4f44e, 0xf4f44f, 0xf4f457 };	// SNOW
    110 //	const std::vector<TWord> magic = { 0xf4f454, 0xf4f449, 0xf4f453, 0xf4f44e, 0xf4f44f, 0xf4f457 };	// TISNOW
    111 //	const std::vector<TWord> magic = { 0xf4f473, 0x407f01, 0xf4f473, 0x401000, 0xf4f46e, 0x407f01, 0xf4f46e, 0x401000, 0xf4f46f, 0x407f01, 0xf4f46f, 0x401000, 0xf4f477, 0x407f01, 0xf4f477, 0x401000 };
    112 //	const std::vector<TWord> magic = { 0xf4f473, 0x407f00, 0xf4f46e, 0x407f01, 0xf4f46f, 0x407f02, 0xf4f477, 0x407f03 };
    113 //	m_hdi08.writeRX(magic);
    114 
    115 	LOG("Sending Init Control Commands");
    116 
    117 	if(m_rom.isTIFamily())
    118 	{
    119 		const std::vector<TWord> initCodeDS =
    120 		{
    121 			0xF4F473, 0x407F00,
    122 			0xF4F473, 0x401000,						// Samplerate 44100 Hz
    123 //			0xF47555, 0x104000, 0x0C0104, 0x000319, 0x007F00, 0x00407F, 0x000000, 0x00007E, 0x003728, 0x607F62, 0x3E3420, 0x190040, 0x406000, 0x663100, 0x402300, 0x401509, 0x2F233E, 0x286B6A, 0x400600, 0x010200, 0x384719, 0x137F00, 0x7F7F40, 0x150000, 0x006501, 0x010057, 0x000040, 0x404040, 0x4B5402, 0x010040, 0x000040, 0x404040, 0x407B01, 0x400400, 0x000269, 0x7F4000, 0x01017F, 0x001060, 0x126801, 0x00011B, 0x7F5010, 0x0C0140, 0x000000, 0x010000, 0x000040, 0x000000, 0x004000, 0x610100, 0x000100, 0x4B0000, 0x390400, 0x000000, 0x7F0000, 0x01423E, 0x010001, 0x000124, 0x000040, 0x000000, 0x000040, 0x40282B, 0x554040, 0x404049, 0x2C4060, 0x4D4040, 0x004040, 0x401603, 0x03107F, 0x144900, 0x004002, 0x490F19, 0x2B186A, 0x184814, 0x010000, 0x410000, 0x7F607F, 0x004864, 0x334040, 0x282000, 0x000000, 0x056556, 0x071845, 0x023C00, 0x202054, 0x202049, 0x202042, 0x430001, 0x000100, 0x010001, 0x440354, 0x373062, 0x000000, 0x400304, 0x020000, 0x000000, 0x7F4040, 0x7F7F40, 0x000005, 0x000200, 0x000000, 0x010000, 0x000000, 0x004000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x400000, 0x000000, 0x000000, 0x007F7F, 0x400000, 0x000000, 0x144600, 0x404614, 0x460040, 0x460135, 0x004000, 0x400040, 0x004000, 0x400040, 0x0B2903, 0x400000, 0x000000, 0x000000, 0x010002, 0x000000, 0x010000, 0x00001F, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x02697F, 0x400001, 0x000006,
    124 			0xF4F473, 0x401000,						// Samplerate 44100 Hz
    125 			0xF00020, 0x330110, 0x734000, 0x00F700,
    126 			0xF00020, 0x330110, 0x734009, 0x02F702,	// USB Mode = 3 out 1 in
    127 			0xF00020, 0x330110, 0x73400E, 0x00F700,	// Set DSP clock base to $0 = 133 Mhz ($1 = 153 MHz)
    128 			0xF00020, 0x330110, 0x73406D, 0x64F764, // DSP clock adjustment in percent (roughly), default $64 = 100%, max $79 = 121%
    129 			0xF00020, 0x330110, 0x73400D, 0x00F700, // ??
    130 			0xF00020, 0x330110, 0x734010, 0x00F700,	// Samplerate = 44100 Hz
    131 			0xF00020, 0x330110, 0x734019, 0x01F701,	// EQ = Enable
    132 			0xF00020, 0x330110, 0x73401A, 0x01F701,	// Arp = Enable
    133 			0xF00020, 0x330110, 0x73401B, 0x01F701, // Delay = Enable
    134 			0xF00020, 0x330110, 0x73401C, 0x01F701, // Reverb = Enable
    135 			0xF00020, 0x330110, 0x73401D, 0x00F700, // Analog Input Phono Mode = off
    136 			0xF00020, 0x330110, 0x73402D, 0x00F700,	// Second Output Select = 0
    137 			0xF00020, 0x330110, 0x734032, 0x6EF76E, // BPM LED Brightness Ratio = $6e
    138 			0xF00020, 0x330110, 0x734033, 0x50F750, // LED Brightness = $50
    139 			0xF00020, 0x330110, 0x734034, 0x7fF77f, // Logo Groove = $7f
    140 			0xF00020, 0x330110, 0x734035, 0x40F740, // Random Patch Generator Depth = $40
    141 			0xF00020, 0x330110, 0x734036, 0x0CF70C, // Random Patch Generator Amount = $54
    142 			0xF00020, 0x330110, 0x73403B, 0x01F701, // USB Output Midi Data Cable Number = 1
    143 			0xF00020, 0x330110, 0x73403C, 0x01F701, // USB Output Panel Data Cable Number = 1
    144 			0xF00020, 0x330110, 0x73403E, 0x40F740, // Keyboard Param Velocity Curve = $40
    145 			0xF00020, 0x330110, 0x734040, 0x01F701, // Keyboard Param Local On = On
    146 			0xF00020, 0x330110, 0x734041, 0x00F700, // Keyboard Param Channel Mode = 0
    147 			0xF00020, 0x330110, 0x734042, 0x40F740, // Keyboard Param Transpose = $40
    148 			0xF00020, 0x330110, 0x734043, 0x01F701, // Keyboard Param Modwheel = 1
    149 			0xF00020, 0x330110, 0x734044, 0x40F740, // Keyboard Param Hold Pedal = $40
    150 			0xF00020, 0x330110, 0x734045, 0x00F700, // Keyboard Param Pedal 2 Mode = 0
    151 			0xF00020, 0x330110, 0x734046, 0x40F740, // Keyboard Param Pressure Sensitivity = $40
    152 			0xF00020, 0x330110, 0x73404C, 0x00F700, // Pure Tuning = 0
    153 			0xF00020, 0x330110, 0x734057, 0x01F701, // Midi Volume Enable = On
    154 			0xF00020, 0x330110, 0x73405A, 0x00F700,	// Input Thru Level = 0
    155 			0xF00020, 0x330110, 0x73405B, 0x00F700, // Input Boost = 0
    156 			0xF00020, 0x330110, 0x73405C, 0x40F740,	// Master Tune = +/- 0
    157 			0xF00020, 0x330110, 0x73405D, 0x10F710, // device ID $10 = omni
    158 			0xF00020, 0x330110, 0x73405E, 0x01F701, // Midi Control Low Page = 1 = allow midi CC
    159 			0xF00020, 0x330110, 0x73405F, 0x00F700, // Midi Control High Page = 0 = Do NOT allow Poly Pressure
    160 			0xF00020, 0x330110, 0x734060, 0x00F700, // Midi Arp Send = 0 = off
    161 			0xF00020, 0x330110, 0x73406A, 0x01F701, // Midi Clock RX = 1 = enabled
    162 			0xF00020, 0x330110, 0x73406E, 0x00F700, // Soft Knob Config Mode 1 = 0
    163 			0xF00020, 0x330110, 0x73406F, 0x00F700, // Soft Knob Config Mode 2 = 0
    164 			0xF00020, 0x330110, 0x734070, 0x00F700, // Soft Knob Config Dest 1 = 0
    165 			0xF00020, 0x330110, 0x734071, 0x00F700, // Soft Knob Config Dest 2 = 0
    166 			0xF00020, 0x330110, 0x734072, 0x00F700, // Soft Knob Config Mode 3 = 0
    167 //			0xF0FFFF, 0x00FFFF, 0x20FFFF, 0x33FFFF, 0x01FFFF, 0x10FFFF, 0x73FFFF, 0x01FFFF, 0x10FFFF, 0x00FFFF, 0xF7FFFF,	// parameter $10 for Part 1 = 0
    168 			0xF00020, 0x330110, 0x734073, 0x00F700,	// Soft Knob Config Dest 3 = 0
    169 			0xF00020, 0x330110, 0x734079, 0x01F701,	// Panel Destination = 1
    170 			0xF00020, 0x330110, 0x73407C, 0x00F700,	// Global Channel = 0
    171 			0xF00020, 0x330110, 0x73407D, 0x02F702, // LED Mode = 2
    172 			0xF00020, 0x330110, 0x73407F, 0x63F763,	// Master Volume = 99
    173 //			0xF47555, 0x104000, 0x0C0104, 0x000319, 0x007F00, 0x00407F, 0x000000, 0x00007E, 0x003728, 0x607F62, 0x3E3420, 0x190040, 0x406000, 0x663100, 0x402300, 0x401509, 0x2F233E, 0x286B6A, 0x400600, 0x010200, 0x384719, 0x137F00, 0x7F7F40, 0x150000, 0x006501, 0x010057, 0x000040, 0x404040, 0x4B5402, 0x010040, 0x000040, 0x404040, 0x407B01, 0x400400, 0x000269, 0x7F4000, 0x01017F, 0x001060, 0x126801, 0x00011B, 0x7F5010, 0x0C0140, 0x000000, 0x010000, 0x000040, 0x000000, 0x004000, 0x610100, 0x000100, 0x4B0000, 0x390400, 0x000000, 0x7F0000, 0x01423E, 0x010001, 0x000124, 0x000040, 0x000000, 0x000040, 0x40282B, 0x554040, 0x404049, 0x2C4060, 0x4D4040, 0x004040, 0x401603, 0x03107F, 0x144900, 0x004002, 0x490F19, 0x2B186A, 0x184814, 0x010000, 0x410000, 0x7F607F, 0x004864, 0x334040, 0x282000, 0x000000, 0x056556, 0x071845, 0x023C00, 0x202054, 0x202049, 0x202042, 0x430001, 0x000100, 0x010001, 0x440354, 0x373062, 0x000000, 0x400304, 0x020000, 0x000000, 0x7F4040, 0x7F7F40, 0x000005, 0x000200, 0x000000, 0x010000, 0x000000, 0x004000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x400000, 0x000000, 0x000000, 0x007F7F, 0x400000, 0x000000, 0x144600, 0x404614, 0x460040, 0x460135, 0x004000, 0x400040, 0x004000, 0x400040, 0x0B2903, 0x400000, 0x000000, 0x000000, 0x010002, 0x000000, 0x010000, 0x00001F, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x406401, 0x406400, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x02697F, 0x400001, 0x000006,
    174 			0xF4F473, 0x401000,						// Samplerate 44100 Hz
    175 //			0xF4F473, 0x401001,						// Samplerate 48000 Hz
    176 		};
    177 
    178 		m_hdi08.writeRX(initCodeDS);
    179 
    180 #if 0
    181 		constexpr uint8_t prts = 0x4f;
    182 
    183 		enum class Output : uint8_t
    184 		{
    185 			Out1L,		Out1,		Out1R,
    186 			Out2L,		Out2,		Out2R,
    187 			Out3L,		Out3,		Out3R,
    188 			Usb1L,		Usb1,		Usb1R,
    189 			Usb2L,		Usb2,		Usb2R,
    190 			Usb3L,		Usb3,		Usb3R
    191 		};
    192 
    193 		constexpr auto oa = static_cast<uint8_t>(Output::Usb1);
    194 		constexpr auto ob = static_cast<uint8_t>(Output::Usb2);
    195 		constexpr auto oc = static_cast<uint8_t>(Output::Usb3);
    196 
    197 		constexpr uint8_t multi[] =
    198 		{
    199 			0x02,0x01,0x00,0x01,0x49,0x6e,0x69,0x74,0x20,0x4d,0x75,0x6c,0x74,0x69,0x00,0x39,	// Internal/"Init Multi"/Clock Tempo
    200 			0x01,0x3c,0x00,0x10,0x00,0x01,0x01,0x00,0x40,0x40,0x40,0x40,0x40,0x00,0x40,0x40,	// Delay/Internal
    201 			0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	// Bank Number
    202 			0x00,0x00,0x00,0x00,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,	// Program Number
    203 			0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,	// Midi Channel
    204 			0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	// Low Key
    205 			0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,	// High Key
    206 			0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,	// Transpose
    207 			0x40,0x42,0x43,0x41,0x47,0x42,0x46,0x41,0x48,0x46,0x44,0x40,0x40,0x40,0x40,0x40,	// Detune
    208 			0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,	// Part Volume
    209 			0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	// Midi Volume Init
    210 			oa  ,oa  ,ob  ,ob  ,oc  ,oc  ,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,	// Output Select
    211 			0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	// Effect Send
    212 			0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,	// Internal
    213 			0x41,0x46,0x40,0x48,0x41,0x49,0x47,0x41,0x42,0x47,0x40,0x45,0x41,0x49,0x47,0x46,	// Internal
    214 			prts,prts,prts,prts,prts,prts,prts,prts,prts,prts,prts,prts,prts,prts,prts,prts		// Part State
    215 		};
    216 
    217 		TPreset data;
    218 		memcpy(&data[0], multi, std::size(multi));
    219 
    220 		sendControlCommand(PLAY_MODE, PlayModeMulti);
    221 
    222 		const auto words = presetToDSPWords(data, true);
    223 
    224 		sendPreset(0, words, true);
    225 #endif
    226 	}
    227 //	else
    228 	{
    229 		sendControlCommand(MIDI_CLOCK_RX, 0x1);				// Enable MIDI clock receive
    230 		sendControlCommand(GLOBAL_CHANNEL, 0x0);			// Set global midi channel to 0
    231 		sendControlCommand(MIDI_CONTROL_LOW_PAGE, 0x1);		// Enable midi CC to edit parameters on page A
    232 		sendControlCommand(MIDI_CONTROL_HIGH_PAGE, 0x0);	// Disable poly pressure to edit parameters on page B
    233 		sendControlCommand(MASTER_VOLUME, _masterVolume <= 127 ? _masterVolume : 92);	// Set master volume to 92, this matches the Virus TI in USB mode
    234 		sendControlCommand(MASTER_TUNE, 64);				// Set master tune to 0
    235 		sendControlCommand(DEVICE_ID, OMNI_DEVICE_ID);		// Set device ID to Omni
    236 	}
    237 }
    238 
    239 void Microcontroller::createDefaultState()
    240 {
    241 	sendControlCommand(PLAY_MODE, g_defaultPlayMode);
    242 
    243 	if constexpr (g_defaultPlayMode == PlayModeSingle)
    244 		writeSingle(BankNumber::EditBuffer, SINGLE, m_singleEditBuffer);
    245 	else
    246 		loadMulti(0, m_multiEditBuffer);
    247 }
    248 
    249 void Microcontroller::writeHostBitsWithWait(const uint8_t flag0, const uint8_t flag1)
    250 {
    251 	m_hdi08.writeHostFlags(flag0, flag1);
    252 }
    253 
    254 bool Microcontroller::sendPreset(const uint8_t program, const TPreset& preset, const bool isMulti)
    255 {
    256 	if(!isMulti && !isValid(preset))
    257 		return false;
    258 
    259 	std::lock_guard lock(m_mutex);
    260 
    261 	if(m_loadingState || waitingForPresetReceiveConfirmation())
    262 	{
    263 		// if we write a multi or a multi mode single, remove a pending single for single mode
    264 		// If we write a single-mode single, remove all multi-related pending writes
    265 		const auto multiRelated = isMulti || program != SINGLE;
    266 
    267 		for (auto it = m_pendingPresetWrites.begin(); it != m_pendingPresetWrites.end();)
    268 		{
    269 			const auto& pendingPreset = *it;
    270 
    271 			const auto pendingIsMultiRelated = pendingPreset.isMulti || pendingPreset.program != SINGLE;
    272 
    273 			if (multiRelated != pendingIsMultiRelated)
    274 				it = m_pendingPresetWrites.erase(it);
    275 			else
    276 				++it;
    277 		}
    278 
    279 		for(auto it = m_pendingPresetWrites.begin(); it != m_pendingPresetWrites.end();)
    280 		{
    281 			const auto& pendingPreset = *it;
    282 			if (pendingPreset.isMulti == isMulti && pendingPreset.program == program)
    283 				it = m_pendingPresetWrites.erase(it);
    284 			else
    285 				++it;
    286 		}
    287 
    288 		m_pendingPresetWrites.emplace_back(SPendingPresetWrite{program, isMulti, preset});
    289 
    290 		return true;
    291 	}
    292 
    293 	receiveUpgradedPreset();
    294 
    295 	if(isMulti)
    296 	{
    297 		m_multiEditBuffer = preset;
    298 
    299 		m_globalSettings[PLAY_MODE] = PlayModeMulti;
    300 	}
    301 	else
    302 	{
    303 		if(program == SINGLE)
    304 		{
    305 			m_globalSettings[PLAY_MODE] = PlayModeSingle;
    306 			m_singleEditBuffer = preset;
    307 		}
    308 		else if(program < m_singleEditBuffers.size())
    309 		{
    310 			if(program >= getPartCount())
    311 				return false;
    312 
    313 			m_singleEditBuffers[program] = preset;
    314 		}
    315 	}
    316 
    317 	writeHostBitsWithWait(0,1);
    318 	// Send header
    319 	TWord buf[] = {0xf47555, static_cast<TWord>(isMulti ? 0x110000 : 0x100000)};
    320 	buf[1] = buf[1] | (program << 8);
    321 	m_hdi08.writeRX(buf, 2);
    322 
    323 	m_hdi08.writeRX(presetToDSPWords(preset, isMulti));
    324 
    325 	LOG("Send to DSP: " << (isMulti ? "Multi" : "Single") << " to program " << static_cast<int>(program));
    326 
    327 	for (auto& parser : m_hdi08TxParsers)
    328 		parser.waitForPreset(isMulti ? m_rom.getMultiPresetSize() : m_rom.getSinglePresetSize());
    329 
    330 	m_sentPresetProgram = program;
    331 	m_sentPresetIsMulti = isMulti;
    332 
    333 	return true;
    334 }
    335 
    336 void Microcontroller::sendControlCommand(const ControlCommand _command, const uint8_t _value)
    337 {
    338 	send(globalSettingsPage(), 0x0, _command, _value);
    339 }
    340 
    341 void Microcontroller::setSamplerate(const float _samplerate)
    342 {
    343 	const auto sr = static_cast<int>(std::floor(_samplerate + 0.5f));
    344 	
    345 	/*
    346 		0 = 44100 Hz @ EXTAL 11289600 Hz (44100 * 256)
    347 		1 = 48000 Hz @ EXTAL 12288000 Hz (48000 * 256)
    348 		2 = 44100 Hz @ ^^
    349 		3 = 32000 Hz @ ^^
    350 		4 = 96000 Hz @ ^^
    351 		5 = 88200 Hz @ ^^
    352 		6 = 64000 Hz @ ^^
    353 	*/
    354 
    355 	TWord v;
    356 
    357 	switch (sr)
    358 	{
    359 	default:
    360 	case 44100:	v = 0;	break;
    361 	case 48000:	v = 1;	break;
    362 	case 32000:	v = 3;	break;
    363 	case 96000:	v = 4;	break;
    364 	case 88200:	v = 5;	break;
    365 	case 64000:	v = 6;	break;
    366 	}
    367 
    368 	std::lock_guard lock(m_mutex);
    369 	m_hdi08.writeRX({0xF4F473, 0x401000 + v});
    370 }
    371 
    372 uint32_t Microcontroller::getPartCount() const
    373 {
    374 	if(m_rom.getModel() == DeviceModel::Snow)
    375 		return 4;
    376 	return 16;
    377 }
    378 
    379 uint8_t Microcontroller::getPartMidiChannel(const uint8_t _part) const
    380 {
    381 	return m_multiEditBuffer[MD_PART_MIDI_CHANNEL + _part];
    382 }
    383 
    384 bool Microcontroller::isPolyPressureForPageBEnabled() const
    385 {
    386 	return m_globalSettings[MIDI_CONTROL_HIGH_PAGE] == 1;
    387 }
    388 
    389 bool Microcontroller::send(const Page _page, const uint8_t _part, const uint8_t _param, const uint8_t _value)
    390 {
    391 	std::lock_guard lock(m_mutex);
    392 
    393 	if(_page == globalSettingsPage())
    394 	{
    395 		if(m_globalSettings[_param] == _value)
    396 			return true;
    397 		m_globalSettings[_param] = _value;
    398 	}
    399 
    400 	writeHostBitsWithWait(0,1);
    401 
    402 	TWord buf[] = {0xf4f400, 0x0};
    403 	buf[0] = buf[0] | _page;
    404 	buf[1] = (_part << 16) | (_param << 8) | _value;
    405 	m_hdi08.writeRX(buf, 2);
    406 
    407 //	LOG("Send command, page " << (int)_page << ", part " << (int)_part << ", param " << (int)_param << ", value " << (int)_value);
    408 
    409 	return true;
    410 }
    411 
    412 bool Microcontroller::sendMIDI(const SMidiEvent& _ev, FrontpanelState* _fpState/* = nullptr*/)
    413 {
    414 	const uint8_t channel = _ev.a & 0x0f;
    415 	const uint8_t status = _ev.a & 0xf0;
    416 
    417 	const auto singleMode = m_globalSettings[PLAY_MODE] == PlayModeSingle;
    418 
    419 	if(status != 0xf0 && singleMode && channel != m_globalSettings[GLOBAL_CHANNEL])
    420 		return true;
    421 
    422 	switch (status)
    423 	{
    424 	case M_PROGRAMCHANGE:
    425 		if(singleMode)
    426 			return partProgramChange(SINGLE, _ev.b);
    427 
    428 		for(uint32_t p=0; p<getPartCount(); ++p)
    429 		{
    430 			if(channel == getPartMidiChannel(static_cast<uint8_t>(p)))
    431 				partProgramChange(static_cast<uint8_t>(p), _ev.b);
    432 		}
    433 		return true;
    434 	case M_CONTROLCHANGE:
    435 		switch(_ev.b)
    436 		{
    437 		case MC_BANKSELECTLSB:
    438 			{
    439 				if(singleMode)
    440 				{
    441 					partBankSelect(SINGLE, _ev.c, false);
    442 				}
    443 				else
    444 				{
    445 					for(uint32_t p=0; p<getPartCount(); ++p)
    446 					{
    447 						if(channel == getPartMidiChannel(static_cast<uint8_t>(p)))
    448 							partBankSelect(static_cast<uint8_t>(p), _ev.c, false);
    449 					}
    450 				}
    451 			}
    452 			return true;
    453 		default:
    454 			if(g_pageA.find(_ev.b) != g_pageA.end())
    455 				applyToSingleEditBuffer(PAGE_A, singleMode ? SINGLE : channel, _ev.b, _ev.c);
    456 			break;
    457 		}
    458 		break;
    459 	case M_POLYPRESSURE:
    460 		if(isPolyPressureForPageBEnabled())
    461 		{
    462 			if(g_pageB.find(_ev.b) != g_pageB.end())
    463 			{
    464 				applyToSingleEditBuffer(PAGE_B, singleMode ? SINGLE : channel, _ev.b, _ev.c);
    465 				auto e = _ev;
    466 				e.source = MidiEventSource::Plugin;
    467 				m_midiOutput.push_back(e);
    468 			}
    469 		}
    470 		break;
    471 	default:
    472 		break;
    473 	}
    474 
    475 	for (auto& midiQueue : m_midiQueues)
    476 		midiQueue.add(_ev);
    477 
    478 	if(status < 0xf0 && _fpState)
    479 	{
    480 		for(uint32_t p=0; p<getPartCount(); ++p)
    481 		{
    482 			if(channel == getPartMidiChannel(static_cast<uint8_t>(p)))
    483 				_fpState->m_midiEventReceived[p] = true;
    484 		}
    485 	}
    486 
    487 	return true;
    488 }
    489 
    490 bool Microcontroller::sendSysex(const std::vector<uint8_t>& _data, std::vector<SMidiEvent>& _responses, const MidiEventSource _source)
    491 {
    492 	if (_data.size() < 7)
    493 		return true;	// invalid sysex or not directed to us
    494 
    495 	const auto manufacturerA = _data[1];
    496 	const auto manufacturerB = _data[2];
    497 	const auto manufacturerC = _data[3];
    498 	const auto productId = _data[4];
    499 	const auto deviceId = _data[5];
    500 	const auto cmd = _data[6];
    501 
    502 	if (deviceId != m_globalSettings[DEVICE_ID] && deviceId != OMNI_DEVICE_ID && m_globalSettings[DEVICE_ID] != OMNI_DEVICE_ID)
    503 	{
    504 		// ignore messages intended for a different device but allow omni requests
    505 		return true;
    506 	}
    507 
    508 	auto buildResponseHeader = [&](SMidiEvent& _ev)
    509 	{
    510 		auto& response = _ev.sysex;
    511 
    512 		response.reserve(1024);
    513 
    514 		response.push_back(M_STARTOFSYSEX);
    515 		response.push_back(manufacturerA);
    516 		response.push_back(manufacturerB);
    517 		response.push_back(manufacturerC);
    518 		response.push_back(productId);
    519 		response.push_back(deviceId);
    520 	};
    521 
    522 	auto buildPresetResponse = [&](const uint8_t _type, const BankNumber _bank, const uint8_t _program, const TPreset& _dump)
    523 	{
    524 		if(!isValid(_dump))
    525 			return;
    526 
    527 		SMidiEvent ev(_source);
    528 
    529 		auto& response = ev.sysex;
    530 
    531 		buildResponseHeader(ev);
    532 
    533 		response.push_back(_type);
    534 		response.push_back(toMidiByte(_bank));
    535 		response.push_back(_program);
    536 
    537 		const auto size = _type == DUMP_SINGLE ? m_rom.getSinglePresetSize() : m_rom.getMultiPresetSize();
    538 
    539 		const auto modelABCsize = ROMFile::getSinglePresetSize(DeviceModel::ABC);
    540 
    541 		for(size_t i=0; i<modelABCsize; ++i)
    542 			response.push_back(_dump[i]);
    543 
    544 		// checksum for ABC models comes after 256 bytes of preset data
    545 		response.push_back(calcChecksum(response));
    546 
    547 		if (size > modelABCsize)
    548 		{
    549 			for (size_t i = modelABCsize; i < size; ++i)
    550 				response.push_back(_dump[i]);
    551 
    552 			// Second checksum for D model: That checksum is to be calculated over the whole preset data, including the ABC checksum
    553 			response.push_back(calcChecksum(response));
    554 		}
    555 
    556 		response.push_back(M_ENDOFSYSEX);
    557 
    558 		_responses.emplace_back(std::move(ev));
    559 	};
    560 
    561 	auto buildSingleResponse = [&](const BankNumber _bank, const uint8_t _program)
    562 	{
    563 		TPreset dump;
    564 		const auto res = requestSingle(_bank, _program, dump);
    565 		if(res)
    566 			buildPresetResponse(DUMP_SINGLE, _bank, _program, dump);
    567 	};
    568 
    569 	auto buildMultiResponse = [&](const BankNumber _bank, const uint8_t _program)
    570 	{
    571 		TPreset dump;
    572 		const auto res = requestMulti(_bank, _program, dump);
    573 		if(res)
    574 			buildPresetResponse(DUMP_MULTI, _bank, _program, dump);
    575 	};
    576 
    577 	auto buildSingleBankResponse = [&](const BankNumber _bank)
    578 	{
    579 		if (_bank == BankNumber::EditBuffer)
    580 			return;
    581 
    582 		const auto bankIndex = toArrayIndex(_bank);
    583 
    584 		if(bankIndex < m_singles.size())
    585 		{
    586 			// eat this, host, whoever you are. 128 single packets
    587 			for(uint8_t i=0; i<m_singles[bankIndex].size(); ++i)
    588 			{
    589 				TPreset data;
    590 				const auto res = requestSingle(_bank, i, data);
    591 				buildPresetResponse(DUMP_SINGLE, _bank, i, data);
    592 			}
    593 		}		
    594 	};
    595 
    596 	auto buildMultiBankResponse = [&](const BankNumber _bank)
    597 	{
    598 		if(_bank == BankNumber::A)
    599 		{
    600 			// eat this, host, whoever you are. 128 multi packets
    601 			for(uint8_t i=0; i<m_rom.getPresetsPerBank(); ++i)
    602 			{
    603 				TPreset data;
    604 				const auto res = requestMulti(_bank, i, data);
    605 				buildPresetResponse(DUMP_MULTI, _bank, i, data);
    606 			}
    607 		}
    608 	};
    609 
    610 	auto buildGlobalResponse = [&](const uint8_t _param)
    611 	{
    612 		SMidiEvent ev(_source);
    613 		auto& response = ev.sysex;
    614 
    615 		buildResponseHeader(ev);
    616 
    617 		response.push_back(globalSettingsPage());
    618 		response.push_back(0);	// part = 0
    619 		response.push_back(_param);
    620 		response.push_back(static_cast<uint8_t>(m_globalSettings[_param]));
    621 		response.push_back(M_ENDOFSYSEX);
    622 
    623 		_responses.emplace_back(std::move(ev));
    624 	};
    625 
    626 	auto buildGlobalResponses = [&]()
    627 	{
    628 		for (uint32_t i=0; i<m_globalSettings.size(); ++i)
    629 		{
    630 			if(m_globalSettings[i] <= 0xff)
    631 				buildGlobalResponse(static_cast<uint8_t>(i));
    632 		}
    633 	};
    634 
    635 	auto buildTotalResponse = [&]()
    636 	{
    637 		buildGlobalResponses();
    638 		buildSingleBankResponse(BankNumber::A);
    639 		buildSingleBankResponse(BankNumber::B);
    640 		buildMultiBankResponse(BankNumber::A);
    641 	};
    642 
    643 	auto buildArrangementResponse = [&]()
    644 	{
    645 		// If we are in multi mode, we return the Single mode single first. If in single mode, it is returned last.
    646 		// The reason is that we want to backup everything but the last loaded multi/single defines the play mode when restoring
    647 		const bool isMultiMode = m_globalSettings[PLAY_MODE] == PlayModeMulti;
    648 
    649 		if(isMultiMode)
    650 			buildSingleResponse(BankNumber::EditBuffer, SINGLE);
    651 
    652 		buildMultiResponse(BankNumber::EditBuffer, 0);
    653 
    654 		for(uint8_t p=0; p<16; ++p)
    655 			buildPresetResponse(DUMP_SINGLE, BankNumber::EditBuffer, p, m_singleEditBuffers[p]);
    656 
    657 		if(!isMultiMode)
    658 			buildSingleResponse(BankNumber::EditBuffer, SINGLE);
    659 	};
    660 
    661 	auto buildControllerDumpResponse = [&](const uint8_t _part)
    662 	{
    663 		TPreset single;
    664 
    665 		requestSingle(BankNumber::EditBuffer, _part, single);
    666 
    667 		const uint8_t channel = _part == SINGLE ? 0 : _part;
    668 
    669 		for (const auto cc : g_pageA)	 _responses.emplace_back(_source, M_CONTROLCHANGE + channel, cc, single[cc], 0);
    670 		for (const auto cc : g_pageB)	 _responses.emplace_back(_source, M_POLYPRESSURE, cc, single[cc + 128], 0);
    671 	};
    672 
    673 	auto enqueue = [&]
    674 	{
    675 		m_pendingSysexInput.emplace_back(_source, _data);
    676 		return false;
    677 	};
    678 
    679 	switch (cmd)
    680 	{
    681 		case DUMP_SINGLE: 
    682 			{
    683 				const auto bank = fromMidiByte(_data[7]);
    684 				const uint8_t program = _data[8];
    685 				LOG("Received Single dump, Bank " << (int)toMidiByte(bank) << ", program " << (int)program);
    686 				TPreset preset;
    687 				preset.fill(0);
    688 				if(_data.size() == 524 && m_rom.isTIFamily())
    689 				{
    690 					// D preset
    691 					auto data(_data);
    692 
    693 					data.erase(data.begin() + 0x100 + g_sysexPresetHeaderSize);	// A/B/C checksum, not needed on D
    694 					std::copy_n(data.data() + g_sysexPresetHeaderSize, std::min(preset.size(), _data.size() - g_sysexPresetHeaderSize - g_sysexPresetFooterSize), preset.begin());
    695 				}
    696 				else
    697 				{
    698 					std::copy_n(_data.data() + g_sysexPresetHeaderSize, std::min(preset.size(), _data.size() - g_sysexPresetHeaderSize - g_sysexPresetFooterSize), preset.begin());
    699 				}
    700 				return writeSingle(bank, program, preset);
    701 			}
    702 		case DUMP_MULTI:
    703 			{
    704 				const auto bank = fromMidiByte(_data[7]);
    705 				const uint8_t program = _data[8];
    706 				LOG("Received Multi dump, Bank " << (int)toMidiByte(bank) << ", program " << (int)program);
    707 				TPreset preset;
    708 				std::copy_n(_data.data() + g_sysexPresetHeaderSize, std::min(preset.size(), _data.size() - g_sysexPresetHeaderSize - g_sysexPresetFooterSize), preset.begin());
    709 				return writeMulti(bank, program, preset);
    710 			}
    711 		case REQUEST_SINGLE:
    712 			{
    713 				const auto bank = fromMidiByte(_data[7]);
    714 				if(!m_pendingPresetWrites.empty() || bank == BankNumber::EditBuffer && waitingForPresetReceiveConfirmation())
    715 					return enqueue();
    716 				const uint8_t program = _data[8];
    717 				LOG("Request Single, Bank " << (int)toMidiByte(bank) << ", program " << (int)program);
    718 				buildSingleResponse(bank, program);
    719 				break;
    720 			}
    721 		case REQUEST_MULTI:
    722 			{
    723 				const auto bank = fromMidiByte(_data[7]);
    724 				if(!m_pendingPresetWrites.empty() || bank == BankNumber::EditBuffer && waitingForPresetReceiveConfirmation())
    725 					return enqueue();
    726 				const uint8_t program = _data[8];
    727 				LOG("Request Multi, Bank " << (int)bank << ", program " << (int)program);
    728 				buildMultiResponse(bank, program);
    729 				break;
    730 			}
    731 		case REQUEST_BANK_SINGLE:
    732 			{
    733 				const auto bank = fromMidiByte(_data[7]);
    734 				buildSingleBankResponse(bank);
    735 				break;
    736 			}
    737 		case REQUEST_BANK_MULTI:
    738 			{
    739 				const auto bank = fromMidiByte(_data[7]);
    740 				buildMultiBankResponse(bank);
    741 				break;
    742 			}
    743 		case REQUEST_CONTROLLER_DUMP:
    744 			{
    745 				const auto part = _data[8];
    746 				if (part < 16 || part == SINGLE)
    747 					buildControllerDumpResponse(part);
    748 				break;
    749 			}
    750 		case REQUEST_GLOBAL:
    751 			buildGlobalResponses();
    752 			break;
    753 		case REQUEST_TOTAL:
    754 			if(!m_pendingPresetWrites.empty() || waitingForPresetReceiveConfirmation())
    755 				return enqueue();
    756 			buildTotalResponse();
    757 			break;
    758 		case REQUEST_ARRANGEMENT:
    759 			if(!m_pendingPresetWrites.empty() || waitingForPresetReceiveConfirmation())
    760 				return enqueue();
    761 			buildArrangementResponse();
    762 			break;
    763 		case PAGE_6E:
    764 		case PAGE_6F:
    765 		case PAGE_A:
    766 		case PAGE_B:
    767 		case PAGE_C:
    768 		case PAGE_D:
    769 			{
    770 				const auto page = static_cast<Page>(cmd);
    771 
    772 				if(!isPageSupported(page))
    773 					break;
    774 
    775 				auto part = _data[7];
    776 				const auto param = _data[8];
    777 				const auto value = _data[9];
    778 
    779 				if(page == globalSettingsPage() && param == PLAY_MODE)
    780 				{
    781 					const auto playMode = value;
    782 
    783 					send(page, part, param, value);
    784 
    785 					switch(playMode)
    786 					{
    787 					case PlayModeSingle:
    788 						{
    789 							LOG("Switch to Single mode");
    790 							return writeSingle(BankNumber::EditBuffer, SINGLE, m_singleEditBuffer);
    791 						}
    792 					case PlayModeMultiSingle:
    793 					case PlayModeMulti:
    794 						{
    795 							writeMulti(BankNumber::EditBuffer, 0, m_multiEditBuffer);
    796 							for(uint8_t i=0; i<16; ++i)
    797 								writeSingle(BankNumber::EditBuffer, i, m_singleEditBuffers[i]);
    798 							return true;
    799 						}
    800 					default:
    801 						return true;
    802 					}
    803 				}
    804 
    805 				if(!m_rom.isTIFamily() && page == PAGE_A && m_globalSettings[PLAY_MODE] != PlayModeSingle)
    806 				{
    807 					applyToMultiEditBuffer(page, part, param, value);
    808 				}
    809 
    810 				if(page == PAGE_C || (page == PAGE_B && param == CLOCK_TEMPO))
    811 				{
    812 					applyToMultiEditBuffer(page, part, param, value);
    813 
    814 					const auto command = static_cast<ControlCommand>(param);
    815 
    816 					switch(command)
    817 					{
    818 					case PART_BANK_SELECT:
    819 						return partBankSelect(part, value, false);
    820 					case PART_BANK_CHANGE:
    821 						return partBankSelect(part, value, true);
    822 					case PART_PROGRAM_CHANGE:
    823 						return partProgramChange(part, value);
    824 					case MULTI_PROGRAM_CHANGE:
    825 						if(part == 0)
    826 						{
    827 							return multiProgramChange(value);
    828 						}
    829 						return true;
    830 					}
    831 				}
    832 				else
    833 				{
    834 					if (m_globalSettings[PLAY_MODE] != PlayModeSingle || part == SINGLE)
    835 					{
    836 						// virus only applies sysex changes to other parts while in multi mode.
    837 						applyToSingleEditBuffer(page, part, param, value);
    838 					}
    839 					if (m_globalSettings[PLAY_MODE] == PlayModeSingle && part == 0)
    840 					{
    841 						// accept parameter changes in single mode even if sent for part 0, this is how the editor does it right now
    842 						applyToSingleEditBuffer(page, SINGLE, param, value);
    843 					}
    844 				}
    845 
    846 				return send(page, part, param, value);
    847 			}
    848 		default:
    849 			LOG("Unknown sysex command " << HEXN(cmd, 2));
    850 	}
    851 
    852 	return true;
    853 }
    854 
    855 std::vector<TWord> Microcontroller::presetToDSPWords(const TPreset& _preset, const bool _isMulti) const
    856 {
    857 	const auto presetVersion = getPresetVersion(_preset);
    858 	const auto presetModel = presetVersion <= C ? DeviceModel::ABC : DeviceModel::Snow;
    859 
    860 	const auto targetByteSize = _isMulti ? m_rom.getMultiPresetSize() : m_rom.getSinglePresetSize();
    861 	const auto sourceByteSize = _isMulti ? ROMFile::getMultiPresetSize(presetModel) : ROMFile::getSinglePresetSize(presetModel);
    862 
    863 	const auto sourceWordSize = (sourceByteSize + 2) / 3;
    864 	const auto targetWordSize = (targetByteSize + 2) / 3;
    865 
    866 	std::vector<TWord> preset;
    867 	preset.resize(targetWordSize, 0);
    868 
    869 	size_t idx = 0;
    870 	for (size_t i = 0; i < sourceWordSize && i < targetWordSize; i++)
    871 	{
    872 		if (i == (sourceWordSize - 1))
    873 		{
    874 			if (idx < sourceByteSize)
    875 				preset[i] = _preset[idx] << 16;
    876 			if ((idx + 1) < sourceByteSize)
    877 				preset[i] |= _preset[idx + 1] << 8;
    878 			if ((idx + 2) < sourceByteSize)
    879 				preset[i] |= _preset[idx + 2];
    880 		}
    881 		else if (i < sourceWordSize)
    882 		{
    883 			preset[i] = ((_preset[idx] << 16) | (_preset[idx + 1] << 8) | _preset[idx + 2]);
    884 		}
    885 
    886 		idx += 3;
    887 	}
    888 
    889 	return preset;
    890 }
    891 
    892 bool Microcontroller::getSingle(BankNumber _bank, uint32_t _preset, TPreset& _result) const
    893 {
    894 	_result[0] = 0;
    895 
    896 	if (_bank == BankNumber::EditBuffer)
    897 		return false;
    898 
    899 	const auto bank = toArrayIndex(_bank);
    900 	
    901 	if(bank >= m_singles.size())
    902 		return false;
    903 
    904 	const auto& s = m_singles[bank];
    905 	
    906 	if(_preset >= s.size())
    907 		return false;
    908 
    909 	_result = s[_preset];
    910 	return true;
    911 }
    912 
    913 bool Microcontroller::requestMulti(BankNumber _bank, uint8_t _program, TPreset& _data)
    914 {
    915 	_data[0] = 0;
    916 
    917 	if (_bank == BankNumber::EditBuffer)
    918 	{
    919 		receiveUpgradedPreset();
    920 
    921 		// Use multi-edit buffer
    922 		_data = m_multiEditBuffer;
    923 		return true;
    924 	}
    925 
    926 	if (_bank != BankNumber::A || _program >= m_multis.size())
    927 		return false;
    928 
    929 	// Load from flash
    930 	_data = m_multis[_program];
    931 	return true;
    932 }
    933 
    934 bool Microcontroller::requestSingle(BankNumber _bank, uint8_t _program, TPreset& _data)
    935 {
    936 	if (_bank == BankNumber::EditBuffer)
    937 	{
    938 		receiveUpgradedPreset();
    939 
    940 		// Use single-edit buffer
    941 		if(_program == SINGLE)
    942 			_data = m_singleEditBuffer;
    943 		else
    944 			_data = m_singleEditBuffers[_program % m_singleEditBuffers.size()];
    945 
    946 		return true;
    947 	}
    948 
    949 	// Load from flash
    950 	return getSingle(_bank, _program, _data);
    951 }
    952 
    953 bool Microcontroller::writeSingle(BankNumber _bank, uint8_t _program, const TPreset& _data)
    954 {
    955 	if (_bank != BankNumber::EditBuffer) 
    956 	{
    957 		const auto bank = toArrayIndex(_bank);
    958 
    959 		if(bank >= m_singles.size() || bank >= g_singleRamBankCount)
    960 			return true;	// out of range
    961 
    962 		if(_program >= m_singles[bank].size())
    963 			return true;	// out of range
    964 
    965 		m_singles[bank][_program] = _data;
    966 
    967 		return true;
    968 	}
    969 
    970 	if(_program >= m_singleEditBuffers.size() && _program != SINGLE)
    971 		return false;
    972 
    973 	LOG("Loading Single " << ROMFile::getSingleName(_data) << " to part " << static_cast<int>(_program));
    974 
    975 	// Send to DSP
    976 	return sendPreset(_program, _data, false);
    977 }
    978 
    979 bool Microcontroller::writeMulti(BankNumber _bank, uint8_t _program, const TPreset& _data)
    980 {
    981 	if(_bank == BankNumber::A && _program < m_multis.size())
    982 	{
    983 		m_multis[_program] = _data;
    984 		return true;
    985 	}
    986 
    987 	if (_bank != BankNumber::EditBuffer) 
    988 	{
    989 		LOG("We do not support writing to RAM or ROM, attempt to write multi to bank " << static_cast<int>(toMidiByte(_bank)) << ", program " << static_cast<int>(_program));
    990 		return true;
    991 	}
    992 
    993 	LOG("Loading Multi " << ROMFile::getMultiName(_data));
    994 
    995 	// Convert array of uint8_t to vector of 24bit TWord
    996 	return sendPreset(_program, _data, true);
    997 }
    998 
    999 bool Microcontroller::partBankSelect(const uint8_t _part, const uint8_t _value, const bool _immediatelySelectSingle)
   1000 {
   1001 	if(_part == SINGLE)
   1002 	{
   1003 		const auto bankIndex = static_cast<uint8_t>(toArrayIndex(fromMidiByte(_value)) % m_singles.size());
   1004 		m_currentBank = bankIndex;
   1005 		return true;
   1006 	}
   1007 
   1008 	m_multiEditBuffer[MD_PART_BANK_NUMBER + _part] = _value;
   1009 
   1010 	if(_immediatelySelectSingle)
   1011 		return partProgramChange(_part, m_multiEditBuffer[MD_PART_PROGRAM_NUMBER + _part]);
   1012 
   1013 	return true;
   1014 }
   1015 
   1016 bool Microcontroller::partProgramChange(const uint8_t _part, const uint8_t _value)
   1017 {
   1018 	TPreset single;
   1019 
   1020 	if(_part == SINGLE)
   1021 	{
   1022 		if (getSingle(fromArrayIndex(m_currentBank), _value, single))
   1023 		{
   1024 			m_currentSingle = _value;
   1025 			return writeSingle(BankNumber::EditBuffer, SINGLE, single);
   1026 		}
   1027 		return false;
   1028 	}
   1029 
   1030 	const auto bank = fromArrayIndex(m_multiEditBuffer[MD_PART_BANK_NUMBER + _part]);
   1031 
   1032 	if(getSingle(bank, _value, single))
   1033 	{
   1034 		m_multiEditBuffer[MD_PART_PROGRAM_NUMBER + _part] = _value;
   1035 		return writeSingle(BankNumber::EditBuffer, _part, single);
   1036 	}
   1037 
   1038 	return true;
   1039 }
   1040 
   1041 bool Microcontroller::multiProgramChange(uint8_t _value)
   1042 {
   1043 	if(_value >= m_multis.size())
   1044 		return true;
   1045 
   1046 	return loadMulti(_value, m_multis[_value]);
   1047 }
   1048 
   1049 bool Microcontroller::loadMulti(uint8_t _program, const TPreset& _multi)
   1050 {
   1051 	if(!writeMulti(BankNumber::EditBuffer, _program, _multi))
   1052 		return false;
   1053 
   1054 	for (uint8_t p = 0; p < 16; ++p)
   1055 		loadMultiSingle(p, _multi);
   1056 
   1057 	return true;
   1058 }
   1059 
   1060 bool Microcontroller::loadMultiSingle(uint8_t _part)
   1061 {
   1062 	return loadMultiSingle(_part, m_multiEditBuffer);
   1063 }
   1064 
   1065 bool Microcontroller::loadMultiSingle(uint8_t _part, const TPreset& _multi)
   1066 {
   1067 	const auto partBank = _multi[MD_PART_BANK_NUMBER + _part];
   1068 	const auto partSingle = _multi[MD_PART_PROGRAM_NUMBER + _part];
   1069 
   1070 	partBankSelect(_part, partBank, false);
   1071 	return partProgramChange(_part, partSingle);
   1072 }
   1073 
   1074 void Microcontroller::process()
   1075 {
   1076 	m_hdi08.exec();
   1077 
   1078 	std::lock_guard lock(m_mutex);
   1079 
   1080 	if(m_loadingState || m_pendingPresetWrites.empty() || !m_hdi08.rxEmpty() || waitingForPresetReceiveConfirmation())
   1081 		return;
   1082 
   1083 	const auto preset = m_pendingPresetWrites.front();
   1084 	m_pendingPresetWrites.pop_front();
   1085 
   1086 	sendPreset(preset.program, preset.data, preset.isMulti);
   1087 }
   1088 
   1089 #if !SYNTHLIB_DEMO_MODE
   1090 bool Microcontroller::getState(std::vector<unsigned char>& _state, const StateType _type)
   1091 {
   1092 	const auto deviceId = static_cast<uint8_t>(m_globalSettings[DEVICE_ID]);
   1093 
   1094 	std::vector<SMidiEvent> responses;
   1095 
   1096 	if(_type == StateTypeGlobal)
   1097 		sendSysex({M_STARTOFSYSEX, 0x00, 0x20, 0x33, 0x01, deviceId, REQUEST_TOTAL, M_ENDOFSYSEX}, responses, MidiEventSource::Internal);
   1098 
   1099 	sendSysex({M_STARTOFSYSEX, 0x00, 0x20, 0x33, 0x01, deviceId, REQUEST_ARRANGEMENT, M_ENDOFSYSEX}, responses, MidiEventSource::Internal);
   1100 
   1101 	if(responses.empty())
   1102 		return false;
   1103 
   1104 	for (const auto& response : responses)
   1105 	{
   1106 		assert(!response.sysex.empty());
   1107 		_state.insert(_state.end(), response.sysex.begin(), response.sysex.end());		
   1108 	}
   1109 
   1110 	return true;
   1111 }
   1112 
   1113 bool Microcontroller::setState(const std::vector<unsigned char>& _state, const StateType _type)
   1114 {
   1115 	std::vector<SMidiEvent> events;
   1116 
   1117 	for(size_t i=0; i<_state.size(); ++i)
   1118 	{
   1119 		if(_state[i] == 0xf0)
   1120 		{
   1121 			const auto begin = i;
   1122 
   1123 			for(++i; i<_state.size(); ++i)
   1124 			{
   1125 				if(_state[i] == 0xf7)
   1126 				{
   1127 					SMidiEvent ev(MidiEventSource::Internal);
   1128 					ev.sysex.resize(i + 1 - begin);
   1129 					memcpy(ev.sysex.data(), &_state[begin], ev.sysex.size());
   1130 					events.emplace_back(ev);
   1131 					break;
   1132 				}
   1133 			}
   1134 		}
   1135 	}
   1136 
   1137 	return setState(events);
   1138 }
   1139 
   1140 bool Microcontroller::setState(const std::vector<synthLib::SMidiEvent>& _events)
   1141 {
   1142 	if(_events.empty())
   1143 		return false;
   1144 
   1145 	// delay all preset loads until everything is loaded
   1146 	m_loadingState = true;
   1147 
   1148 	std::vector<SMidiEvent> unusedResponses;
   1149 
   1150 	for (const auto& event : _events)
   1151 	{
   1152 		if(!event.sysex.empty())
   1153 		{
   1154 			sendSysex(event.sysex, unusedResponses, MidiEventSource::Internal);
   1155 			unusedResponses.clear();
   1156 		}
   1157 		else
   1158 		{
   1159 			sendMIDI(event);
   1160 		}
   1161 	}
   1162 
   1163 	m_loadingState = false;
   1164 
   1165 	return true;
   1166 }
   1167 #endif
   1168 
   1169 void Microcontroller::addDSP(DspSingle& _dsp, bool _useEsaiBasedMidiTiming)
   1170 {
   1171 	m_hdi08.addHDI08(_dsp.getHDI08());
   1172 	m_hdi08TxParsers.emplace_back(*this);
   1173 	m_midiQueues.emplace_back(_dsp, m_hdi08.getQueue(m_hdi08.size()-1), _useEsaiBasedMidiTiming, m_rom.isTIFamily());
   1174 }
   1175 
   1176 void Microcontroller::processHdi08Tx(std::vector<synthLib::SMidiEvent>& _midiEvents)
   1177 {
   1178 	for(size_t i=0; i<m_hdi08.size(); ++i)
   1179 	{
   1180 		auto& hdi08 = m_hdi08.getHDI08(i);
   1181 		auto& parser = m_hdi08TxParsers[i];
   1182 
   1183 		while(hdi08.hasTX())
   1184 		{
   1185 			if(parser.append(hdi08.readTX()))
   1186 			{
   1187 				const auto midi = parser.getMidiData();
   1188 				if(i == 0)
   1189 					_midiEvents.insert(_midiEvents.end(), midi.begin(), midi.end());
   1190 				parser.clearMidiData();
   1191 			}
   1192 		}
   1193 	}
   1194 }
   1195 
   1196 void Microcontroller::readMidiOut(std::vector<synthLib::SMidiEvent>& _midiOut)
   1197 {
   1198 	std::lock_guard lock(m_mutex);
   1199 	processHdi08Tx(_midiOut);
   1200 
   1201 	if (!m_pendingSysexInput.empty())
   1202 	{
   1203 		uint32_t eraseCount = 0;
   1204 
   1205 		for (const auto& input : m_pendingSysexInput)
   1206 		{
   1207 			if(!m_pendingPresetWrites.empty() || waitingForPresetReceiveConfirmation())
   1208 				break;
   1209 
   1210 			sendSysex(input.second, _midiOut, input.first);
   1211 			++eraseCount;
   1212 		}
   1213 
   1214 		if(eraseCount == m_pendingSysexInput.size())
   1215 			m_pendingSysexInput.clear();
   1216 		else if(eraseCount > 0)
   1217 			m_pendingSysexInput.erase(m_pendingSysexInput.begin(), m_pendingSysexInput.begin() + eraseCount);
   1218 	}
   1219 
   1220 	if(!m_midiOutput.empty())
   1221 	{
   1222 		_midiOut.insert(_midiOut.end(), m_midiOutput.begin(), m_midiOutput.end());
   1223 		m_midiOutput.clear();
   1224 	}
   1225 }
   1226 
   1227 void Microcontroller::sendPendingMidiEvents(const uint32_t _maxOffset)
   1228 {
   1229 	for (auto& midiQueue : m_midiQueues)
   1230 		midiQueue.sendPendingMidiEvents(_maxOffset);
   1231 }
   1232 
   1233 PresetVersion Microcontroller::getPresetVersion(const TPreset& _preset)
   1234 {
   1235 	return getPresetVersion(_preset[0]);
   1236 }
   1237 
   1238 PresetVersion Microcontroller::getPresetVersion(const uint8_t v)
   1239 {
   1240 	if(v >= D2)		return D2;
   1241 	if(v >= D)		return D;
   1242 	if(v >= C)		return C;
   1243 	if(v >= B)		return B;
   1244 	return A;
   1245 }
   1246 
   1247 uint8_t Microcontroller::calcChecksum(const std::vector<uint8_t>& _data, const size_t _offset, const size_t _count/* = std::numeric_limits<size_t>::max()*/)
   1248 {
   1249 	uint8_t cs = 0;
   1250 
   1251 	for (size_t i = _offset; i < std::min(_data.size(), _count); ++i)
   1252 		cs += _data[i];
   1253 
   1254 	return cs & 0x7f;
   1255 }
   1256 
   1257 bool Microcontroller::dspHasBooted() const
   1258 {
   1259 	for (const auto &p : m_hdi08TxParsers)
   1260 	{
   1261 		if(!p.hasDspBooted())
   1262 			return false;
   1263 	}
   1264 	return true;
   1265 }
   1266 
   1267 void Microcontroller::applyToSingleEditBuffer(const Page _page, const uint8_t _part, const uint8_t _param, const uint8_t _value)
   1268 {
   1269 	if(_part == SINGLE)
   1270 		applyToSingleEditBuffer(m_singleEditBuffer, _page, _param, _value);
   1271 	else
   1272 		applyToSingleEditBuffer(m_singleEditBuffers[_part], _page, _param, _value);
   1273 }
   1274 
   1275 void Microcontroller::applyToSingleEditBuffer(TPreset& _single, const Page _page, const uint8_t _param, const uint8_t _value)
   1276 {
   1277 	constexpr uint32_t paramsPerPage = 128;
   1278 
   1279 	uint32_t offset;
   1280 	switch(_page)
   1281 	{
   1282 	case PAGE_A:
   1283 		// prevent that someone attempts to modify the preset version
   1284 		if(_param == 0)
   1285 			return;
   1286 		offset = 0;	break;
   1287 	case PAGE_B:	offset = 1;	break;
   1288 	case PAGE_6E:	offset = 2;	break;
   1289 	case PAGE_6F:	offset = 3;	break;
   1290 	default:
   1291 		return;
   1292 	}
   1293 
   1294 	offset *= paramsPerPage;
   1295 	offset += _param;
   1296 
   1297 	if(offset >= _single.size())
   1298 		return;
   1299 
   1300 	_single[offset] = _value;
   1301 }
   1302 
   1303 void Microcontroller::applyToMultiEditBuffer(const Page _page, const uint8_t _part, const uint8_t _param, const uint8_t _value)
   1304 {
   1305 	// multi edit buffer duplicates parameters of a single, apply them to the multi edit buffer
   1306 	switch (_page)
   1307 	{
   1308 	case PAGE_A:
   1309 		switch (_param)
   1310 		{
   1311 		case EFFECT_SEND:			m_multiEditBuffer[MD_PART_EFFECT_SEND + _part] = _value;		break;
   1312 		case DELAY_REVERB_MODE:		m_multiEditBuffer[MD_DELAY_MODE] = _value;						break;
   1313 		case DELAY_TIME:			m_multiEditBuffer[MD_DELAY_TIME] = _value;						break;
   1314 		case DELAY_FEEDBACK:		m_multiEditBuffer[MD_DELAY_FEEDBACK] = _value;					break;
   1315 		case DELAY_RATE:			m_multiEditBuffer[MD_DELAY_RATE] = _value;						break;
   1316 		case DELAY_DEPTH:			m_multiEditBuffer[MD_DELAY_DEPTH] = _value;						break;
   1317 		case DELAY_LFO_SHAPE:		m_multiEditBuffer[MD_DELAY_SHAPE] = _value;						break;
   1318 		case DELAY_COLOR:			m_multiEditBuffer[MD_DELAY_COLOR] = _value;						break;
   1319 		}
   1320 		break;
   1321 	case PAGE_B:
   1322 		if (_param == CLOCK_TEMPO)
   1323 		{
   1324 			m_multiEditBuffer[MD_CLOCK_TEMPO] = _value;
   1325 		}
   1326 		break;
   1327 	case PAGE_C:
   1328 		if (_param >= PART_MIDI_CHANNEL && _param <= PART_OUTPUT_SELECT)
   1329 		{
   1330 			const auto idx = MD_PART_MIDI_CHANNEL + ((_param-PART_MIDI_CHANNEL)*16) + _part;
   1331 			m_multiEditBuffer[idx] = _value;
   1332 		}
   1333 		break;
   1334 	}
   1335 }
   1336 
   1337 Page Microcontroller::globalSettingsPage() const
   1338 {
   1339 	return m_rom.isTIFamily() ? PAGE_D : PAGE_C;
   1340 }
   1341 
   1342 bool Microcontroller::isPageSupported(Page _page) const
   1343 {
   1344 	switch (_page)
   1345 	{
   1346 	case PAGE_6E:
   1347 	case PAGE_6F:
   1348 	case PAGE_D:
   1349 		return m_rom.isTIFamily();
   1350 	case PAGE_A:
   1351 	case PAGE_B:
   1352 	case PAGE_C:
   1353 		return true;
   1354 	default:
   1355 		return false;
   1356 	}
   1357 }
   1358 
   1359 bool Microcontroller::waitingForPresetReceiveConfirmation() const
   1360 {
   1361 	for (const auto& parser : m_hdi08TxParsers)
   1362 	{
   1363 		if(parser.waitingForPreset())
   1364 			return true;
   1365 	}
   1366 	return false;
   1367 }
   1368 
   1369 void Microcontroller::receiveUpgradedPreset()
   1370 {
   1371 	const auto waitingForPresetConfirmation = waitingForPresetReceiveConfirmation();
   1372 
   1373 	if(waitingForPresetConfirmation)
   1374 		return;
   1375 
   1376 	std::vector<uint8_t> upgradedPreset;
   1377 	m_hdi08TxParsers.front().getPresetData(upgradedPreset);
   1378 
   1379 	if(upgradedPreset.empty())
   1380 		return;
   1381 
   1382 	LOG("Replacing edit buffer for " << (m_sentPresetIsMulti ? "multi" : "single") << " program " << static_cast<int>(m_sentPresetProgram) << " with upgraded preset");
   1383 
   1384 	auto copyTo = [&upgradedPreset, this](TPreset& _preset)
   1385 	{
   1386 		std::copy(upgradedPreset.begin(), upgradedPreset.end(), _preset.begin());
   1387 	};
   1388 
   1389 	if(m_sentPresetIsMulti)
   1390 	{
   1391 		copyTo(m_multiEditBuffer);
   1392 	}
   1393 	else if(m_sentPresetProgram == SINGLE)
   1394 	{
   1395 		copyTo(m_singleEditBuffer);
   1396 	}
   1397 	else if(m_sentPresetProgram < m_singleEditBuffers.size())
   1398 	{
   1399 		copyTo(m_singleEditBuffers[m_sentPresetProgram]);
   1400 	}
   1401 }
   1402 
   1403 bool Microcontroller::isValid(const TPreset& _preset)
   1404 {
   1405 	return _preset[240] >= 32 && _preset[240] <= 127;
   1406 }
   1407 }