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

controller.cpp (17930B)


      1 #include "controller.h"
      2 
      3 #include <cassert>
      4 #include <fstream>
      5 
      6 #include "parameter.h"
      7 #include "processor.h"
      8 
      9 #include "dsp56kEmu/logging.h"
     10 
     11 #include "juceUiLib/messageBox.h"
     12 
     13 #include "synthLib/os.h"
     14 
     15 namespace pluginLib
     16 {
     17 	uint8_t getParameterValue(const Parameter* _p)
     18 	{
     19 		return static_cast<uint8_t>(_p->getUnnormalizedValue());
     20 	}
     21 
     22 	Controller::Controller(Processor& _processor, const std::string& _parameterDescJsonFilename)
     23 		: m_processor(_processor)
     24 		, m_descriptions(loadParameterDescriptions(_parameterDescJsonFilename))
     25 		, m_locking(*this)
     26 		, m_parameterLinks(*this)
     27 	{
     28 		if(!m_descriptions.isValid())
     29 		{
     30 			genericUI::MessageBox::showOk(juce::MessageBoxIconType::WarningIcon, 
     31 				_processor.getProperties().name + " - Failed to parse Parameter Descriptions json", 
     32 				"Encountered errors while parsing parameter descriptions:\n\n" + m_descriptions.getErrors());
     33 		}
     34 	}
     35 
     36 	Controller::~Controller()
     37 	{
     38 		stopTimer();
     39 		m_softKnobs.clear();
     40 	}
     41 
     42 	void Controller::registerParams(juce::AudioProcessor& _processor, Parameter::PartFormatter _partFormatter/* = nullptr*/)
     43     {
     44 		auto globalParams = std::make_unique<juce::AudioProcessorParameterGroup>("global", "Global", "|");
     45 
     46 		if(!_partFormatter)
     47 		{
     48 			_partFormatter = [](const uint8_t& _part, bool)
     49 			{
     50 				return juce::String("Ch ") + juce::String(_part + 1);
     51 			};
     52 		}
     53 		std::map<ParamIndex, int> knownParameterIndices;
     54 
     55     	for (uint8_t part = 0; part < getPartCount(); part++)
     56 		{
     57 			m_paramsByParamType[part].reserve(m_descriptions.getDescriptions().size());
     58 
     59     		const auto partNumber = juce::String(part + 1);
     60 			auto group = std::make_unique<juce::AudioProcessorParameterGroup>("ch" + partNumber, _partFormatter(part, false), "|");
     61 
     62 			for (const auto& desc : m_descriptions.getDescriptions())
     63 			{
     64 				const ParamIndex idx = {static_cast<uint8_t>(desc.page), part, desc.index};
     65 
     66 				int uid = 0;
     67 
     68 				auto itKnownParamIdx = knownParameterIndices.find(idx);
     69 
     70 				if(itKnownParamIdx == knownParameterIndices.end())
     71 					knownParameterIndices.insert(std::make_pair(idx, 0));
     72 				else
     73 					uid = ++itKnownParamIdx->second;
     74 
     75 				std::unique_ptr<Parameter> p;
     76 				p.reset(createParameter(*this, desc, part, uid, _partFormatter));
     77 
     78 				if(uid > 0)
     79 				{
     80 					const auto& existingParams = findSynthParam(idx);
     81 
     82 					for (auto& existingParam : existingParams)
     83 					{
     84 						if(isDerivedParameter(*existingParam, *p))
     85 							existingParam->addDerivedParameter(p.get());
     86 					}
     87 				}
     88 
     89 				const bool isNonPartExclusive = desc.isNonPartSensitive();
     90 
     91 				if (isNonPartExclusive && part != 0)
     92 				{
     93 					// only register on first part!
     94 					m_paramsByParamType[part].push_back(m_paramsByParamType[0][m_paramsByParamType[part].size()]);
     95 					continue;
     96 				}
     97 
     98 				m_paramsByParamType[part].push_back(p.get());
     99 
    100 				if (p->getDescription().isPublic)
    101 				{
    102 					// lifecycle managed by Juce
    103 
    104 					auto itExisting = m_synthParams.find(idx);
    105 					if (itExisting != m_synthParams.end())
    106 					{
    107 						itExisting->second.push_back(p.get());
    108 					}
    109 					else
    110 					{
    111 						ParameterList params;
    112 						params.emplace_back(p.get());
    113 						m_synthParams.insert(std::make_pair(idx, std::move(params)));
    114 					}
    115 
    116 					if (isNonPartExclusive)
    117 					{
    118 						jassert(part == 0);
    119 						globalParams->addChild(std::move(p));
    120 					}
    121 					else
    122 						group->addChild(std::move(p));
    123 				}
    124 				else
    125 				{
    126 					// lifecycle handled by us
    127 
    128 					auto itExisting = m_synthInternalParams.find(idx);
    129 					if (itExisting != m_synthInternalParams.end())
    130 					{
    131 						itExisting->second.push_back(p.get());
    132 					}
    133 					else
    134 					{
    135 						ParameterList params;
    136 						params.emplace_back(p.get());
    137 						m_synthInternalParams.insert(std::make_pair(idx, std::move(params)));
    138 					}
    139 					m_synthInternalParamList.emplace_back(std::move(p));
    140 				}
    141 			}
    142 			_processor.addParameterGroup(std::move(group));
    143 		}
    144 
    145 		_processor.addParameterGroup(std::move(globalParams));
    146 
    147 		// initialize all soft knobs for all parts
    148 		std::vector<size_t> softKnobs;
    149 
    150 		for (size_t i=0; i<m_descriptions.getDescriptions().size(); ++i)
    151 		{
    152 			const auto& desc = m_descriptions.getDescriptions()[i];
    153 			if(!desc.isSoftKnob())
    154 				continue;
    155 			softKnobs.push_back(i);
    156 		}
    157 
    158 		for(size_t part = 0; part<getPartCount(); ++part)
    159 		{
    160 			for (const auto& softKnobParam : softKnobs)
    161 			{
    162 				auto* sk = new SoftKnob(*this, static_cast<uint8_t>(part), static_cast<uint32_t>(softKnobParam));
    163 				m_softKnobs.insert({sk->getParameter(), std::unique_ptr<SoftKnob>(sk)});
    164 			}
    165 		}
    166     }
    167 
    168 	void Controller::sendSysEx(const pluginLib::SysEx& _msg) const
    169     {
    170         synthLib::SMidiEvent ev(synthLib::MidiEventSource::Editor);
    171         ev.sysex = _msg;
    172 		sendMidiEvent(ev);
    173     }
    174 
    175 	void Controller::sendMidiEvent(const synthLib::SMidiEvent& _ev) const
    176     {
    177         m_processor.addMidiEvent(_ev);
    178     }
    179 
    180 	void Controller::sendMidiEvent(const uint8_t _a, const uint8_t _b, const uint8_t _c, const uint32_t _offset/* = 0*/, const synthLib::MidiEventSource _source/* = synthLib::MidiEventSource::Editor*/) const
    181 	{
    182         m_processor.addMidiEvent(synthLib::SMidiEvent(_source, _a, _b, _c, _offset));
    183 	}
    184 
    185 	bool Controller::combineParameterChange(uint8_t& _result, const std::string& _midiPacket, const Parameter& _parameter, ParamValue _value) const
    186 	{
    187 		const auto &desc = _parameter.getDescription();
    188 
    189 		std::map<MidiDataType, uint8_t> data;
    190 
    191 		const auto *packet = getMidiPacket(_midiPacket);
    192 
    193 		if (!packet)
    194 		{
    195 			LOG("Failed to find midi packet " << _midiPacket);
    196 			return false;
    197 		}
    198 
    199 		const ParamIndex idx = {static_cast<uint8_t>(desc.page), _parameter.getPart(), desc.index};
    200 
    201 		const auto params = findSynthParam(idx);
    202 
    203 		uint32_t byte = MidiPacket::InvalidIndex;
    204 
    205 		for (auto param : params)
    206 		{
    207 			byte = packet->getByteIndexForParameterName(param->getDescription().name);
    208 			if (byte != MidiPacket::InvalidIndex)
    209 				break;
    210 		}
    211 
    212 		if (byte == MidiPacket::InvalidIndex)
    213 		{
    214 			LOG("Failed to find byte index for parameter " << desc.name);
    215 			return false;
    216 		}
    217 
    218 		std::vector<const MidiPacket::MidiDataDefinition*> definitions;
    219 
    220 		if(!packet->getDefinitionsForByteIndex(definitions, byte))
    221 			return false;
    222 
    223 		if (definitions.size() == 1)
    224 		{
    225 			_result = static_cast<uint8_t>(_value);
    226 			return true;
    227 		}
    228 
    229 		_result = 0;
    230 
    231 	    for (const auto& it : definitions)
    232 	    {
    233 			uint32_t i = 0;
    234 
    235 	    	if(!m_descriptions.getIndexByName(i, it->paramName))
    236 			{
    237 				LOG("Failed to find index for parameter " << it->paramName);
    238 				return false;
    239 			}
    240 
    241 			auto* p = getParameter(i, _parameter.getPart());
    242 			const auto v = p == &_parameter ? _value : getParameterValue(p);
    243 			_result |= it->packValue(v);
    244 	    }
    245 
    246 		return true;
    247 	}
    248 
    249 	void Controller::applyPatchParameters(const MidiPacket::ParamValues& _params, const uint8_t _part) const
    250 	{
    251 		for (const auto& it : _params)
    252 		{
    253 			auto* p = getParameter(it.first.second, _part);
    254 			p->setValueFromSynth(it.second, pluginLib::Parameter::Origin::PresetChange);
    255 
    256 			for (const auto& derivedParam : p->getDerivedParameters())
    257 				derivedParam->setValueFromSynth(it.second, pluginLib::Parameter::Origin::PresetChange);
    258 		}
    259 
    260 		getProcessor().updateHostDisplay(juce::AudioProcessorListener::ChangeDetails().withProgramChanged(true));
    261 	}
    262 
    263 	void Controller::timerCallback()
    264 	{
    265 		processMidiMessages();
    266 	}
    267 
    268 	bool Controller::sendSysEx(const std::string& _packetName) const
    269     {
    270         return sendSysEx(_packetName, {});
    271     }
    272 
    273     bool Controller::sendSysEx(const std::string& _packetName, const std::map<MidiDataType, uint8_t>& _params) const
    274     {
    275 	    std::vector<uint8_t> sysex;
    276 
    277     	if(!createMidiDataFromPacket(sysex, _packetName, _params, 0))
    278             return false;
    279 
    280         sendSysEx(sysex);
    281         return true;
    282     }
    283 
    284 	const Controller::ParameterList& Controller::findSynthParam(const uint8_t _part, const uint8_t _page, const uint8_t _paramIndex) const
    285 	{
    286 		const ParamIndex paramIndex{ _page, _part, _paramIndex };
    287 
    288 		return findSynthParam(paramIndex);
    289 	}
    290 
    291 	const Controller::ParameterList& Controller::findSynthParam(const ParamIndex& _paramIndex) const
    292     {
    293 		const auto it = m_synthParams.find(_paramIndex);
    294 
    295 		if (it != m_synthParams.end())
    296 			return it->second;
    297 
    298     	const auto iti = m_synthInternalParams.find(_paramIndex);
    299 
    300 		if (iti == m_synthInternalParams.end())
    301 		{
    302 			static ParameterList empty;
    303 			return empty;
    304 		}
    305 
    306 		return iti->second;
    307     }
    308 
    309 	void Controller::sendLockedParameters(const uint8_t _part)
    310 	{
    311         const auto lockedParameters = m_locking.getLockedParameters(_part);
    312 
    313         for (const auto& p : lockedParameters)
    314         {
    315 	        const auto v = p->getUnnormalizedValue();
    316 	        sendParameterChange(*p, static_cast<uint8_t>(v));
    317         }
    318 	}
    319 
    320     juce::Value* Controller::getParamValueObject(const uint32_t _index, const uint8_t _part) const
    321     {
    322 	    const auto res = getParameter(_index, _part);
    323 		return res ? &res->getValueObject() : nullptr;
    324     }
    325 
    326     Parameter* Controller::getParameter(const uint32_t _index) const
    327     {
    328 		return getParameter(_index, 0);
    329 	}
    330 
    331 	Parameter* Controller::getParameter(const uint32_t _index, const uint8_t _part) const
    332 	{
    333 		if (_part >= m_paramsByParamType.size())
    334 			return nullptr;
    335 
    336 		if (_index >= m_paramsByParamType[_part].size())
    337 			return nullptr;
    338 
    339 		return m_paramsByParamType[_part][_index];
    340 	}
    341 
    342 	Parameter* Controller::getParameter(const std::string& _name, const uint8_t _part) const
    343 	{
    344 		const auto idx = getParameterIndexByName(_name);
    345 		if(idx == InvalidParameterIndex)
    346 			return nullptr;
    347 		return getParameter(idx, _part);
    348 	}
    349 
    350 	uint32_t Controller::getParameterIndexByName(const std::string& _name) const
    351 	{
    352 		uint32_t index;
    353 		return m_descriptions.getIndexByName(index, _name) ? index : InvalidParameterIndex;
    354 	}
    355 
    356 	bool Controller::setParameters(const std::map<std::string, ParamValue>& _values, const uint8_t _part, const Parameter::Origin _changedBy) const
    357 	{
    358 		bool res = false;
    359 
    360 		for (const auto& it : _values)
    361 		{
    362 			const auto& name = it.first;
    363 			const auto& value = it.second;
    364 
    365 			if(auto* param = getParameter(name, _part))
    366 			{
    367 				res = true;
    368 				param->setUnnormalizedValueNotifyingHost(value, _changedBy);
    369 			}
    370 		}
    371 
    372 		return res;
    373 	}
    374 
    375 	const MidiPacket* Controller::getMidiPacket(const std::string& _name) const
    376 	{
    377 		return m_descriptions.getMidiPacket(_name);
    378 	}
    379 
    380 	bool Controller::createNamedParamValues(MidiPacket::NamedParamValues& _params, const std::string& _packetName, const uint8_t _part) const
    381 	{
    382         const auto* m = getMidiPacket(_packetName);
    383 		assert(m && "midi packet not found");
    384         if(!m)
    385             return false;
    386 
    387         MidiPacket::ParamIndices indices;
    388 		m->getParameterIndices(indices, m_descriptions);
    389 
    390 		if(indices.empty())
    391 			return true;
    392 
    393 		for (const auto& index : indices)
    394         {
    395 			auto* p = getParameter(index.second, _part);
    396 			if(!p)
    397 				return false;
    398 			const auto* largestP = p;
    399 			// we might have more than 1 parameter per index, use the one with the largest range
    400 			const auto& derived = p->getDerivedParameters();
    401 			for (const auto& parameter : derived)
    402 	        {
    403 				if(parameter->getDescription().range.getLength() > p->getDescription().range.getLength())
    404 					largestP = parameter;
    405 	        }
    406 	        const auto v = getParameterValue(largestP);
    407 	        _params.insert(std::make_pair(std::make_pair(index.first, p->getDescription().name), v));
    408         }
    409 
    410 		return true;
    411 	}
    412 
    413 	bool Controller::createNamedParamValues(MidiPacket::NamedParamValues& _dest, const MidiPacket::AnyPartParamValues& _source) const
    414 	{
    415         for(uint32_t i=0; i<_source.size(); ++i)
    416         {
    417             const auto& v = _source[i];
    418             if(!v)
    419                 continue;
    420             const auto* p = getParameter(i);
    421             assert(p);
    422             if(!p)
    423                 return false;
    424             const auto key = std::make_pair(MidiPacket::AnyPart, p->getDescription().name);
    425             _dest.insert(std::make_pair(key, *v));
    426         }
    427 		return true;
    428 	}
    429 
    430 	bool Controller::createMidiDataFromPacket(std::vector<uint8_t>& _sysex, const std::string& _packetName, const std::map<MidiDataType, uint8_t>& _data, uint8_t _part) const
    431 	{
    432 		MidiPacket::NamedParamValues paramValues;
    433 
    434 		if(!createNamedParamValues(paramValues, _packetName, _part))
    435 			return false;
    436 
    437 		return createMidiDataFromPacket(_sysex, _packetName, _data, paramValues);
    438 	}
    439 
    440 	bool Controller::createMidiDataFromPacket(std::vector<uint8_t>& _sysex, const std::string& _packetName, const std::map<MidiDataType, uint8_t>& _data, const MidiPacket::NamedParamValues& _values) const
    441 	{
    442         const auto* m = getMidiPacket(_packetName);
    443 
    444 		if(!m->create(_sysex, _data, _values))
    445         {
    446 	        assert(false && "failed to create midi packet");
    447 	        _sysex.clear();
    448 	        return false;
    449         }
    450         return true;
    451 	}
    452 
    453 	bool Controller::createMidiDataFromPacket(std::vector<uint8_t>& _sysex, const std::string& _packetName, const std::map<MidiDataType, uint8_t>& _data, const MidiPacket::AnyPartParamValues& _values) const
    454 	{
    455 		MidiPacket::NamedParamValues namedParams;
    456 		if(!createNamedParamValues(namedParams, _values))
    457 			return false;
    458 		return createMidiDataFromPacket(_sysex, _packetName, _data, namedParams);
    459 	}
    460 
    461 	bool Controller::parseMidiPacket(const MidiPacket& _packet, MidiPacket::Data& _data, MidiPacket::ParamValues& _parameterValues, const std::vector<uint8_t>& _src) const
    462 	{
    463 		_data.clear();
    464 		_parameterValues.clear();
    465 		return _packet.parse(_data, _parameterValues, m_descriptions, _src);
    466 	}
    467 
    468 	bool Controller::parseMidiPacket(const MidiPacket& _packet, MidiPacket::Data& _data, MidiPacket::AnyPartParamValues& _parameterValues, const std::vector<uint8_t>& _src) const
    469 	{
    470 		_data.clear();
    471 		_parameterValues.clear();
    472 		return _packet.parse(_data, _parameterValues, m_descriptions, _src);
    473 	}
    474 
    475 	bool Controller::parseMidiPacket(const MidiPacket& _packet, MidiPacket::Data& _data, const std::function<void(MidiPacket::ParamIndex, ParamValue)>& _parameterValues, const std::vector<uint8_t>& _src) const
    476 	{
    477 		_data.clear();
    478 		return _packet.parse(_data, _parameterValues, m_descriptions, _src);
    479 	}
    480 
    481 	bool Controller::parseMidiPacket(const std::string& _name, MidiPacket::Data& _data, MidiPacket::ParamValues& _parameterValues, const std::vector<uint8_t>& _src) const
    482 	{
    483 		auto* m = getMidiPacket(_name);
    484 		assert(m);
    485 		if(!m)
    486 			return false;
    487 		return parseMidiPacket(*m, _data, _parameterValues, _src);
    488 	}
    489 
    490 	bool Controller::parseMidiPacket(std::string& _name, MidiPacket::Data& _data, MidiPacket::ParamValues& _parameterValues, const std::vector<uint8_t>& _src) const
    491 	{
    492 		const auto& packets = m_descriptions.getMidiPackets();
    493 
    494 		for (const auto& packet : packets)
    495 		{
    496 			if(!parseMidiPacket(packet.second, _data, _parameterValues, _src))
    497 				continue;
    498 
    499 			_name = packet.first;
    500 			return true;
    501 		}
    502 		return false;
    503 	}
    504 
    505 	bool Controller::setCurrentPart(const uint8_t _part)
    506 	{
    507 		if(_part == m_currentPart)
    508 			return false;
    509 		m_currentPart = _part;
    510 		onCurrentPartChanged(m_currentPart);
    511 		return true;
    512 	}
    513 
    514 	bool Controller::parseMidiMessage(const synthLib::SMidiEvent& _e)
    515 	{
    516 		if(_e.sysex.empty())
    517 			return parseControllerMessage(_e);
    518 		return parseSysexMessage(_e.sysex, _e.source);
    519 	}
    520 
    521 	void Controller::enqueueMidiMessages(const std::vector<synthLib::SMidiEvent>& _events)
    522 	{
    523 		if(_events.empty())
    524 			return;
    525 
    526         const std::lock_guard l(m_midiMessagesLock);
    527         m_midiMessages.insert(m_midiMessages.end(), _events.begin(), _events.end());
    528 		if(!isTimerRunning())
    529 			startTimer(1);
    530 	}
    531 
    532 	void Controller::loadChunkData(baseLib::ChunkReader& _cr)
    533 	{
    534 		m_parameterLinks.loadChunkData(_cr);
    535 	}
    536 
    537 	void Controller::saveChunkData(baseLib::BinaryStream& _s) const
    538 	{
    539 		m_parameterLinks.saveChunkData(_s);
    540 	}
    541 
    542 	Parameter::Origin Controller::midiEventSourceToParameterOrigin(const synthLib::MidiEventSource _source)
    543 	{
    544 		switch (_source)
    545 		{
    546 		case synthLib::MidiEventSource::Unknown:
    547 			return Parameter::Origin::Unknown;
    548 		case synthLib::MidiEventSource::Editor:
    549 			return Parameter::Origin::Ui;
    550 		case synthLib::MidiEventSource::Host:
    551 			return Parameter::Origin::HostAutomation;
    552 		case synthLib::MidiEventSource::PhysicalInput:
    553 		case synthLib::MidiEventSource::Plugin:
    554 			return Parameter::Origin::Midi;
    555 		case synthLib::MidiEventSource::Internal:
    556 			return Parameter::Origin::Unknown;
    557 		default:
    558 			assert(false && "implement new midi event source type");
    559 			return Parameter::Origin::Unknown;
    560 		}
    561 	}
    562 
    563 	void Controller::getMidiMessages(std::vector<synthLib::SMidiEvent>& _events)
    564 	{
    565 		const std::lock_guard l(m_midiMessagesLock);
    566         std::swap(m_midiMessages, _events);
    567 		m_midiMessages.clear();
    568 		stopTimer();
    569 	}
    570 
    571 	void Controller::processMidiMessages()
    572 	{
    573 	    std::vector<synthLib::SMidiEvent> events;
    574 	    getMidiMessages(events);
    575 
    576 	    for (const auto& e : events)
    577 		    parseMidiMessage(e);
    578 	}
    579 
    580 	std::string Controller::loadParameterDescriptions(const std::string& _filename) const
    581 	{
    582 	    const auto path = synthLib::getModulePath() + _filename;
    583 
    584 	    const std::ifstream f(path.c_str(), std::ios::in);
    585 	    if(f.is_open())
    586 	    {
    587 			std::stringstream buf;
    588 			buf << f.rdbuf();
    589 	        return buf.str();
    590 	    }
    591 
    592 	    const auto res = m_processor.findResource(_filename);
    593 	    if(res)
    594 	        return {res->first, res->second};
    595 	    return {};
    596 	}
    597 
    598 	std::set<std::string> Controller::getRegionIdsForParameter(const Parameter* _param) const
    599 	{
    600 		if(!_param)
    601 			return {};
    602 		return getRegionIdsForParameter(_param->getDescription().name);
    603 	}
    604 
    605 	std::set<std::string> Controller::getRegionIdsForParameter(const std::string& _name) const
    606 	{
    607 		const auto& regions = getParameterDescriptions().getRegions();
    608 
    609 		std::set<std::string> result;
    610 		for (const auto& region : regions)
    611 		{
    612 			if(region.second.containsParameter(_name))
    613 				result.insert(region.first);
    614 		}
    615 		return result;
    616 	}
    617 
    618 	Parameter* Controller::createParameter(Controller& _controller, const Description& _desc, const uint8_t _part, const int _uid, const Parameter::PartFormatter& _partFormatter)
    619 	{
    620 		return new Parameter(_controller, _desc, _part, _uid, _partFormatter);
    621 	}
    622 }