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

parameterbinding.cpp (13045B)


      1 #include "parameterbinding.h"
      2 
      3 #include <cassert>
      4 
      5 #include "parameter.h"
      6 #include "controller.h"
      7 
      8 namespace pluginLib
      9 {
     10 	ParameterBinding::MouseListener::MouseListener(Parameter* _param, juce::Slider& _slider)
     11 	: m_param(_param), m_slider(&_slider)
     12 	{
     13 	}
     14 
     15 	void ParameterBinding::MouseListener::mouseDown(const juce::MouseEvent& event)
     16 	{
     17 		m_param->pushChangeGesture();
     18 	}
     19 
     20 	void ParameterBinding::MouseListener::mouseUp(const juce::MouseEvent& event)
     21 	{
     22 		m_param->popChangeGesture();
     23 	}
     24 
     25 	void ParameterBinding::MouseListener::mouseDrag(const juce::MouseEvent& event)
     26 	{
     27 		m_param->setUnnormalizedValueNotifyingHost(static_cast<float>(m_slider->getValue()), Parameter::Origin::Ui);
     28 	}
     29 
     30 	void ParameterBinding::MouseListener::mouseWheelMove(const juce::MouseEvent& event, const juce::MouseWheelDetails& wheel)
     31 	{
     32 		m_param->setUnnormalizedValueNotifyingHost(static_cast<float>(m_slider->getValue()), Parameter::Origin::Ui);
     33 	}
     34 
     35 	void ParameterBinding::MouseListener::mouseDoubleClick(const juce::MouseEvent& event)
     36 	{
     37 		m_param->setValueNotifyingHost(m_param->getDefaultValue(), Parameter::Origin::Ui);
     38 	}
     39 
     40 	ParameterBinding::~ParameterBinding()
     41 	{
     42 		clearBindings();
     43 	}
     44 
     45 	void ParameterBinding::bind(juce::Slider &_slider, uint32_t _param)
     46 	{
     47 		bind(_slider, _param, CurrentPart);
     48 	}
     49 	void ParameterBinding::bind(juce::Slider &_slider, uint32_t _param, const uint8_t _part)
     50 	{
     51 		const auto param = m_controller.getParameter(_param, _part == CurrentPart ? m_controller.getCurrentPart() : _part);
     52 
     53 		if (!param)
     54 		{
     55 			assert(false && "Failed to find parameter");
     56 			return;
     57 		}
     58 
     59 		removeMouseListener(_slider);
     60 
     61 		auto* listener = new MouseListener(param, _slider);
     62 		m_sliderMouseListeners.insert(std::make_pair(&_slider, listener));
     63 
     64 		_slider.addMouseListener(listener, false);
     65 
     66 		const auto& range = param->getNormalisableRange();
     67 
     68 		_slider.setRange(range.start, range.end, range.interval);
     69 		_slider.setDoubleClickReturnValue(true, param->convertFrom0to1(param->getDefaultValue()));
     70 		_slider.getValueObject().referTo(param->getValueObject());
     71 		_slider.getProperties().set("type", "slider");
     72 		_slider.getProperties().set("name", juce::String(param->getDescription().name));
     73 
     74 		if (param->isBipolar()) 
     75 			_slider.getProperties().set("bipolar", true);
     76 
     77 		// Juce bug: If the range changes but the value doesn't, Juce doesn't issue a repaint. Do it manually
     78 		_slider.repaint();
     79 
     80 		BoundParameter p{param, &_slider, _param, _part};
     81 
     82 		if(param->getDescription().isSoftKnob())
     83 		{
     84 			auto* softknob = m_controller.getSoftknob(param);
     85 
     86 			if(softknob)
     87 			{
     88 				p.softknobTargetChangedListenerId = softknob->onBind.addListener([&_slider](Parameter* const& _parameter)
     89 				{
     90 					if(!_parameter)
     91 						return;
     92 					const auto& range = _parameter->getNormalisableRange();
     93 					_slider.setRange(range.start, range.end, range.interval);
     94 				});
     95 
     96 				if(const auto* target = softknob->getTargetParameter())
     97 				{
     98 					const auto& r = target->getNormalisableRange();
     99 					_slider.setRange(r.start, r.end, r.interval);
    100 				}
    101 			}
    102 		}
    103 
    104 		addBinding(p);
    105 	}
    106 
    107 	void ParameterBinding::bind(juce::ComboBox& _combo, uint32_t _param)
    108 	{
    109 		bind(_combo, _param, CurrentPart);
    110 	}
    111 
    112 	void ParameterBinding::bind(juce::ComboBox& _combo, const uint32_t _param, uint8_t _part)
    113 	{
    114 		const auto v = m_controller.getParameter(_param, _part == CurrentPart ? m_controller.getCurrentPart() : _part);
    115 
    116 		if (!v)
    117 		{
    118 			assert(false && "Failed to find parameter");
    119 			return;
    120 		}
    121 
    122 		_combo.setTextWhenNothingSelected("-");
    123 		_combo.setScrollWheelEnabled(true);
    124 
    125 		_combo.onChange = nullptr;
    126 		_combo.clear(juce::dontSendNotification);
    127 
    128 		using Entry = std::pair<uint8_t, std::string>;
    129 
    130 		std::vector<Entry> sortedValues;
    131 
    132 		const auto& desc = v->getDescription();
    133 		const auto& valueList = desc.valueList;
    134 		const auto& range = desc.range;
    135 
    136 		if(valueList.order.empty())
    137 		{
    138 			uint8_t i = 0;
    139 			const auto& allValues = v->getAllValueStrings();
    140 			for (const auto& vs : allValues)
    141 			{
    142 				if(vs.isNotEmpty() && i >= range.getStart() && i <= range.getEnd())
    143 					sortedValues.emplace_back(i, vs.toStdString());
    144 				++i;
    145 			}
    146 		}
    147 		else
    148 		{
    149 			for(uint32_t i=0; i<valueList.order.size(); ++i)
    150 			{
    151 				const auto value = valueList.orderToValue(i);
    152 				if(value == ValueList::InvalidValue)
    153 					continue;
    154 				if(value < range.getStart() || value > range.getEnd())
    155 					continue;
    156 				const auto text = valueList.valueToText(value);
    157 				if(text.empty())
    158 					continue;
    159 				sortedValues.emplace_back(value, text);
    160 			}
    161 		}
    162 	
    163 		uint32_t count = 0;
    164 
    165 		// we want our long menus to be split into columns of 16 rows each
    166 		// but only if we have more entries than one and a half such column
    167 		const uint32_t splitAt = (sortedValues.size() > 24) ? 16 : 0;
    168 
    169 		for (const auto &vs : sortedValues)
    170 		{
    171 			_combo.addItem(vs.second, vs.first + 1);
    172 
    173 			if (++count == splitAt)
    174 			{
    175 				_combo.getRootMenu()->addColumnBreak();
    176 				count = 0;
    177 			}
    178 		}
    179 
    180 		_combo.setSelectedId(static_cast<int>(v->getValueObject().getValueSource().getValue()) + 1, juce::dontSendNotification);
    181 
    182 		_combo.onChange = [this, &_combo, v]()
    183 		{
    184 			const auto id = _combo.getSelectedId();
    185 
    186 			if(id == 0)
    187 				return;
    188 
    189 			const int current = v->getValueObject().getValueSource().getValue();
    190 
    191 			if((id - 1) == current)
    192 				return;
    193 
    194 			if(v->getDescription().isPublic)
    195 			{
    196 				v->setUnnormalizedValueNotifyingHost(id - 1, Parameter::Origin::Ui);
    197 			}
    198 			else
    199 			{
    200 				v->setUnnormalizedValue(id - 1, Parameter::Origin::Ui);
    201 			}
    202 		};
    203 
    204 		const auto listenerId = v->onValueChanged.addListener([this, &_combo](const pluginLib::Parameter* v)
    205 		{
    206 			const auto value = v->getUnnormalizedValue();
    207 			_combo.setSelectedId(value + 1, juce::dontSendNotification);
    208 		});
    209 
    210 		const BoundParameter p{v, &_combo, _param, _part, listenerId};
    211 		addBinding(p);
    212 	}
    213 
    214 	void ParameterBinding::bind(juce::Button &_btn, const uint32_t _param)
    215 	{
    216 		bind(_btn, _param, CurrentPart);
    217 	}
    218 
    219 	void ParameterBinding::bind(juce::Button& _control, const uint32_t _param, const uint8_t _part)
    220 	{
    221 		const auto param = m_controller.getParameter(_param, _part == CurrentPart ? m_controller.getCurrentPart() : _part);
    222 		if (!param)
    223 		{
    224 			assert(false && "Failed to find parameter");
    225 			return;
    226 		}
    227 
    228 		const bool hasCustomValue = _control.getProperties().contains("parametervalue");
    229 		int paramValue = 1;
    230 		int paramValueOff = -1;
    231 		if(hasCustomValue)
    232 			paramValue = _control.getProperties()["parametervalue"];
    233 		if(_control.getProperties().contains("parametervalueoff"))
    234 			paramValueOff = _control.getProperties()["parametervalueoff"];
    235 
    236 		_control.onClick = [&_control, param, paramValue, hasCustomValue, paramValueOff]
    237 		{
    238 			const auto on = _control.getToggleState();
    239 			if(hasCustomValue)
    240 			{
    241 				if(on)
    242 					param->setUnnormalizedValueNotifyingHost(paramValue, Parameter::Origin::Ui);
    243 				else if(paramValueOff != -1)
    244 					param->setUnnormalizedValueNotifyingHost(paramValueOff, Parameter::Origin::Ui);
    245 				else
    246 					_control.setToggleState(true, juce::dontSendNotification);
    247 			}
    248 			else
    249 			{
    250 				param->setUnnormalizedValueNotifyingHost(on ? 1 : 0, Parameter::Origin::Ui);
    251 			}
    252 		};
    253 
    254 		_control.setToggleState(param->getUnnormalizedValue() == paramValue, juce::dontSendNotification);
    255 
    256 		const auto listenerId = param->onValueChanged.addListener([this, &_control, paramValue](const Parameter* _p)
    257 		{
    258 			const auto value = _p->getUnnormalizedValue();
    259 			_control.setToggleState(value == paramValue, juce::dontSendNotification);
    260 		});
    261 
    262 		const BoundParameter p{param, &_control, _param, CurrentPart, listenerId};
    263 		addBinding(p);
    264 	}
    265 
    266 	void ParameterBinding::bind(juce::Label& _label, const uint32_t _param)
    267 	{
    268 		bind(_label, _param, CurrentPart);
    269 	}
    270 
    271 	void ParameterBinding::bind(juce::Label& _control, const uint32_t _param, const uint8_t _part)
    272 	{
    273 		const auto param = m_controller.getParameter(_param, _part == CurrentPart ? m_controller.getCurrentPart() : _part);
    274 		if (!param)
    275 		{
    276 			assert(false && "Failed to find parameter");
    277 			return;
    278 		}
    279 		const auto listenerId = param->onValueChanged.addListener([this, &_control](const Parameter* _p)
    280 		{
    281 			_control.setText(_p->getCurrentValueAsText(), juce::dontSendNotification);
    282 		});
    283 		_control.setText(param->getCurrentValueAsText(), juce::dontSendNotification);
    284 		const BoundParameter p{ param, &_control, _param, _part, listenerId};
    285 		addBinding(p);
    286 	}
    287 
    288 	bool ParameterBinding::bind(juce::Component& _component, const uint32_t _param, const uint8_t _part)
    289 	{
    290 		if(auto* slider = dynamic_cast<juce::Slider*>(&_component))
    291 		{
    292 			bind(*slider, _param, _part);
    293 			return true;
    294 		}
    295 		if(auto* button = dynamic_cast<juce::DrawableButton*>(&_component))
    296 		{
    297 			bind(*button, _param, _part);
    298 			return true;
    299 		}
    300 		if(auto* comboBox = dynamic_cast<juce::ComboBox*>(&_component))
    301 		{
    302 			bind(*comboBox, _param, _part);
    303 			return true;
    304 		}
    305 		if(auto* label = dynamic_cast<juce::Label*>(&_component))
    306 		{
    307 			bind(*label, _param, _part);
    308 			return true;
    309 		}
    310 		assert(false && "unknown component type");
    311 		return false;
    312 	}
    313 
    314 	juce::Component* ParameterBinding::getBoundComponent(const Parameter* _parameter) const
    315 	{
    316 		const auto it = m_boundParameters.find(_parameter);
    317 		if(it == m_boundParameters.end())
    318 			return nullptr;
    319 		return it->second;
    320 	}
    321 
    322 	Parameter* ParameterBinding::getBoundParameter(const juce::Component* _component) const
    323 	{
    324 		const auto it = m_boundComponents.find(_component);
    325 		if(it == m_boundComponents.end())
    326 			return nullptr;
    327 		return it->second;
    328 	}
    329 
    330 	bool ParameterBinding::unbind(const Parameter* _param)
    331 	{
    332 		for (auto it= m_bindings.begin(); it != m_bindings.end(); ++it)
    333 		{
    334 			if(it->parameter != _param)
    335 				continue;
    336 
    337 			m_bindings.erase(it);
    338 
    339 			return true;
    340 		}
    341 		return false;
    342 	}
    343 
    344 	bool ParameterBinding::unbind(const juce::Component* _component)
    345 	{
    346 		for (auto it= m_bindings.begin(); it != m_bindings.end(); ++it)
    347 		{
    348 			if(it->component != _component)
    349 				continue;
    350 
    351 			disableBinding(*it);
    352 
    353 			m_bindings.erase(it);
    354 
    355 			return true;
    356 		}
    357 		return false;
    358 	}
    359 
    360 	void ParameterBinding::removeMouseListener(juce::Slider& _slider)
    361 	{
    362 		const auto it = m_sliderMouseListeners.find(&_slider);
    363 
    364 		if(it != m_sliderMouseListeners.end())
    365 		{
    366 			_slider.removeMouseListener(it->second);
    367 			delete it->second;
    368 			m_sliderMouseListeners.erase(it);
    369 		}
    370 	}
    371 
    372 	void ParameterBinding::bind(const std::vector<BoundParameter>& _bindings, const bool _currentPartOnly)
    373 	{
    374 		for (const auto& b : _bindings)
    375 			bind(*b.component, b.paramIndex, b.part);
    376 	}
    377 
    378 	void ParameterBinding::addBinding(const BoundParameter& _boundParameter)
    379 	{
    380 		m_bindings.emplace_back(_boundParameter);
    381 
    382 		const auto paramIndex = _boundParameter.paramIndex;
    383 		_boundParameter.component->getProperties().set("parameter", static_cast<int>(paramIndex));
    384 		if(_boundParameter.part != CurrentPart)
    385 			_boundParameter.component->getProperties().set("part", _boundParameter.part);
    386 		else
    387 			_boundParameter.component->getProperties().remove("part");
    388 
    389 		m_boundComponents.erase(_boundParameter.component);
    390 		m_boundParameters.erase(_boundParameter.parameter);
    391 
    392 		m_boundParameters.insert(std::make_pair(_boundParameter.parameter, _boundParameter.component));
    393 		m_boundComponents.insert(std::make_pair(_boundParameter.component, _boundParameter.parameter));
    394 
    395 		onBind(_boundParameter);
    396 	}
    397 
    398 	void ParameterBinding::disableBinding(BoundParameter& _b)
    399 	{
    400 		onUnbind(_b);
    401 
    402 		m_boundParameters.erase(_b.parameter);
    403 		m_boundComponents.erase(_b.component);
    404 
    405 		auto* slider = dynamic_cast<juce::Slider*>(_b.component);
    406 
    407 		if(slider != nullptr)
    408 		{
    409 			removeMouseListener(*slider);
    410 			slider->getValueObject().referTo(juce::Value());
    411 		}
    412 
    413 		auto* combo = dynamic_cast<juce::ComboBox*>(_b.component);
    414 		if(combo != nullptr)
    415 			combo->onChange = nullptr;
    416 
    417 		auto* button = dynamic_cast<juce::Button*>(_b.component);
    418 		if(button != nullptr)
    419 			button->onClick = nullptr;
    420 
    421 		if(_b.onChangeListenerId != ParameterListener::InvalidListenerId)
    422 		{
    423 			_b.parameter->onValueChanged.removeListener(_b.onChangeListenerId);
    424 			_b.onChangeListenerId = ParameterListener::InvalidListenerId;
    425 		}
    426 
    427 		if(_b.softknobTargetChangedListenerId != ParameterListener::InvalidListenerId)
    428 		{
    429 			auto* softknob = m_controller.getSoftknob(_b.parameter);
    430 			assert(softknob);
    431 			softknob->onBind.removeListener(_b.softknobTargetChangedListenerId);
    432 			_b.softknobTargetChangedListenerId = ParameterListener::InvalidListenerId;
    433 		}
    434 	}
    435 
    436 	void ParameterBinding::clearBindings()
    437 	{
    438 		for (auto& b : m_bindings)
    439 			disableBinding(b);
    440 		clear();
    441 	}
    442 
    443 	void ParameterBinding::clear()
    444 	{
    445 		m_bindings.clear();
    446 		m_boundParameters.clear();
    447 		m_boundComponents.clear();
    448 	}
    449 
    450 	void ParameterBinding::setPart(uint8_t _part)
    451 	{
    452 		const std::vector<BoundParameter> bindings = m_bindings;
    453 
    454 		clearBindings();
    455 
    456 		m_controller.setCurrentPart(_part);
    457 
    458 		bind(bindings, true);
    459 	}
    460 
    461 	void ParameterBinding::disableBindings()
    462 	{
    463 		m_disabledBindings.clear();
    464 		std::swap(m_bindings, m_disabledBindings);
    465 
    466 		for (auto& b : m_disabledBindings)
    467 			disableBinding(b);
    468 	}
    469 
    470 	void ParameterBinding::enableBindings()
    471 	{
    472 		bind(m_disabledBindings, false);
    473 		m_disabledBindings.clear();
    474 	}
    475 }