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 }