Vital
Loading...
Searching...
No Matches
modulation_manager.cpp
Go to the documentation of this file.
2
3#include "bar_renderer.h"
4#include "skin.h"
6#include "modulation_matrix.h"
7#include "modulation_meter.h"
8#include "shaders.h"
10
11namespace {
12 constexpr float kDefaultModulationRatio = 0.25f;
13 constexpr float kModSourceMeterWidth = 0.0018f;
14 constexpr float kModSourceMeterBuffer = 0.002f;
15 constexpr float kModSourceMinRadius = 0.005f;
16 constexpr float kModSmoothDecay = 0.25f;
17
18 bool showingInParents(Component* component) {
19 if (component == nullptr || component->getParentComponent() == nullptr)
20 return true;
21
22 return component->isVisible() && showingInParents(component->getParentComponent());
23 }
24}
25
27 public:
29 num_sliders_(0), amount_quad_(Shaders::kRingFragment) {
31 setTriggeredOnMouseDown(true);
32 setMouseClickGrabsKeyboardFocus(false);
33 amount_quad_.setTargetComponent(this);
34 amount_quad_.setThickness(2.0f);
35 }
36
37 int getNumColumns(int num_sliders) {
38 float height_width_ratio = getHeight() * 1.0f / getWidth();
39
40 int columns = 1;
41 while (columns * (int)(height_width_ratio * columns) < num_sliders)
42 columns++;
43 return columns;
44 }
45
46 void setSliders(std::vector<ModulationAmountKnob*> sliders) {
47 sliders_ = sliders;
48 for (int i = 0; i < sliders.size(); ++i)
49 colors_[i] = sliders_[i]->findColour(Skin::kRotaryArc, true);
50 num_sliders_ = static_cast<int>(sliders_.size());
51 }
52
53 std::vector<ModulationAmountKnob*> getSliders() { return sliders_; }
54
55 void renderSliderQuads(OpenGlWrapper& open_gl, bool animate) {
56 int num_sliders = num_sliders_;
57
58 float width = getWidth();
59 float height = getHeight();
60
61 int columns = getNumColumns(num_sliders);
62 float cell_width = width / columns;
63 int rows = (num_sliders + columns - 1) / columns;
64 int y_offset = (height - (rows * cell_width)) / 2;
65 float gl_width = 2.0f * cell_width / width;
66 float gl_height = 2.0f * cell_width / height;
67
68 int row = 0;
69 int column = 0;
70 for (int i = 0; i < num_sliders; ++i) {
71 float x = column * cell_width;
72 float y = height - y_offset - (row + 1) * cell_width;
73
74 amount_quad_.setColor(colors_[i]);
75 amount_quad_.setAltColor(colors_[i].withMultipliedAlpha(0.5f));
76 amount_quad_.setQuad(0, 2.0f * x / width - 1.0f, 1.0f - 2.0f * y / height - gl_height, gl_width, gl_height);
77 amount_quad_.render(open_gl, animate);
78
79 column++;
80 if (column >= columns) {
81 row++;
82 column = 0;
83 }
84 }
85 }
86
87 private:
88 std::vector<ModulationAmountKnob*> sliders_;
89 int num_sliders_;
91
92 OpenGlQuad amount_quad_;
93
94 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ExpandModulationButton)
95};
96
97class ModulationDestination : public Component {
98 public:
99 ModulationDestination(SynthSlider* source) : destination_slider_(source), margin_(0), index_(0),
100 size_multiple_(1.0f),
101 active_(false), rectangle_(false), rotary_(true) {
102 setName(source->getName());
103 }
105
107
108 SynthSlider* getDestinationSlider() { return destination_slider_; }
109 void setActive(bool active) { active_ = active; }
110
111 void setSizeMultiple(float multiple) {
112 size_multiple_ = multiple;
113 repaint();
114 }
115
116 Rectangle<float> getFillBounds() {
117 static constexpr float kBufferPercent = 0.4f;
118
119 float width = getWidth();
120 float height = getHeight();
121
122 if (!rectangle_ && rotary_) {
123 float offset = destination_slider_->findValue(Skin::kKnobOffset);
124 float rotary_width = size_multiple_ * destination_slider_->findValue(Skin::kKnobModMeterArcSize);
125 float x = (width - rotary_width) / 2.0f;
126 float y = offset + (height - rotary_width) / 2.0f;
127 return Rectangle<float>(x, y, rotary_width, rotary_width);
128 }
129
130 if (rectangle_)
131 return getLocalBounds().toFloat();
132
133 if (destination_slider_->getSliderStyle() == Slider::LinearBar) {
134 float y = height * 0.5f * (1.0f - SynthSlider::kLinearWidthPercent);
135 float glow_height = height * SynthSlider::kLinearWidthPercent;
136 y -= 2.0f * glow_height * kBufferPercent;
137 glow_height += 4.0f * kBufferPercent * glow_height;
138
139 return Rectangle<float>(margin_, y, width - 2 * margin_, glow_height);
140 }
141
142 float x = width * 0.5f * (1.0f - SynthSlider::kLinearWidthPercent);
143 float glow_width = width * SynthSlider::kLinearWidthPercent;
144 x -= 2.0f * glow_width * kBufferPercent;
145 glow_width += 4.0f * kBufferPercent * glow_width;
146 return Rectangle<float>(x, margin_, glow_width, height - 2 * margin_);
147 }
148
149 void setRectangle(bool rectangle) { rectangle_ = rectangle; }
150 void setRotary(bool rotary) { rotary_ = rotary; }
151 void setMargin(int margin) { margin_ = margin; }
152 void setIndex(int index) { index_ = index; }
153
154 bool hasExtraModulationTarget() { return destination_slider_->getExtraModulationTarget() != nullptr; }
155 bool isRotary() { return !rectangle_ && rotary_; }
156 bool isActive() { return active_; }
157 int getIndex() { return index_; }
158
159 private:
160 Component* viewport_container_;
161 SynthSlider* destination_slider_;
162 int margin_;
163 int index_;
164 float size_multiple_;
165 bool active_;
166 bool rectangle_;
167 bool rotary_;
168
169 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ModulationDestination)
170};
171
173 color_component_(nullptr), index_(index) {
175 bypass_ = false;
176 stereo_ = false;
177 bipolar_ = false;
178 draw_background_ = false;
179 name_ = name;
180 editing_ = false;
181
183 setTextEntrySizePercent(2.0f, 1.0f);
184 setDoubleClickReturnValue(false, 0.0f);
185 setWantsKeyboardFocus(false);
186 showing_ = true;
187 hovering_ = false;
188 current_modulator_ = false;
189}
190
191void ModulationAmountKnob::mouseDown(const MouseEvent& e) {
192 if (e.mods.isMiddleButtonDown())
193 toggleBypass();
194
195 if (e.mods.isPopupMenu()) {
197
198 PopupItems options;
199 options.addItem(kDisconnect, "Remove");
200 options.addItem(kToggleBypass, bypass_ ? "Unbypass" : "Bypass");
201 options.addItem(kToggleBipolar, bipolar_ ? "Make Unipolar" : "Make Bipolar");
202 options.addItem(kToggleStereo, stereo_ ? "Make Mono" : "Make Stereo");
203 options.addItem(-1, "");
204
206 options.addItem(kArmMidiLearn, "Learn MIDI Assignment");
207
208 if (has_parameter_assignment_ && synth_interface_->getSynth()->isMidiMapped(getName().toStdString()))
209 options.addItem(kClearMidiLearn, "Clear MIDI Assignment");
210
211 options.addItem(kManualEntry, "Enter Value");
212
213 hovering_ = false;
214 redoImage();
215
216 auto callback = [=](int selection) { handleModulationMenuCallback(selection); };
217 auto cancel = [=]() {
218 for (SliderListener* listener : slider_listeners_)
219 listener->menuFinished(this);
220 };
221 parent_->showPopupSelector(this, e.getPosition(), options, callback, cancel);
222
223 for (SliderListener* listener : slider_listeners_)
224 listener->mouseDown(this);
225 }
226 else {
228 MouseInputSource source = e.source;
229
230 if (source.isMouse() && source.canDoUnboundedMovement()) {
231 editing_ = true;
232 source.hideCursor();
233 source.enableUnboundedMouseMovement(true);
234 mouse_down_position_ = e.getScreenPosition();
235 for (SliderListener* listener : slider_listeners_)
236 listener->beginModulationEdit(this);
237 }
238 }
239}
240
241void ModulationAmountKnob::mouseUp(const MouseEvent& e) {
242 if (!e.mods.isPopupMenu()) {
244
245 MouseInputSource source = e.source;
246 if (source.isMouse() && source.canDoUnboundedMovement()) {
247 source.showMouseCursor(MouseCursor::NormalCursor);
248 source.enableUnboundedMouseMovement(false);
249 if (getScreenBounds().contains(e.getScreenPosition()))
250 editing_ = false;
251 source.setScreenPosition(mouse_down_position_.toFloat());
252 }
253 }
254}
255
256void ModulationAmountKnob::mouseExit(const MouseEvent& e) {
257 if (!editing_) {
258 for (SliderListener* listener : slider_listeners_)
259 listener->endModulationEdit(this);
260 }
261 editing_ = false;
263}
264
265void ModulationAmountKnob::toggleBypass() {
266 bypass_ = !bypass_;
267 for (Listener* listener : listeners_)
268 listener->setModulationBypass(this, bypass_);
269 setColors();
270}
271
273 if (result == kDisconnect) {
274 for (Listener* listener : listeners_)
275 listener->disconnectModulation(this);
276 }
277 else if (result == kToggleBypass)
278 toggleBypass();
279 else if (result == kToggleBipolar) {
280 bipolar_ = !bipolar_;
281 for (Listener* listener : listeners_)
282 listener->setModulationBipolar(this, bipolar_);
283 }
284 else if (result == kToggleStereo) {
285 stereo_ = !stereo_;
286 for (Listener* listener : listeners_)
287 listener->setModulationStereo(this, stereo_);
288 }
289 else
290 handlePopupResult(result);
291
292 if (result != kManualEntry) {
293 for (SliderListener* listener : slider_listeners_)
294 listener->menuFinished(this);
295 }
296}
297
299 if (visible == showing_)
300 return;
301
302 showing_ = visible;
303 setVisible(visible);
304 setAlpha((showing_ || hovering_) ? 1.0f : 0.0f);
305}
306
308 setAlpha(0.0f, true);
309 showing_ = false;
310 hovering_ = false;
311 setVisible(false);
312}
313
315 if (current_modulator_ == current)
316 return;
317
318 setColour(Skin::kRotaryArc, findColour(Skin::kModulationMeterControl, true));
319 current_modulator_ = current;
320}
321
322void ModulationAmountKnob::setDestinationComponent(Component* component, const std::string& name) {
323 color_component_ = component;
325
326 if (color_component_)
327 setColour(Skin::kRotaryArc, color_component_->findColour(Skin::kRotaryArc, true));
328}
329
331 if (color_component_)
332 return color_component_->findColour(Skin::kRotaryArc, true);
333 return findColour(Skin::kModulationMeterControl, true);
334}
335
336void ModulationAmountKnob::setSource(const std::string& name) {
338 repaint();
339}
340
342 std::map<std::string, ModulationButton*> modulation_buttons,
343 std::map<std::string, SynthSlider*> sliders,
344 vital::output_map mono_modulations,
345 vital::output_map poly_modulations) : SynthSection("modulation_manager"),
346 drag_quad_(Shaders::kRingFragment),
347 current_modulator_quad_(Shaders::kRoundedRectangleBorderFragment),
348 editing_rotary_amount_quad_(Shaders::kRotaryModulationFragment),
349 editing_linear_amount_quad_(Shaders::kLinearModulationFragment),
350 modifying_(false), dragging_(false), changing_hover_modulation_(false),
351 current_modulator_(nullptr) {
352 current_modulator_quad_.setQuad(0, -1.0f, -1.0f, 2.0f, 2.0f);
353 drag_quad_.setTargetComponent(this);
354 editing_rotary_amount_quad_.setTargetComponent(this);
355 editing_rotary_amount_quad_.setActive(false);
356 editing_rotary_amount_quad_.setQuad(0, -1.0f, -1.0f, 2.0f, 2.0f);
357 editing_linear_amount_quad_.setTargetComponent(this);
358 editing_linear_amount_quad_.setActive(false);
359 editing_linear_amount_quad_.setQuad(0, -1.0f, -1.0f, 2.0f, 2.0f);
360 addOpenGlComponent(&modulation_expansion_box_);
361 modulation_expansion_box_.setVisible(false);
362 modulation_expansion_box_.setWantsKeyboardFocus(true);
363 modulation_expansion_box_.addListener(this);
364 modulation_expansion_box_.setAlwaysOnTop(true);
365
367
368 last_milliseconds_ = Time::currentTimeMillis();
369 current_source_ = nullptr;
370 current_expanded_modulation_ = nullptr;
371 temporarily_set_destination_ = nullptr;
372 temporarily_set_synth_slider_ = nullptr;
373 temporarily_set_hover_slider_ = nullptr;
374 temporarily_set_bipolar_ = false;
375 num_voices_readout_ = nullptr;
376
377 modulation_buttons_ = modulation_buttons;
378 for (auto& modulation_button : modulation_buttons_) {
379 modulation_button.second->addListener(this);
380
381 modulation_callout_buttons_[modulation_button.first] = std::make_unique<ExpandModulationButton>();
382 addChildComponent(modulation_callout_buttons_[modulation_button.first].get());
383 addOpenGlComponent(modulation_callout_buttons_[modulation_button.first]->getGlComponent());
384 modulation_callout_buttons_[modulation_button.first]->addListener(this);
385 }
386
387 modulation_source_meters_ = std::make_unique<BarRenderer>(modulation_buttons.size());
388 modulation_source_meters_->setBarWidth(0.0f);
389 addAndMakeVisible(modulation_source_meters_.get());
390 modulation_source_meters_->setInterceptsMouseClicks(false, false);
391
392 setInterceptsMouseClicks(false, true);
393
394 modulation_destinations_ = std::make_unique<Component>();
395 modulation_destinations_->setInterceptsMouseClicks(false, true);
396
397 slider_model_lookup_ = sliders;
398 std::map<Viewport*, int> num_rotary_meters;
399 std::map<Viewport*, int> num_linear_meters;
400 for (auto& slider : slider_model_lookup_) {
401 if (mono_modulations[slider.first]) {
402 bool rotary = slider.second->isRotary() && !slider.second->isTextOrCurve();
403 Viewport* viewport = slider.second->findParentComponentOfClass<Viewport>();
404 if (rotary)
405 num_rotary_meters[viewport] = num_rotary_meters[viewport] + 1;
406 else
407 num_linear_meters[viewport] = num_linear_meters[viewport] + 1;
408 }
409 }
410
411 for (auto& rotary_meters : num_rotary_meters) {
412 rotary_destinations_[rotary_meters.first] = std::make_unique<OpenGlMultiQuad>(rotary_meters.second,
414 rotary_destinations_[rotary_meters.first]->setTargetComponent(this);
415 rotary_destinations_[rotary_meters.first]->setScissorComponent(rotary_meters.first);
416 rotary_destinations_[rotary_meters.first]->setAlpha(0.0f, true);
417
418 rotary_meters_[rotary_meters.first] = std::make_unique<OpenGlMultiQuad>(rotary_meters.second,
420 rotary_meters_[rotary_meters.first]->setTargetComponent(this);
421 rotary_meters_[rotary_meters.first]->setScissorComponent(rotary_meters.first);
422 }
423 for (auto& linear_meters : num_linear_meters) {
424 linear_destinations_[linear_meters.first] = std::make_unique<OpenGlMultiQuad>(linear_meters.second,
426 linear_destinations_[linear_meters.first]->setTargetComponent(this);
427 linear_destinations_[linear_meters.first]->setScissorComponent(linear_meters.first);
428 linear_destinations_[linear_meters.first]->setAlpha(0.0f, true);
429
430 linear_meters_[linear_meters.first] = std::make_unique<OpenGlMultiQuad>(linear_meters.second,
432 linear_meters_[linear_meters.first]->setTargetComponent(this);
433 linear_meters_[linear_meters.first]->setScissorComponent(linear_meters.first);
434 }
435
436 for (auto& slider : slider_model_lookup_) {
437 std::string name = slider.first;
438 const vital::Output* mono_total = mono_modulations[name];
439
440 if (mono_total == nullptr)
441 continue;
442
443 bool rotary = slider.second->isRotary() && !slider.second->isTextOrCurve();
444 Viewport* viewport = slider.second->findParentComponentOfClass<Viewport>();
445 const vital::Output* poly_total = poly_modulations[name];
446
447 if (rotary) {
448 int index = num_rotary_meters[viewport] - 1;
449 num_rotary_meters[viewport] = index;
450 createModulationMeter(mono_total, poly_total, slider.second, rotary_meters_[viewport].get(), index);
451 }
452 else {
453 int index = num_linear_meters[viewport] - 1;
454 num_linear_meters[viewport] = index;
455 createModulationMeter(mono_total, poly_total, slider.second, linear_meters_[viewport].get(), index);
456 }
457
458 slider.second->addSliderListener(this);
459 createModulationSlider(name, slider.second, poly_total != nullptr);
460 }
461
462 addChildComponent(modulation_destinations_.get());
463
464 for (int i = 0; i < vital::kMaxModulationConnections; ++i) {
465 std::string name = "modulation_" + std::to_string(i + 1) + "_amount";
466 modulation_amount_sliders_[i] = std::make_unique<ModulationAmountKnob>(name, i);
467 modulation_amount_sliders_[i]->setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
468 addSlider(modulation_amount_sliders_[i].get());
469 modulation_amount_sliders_[i]->addSliderListener(this);
470 modulation_amount_sliders_[i]->addModulationAmountListener(this);
471 modulation_amount_lookup_[name] = modulation_amount_sliders_[i].get();
472
473 modulation_hover_sliders_[i] = std::make_unique<ModulationAmountKnob>(name, i);
474 modulation_hover_sliders_[i]->setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
475 addSlider(modulation_hover_sliders_[i].get());
476 modulation_hover_sliders_[i]->setAlpha(0.0f, true);
477 modulation_hover_sliders_[i]->addSliderListener(this);
478 modulation_hover_sliders_[i]->addModulationAmountListener(this);
479 modulation_hover_sliders_[i]->setDrawWhenNotVisible(true);
480
481 selected_modulation_sliders_[i] = std::make_unique<ModulationAmountKnob>(name, i);
482 selected_modulation_sliders_[i]->setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
483 addSlider(selected_modulation_sliders_[i].get());
484 selected_modulation_sliders_[i]->setAlpha(0.0f, true);
485 selected_modulation_sliders_[i]->addSliderListener(this);
486 selected_modulation_sliders_[i]->addModulationAmountListener(this);
487 selected_modulation_sliders_[i]->setDrawWhenNotVisible(true);
488 }
489}
490
492 const vital::Output* poly_total,
493 SynthSlider* slider, OpenGlMultiQuad* quads, int index) {
494 std::string name = slider->getName().toStdString();
495 std::unique_ptr<ModulationMeter> meter = std::make_unique<ModulationMeter>(mono_total, poly_total,
496 slider, quads, index);
497 addChildComponent(meter.get());
498 meter->setName(name);
499 meter->setBounds(getLocalArea(slider, slider->getLocalBounds()));
500 meter_lookup_[name] = std::move(meter);
501}
502
503void ModulationManager::createModulationSlider(std::string name, SynthSlider* slider, bool poly) {
504 std::unique_ptr<ModulationDestination> destination = std::make_unique<ModulationDestination>(slider);
505 modulation_destinations_->addAndMakeVisible(destination.get());
506 destination->setRectangle(slider->isTextOrCurve());
507 destination->setRotary(slider->isRotary());
508 destination->setSizeMultiple(slider->getKnobSizeScale());
509
510 destination_lookup_[name] = destination.get();
511 all_destinations_.push_back(std::move(destination));
512}
513
515
517 float meter_thickness = findValue(Skin::kKnobModMeterArcThickness);
518 Colour meter_center_color = findColour(Skin::kModulationMeter, true);
519 Colour meter_left_color = findColour(Skin::kModulationMeterLeft, true);
520 Colour meter_right_color = findColour(Skin::kModulationMeterRight, true);
521
522 editing_rotary_amount_quad_.setColor(meter_center_color);
523 editing_rotary_amount_quad_.setAltColor(meter_center_color);
524 editing_rotary_amount_quad_.setModColor(meter_center_color);
525 editing_linear_amount_quad_.setColor(meter_center_color);
526 editing_linear_amount_quad_.setAltColor(meter_center_color);
527 editing_linear_amount_quad_.setModColor(meter_center_color);
528
529 for (auto& rotary_meter_group : rotary_meters_) {
530 rotary_meter_group.second->setThickness(meter_thickness);
531 rotary_meter_group.second->setModColor(meter_center_color);
532 rotary_meter_group.second->setColor(meter_left_color);
533 rotary_meter_group.second->setAltColor(meter_right_color);
534 }
535
536 for (auto& linear_meter_group : linear_meters_) {
537 linear_meter_group.second->setModColor(meter_center_color);
538 linear_meter_group.second->setColor(meter_left_color);
539 linear_meter_group.second->setAltColor(meter_right_color);
540 }
541
542 modulation_destinations_->setBounds(getLocalBounds());
543 modulation_source_meters_->setBounds(getLocalBounds());
544
546
547 Colour meter_control = findColour(Skin::kModulationMeterControl, true);
548 current_modulator_quad_.setColor(meter_control);
549 drag_quad_.setColor(meter_control);
550 drag_quad_.setAltColor(findColour(Skin::kWidgetBackground, true));
551
552 modulation_expansion_box_.setColor(findColour(Skin::kBody, true));
553
554 Colour lighten_screen = findColour(Skin::kLightenScreen, true);
556
557 for (auto& rotary_destination_group : rotary_destinations_)
558 rotary_destination_group.second->setColor(lighten_screen);
559
560 for (auto& linear_destination_group : linear_destinations_) {
561 linear_destination_group.second->setColor(lighten_screen);
562 linear_destination_group.second->setRounding(rounding);
563 }
564
567 positionModulationAmountSliders();
568}
569
571 SynthSection::parentHierarchyChanged();
572 if (!modulation_source_readouts_.empty())
573 return;
574
575 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
576 if (parent == nullptr)
577 return;
578
579 for (auto& mod_button : modulation_buttons_) {
580 modulation_source_readouts_[mod_button.first] = parent->getSynth()->getStatusOutput(mod_button.first);
581 smooth_mod_values_[mod_button.first] = 0.0f;
582 active_mod_values_[mod_button.first] = false;
583 }
584
585 num_voices_readout_ = parent->getSynth()->getStatusOutput("num_voices");
586}
587
589 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
590
591 for (auto& meter : meter_lookup_) {
592 SynthSlider* model = slider_model_lookup_[meter.first];
593 if (model)
594 meter.second->setBounds(getLocalArea(model, model->getModulationMeterBounds()));
595
596 if (parent) {
597 int num_modulations = parent->getSynth()->getNumModulations(meter.first);
598 meter.second->setModulated(num_modulations);
599 meter.second->setVisible(num_modulations);
600 }
601 }
602}
603
605 std::string slider_name = slider->getName().toStdString();
606 std::string source_name = current_modulator_->getName().toStdString();
607 setModulationValues(source_name, slider_name,
608 slider->getModulationAmount(), slider->isModulationBipolar(),
609 slider->isModulationStereo(), slider->isModulationBypassed());
610 modulation_buttons_[source_name]->repaint();
611}
612
614 std::string slider_name = slider->getName().toStdString();
615 std::string source_name = current_modulator_->getName().toStdString();
616
617 removeModulation(source_name, slider_name);
618 modulation_buttons_[source_name]->repaint();
619}
620
622 if (current_modulator_ == nullptr)
623 return;
624
625 if (meter_lookup_.count(connection->destination_name)) {
626 meter_lookup_[connection->destination_name]->setModulated(!last);
627 meter_lookup_[connection->destination_name]->setVisible(!last);
628 }
629}
630
632 for (auto& button : modulation_buttons_)
633 button.second->setActiveModulation(button.second == source);
634
635 current_modulator_ = source;
636 for (auto& hover_slider : modulation_hover_sliders_)
637 hover_slider->makeVisible(false);
638 makeCurrentModulatorAmountsVisible();
640 positionModulationAmountSliders();
641}
642
645 positionModulationAmountSliders();
646}
647
649 makeCurrentModulatorAmountsVisible();
650}
651
653 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
655 for (int i = 0; i < vital::kMaxModulationConnections; ++i) {
656 vital::ModulationConnection* connection = bank.atIndex(i);
657 if (connection->source_name.empty() && connection->destination_name.empty())
658 return true;
659 }
660 return false;
661}
662
663void ModulationManager::startModulationMap(ModulationButton* source, const MouseEvent& e) {
664 if (!hasFreeConnection())
665 return;
666
667 mouse_drag_position_ = getLocalPoint(source, e.getPosition());
668 current_source_ = source;
669 dragging_ = true;
670 Rectangle<int> global_bounds = getLocalArea(current_source_, current_source_->getLocalBounds());
671 Point<int> global_start = global_bounds.getCentre();
672 mouse_drag_start_ = global_start;
673 modulation_destinations_->setVisible(true);
674 int widget_margin = findValue(Skin::kWidgetMargin);
675
676 std::map<Viewport*, int> rotary_indices;
677 std::map<Viewport*, int> linear_indices;
678 for (auto& rotary_destination_group : rotary_destinations_)
679 rotary_indices[rotary_destination_group.first] = 0;
680
681 for (auto& linear_destination_group : linear_destinations_)
682 linear_indices[linear_destination_group.first] = 0;
683
684 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
685 std::string source_name = source->getName().toStdString();
686 std::set<std::string> active_destinations;
687 std::vector<vital::ModulationConnection*> connections = parent->getSynth()->getSourceConnections(source_name);
688 for (vital::ModulationConnection* connection : connections)
689 active_destinations.insert(connection->destination_name);
690
691 for (auto& destination : destination_lookup_) {
692 SynthSlider* model = slider_model_lookup_[destination.first];
693 bool should_show = model->isShowing() && model->getSectionParent()->isActive() &&
694 current_source_->getName() != String(destination.first);
695 Viewport* viewport = model->findParentComponentOfClass<Viewport>();
696 destination.second->setVisible(should_show);
697 destination.second->setActive(active_destinations.count(destination.first));
698 destination.second->setMargin(widget_margin);
699
700 Point<int> position = getLocalPoint(model, Point<int>(0, 0));
701 Rectangle<int> slider_bounds = model->getLocalBounds() + position;
702 destination.second->setBounds(slider_bounds);
703
704 Component* extra_target = model->getExtraModulationTarget();
705 if (extra_target) {
706 Rectangle<int> bounds = destination.second->getFillBounds().toNearestInt() + position;
707
708 Point<int> top_left = getLocalPoint(extra_target, Point<int>(0, 0));
709 Rectangle<int> extra_bounds(top_left.x, top_left.y, extra_target->getWidth(), extra_target->getHeight());
710 bounds = bounds.getUnion(extra_bounds);
711 destination.second->setBounds(bounds);
712 }
713
714 if (should_show) {
715 if (destination.second->isRotary()) {
716 destination.second->setIndex(rotary_indices[viewport]);
717 rotary_indices[viewport] = rotary_indices[viewport] + 1;
718 }
719 else {
720 destination.second->setIndex(linear_indices[viewport]);
721 linear_indices[viewport] = linear_indices[viewport] + 1;
722 }
723 setDestinationQuadBounds(destination.second);
724 }
725 }
726
727 for (auto& index_count : rotary_indices) {
728 rotary_destinations_[index_count.first]->setNumQuads(index_count.second);
729 rotary_destinations_[index_count.first]->setAlpha(index_count.second > 0 ? 1.0f : 0.0f);
730 }
731
732 for (auto& index_count : linear_indices) {
733 linear_destinations_[index_count.first]->setNumQuads(index_count.second);
734 linear_destinations_[index_count.first]->setAlpha(index_count.second > 0 ? 1.0f : 0.0f);
735 }
736}
737
738void ModulationManager::setDestinationQuadBounds(ModulationDestination* destination) {
739 Point<float> top_left = destination->getBounds().getTopLeft().toFloat();
740 Rectangle<float> draw_bounds = destination->getLocalBounds().toFloat() + top_left;
741 if (!destination->hasExtraModulationTarget())
742 draw_bounds = destination->getFillBounds() + top_left;
743 float global_width = getWidth();
744 float global_height = getHeight();
745 float x = 2.0f * draw_bounds.getX() / global_width - 1.0f;
746 float y = 1.0f - 2.0f * draw_bounds.getBottom() / global_height;
747 float width = 2.0f * draw_bounds.getWidth() / global_width;
748 float height = 2.0f * draw_bounds.getHeight() / global_height;
749
750 float offset = destination->isActive() ? -2.0f : 0.0f;
751
752 Viewport* viewport = destination->getDestinationSlider()->findParentComponentOfClass<Viewport>();
753 if (destination->isRotary())
754 rotary_destinations_[viewport]->setQuad(destination->getIndex(), x + offset, y, width, height);
755 else
756 linear_destinations_[viewport]->setQuad(destination->getIndex(), x + offset, y, width, height);
757}
758
760 if (hover_slider->isCurrentModulator() || hover_slider->hasAux() || current_modulator_ == nullptr)
761 return;
762
763 std::string name = hover_slider->getOriginalName().toStdString();
764 std::string source_name = current_modulator_->getName().toStdString();
765 vital::ModulationConnection* connection = getConnection(source_name, name);
766 if (connection == nullptr) {
767 float value = hover_slider->getValue() * 0.5f;
768 hover_slider->setValue(0.0f, sendNotificationSync);
769 temporarily_set_hover_slider_ = hover_slider;
770
771 connectModulation(source_name, name);
772 setModulationValues(source_name, name, value, false, false, false);
773 connection = getConnection(source_name, name);
774
775 int new_index = connection->modulation_processor->index();
776 addAuxConnection(new_index, hover_slider->index());
777 setModulationSliderValues(new_index, value);
778 }
779}
780
781void ModulationManager::modulationDraggedToComponent(Component* component, bool bipolar) {
782 if (component && current_modulator_ && destination_lookup_.count(component->getName().toStdString())) {
783 std::string name = component->getName().toStdString();
784
785 if (getConnection(current_modulator_->getName().toStdString(), name) == nullptr) {
786 ModulationDestination* destination = destination_lookup_[name];
787 SynthSlider* slider = destination->getDestinationSlider();
788
789 float percent = slider->valueToProportionOfLength(slider->getValue());
790 float modulation_amount = 1.0f - percent;
791 if (bipolar)
792 modulation_amount = std::min(modulation_amount, percent) * 2.0f;
793 modulation_amount = std::max(modulation_amount, kDefaultModulationRatio);
794
795 temporarily_set_destination_ = destination;
796 temporarily_set_synth_slider_ = slider_model_lookup_[name];
797
798 std::string source_name = current_modulator_->getName().toStdString();
799 connectModulation(source_name, name);
800 setModulationValues(source_name, name, modulation_amount, bipolar, false, false);
801 destination->setActive(true);
802 setDestinationQuadBounds(destination);
803
804 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
805 std::vector<vital::ModulationConnection*> connections = parent->getSynth()->getDestinationConnections(name);
806
807 for (vital::ModulationConnection* connection : connections) {
808 if (connection->source_name == source_name && connection->destination_name == name) {
809 int index = connection->modulation_processor->index();
810 showModulationAmountOverlay(selected_modulation_sliders_[index].get());
811 }
812 }
813
815 makeModulationsVisible(slider, true);
816 }
817 else
818 modulationsChanged(name);
819 }
820}
821
822void ModulationManager::setTemporaryModulationBipolar(Component* component, bool bipolar) {
823 if (current_modulator_ == nullptr || component != temporarily_set_destination_ || component == nullptr)
824 return;
825
826 std::string source_name = current_modulator_->getName().toStdString();
827 std::string name = component->getName().toStdString();
828 ModulationDestination* destination = destination_lookup_[name];
829 SynthSlider* slider = destination->getDestinationSlider();
830
831 float percent = slider->valueToProportionOfLength(slider->getValue());
832 float modulation_amount = 1.0f - percent;
833 if (bipolar)
834 modulation_amount = std::min(modulation_amount, percent) * 2.0f;
835 modulation_amount = std::max(modulation_amount, kDefaultModulationRatio);
836
837 int index = getModulationIndex(source_name, name);
838 setModulationValues(source_name, name, modulation_amount, bipolar, false, false);
839 temporarily_set_bipolar_ = bipolar;
840 showModulationAmountOverlay(selected_modulation_sliders_[index].get());
841}
842
844 if (temporarily_set_destination_ && current_modulator_) {
845 temporarily_set_destination_->setActive(false);
846 setDestinationQuadBounds(temporarily_set_destination_);
847 temporarily_set_destination_ = nullptr;
848 std::string source_name = current_modulator_->getName().toStdString();
849 removeModulation(source_name, temporarily_set_synth_slider_->getName().toStdString());
850 temporarily_set_synth_slider_ = nullptr;
851
852 hideModulationAmountOverlay();
853 }
854}
855
857 if (temporarily_set_hover_slider_ && current_modulator_) {
858 std::string name = temporarily_set_hover_slider_->getOriginalName().toStdString();
859
860 std::string source_name = current_modulator_->getName().toStdString();
861 removeModulation(source_name, temporarily_set_hover_slider_->getOriginalName().toStdString());
862 temporarily_set_hover_slider_ = nullptr;
863 }
864}
865
866void ModulationManager::modulationDragged(const MouseEvent& e) {
867 if (!dragging_)
868 return;
869
870 mouse_drag_position_ = getLocalPoint(current_source_, e.getPosition());
871 Component* component = getComponentAt(mouse_drag_position_.x, mouse_drag_position_.y);
872 ModulationAmountKnob* hover_knob = nullptr;
873 for (int i = 0; i < vital::kMaxModulationConnections; ++i) {
874 if (modulation_amount_sliders_[i].get() == component)
875 hover_knob = modulation_amount_sliders_[i].get();
876 else if (modulation_hover_sliders_[i].get() == component)
877 hover_knob = modulation_hover_sliders_[i].get();
878 else if (selected_modulation_sliders_[i].get() == component)
879 hover_knob = selected_modulation_sliders_[i].get();
880 }
881
882 if (hover_knob && hover_knob->isCurrentModulator())
883 return;
884
885 bool bipolar = e.mods.isAnyModifierKeyDown();
886 if (temporarily_set_destination_ && temporarily_set_destination_ != component)
888 if (temporarily_set_hover_slider_ && temporarily_set_hover_slider_ != component)
890
891 else if (temporarily_set_synth_slider_ && temporarily_set_bipolar_ != bipolar)
892 setTemporaryModulationBipolar(component, bipolar);
893
894 if (hover_knob)
896 else
897 modulationDraggedToComponent(component, bipolar);
898}
899
900void ModulationManager::modulationWheelMoved(const MouseEvent& e, const MouseWheelDetails& wheel) {
901 if (!dragging_ || current_modulator_ == nullptr || temporarily_set_destination_ == nullptr)
902 return;
903
904 MouseEvent new_event(e.source, e.position, ModifierKeys(), e.pressure, e.orientation, e.rotation,
905 e.tiltX, e.tiltY, e.eventComponent, e.originalComponent, e.eventTime, e.mouseDownPosition,
906 e.mouseDownTime, e.getNumberOfClicks(), e.mouseWasDraggedSinceMouseDown());
907 std::string source_name = current_modulator_->getName().toStdString();
908 std::string destination_name = temporarily_set_destination_->getName().toStdString();
909 int index = getModulationIndex(source_name, destination_name);
910 if (index >= 0)
911 selected_modulation_sliders_[index]->mouseWheelMove(new_event, wheel);
912}
913
915 temporarily_set_destination_ = nullptr;
916 temporarily_set_synth_slider_ = nullptr;
917 temporarily_set_hover_slider_ = nullptr;
918 dragging_ = false;
919
921 positionModulationAmountSliders();
922 current_source_ = nullptr;
923 for (auto& rotary_destination_group : rotary_destinations_)
924 rotary_destination_group.second->setAlpha(0.0f);
925
926 for (auto& linear_destination_group : linear_destinations_)
927 linear_destination_group.second->setAlpha(0.0f);
928
929 modulation_destinations_->setVisible(false);
930 drag_quad_.setThickness(0.0f, true);
931 hideModulationAmountOverlay();
932}
933
938
940 if (current_modulator_) {
941 for (auto& selected_slider : selected_modulation_sliders_)
942 selected_slider->makeVisible(false);
943 }
944 current_modulator_ = nullptr;
946}
947
950 while (connection && !connection->source_name.empty() && !connection->destination_name.empty()) {
951 removeModulation(connection->source_name, connection->destination_name);
952 connection = getConnectionForModulationSlider(modulation_knob);
953 }
954
956}
957
960 float value = modulation_knob->getValue();
961 bool bipolar = modulation_knob->isBipolar();
962 bool stereo = modulation_knob->isStereo();
963 bool bypass = modulation_knob->isBypass();
964
965 int index = modulation_knob->index();
966 modulation_amount_sliders_[index]->setBipolar(bipolar);
967 modulation_amount_sliders_[index]->setStereo(stereo);
968 modulation_amount_sliders_[index]->setBypass(bypass);
969 modulation_hover_sliders_[index]->setBipolar(bipolar);
970 modulation_hover_sliders_[index]->setStereo(stereo);
971 modulation_hover_sliders_[index]->setBypass(bypass);
972 selected_modulation_sliders_[index]->setBipolar(bipolar);
973 selected_modulation_sliders_[index]->setStereo(stereo);
974 selected_modulation_sliders_[index]->setBypass(bypass);
975
976 setModulationValues(connection->source_name, connection->destination_name, value, bipolar, stereo, bypass);
977}
978
980 setModulationSettings(modulation_knob);
981}
982
984 setModulationSettings(modulation_knob);
985}
986
988 setModulationSettings(modulation_knob);
989}
990
992 drag_quad_.init(open_gl);
993 modulation_expansion_box_.init(open_gl);
994 modulation_source_meters_->init(open_gl);
995 for (auto& rotary_destination_group : rotary_destinations_)
996 rotary_destination_group.second->init(open_gl);
997
998 for (auto& linear_destination_group : linear_destinations_)
999 linear_destination_group.second->init(open_gl);
1000
1001 for (auto& rotary_meter_group : rotary_meters_)
1002 rotary_meter_group.second->init(open_gl);
1003
1004 for (auto& linear_meter_group : linear_meters_)
1005 linear_meter_group.second->init(open_gl);
1006
1008}
1009
1011 for (auto& rotary_destination_group : rotary_destinations_)
1012 rotary_destination_group.second->render(open_gl, true);
1013
1014 for (auto& linear_destination_group : linear_destinations_)
1015 linear_destination_group.second->render(open_gl, true);
1016}
1017
1019 Component* component = current_modulator_;
1020 if (component) {
1021 current_modulator_quad_.setTargetComponent(component);
1022 current_modulator_quad_.setAlpha(1.0f);
1023 }
1024 else
1025 current_modulator_quad_.setAlpha(0.0f);
1026
1027 current_modulator_quad_.setThickness(dragging_ ? 2.6f : 1.0f);
1028 current_modulator_quad_.render(open_gl, true);
1029}
1030
1032 static constexpr float kRadiusWidthRatio = 0.022f;
1033 static constexpr float kThicknessWidthRatio = 0.003f;
1034 if (current_source_ == nullptr || temporarily_set_destination_ || temporarily_set_hover_slider_)
1035 return;
1036
1037 vital::poly_float mod_percent = modulation_source_readouts_[current_source_->getName().toStdString()]->value();
1038 float draw_radius = kRadiusWidthRatio * getWidth();
1039 float radius_x = draw_radius / getWidth();
1040 float radius_y = draw_radius / getHeight();
1041 float x = mouse_drag_position_.x * 2.0f / getWidth() - 1.0f;
1042 float y = -mouse_drag_position_.y * 2.0f / getHeight() + 1.0f;
1043
1044 Colour widget_background = findColour(Skin::kWidgetBackground, true);
1045 Colour control = findColour(Skin::kModulationMeterControl, true);
1046 drag_quad_.setAltColor(widget_background.interpolatedWith(control, mod_percent[0]));
1047 drag_quad_.setQuad(0, x - radius_x, y - radius_y, 2.0f * radius_x, 2.0f * radius_y);
1048 drag_quad_.setThickness(getWidth() * kThicknessWidthRatio);
1049 drag_quad_.render(open_gl, true);
1050}
1051
1053 if (!animate)
1054 return;
1055
1056 drawCurrentModulator(open_gl);
1057 for (auto& callout_button : modulation_callout_buttons_) {
1058 if (callout_button.second->isVisible())
1059 callout_button.second->renderSliderQuads(open_gl, animate);
1060 }
1061
1062 OpenGlComponent::setViewPort(this, open_gl);
1064
1065 Colour first_color = findColour(Skin::kWidgetPrimary1, true);
1066 Colour second_color = findColour(Skin::kWidgetPrimary2, true);
1067
1068 modulation_source_meters_->setAdditiveBlending(second_color.getBrightness() > 0.5f);
1069 modulation_source_meters_->setColor(second_color);
1070 renderSourceMeters(open_gl, 1);
1071 modulation_source_meters_->setAdditiveBlending(first_color.getBrightness() > 0.5f);
1072 modulation_source_meters_->setColor(first_color);
1073 renderSourceMeters(open_gl, 0);
1075
1076 editing_rotary_amount_quad_.render(open_gl, animate);
1077 editing_linear_amount_quad_.render(open_gl, animate);
1078
1080
1081 drawDraggingModulation(open_gl);
1082}
1083
1085 if (!animate)
1086 return;
1087
1088 int num_voices = 1;
1089 if (num_voices_readout_)
1090 num_voices = std::max<float>(0.0f, num_voices_readout_->value()[0]);
1091 for (auto& meter : meter_lookup_) {
1092 SynthSlider* slider = slider_model_lookup_[meter.first];
1093 bool show = meter.second->isModulated() && showingInParents(slider) && slider->isActive();
1094 meter.second->setActive(show);
1095 if (show)
1096 meter.second->updateDrawing(num_voices);
1097 }
1098
1099 OpenGlComponent::setViewPort(this, open_gl);
1100 for (auto& rotary_meter_group : rotary_meters_)
1101 rotary_meter_group.second->render(open_gl, animate);
1102
1103 for (auto& linear_meter_group : linear_meters_)
1104 linear_meter_group.second->render(open_gl, animate);
1105}
1106
1108 int i = 0;
1109 float width = getWidth();
1110 float height = getHeight();
1111 for (auto& mod_readout : modulation_source_readouts_) {
1112 ModulationButton* button = modulation_buttons_[mod_readout.first];
1113 float readout_value = mod_readout.second->value()[index];
1114
1115 float clamped_value = vital::utils::clamp(readout_value, 0.0f, 1.0f);
1116 if (!active_mod_values_[mod_readout.first] && !mod_readout.second->isClearValue(readout_value))
1117 smooth_mod_values_[mod_readout.first].set(index, clamped_value);
1118 float smooth_value = smooth_mod_values_[mod_readout.first][index];
1119
1120 Rectangle<int> bounds = getLocalArea(button, button->getMeterBounds());
1121 float left = 2.0f * ((bounds.getX() - 1.0f) / width) - 1.0f + kModSourceMeterBuffer;
1122 float w = 2.0f * bounds.getWidth() / width - 2.0f * kModSourceMeterBuffer;
1123 float h = 2.0f * bounds.getHeight() / height - 2.0f * kModSourceMeterBuffer;
1124 float y = 1.0f - 2.0f * bounds.getY() / height - kModSourceMeterBuffer;
1125 float y_center = y - h * (1.0f - clamped_value);
1126 float smooth_y_center = y - h * (1.0f - smooth_value);
1127
1128 float top = std::min(y_center, smooth_y_center) - kModSourceMinRadius;
1129 float bottom = std::max(y_center, smooth_y_center) + kModSourceMinRadius;
1130
1131 bool active = button->isActiveModulation() || button->hasAnyModulation();
1132 if (w <= 0.0f || mod_readout.second->isClearValue(readout_value) || !showingInParents(button) || !active) {
1133 left = -2.0f;
1134 top = -2.0f;
1135 bottom = -2.0f;
1136 }
1137
1138 modulation_source_meters_->positionBar(i, left, top, w, bottom - top);
1139
1140 i++;
1141 }
1142
1143 modulation_source_meters_->render(open_gl, true);
1144}
1145
1147 static constexpr float kTimeDecayScale = 60.0f;
1148 long long current_milliseconds = Time::currentTimeMillis();
1149 long long delta_milliseconds = current_milliseconds - last_milliseconds_;
1150 last_milliseconds_ = current_milliseconds;
1151
1152 float seconds = delta_milliseconds / 1000.0f;
1153 float decay = std::max(std::min(kModSmoothDecay * seconds * kTimeDecayScale, 1.0f), 0.0f);
1154
1155 for (auto& mod_readout : modulation_source_readouts_) {
1156 vital::poly_float readout_value = mod_readout.second->value();
1157 vital::poly_float clamped_value = vital::utils::clamp(readout_value, 0.0f, 1.0f);
1158 vital::poly_float smooth_value = smooth_mod_values_[mod_readout.first];
1159 bool active = !mod_readout.second->isClearValue(readout_value);
1160 active_mod_values_[mod_readout.first] = active;
1161 if (active)
1162 smooth_mod_values_[mod_readout.first] = vital::utils::interpolate(smooth_value, clamped_value, decay);
1163 }
1164}
1165
1168
1169 drag_quad_.destroy(open_gl);
1170 modulation_expansion_box_.destroy(open_gl);
1171 modulation_source_meters_->destroy(open_gl);
1172 for (auto& rotary_destination_group : rotary_destinations_)
1173 rotary_destination_group.second->destroy(open_gl);
1174
1175 for (auto& linear_destination_group : linear_destinations_)
1176 linear_destination_group.second->destroy(open_gl);
1177
1178 for (auto& rotary_meter_group : rotary_meters_)
1179 rotary_meter_group.second->destroy(open_gl);
1180
1181 for (auto& linear_meter_group : linear_meters_)
1182 linear_meter_group.second->destroy(open_gl);
1183}
1184
1185void ModulationManager::showModulationAmountOverlay(ModulationAmountKnob* slider) {
1186 vital::ModulationConnection* connection = getConnection(slider->index());
1187 if (connection == nullptr || meter_lookup_.count(connection->destination_name) == 0)
1188 return;
1189
1190 ModulationMeter* meter = meter_lookup_[connection->destination_name].get();
1191 if (!meter->destination()->isShowing())
1192 return;
1193
1194 if (meter->isRotary()) {
1195 editing_rotary_amount_quad_.setTargetComponent(meter);
1196 editing_rotary_amount_quad_.setAdditive(false);
1197 meter->setAmountQuadVertices(editing_rotary_amount_quad_);
1198 meter->setModulationAmountQuad(editing_rotary_amount_quad_, slider->getValue(), slider->isBipolar());
1199
1200 editing_rotary_amount_quad_.setThickness(2.0f);
1201 editing_rotary_amount_quad_.setAlpha(1.0f);
1202 editing_rotary_amount_quad_.setActive(true);
1203 }
1204 else {
1205 editing_linear_amount_quad_.setTargetComponent(meter);
1206 editing_linear_amount_quad_.setAdditive(false);
1207 meter->setAmountQuadVertices(editing_linear_amount_quad_);
1208 meter->setModulationAmountQuad(editing_linear_amount_quad_, slider->getValue(), slider->isBipolar());
1209
1210 editing_linear_amount_quad_.setAlpha(1.0f);
1211 editing_linear_amount_quad_.setActive(true);
1212 }
1213}
1214
1215void ModulationManager::hideModulationAmountOverlay() {
1216 if (changing_hover_modulation_)
1217 return;
1218
1219 editing_rotary_amount_quad_.setAlpha(0.0f);
1220 editing_linear_amount_quad_.setAlpha(0.0f);
1221}
1222
1224 if (changing_hover_modulation_)
1225 return;
1226
1227 if (!enteringHoverValue())
1228 makeModulationsVisible(slider, true);
1229
1230 ModulationAmountKnob* amount_knob = dynamic_cast<ModulationAmountKnob*>(slider);
1231 if (amount_knob)
1232 showModulationAmountOverlay(amount_knob);
1233 else
1234 hideModulationAmountOverlay();
1235}
1236
1238 hideModulationAmountOverlay();
1239}
1240
1242 if (current_modulator_ && current_modulator_->isVisible())
1243 current_modulator_->grabKeyboardFocus();
1244}
1245
1246void ModulationManager::modulationsChanged(const std::string& destination) {
1247 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
1249 SynthSlider* slider = slider_model_lookup_[destination];
1250 if (current_modulator_)
1251 makeCurrentModulatorAmountsVisible();
1252 else if (slider)
1253 makeModulationsVisible(slider, slider->isShowing());
1254
1255 if (parent == nullptr)
1256 return;
1257
1258 if (meter_lookup_.count(destination) == 0)
1259 return;
1260
1261 int num_modulations = parent->getSynth()->getNumModulations(destination);
1262 meter_lookup_[destination]->setModulated(num_modulations);
1263 meter_lookup_[destination]->setVisible(num_modulations);
1264}
1265
1266int ModulationManager::getModulationIndex(std::string source, std::string destination) {
1267 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
1268 std::vector<vital::ModulationConnection*> connections = parent->getSynth()->getDestinationConnections(destination);
1269
1270 for (vital::ModulationConnection* connection : connections) {
1271 if (connection->source_name == source)
1272 return connection->modulation_processor->index();
1273 }
1274
1275 return -1;
1276}
1277
1279 ModulationAmountKnob* amount_knob = dynamic_cast<ModulationAmountKnob*>(slider);
1280 if (amount_knob)
1281 return amount_knob->index();
1282 return -1;
1283}
1284
1286 int index = getIndexForModulationSlider(slider);
1287 if (index < 0)
1288 return nullptr;
1289
1290 while (aux_connections_to_from_.count(index))
1291 index = aux_connections_to_from_[index];
1292
1293 return getConnection(index);
1294}
1295
1297 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
1298 if (parent == nullptr)
1299 return nullptr;
1300
1301 return parent->getSynth()->getModulationBank().atIndex(index);
1302}
1303
1304vital::ModulationConnection* ModulationManager::getConnection(const std::string& source, const std::string& dest) {
1305 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
1306 if (parent == nullptr)
1307 return nullptr;
1308
1309 std::vector<vital::ModulationConnection*> connections = parent->getSynth()->getSourceConnections(source);
1310 for (vital::ModulationConnection* connection : connections) {
1311 if (connection->destination_name == dest)
1312 return connection;
1313 }
1314
1315 return nullptr;
1316}
1317
1319 for (auto& amount_knob : modulation_hover_sliders_) {
1320 if (slider == amount_knob.get())
1321 return;
1322 }
1323
1324 if (modulation_expansion_box_.isVisible())
1325 return;
1326
1328 if (connection && !connection->source_name.empty() && !connection->destination_name.empty())
1329 modulationSelected(modulation_buttons_[connection->source_name]);
1330 else {
1332 hideModulationAmountOverlay();
1333 makeModulationsVisible(slider, true);
1334 }
1335}
1336
1338 if (current_modulator_ && current_modulator_->isVisible())
1339 current_modulator_->grabKeyboardFocus();
1340}
1341
1343 changing_hover_modulation_ = false;
1345 if (connection)
1346 removeModulation(connection->source_name, connection->destination_name);
1348
1349 if (current_modulator_ && current_modulator_->isVisible())
1350 current_modulator_->grabKeyboardFocus();
1351}
1352
1354 changing_hover_modulation_ = true;
1355}
1356
1358 changing_hover_modulation_ = false;
1359}
1360
1362 ModulationAmountKnob* amount_knob = dynamic_cast<ModulationAmountKnob*>(slider);
1363 if (amount_knob == nullptr)
1364 return;
1365
1366 float value = slider->getValue();
1367 int index = getIndexForModulationSlider(slider);
1368 float scale = getAuxMultiplier(index);
1369 float scaled_value = value * scale;
1370 while (aux_connections_to_from_.count(index))
1371 index = aux_connections_to_from_[index];
1372
1373 vital::ModulationConnection* connection = getConnection(index);
1374 bool bipolar = connection->modulation_processor->isBipolar();
1375 bool stereo = connection->modulation_processor->isStereo();
1376 bool bypass = connection->modulation_processor->isBypassed();
1377
1378 setModulationValues(connection->source_name, connection->destination_name, scaled_value, bipolar, stereo, bypass);
1379 showModulationAmountOverlay(amount_knob);
1380
1381 SynthSection::sliderValueChanged(modulation_amount_sliders_[index].get());
1382}
1383
1385 for (auto& callout_button : modulation_callout_buttons_) {
1386 if (button == callout_button.second.get()) {
1387 bool new_button = button != current_expanded_modulation_;
1388 hideModulationAmountCallout();
1389 if (new_button)
1390 showModulationAmountCallout(callout_button.first);
1391 return;
1392 }
1393 }
1394
1396}
1397
1398void ModulationManager::connectModulation(std::string source, std::string destination) {
1399 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
1400 if (parent == nullptr || source.empty() || destination.empty())
1401 return;
1402
1403 modifying_ = true;
1404 parent->connectModulation(source, destination);
1405 modifying_ = false;
1406}
1407
1408void ModulationManager::removeModulation(std::string source, std::string destination) {
1409 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
1410 if (parent == nullptr || source.empty() || destination.empty())
1411 return;
1412
1413 vital::ModulationConnection* connection = getConnection(source, destination);
1414 if (connection == nullptr) {
1415 positionModulationAmountSliders();
1416 return;
1417 }
1418
1419 int index = connection->modulation_processor->index();
1420 if (aux_connections_from_to_.count(index)) {
1421 float current_value = connection->modulation_processor->currentBaseValue();
1422 int dest_index = aux_connections_from_to_[index];
1423 ModulationAmountKnob* modulation_amount = modulation_amount_sliders_[dest_index].get();
1425 float reset_value = current_value == 0.0f ? 1.0f : -current_value;
1426 modulation_amount->setValue(reset_value, dontSendNotification);
1427 modulation_amount->setValue(current_value * 2.0f, sendNotificationSync);
1428 }
1429 else
1431
1432 modifying_ = true;
1433 parent->disconnectModulation(source, destination);
1434 modulationsChanged(destination);
1435 modifying_ = false;
1436 positionModulationAmountSliders();
1437}
1438
1440 modulation_amount_sliders_[index]->setValue(value, dontSendNotification);
1441 modulation_hover_sliders_[index]->setValue(value, dontSendNotification);
1442 selected_modulation_sliders_[index]->setValue(value, dontSendNotification);
1443 modulation_amount_sliders_[index]->redoImage();
1444 modulation_hover_sliders_[index]->redoImage();
1445 selected_modulation_sliders_[index]->redoImage();
1446}
1447
1449 modulation_amount_sliders_[index]->setBipolar(bipolar);
1450 modulation_hover_sliders_[index]->setBipolar(bipolar);
1451 selected_modulation_sliders_[index]->setBipolar(bipolar);
1452}
1453
1455 setModulationSliderValue(index, value);
1456 float from_value = value;
1457 int from_index = index;
1458 while (aux_connections_from_to_.count(from_index)) {
1459 from_index = aux_connections_from_to_[from_index];
1460 from_value *= 2.0f;
1461 setModulationSliderValue(from_index, from_value);
1462 }
1463
1464 float to_value = value;
1465 int to_index = index;
1466 while (aux_connections_to_from_.count(to_index)) {
1467 to_index = aux_connections_to_from_[to_index];
1468 to_value *= 0.5f;
1469 setModulationSliderValue(to_index, to_value);
1470 }
1471
1473}
1474
1476 int end_index = index;
1477 float scale = 1.0f;
1478 while (aux_connections_from_to_.count(end_index)) {
1479 end_index = aux_connections_from_to_[end_index];
1480 scale *= 2.0f;
1481 }
1482
1483 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
1484 if (parent == nullptr)
1485 return;
1486
1488 vital::ModulationConnection* connection = bank.atIndex(end_index);
1489 if (!connection->destination_name.empty()) {
1492 float display_multiply = scale * (details.max - details.min);
1493 modulation_amount_sliders_[index]->setDisplayMultiply(display_multiply);
1494 modulation_hover_sliders_[index]->setDisplayMultiply(display_multiply);
1495 selected_modulation_sliders_[index]->setDisplayMultiply(display_multiply);
1496 return;
1497 }
1498 }
1499 modulation_amount_sliders_[index]->setDisplayMultiply(1.0f);
1500 modulation_hover_sliders_[index]->setDisplayMultiply(1.0f);
1501 selected_modulation_sliders_[index]->setDisplayMultiply(1.0f);
1502}
1503
1504void ModulationManager::setModulationValues(std::string source, std::string destination,
1505 vital::mono_float amount, bool bipolar, bool stereo, bool bypass) {
1506 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
1507 if (parent == nullptr || source.empty() || destination.empty())
1508 return;
1509
1510 modifying_ = true;
1511 parent->setModulationValues(source, destination, amount, bipolar, stereo, bypass);
1512 int index = getModulationIndex(source, destination);
1513 parent->notifyModulationValueChanged(index);
1514 setModulationSliderValues(index, amount);
1515 setModulationSliderBipolar(index, bipolar);
1516
1517 modifying_ = false;
1518}
1519
1521 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
1522 if (parent == nullptr)
1523 return;
1524
1525 for (int i = 0; i < vital::kMaxModulationConnections; ++i) {
1526 modulation_amount_sliders_[i]->removeAux();
1527 modulation_hover_sliders_[i]->removeAux();
1528 selected_modulation_sliders_[i]->removeAux();
1529 }
1530
1531 aux_connections_from_to_.clear();
1532 aux_connections_to_from_.clear();
1533
1535 for (int i = 0; i < vital::kMaxModulationConnections; ++i) {
1536 vital::ModulationConnection* connection = bank.atIndex(i);
1537 int index = connection->modulation_processor->index();
1538
1539 if (modulation_amount_lookup_.count(connection->destination_name)) {
1540 int modulation_index = modulation_amount_lookup_[connection->destination_name]->index();
1541 addAuxConnection(index, modulation_index);
1542 }
1543 }
1544}
1545
1547 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
1548 if (parent == nullptr || modifying_)
1549 return;
1550
1551 for (auto& meter : meter_lookup_) {
1552 int num_modulations = parent->getSynth()->getNumModulations(meter.first);
1553 meter.second->setModulated(num_modulations);
1554 meter.second->setVisible(num_modulations);
1555 }
1556
1557 for (auto& button : modulation_buttons_)
1558 button.second->setActiveModulation(button.second->isActiveModulation());
1559
1561 if (getWidth() > 0)
1562 positionModulationAmountSliders();
1564}
1565
1567 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
1568 if (parent == nullptr || changing_hover_modulation_)
1569 return;
1570
1572 for (int i = 0; i < vital::kMaxModulationConnections; ++i) {
1573 vital::ModulationConnection* connection = bank.atIndex(i);
1574 int index = connection->modulation_processor->index();
1575 if (connection->source_name.empty() || connection->destination_name.empty())
1576 modulation_hover_sliders_[index]->hideImmediately();
1577 else {
1578 SynthSlider* slider = slider_model_lookup_[connection->destination_name];
1579 if (slider == nullptr || !slider->isShowing())
1580 modulation_hover_sliders_[index]->hideImmediately();
1581 }
1582 }
1583}
1584
1586 float mult = 1.0f;
1587 while (aux_connections_to_from_.count(index)) {
1588 index = aux_connections_to_from_[index];
1589 mult *= 0.5f;
1590 }
1591
1592 return mult;
1593}
1594
1595void ModulationManager::addAuxConnection(int from_index, int to_index) {
1596 if (from_index == to_index)
1597 return;
1598
1599 aux_connections_to_from_[to_index] = from_index;
1600 aux_connections_from_to_[from_index] = to_index;
1601 std::string aux_name = "modulation_" + std::to_string(from_index + 1) + "_amount";
1602 modulation_hover_sliders_[to_index]->setAux(aux_name);
1603 modulation_amount_sliders_[to_index]->setAux(aux_name);
1604}
1605
1607 if (aux_connections_from_to_.count(from_index) == 0)
1608 return;
1609
1610 int to_index = aux_connections_from_to_[from_index];
1611 modulation_hover_sliders_[to_index]->removeAux();
1612 modulation_amount_sliders_[to_index]->removeAux();
1613 aux_connections_from_to_.erase(from_index);
1614 aux_connections_to_from_.erase(to_index);
1615}
1616
1618 if (aux_connections_to_from_.count(to_index) == 0)
1619 return;
1620
1621 modulation_hover_sliders_[to_index]->removeAux();
1622 modulation_amount_sliders_[to_index]->removeAux();
1623 aux_connections_from_to_.erase(aux_connections_to_from_[to_index]);
1624 aux_connections_to_from_.erase(to_index);
1625}
1626
1627void ModulationManager::makeCurrentModulatorAmountsVisible() {
1628 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
1629 if (current_modulator_ == nullptr || parent == nullptr)
1630 return;
1631
1632 std::string source_name = current_modulator_->getName().toStdString();
1633 std::vector<vital::ModulationConnection*> connections = parent->getSynth()->getSourceConnections(source_name);
1634 std::set<ModulationAmountKnob*> selected_modulation_sliders;
1635
1636 int width = size_ratio_ * 24.0f;
1637 for (vital::ModulationConnection* connection : connections) {
1638 int index = connection->modulation_processor->index();
1639 ModulationAmountKnob* selected_slider = selected_modulation_sliders_[index].get();
1640 selected_slider->setCurrentModulator(true);
1641 selected_modulation_sliders.insert(selected_slider);
1642 if (!selected_slider->hasAux()) {
1643 selected_slider->setValue(connection->modulation_processor->currentBaseValue(), dontSendNotification);
1644 selected_slider->redoImage();
1645 }
1646 selected_slider->setSource(connection->source_name);
1647 selected_slider->setCurrentModulator(connection->source_name == source_name);
1648 selected_slider->setBipolar(connection->modulation_processor->isBipolar());
1649 selected_slider->setStereo(connection->modulation_processor->isStereo());
1650 selected_slider->setBypass(connection->modulation_processor->isBypassed());
1651
1652 if (slider_model_lookup_.count(connection->destination_name) == 0)
1653 continue;
1654
1655 SynthSlider* destination_slider = slider_model_lookup_[connection->destination_name];
1656 if (slider_model_lookup_[connection->destination_name] == nullptr)
1657 return;
1658 Rectangle<int> destination_bounds = getLocalArea(destination_slider, destination_slider->getLocalBounds());
1659
1660 int center_x = destination_bounds.getCentreX();
1661 int left = destination_bounds.getX();
1662 int right = destination_bounds.getRight();
1663
1664 int bottom = destination_bounds.getBottom();
1665 int top = destination_bounds.getY();
1666 int center_y = destination_bounds.getCentreY();
1667
1668 BubbleComponent::BubblePlacement placement = destination_slider->getModulationPlacement();
1669 selected_slider->setPopupPlacement(placement);
1670 if (placement == BubbleComponent::below)
1671 selected_slider->setBounds(center_x - width / 2, bottom, width, width);
1672 else if (placement == BubbleComponent::above)
1673 selected_slider->setBounds(center_x - width / 2, top - width, width, width);
1674 else if (placement == BubbleComponent::left)
1675 selected_slider->setBounds(left - width, center_y - width / 2, width, width);
1676 else
1677 selected_slider->setBounds(right, center_y - width / 2, width, width);
1678
1679 selected_slider->makeVisible(destination_slider->isShowing());
1680 }
1681
1682 for (auto& selected_slider : selected_modulation_sliders_) {
1683 if (selected_modulation_sliders.count(selected_slider.get()) == 0)
1684 selected_slider->makeVisible(false);
1685 }
1686}
1687
1688void ModulationManager::makeModulationsVisible(SynthSlider* destination, bool visible) {
1689 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
1690 if (destination == nullptr || parent == nullptr || changing_hover_modulation_)
1691 return;
1692
1693 std::string name = destination->getName().toStdString();
1694 if (slider_model_lookup_[name] != destination)
1695 return;
1696
1697 std::vector<vital::ModulationConnection*> connections = parent->getSynth()->getDestinationConnections(name);
1698 std::vector<ModulationAmountKnob*> modulation_hover_sliders;
1699
1700 bool current_modulation_showing = false;
1701 for (vital::ModulationConnection* connection : connections) {
1702 int index = connection->modulation_processor->index();
1703 ModulationAmountKnob* hover_slider = modulation_hover_sliders_[index].get();
1704 if (current_modulator_ && current_modulator_->getName() == String(connection->source_name))
1705 current_modulation_showing = true;
1706 else
1707 modulation_hover_sliders.push_back(hover_slider);
1708 if (!hover_slider->hasAux()) {
1709 hover_slider->setValue(connection->modulation_processor->currentBaseValue(), dontSendNotification);
1710 hover_slider->redoImage();
1711 }
1712 hover_slider->setSource(connection->source_name);
1713 hover_slider->setBipolar(connection->modulation_processor->isBipolar());
1714 hover_slider->setStereo(connection->modulation_processor->isStereo());
1715 hover_slider->setBypass(connection->modulation_processor->isBypassed());
1716 }
1717
1718 int hover_slider_width = size_ratio_ * 24.0f;
1719 if (current_modulation_showing) {
1720 auto position = modulation_hover_sliders.begin() + (modulation_hover_sliders.size() + 1) / 2;
1721 modulation_hover_sliders.insert(position, nullptr);
1722 if (modulation_hover_sliders.size() % 2 == 0)
1723 modulation_hover_sliders.insert(modulation_hover_sliders.end(), nullptr);
1724 }
1725 int num_sliders = (int)modulation_hover_sliders.size();
1726
1727 Rectangle<int> destination_bounds = getLocalArea(destination, destination->getLocalBounds());
1728 int x = destination_bounds.getRight();
1729 int y = destination_bounds.getBottom();
1730 int beginning_offset = hover_slider_width * num_sliders / 2;
1731 int delta_x = 0;
1732 int delta_y = 0;
1733
1734 BubbleComponent::BubblePlacement placement = destination->getModulationPlacement();
1735 if (placement == BubbleComponent::below) {
1736 x = destination_bounds.getCentreX() - beginning_offset;
1737 delta_x = hover_slider_width;
1738 }
1739 else if (placement == BubbleComponent::above) {
1740 x = destination_bounds.getCentreX() - beginning_offset;
1741 y = destination_bounds.getY() - hover_slider_width;
1742 delta_x = hover_slider_width;
1743 }
1744 else if (placement == BubbleComponent::left) {
1745 x = destination_bounds.getX() - hover_slider_width;
1746 y = destination_bounds.getCentreY() - beginning_offset;
1747 delta_y = hover_slider_width;
1748 }
1749 else {
1750 y = destination_bounds.getCentreY() - beginning_offset;
1751 delta_y = hover_slider_width;
1752 }
1753
1754 std::unordered_set<ModulationAmountKnob*> lookup(modulation_hover_sliders.begin(), modulation_hover_sliders.end());
1755 for (auto& hover_slider : modulation_hover_sliders_) {
1756 if (lookup.count(hover_slider.get()) == 0)
1757 hover_slider->makeVisible(false);
1758 }
1759
1760 for (ModulationAmountKnob* hover_slider : modulation_hover_sliders) {
1761 if (hover_slider) {
1762 hover_slider->setPopupPlacement(placement);
1763 hover_slider->setBounds(x, y, hover_slider_width, hover_slider_width);
1764 hover_slider->makeVisible(visible);
1765 hover_slider->redoImage();
1766 }
1767 x += delta_x;
1768 y += delta_y;
1769 }
1770}
1771
1772void ModulationManager::positionModulationAmountSlidersInside(const std::string& source,
1773 std::vector<vital::ModulationConnection*> connections) {
1774 static constexpr float kRightPopupPositionX = 150;
1775 int total_connections = static_cast<int>(connections.size());
1776 ModulationButton* modulation_button = modulation_buttons_[source];
1777 ExpandModulationButton* expand_button = modulation_callout_buttons_[source].get();
1778 expand_button->setVisible(false);
1779
1780 if (expand_button == current_expanded_modulation_)
1781 hideModulationAmountCallout();
1782
1783 for (int i = 0; i < total_connections; ++i) {
1784 vital::ModulationConnection* connection = connections[i];
1785 int index = connection->modulation_processor->index();
1786 ModulationAmountKnob* slider = modulation_amount_sliders_[index].get();
1787 slider->setVisible(showingInParents(modulation_button));
1788 Point<int> point = getLocalPoint(modulation_button, Point<int>(0, 0));
1789 slider->setBounds(modulation_button->getModulationAmountBounds(i, total_connections) + point);
1790
1791 BubbleComponent::BubblePlacement popup_position = BubbleComponent::below;
1792 if (slider->getX() < kRightPopupPositionX)
1793 popup_position = BubbleComponent::right;
1794 if (getWidth() - slider->getRight() < kRightPopupPositionX)
1795 popup_position = BubbleComponent::left;
1796 slider->setPopupPlacement(popup_position);
1797
1798 std::string name = connection->destination_name;
1799 if (slider_model_lookup_.count(name))
1800 slider->setDestinationComponent(slider_model_lookup_[name], name);
1801 else
1802 slider->setDestinationComponent(nullptr, name);
1803
1804 slider->setMouseClickGrabsKeyboardFocus(true);
1805 slider->redoImage();
1806 }
1807}
1808
1809void ModulationManager::positionModulationAmountSlidersCallout(const std::string& source,
1810 std::vector<vital::ModulationConnection*> connections) {
1811 ModulationButton* modulation_button = modulation_buttons_[source];
1812 ExpandModulationButton* expand_button = modulation_callout_buttons_[source].get();
1813 expand_button->setBounds(getLocalArea(modulation_button, modulation_button->getModulationAreaBounds()));
1814 expand_button->setVisible(showingInParents(modulation_button));
1815
1816 std::vector<ModulationAmountKnob*> amount_controls;
1817 for (vital::ModulationConnection* connection : connections) {
1818 int index = connection->modulation_processor->index();
1819 amount_controls.push_back(modulation_amount_sliders_[index].get());
1820 ModulationAmountKnob* slider = modulation_amount_sliders_[index].get();
1821
1822 std::string name = connection->destination_name;
1823 if (slider_model_lookup_.count(name))
1824 slider->setDestinationComponent(slider_model_lookup_[name], name);
1825 else
1826 slider->setDestinationComponent(nullptr, name);
1827
1828 slider->setVisible(false);
1829 }
1830
1831 expand_button->setSliders(amount_controls);
1832 if (expand_button == current_expanded_modulation_)
1833 showModulationAmountCallout(source);
1834}
1835
1836void ModulationManager::showModulationAmountCallout(const std::string& source) {
1837 static constexpr int kSliderWidth = 30;
1838 static constexpr int kPadding = 5;
1839
1840 ModulationButton* modulation_button = modulation_buttons_[source];
1841 current_expanded_modulation_ = modulation_callout_buttons_[source].get();
1842 std::vector<ModulationAmountKnob*> amount_controls = current_expanded_modulation_->getSliders();
1843
1844 int num_sliders = static_cast<int>(amount_controls.size());
1845 int columns = current_expanded_modulation_->getNumColumns(num_sliders);
1846 int rows = (num_sliders + columns - 1) / columns;
1847 int width = kSliderWidth * columns + 2 * kPadding;
1848 int height = kSliderWidth * rows + 2 * kPadding;
1849 Rectangle<int> top_level_modulation_bounds = getLocalArea(modulation_button, modulation_button->getLocalBounds());
1850 int start_x = top_level_modulation_bounds.getX() + (modulation_button->getWidth() - width) / 2;
1851 start_x = std::min(getWidth() - width, std::max(0, start_x));
1852 int start_y = top_level_modulation_bounds.getBottom();
1853 start_y = std::min(getHeight() - height, start_y);
1854
1855 modulation_expansion_box_.setVisible(true);
1856 modulation_expansion_box_.setAmountControls(amount_controls);
1857 modulation_expansion_box_.setBounds(start_x, start_y, width, height);
1858 modulation_expansion_box_.setRounding(findValue(Skin::kBodyRounding));
1859 modulation_expansion_box_.grabKeyboardFocus();
1860
1861 int row = 0;
1862 int column = 0;
1863 for (ModulationAmountKnob* slider : amount_controls) {
1864 int x = column * kSliderWidth + kPadding;
1865 int y = height - (row + 1) * kSliderWidth - kPadding;
1866 slider->setBounds(start_x + x, start_y + y, kSliderWidth, kSliderWidth);
1867 slider->setVisible(true);
1868 slider->setMouseClickGrabsKeyboardFocus(false);
1869 slider->redoImage();
1870 slider->getQuadComponent()->setAlwaysOnTop(true);
1871
1872 column++;
1873 if (column >= columns) {
1874 column = 0;
1875 row++;
1876 }
1877 }
1878}
1879
1880void ModulationManager::hideModulationAmountCallout() {
1881 if (current_expanded_modulation_ == nullptr)
1882 return;
1883
1884 std::vector<ModulationAmountKnob*> amount_controls = current_expanded_modulation_->getSliders();
1885 for (ModulationAmountKnob* slider : amount_controls) {
1886 slider->setVisible(false);
1887 slider->getQuadComponent()->setAlwaysOnTop(false);
1888 }
1889
1890 modulation_expansion_box_.setVisible(false);
1891 current_expanded_modulation_ = nullptr;
1892}
1893
1894void ModulationManager::positionModulationAmountSliders(const std::string& source) {
1895 static constexpr int kMaxModulationsAcross = 3;
1896 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
1897 if (parent == nullptr)
1898 return;
1899
1900 ModulationButton* modulation_button = modulation_buttons_[source];
1901 Rectangle<int> modulation_area = modulation_button->getModulationAreaBounds();
1902 int area_width = std::max(1, modulation_area.getWidth());
1903 int max_modulation_height = (kMaxModulationsAcross * modulation_area.getHeight()) / area_width;
1904 int max_modulations_inside = kMaxModulationsAcross * max_modulation_height;
1905
1906 std::vector<vital::ModulationConnection*> connections = parent->getSynth()->getSourceConnections(source);
1907 int total_connections = static_cast<int>(connections.size());
1908 if (total_connections) {
1909 if (total_connections && total_connections > max_modulations_inside)
1910 positionModulationAmountSlidersCallout(source, connections);
1911 else
1912 positionModulationAmountSlidersInside(source, connections);
1913 }
1914 else
1915 modulation_callout_buttons_[source]->setVisible(false);
1916}
1917
1918void ModulationManager::positionModulationAmountSliders() {
1919 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
1920 if (parent == nullptr)
1921 return;
1922
1923 for (auto& modulation_slider : modulation_amount_sliders_)
1924 modulation_slider->setVisible(false);
1925
1926 for (auto& modulation_button : modulation_buttons_) {
1927 std::string name = modulation_button.second->getName().toStdString();
1928 positionModulationAmountSliders(name);
1929 }
1930}
1931
1932bool ModulationManager::enteringHoverValue() {
1933 for (int i = 0; i < vital::kMaxModulationConnections; ++i) {
1934 if (modulation_amount_sliders_[i] && modulation_amount_sliders_[i]->enteringValue())
1935 return true;
1936 if (modulation_hover_sliders_[i] && modulation_hover_sliders_[i]->enteringValue())
1937 return true;
1938 if (selected_modulation_sliders_[i] && selected_modulation_sliders_[i]->enteringValue())
1939 return true;
1940 }
1941 return false;
1942}
1943
1945 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
1946 if (parent == nullptr || modifying_)
1947 return;
1948
1950 for (int i = 0; i < vital::kMaxModulationConnections; ++i) {
1951 vital::ModulationConnection* connection = bank.atIndex(i);
1952 if (aux_connections_to_from_.count(i) == 0)
1953 setModulationSliderValues(i, connection->modulation_processor->currentBaseValue());
1954
1955 bool bipolar = connection->modulation_processor->isBipolar();
1956 bool stereo = connection->modulation_processor->isStereo();
1957 bool bypass = connection->modulation_processor->isBypassed();
1958 modulation_amount_sliders_[i]->setBipolar(bipolar);
1959 modulation_amount_sliders_[i]->setStereo(stereo);
1960 modulation_amount_sliders_[i]->setBypass(bypass);
1961
1962 modulation_hover_sliders_[i]->setBipolar(bipolar);
1963 modulation_hover_sliders_[i]->setStereo(stereo);
1964 modulation_hover_sliders_[i]->setBypass(bypass);
1965 }
1966}
1967
1969 for (auto& meter : meter_lookup_) {
1970 SynthSlider* slider = slider_model_lookup_[meter.first];
1971 if (slider && slider->isShowing()) {
1972 Rectangle<int> local_bounds = getLocalArea(slider, slider->getModulationMeterBounds());
1973 meter.second->setBounds(local_bounds);
1974 }
1975 }
1976}
Definition modulation_manager.cpp:26
ExpandModulationButton()
Definition modulation_manager.cpp:28
void renderSliderQuads(OpenGlWrapper &open_gl, bool animate)
Definition modulation_manager.cpp:55
int getNumColumns(int num_sliders)
Definition modulation_manager.cpp:37
std::vector< ModulationAmountKnob * > getSliders()
Definition modulation_manager.cpp:53
void setSliders(std::vector< ModulationAmountKnob * > sliders)
Definition modulation_manager.cpp:46
Interface for objects interested in ModulationAmountKnob events.
Definition modulation_manager.h:44
A specialized SynthSlider that represents a single modulation amount control.
Definition modulation_manager.h:27
void setStereo(bool stereo)
Definition modulation_manager.h:147
bool isBypass()
Definition modulation_manager.h:149
bool isCurrentModulator()
Definition modulation_manager.h:153
String getOriginalName()
Gets the knob's original name before auxiliary assignment.
Definition modulation_manager.h:185
void handleModulationMenuCallback(int result)
Handles a selection from the modulation context menu.
Definition modulation_manager.cpp:272
void makeVisible(bool visible)
Toggles the knob's visibility smoothly or immediately.
Definition modulation_manager.cpp:298
bool isStereo()
Definition modulation_manager.h:150
void setBipolar(bool bipolar)
Definition modulation_manager.h:148
int index()
Definition modulation_manager.h:154
void setDestinationComponent(Component *component, const std::string &name)
Associates the knob with a destination component (slider or UI element).
Definition modulation_manager.cpp:322
void setSource(const std::string &name)
Sets the source parameter name for this modulation.
Definition modulation_manager.cpp:336
bool hasAux()
Checks if this knob currently has an auxiliary modulation.
Definition modulation_manager.h:170
ModulationAmountKnob(String name, int index)
Constructs a ModulationAmountKnob.
Definition modulation_manager.cpp:172
void setCurrentModulator(bool current)
Marks this knob as representing the currently selected modulator.
Definition modulation_manager.cpp:314
void hideImmediately()
Hides the knob immediately without animations.
Definition modulation_manager.cpp:307
void mouseExit(const MouseEvent &e) override
Definition modulation_manager.cpp:256
void mouseDown(const MouseEvent &e) override
Mouse event overrides for custom behavior.
Definition modulation_manager.cpp:191
Colour getInternalColor()
Returns the internal color for drawing this knob.
Definition modulation_manager.cpp:330
@ kDisconnect
Removes the modulation connection.
Definition modulation_manager.h:34
@ kToggleBypass
Toggles bypassing the modulation.
Definition modulation_manager.h:35
@ kToggleBipolar
Toggles bipolar (positive and negative) modulation.
Definition modulation_manager.h:36
@ kToggleStereo
Toggles stereo modulation mode.
Definition modulation_manager.h:37
void setBypass(bool bypass)
Definition modulation_manager.h:146
void mouseUp(const MouseEvent &e) override
Definition modulation_manager.cpp:241
bool isBipolar()
Definition modulation_manager.h:151
A component representing a modulation source or connection button.
Definition modulation_button.h:21
void setActiveModulation(bool active)
Sets whether this modulation source is actively modulating something.
Definition modulation_button.cpp:303
Rectangle< int > getMeterBounds()
Gets the bounding area used for a modulation meter.
Definition modulation_button.cpp:51
Rectangle< int > getModulationAmountBounds(int index, int total)
Gets the bounds for a modulation amount knob at a given index.
Definition modulation_button.cpp:37
bool isActiveModulation() const
Checks if the modulation source is active.
Definition modulation_button.h:238
bool hasAnyModulation()
Checks if this modulation source is currently connected to any parameters.
Definition modulation_button.cpp:31
Rectangle< int > getModulationAreaBounds()
Gets the bounding area used for modulation knobs.
Definition modulation_button.cpp:60
Definition modulation_manager.cpp:97
bool isActive()
Definition modulation_manager.cpp:156
void setIndex(int index)
Definition modulation_manager.cpp:152
void setRectangle(bool rectangle)
Definition modulation_manager.cpp:149
void setActive(bool active)
Definition modulation_manager.cpp:109
ModulationDestination()=delete
virtual ~ModulationDestination()
Definition modulation_manager.cpp:106
void setRotary(bool rotary)
Definition modulation_manager.cpp:150
void setMargin(int margin)
Definition modulation_manager.cpp:151
bool hasExtraModulationTarget()
Definition modulation_manager.cpp:154
int getIndex()
Definition modulation_manager.cpp:157
bool isRotary()
Definition modulation_manager.cpp:155
void setSizeMultiple(float multiple)
Definition modulation_manager.cpp:111
Rectangle< float > getFillBounds()
Definition modulation_manager.cpp:116
ModulationDestination(SynthSlider *source)
Definition modulation_manager.cpp:99
SynthSlider * getDestinationSlider()
Definition modulation_manager.cpp:108
void setAmountControls(std::vector< ModulationAmountKnob * > amount_controls)
Sets the amount controls displayed inside this expansion box.
Definition modulation_manager.h:260
void addListener(Listener *listener)
Adds a Listener for expansion box focus events.
Definition modulation_manager.h:266
void modulationDragged(const MouseEvent &e) override
Called while modulation is being dragged out (during mouse drag).
Definition modulation_manager.cpp:866
void disconnectModulation(ModulationAmountKnob *modulation_knob) override
Called when the modulation is disconnected.
Definition modulation_manager.cpp:948
void renderMeters(OpenGlWrapper &open_gl, bool animate)
Definition modulation_manager.cpp:1084
void renderOpenGlComponents(OpenGlWrapper &open_gl, bool animate) override
Renders all OpenGL components in this section and sub-sections.
Definition modulation_manager.cpp:1052
void setModulationBipolar(ModulationAmountKnob *modulation_knob, bool bipolar) override
Called when modulation bipolar state changes.
Definition modulation_manager.cpp:983
void drawDraggingModulation(OpenGlWrapper &open_gl)
Definition modulation_manager.cpp:1031
void setModulationSliderValue(int index, float value)
Sets the modulation value for a given modulation index.
Definition modulation_manager.cpp:1439
void removeModulation(std::string source, std::string destination)
Removes a modulation connection between a source and a destination.
Definition modulation_manager.cpp:1408
void drawModulationDestinations(OpenGlWrapper &open_gl)
Definition modulation_manager.cpp:1010
void modulationWheelMoved(const MouseEvent &e, const MouseWheelDetails &wheel) override
Called when the mouse wheel moves while interacting with the modulation button.
Definition modulation_manager.cpp:900
int getModulationIndex(std::string source, std::string destination)
Definition modulation_manager.cpp:1266
void modulationAmountChanged(SynthSlider *slider) override
Definition modulation_manager.cpp:604
void resized() override
Called when the component is resized. Arranges layout of child components.
Definition modulation_manager.cpp:516
void modulationsChanged(const std::string &name) override
Definition modulation_manager.cpp:1246
void initOpenGlComponents(OpenGlWrapper &open_gl) override
Initializes all OpenGL components in this section and sub-sections.
Definition modulation_manager.cpp:991
ModulationManager(std::map< std::string, ModulationButton * > modulation_buttons, std::map< std::string, SynthSlider * > sliders, vital::output_map mono_modulations, vital::output_map poly_modulations)
Constructs a ModulationManager.
Definition modulation_manager.cpp:341
void mouseDown(SynthSlider *slider) override
Definition modulation_manager.cpp:1318
void createModulationMeter(const vital::Output *mono_total, const vital::Output *poly_total, SynthSlider *slider, OpenGlMultiQuad *quads, int index)
Creates a modulation meter for a given slider and places it into an OpenGlMultiQuad for visualization...
Definition modulation_manager.cpp:491
void setModulationSliderValues(int index, float value)
Sets the modulation values and updates all connected auxiliary nodes.
Definition modulation_manager.cpp:1454
void mouseUp(SynthSlider *slider) override
Definition modulation_manager.cpp:1337
int getIndexForModulationSlider(Slider *slider)
Definition modulation_manager.cpp:1278
void startModulationMap(ModulationButton *source, const MouseEvent &e) override
Called when the user begins dragging out to map the modulation (dragging out of the button).
Definition modulation_manager.cpp:663
void clearTemporaryHoverModulation()
Definition modulation_manager.cpp:856
float getAuxMultiplier(int index)
Definition modulation_manager.cpp:1585
void modulationSelected(ModulationButton *source) override
Called when this modulation button is selected.
Definition modulation_manager.cpp:631
void setModulationSettings(ModulationAmountKnob *modulation_knob)
Definition modulation_manager.cpp:958
void setModulationSliderBipolar(int index, bool bipolar)
Sets the bipolar state for a given modulation index.
Definition modulation_manager.cpp:1448
void clearModulationSource()
Definition modulation_manager.cpp:939
void destroyOpenGlComponents(OpenGlWrapper &open_gl) override
Destroys all OpenGL components in this section and sub-sections.
Definition modulation_manager.cpp:1166
void reset() override
Resets the section and all sub-sections.
Definition modulation_manager.cpp:1546
void hoverEnded(SynthSlider *slider) override
Definition modulation_manager.cpp:1237
void doubleClick(SynthSlider *slider) override
Definition modulation_manager.cpp:1342
void modulationCleared() override
Called when all modulations associated with this button have been cleared.
Definition modulation_manager.cpp:648
void renderSourceMeters(OpenGlWrapper &open_gl, int index)
Definition modulation_manager.cpp:1107
void endModulationMap() override
Called when the user finishes dragging out modulation connections.
Definition modulation_manager.cpp:914
void modulationDraggedToHoverSlider(ModulationAmountKnob *hover_slider)
Definition modulation_manager.cpp:759
void modulationLostFocus(ModulationButton *source) override
Called when the modulation button loses keyboard focus.
Definition modulation_manager.cpp:934
void drawCurrentModulator(OpenGlWrapper &open_gl)
Definition modulation_manager.cpp:1018
void connectModulation(std::string source, std::string destination)
Connects a modulation source to a destination parameter.
Definition modulation_manager.cpp:1398
void hoverStarted(SynthSlider *slider) override
Definition modulation_manager.cpp:1223
void createModulationSlider(std::string name, SynthSlider *slider, bool poly)
Creates internal structures for handling modulation of a given parameter (slider).
Definition modulation_manager.cpp:503
void updateSmoothModValues()
Definition modulation_manager.cpp:1146
void setModulationBypass(ModulationAmountKnob *modulation_knob, bool bypass) override
Called when modulation bypass state changes.
Definition modulation_manager.cpp:979
void hideUnusedHoverModulations()
Definition modulation_manager.cpp:1566
void setVisibleMeterBounds()
Definition modulation_manager.cpp:1968
~ModulationManager()
Destructor.
Definition modulation_manager.cpp:514
void setModulationSliderScale(int index)
Adjusts the displayed range of a modulation slider based on parameter scaling.
Definition modulation_manager.cpp:1475
void setModulationAmounts()
Definition modulation_manager.cpp:1944
void modulationClicked(ModulationButton *source) override
Called when the modulation button is clicked (mouse up) without dragging out.
Definition modulation_manager.cpp:643
void initAuxConnections()
Definition modulation_manager.cpp:1520
void addAuxConnection(int from_index, int to_index)
Definition modulation_manager.cpp:1595
void buttonClicked(Button *button) override
Called when a button is clicked. Updates the synth parameter accordingly.
Definition modulation_manager.cpp:1384
void setTemporaryModulationBipolar(Component *component, bool bipolar)
Definition modulation_manager.cpp:822
void clearTemporaryModulation()
Definition modulation_manager.cpp:843
void modulationDisconnected(vital::ModulationConnection *connection, bool last) override
Called when a modulation connection is disconnected.
Definition modulation_manager.cpp:621
void menuFinished(SynthSlider *slider) override
Definition modulation_manager.cpp:1241
void removeAuxDestinationConnection(int to_index)
Definition modulation_manager.cpp:1617
void modulationRemoved(SynthSlider *slider) override
Definition modulation_manager.cpp:613
void modulationDraggedToComponent(Component *component, bool bipolar)
Definition modulation_manager.cpp:781
void parentHierarchyChanged() override
Definition modulation_manager.cpp:570
vital::ModulationConnection * getConnection(int index)
Definition modulation_manager.cpp:1296
void updateModulationMeterLocations()
Definition modulation_manager.cpp:588
bool hasFreeConnection()
Definition modulation_manager.cpp:652
void removeAuxSourceConnection(int from_index)
Definition modulation_manager.cpp:1606
void endModulationEdit(SynthSlider *slider) override
Definition modulation_manager.cpp:1357
void sliderValueChanged(Slider *slider) override
Called when a slider value changes. Updates the synth parameter accordingly.
Definition modulation_manager.cpp:1361
void setModulationValues(std::string source, std::string destination, vital::mono_float amount, bool bipolar, bool stereo, bool bypass)
Sets multiple modulation properties at once.
Definition modulation_manager.cpp:1504
vital::ModulationConnection * getConnectionForModulationSlider(Slider *slider)
Definition modulation_manager.cpp:1285
void setModulationStereo(ModulationAmountKnob *modulation_knob, bool stereo) override
Called when modulation stereo state changes.
Definition modulation_manager.cpp:987
void beginModulationEdit(SynthSlider *slider) override
Definition modulation_manager.cpp:1353
static String getMenuSourceDisplayName(const String &original)
Returns a user-friendly display name for a given source string in menu context.
Definition modulation_matrix.cpp:513
A visual component that displays the current modulation amount applied to a slider parameter.
Definition modulation_meter.h:21
void setModulationAmountQuad(OpenGlQuad &quad, float amount, bool bipolar)
Sets the given quad to represent a modulation amount line or arc.
Definition modulation_meter.cpp:180
const SynthSlider * destination()
Gets the destination slider that the meter is visualizing.
Definition modulation_meter.h:100
bool isRotary() const
Checks if the associated parameter control is a rotary knob.
Definition modulation_meter.h:82
void setModulated(bool modulated)
Sets the modulated state of the meter.
Definition modulation_meter.h:88
void setAmountQuadVertices(OpenGlQuad &quad)
Sets up the quad coordinates for the modulation amount indicator.
Definition modulation_meter.cpp:115
static bool setViewPort(Component *component, Rectangle< int > bounds, OpenGlWrapper &open_gl)
Sets the OpenGL viewport to match a specified rectangle within a component.
Definition open_gl_component.cpp:42
A component for rendering multiple quads using OpenGL, with customizable colors, rounding,...
Definition open_gl_multi_quad.h:16
void setThickness(float thickness, bool reset=false)
Sets the thickness used by some shaders and can reset to this thickness.
Definition open_gl_multi_quad.h:338
void setActive(bool active)
Activates or deactivates rendering of this component.
Definition open_gl_multi_quad.h:331
virtual void init(OpenGlWrapper &open_gl) override
Initializes OpenGL buffers and shader attributes.
Definition open_gl_multi_quad.cpp:37
void setQuad(int i, float x, float y, float w, float h)
Sets the position and size of a quad in normalized device space.
Definition open_gl_multi_quad.h:313
void setTargetComponent(Component *target_component)
Sets a target component to help position the quads.
Definition open_gl_multi_quad.h:358
force_inline void setAltColor(Colour color)
Sets an alternate color, often used by custom shaders.
Definition open_gl_multi_quad.h:118
virtual void render(OpenGlWrapper &open_gl, bool animate) override
Renders the quads using OpenGL.
Definition open_gl_multi_quad.cpp:92
virtual void destroy(OpenGlWrapper &open_gl) override
Releases OpenGL resources when the component is destroyed.
Definition open_gl_multi_quad.cpp:69
force_inline void setColor(Colour color)
Sets the base color for the quads.
Definition open_gl_multi_quad.h:102
void setRounding(float rounding)
Sets the rounding radius of the quads.
Definition open_gl_multi_quad.h:347
force_inline void setModColor(Colour color)
Sets a modulation color for custom effects in the shader.
Definition open_gl_multi_quad.h:126
void setAlpha(float alpha, bool reset=false)
Sets the alpha blending multiplier, can reset to this alpha.
Definition open_gl_multi_quad.h:382
void setAdditive(bool additive)
Enables or disables additive blending for rendering.
Definition open_gl_multi_quad.h:377
A convenience class for a single quad rendered via OpenGL.
Definition open_gl_multi_quad.h:447
float getModulationAmount() const
Definition synth_slider.h:160
bool isTextOrCurve() const
Definition synth_slider.h:105
void setActive(bool active=true)
Definition synth_slider.h:186
void setColors()
Updates internal colors based on the current skin and state.
Definition synth_slider.cpp:144
void redoImage(bool skip_image=false)
Definition synth_slider.cpp:64
SynthSection * getSectionParent()
Definition synth_slider.h:286
void setAlpha(float alpha, bool reset=false)
Definition synth_slider.h:278
void setModulationKnob()
Marks this slider as a modulation knob.
Definition synth_slider.h:152
OpenGlComponent * getQuadComponent()
Definition synth_slider.h:141
SynthSection * parent_
The parent SynthSection.
Definition synth_slider.h:289
bool isActive() const
Definition synth_slider.h:172
A ToggleButton that uses an OpenGlButtonComponent for its rendering.
Definition synth_button.h:342
void setLightenButton()
Configures the button as a lighten button.
Definition synth_button.h:387
Manages and provides access to vertex and fragment shaders used by the OpenGL rendering pipeline.
Definition shaders.h:19
@ kLinearModulationFragment
Definition shaders.h:75
@ kRotaryModulationFragment
Definition shaders.h:72
@ kRoundedRectangleFragment
Definition shaders.h:69
@ kCircleFragment
Definition shaders.h:65
@ kKnobModMeterArcThickness
Definition skin.h:96
@ kWidgetMargin
Definition skin.h:103
@ kBodyRounding
Definition skin.h:71
@ kKnobOffset
Definition skin.h:97
@ kLabelBackgroundRounding
Definition skin.h:74
@ kKnobModMeterArcSize
Definition skin.h:95
@ kModulationMeterRight
Definition skin.h:177
@ kModulationMeter
Definition skin.h:175
@ kModulationMeterLeft
Definition skin.h:176
@ kWidgetPrimary2
Definition skin.h:166
@ kWidgetPrimary1
Definition skin.h:165
@ kWidgetBackground
Definition skin.h:173
@ kLightenScreen
Definition skin.h:141
@ kRotaryArc
Definition skin.h:150
@ kModulationMeterControl
Definition skin.h:178
@ kBody
Definition skin.h:129
@ kModulationDragDrop
Definition skin.h:55
std::vector< vital::ModulationConnection * > getSourceConnections(const std::string &source)
Returns all modulation connections from a particular source.
Definition synth_base.cpp:236
bool isMidiMapped(const std::string &name)
Checks if a given parameter name is MIDI mapped.
Definition synth_base.cpp:677
vital::ModulationConnectionBank & getModulationBank()
Retrieves the ModulationConnectionBank managing all modulation connections.
Definition synth_base.cpp:730
int getNumModulations(const std::string &destination)
Counts how many modulations target a given parameter.
Definition synth_base.cpp:227
std::vector< vital::ModulationConnection * > getDestinationConnections(const std::string &destination)
Returns all modulation connections targeting a given destination parameter.
Definition synth_base.cpp:253
const vital::StatusOutput * getStatusOutput(const std::string &name)
Retrieves a status output by name.
Definition synth_base.cpp:262
An interface class linking the Vital synthesizer backend (SynthBase) with a GUI.
Definition synth_gui_interface.h:56
SynthBase * getSynth()
Returns the SynthBase instance this interface is managing.
Definition synth_gui_interface.h:85
void setModulationValues(const std::string &source, const std::string &destination, vital::mono_float amount, bool bipolar, bool stereo, bool bypass)
Sets various modulation parameters (amount, bipolar, stereo, bypass) for a given connection.
Definition synth_gui_interface.cpp:119
void disconnectModulation(std::string source, std::string destination)
Disconnects a modulation from the GUI layer.
Definition synth_gui_interface.cpp:146
void connectModulation(std::string source, std::string destination)
Connects a modulation source to a destination parameter through the GUI.
Definition synth_gui_interface.cpp:91
void notifyModulationValueChanged(int index)
Notifies the GUI that a specific modulation's value changed.
Definition synth_gui_interface.cpp:87
Base class for all synthesizer sections, providing UI layout, painting, and interaction logic.
Definition synth_section.h:193
virtual void buttonClicked(Button *clicked_button) override
Called when a button is clicked. Updates the synth parameter accordingly.
Definition synth_section.cpp:398
virtual void renderOpenGlComponents(OpenGlWrapper &open_gl, bool animate)
Renders all OpenGL components in this section and sub-sections.
Definition synth_section.cpp:357
void addSlider(SynthSlider *slider, bool show=true, bool listen=true)
Definition synth_section.cpp:445
virtual void animate(bool animate)
Triggers animation state change in sub-sections if needed.
Definition synth_section.cpp:822
virtual void resized() override
Called when the component is resized. Arranges layout of child components.
Definition synth_section.cpp:35
virtual void destroyOpenGlComponents(OpenGlWrapper &open_gl)
Destroys all OpenGL components in this section and sub-sections.
Definition synth_section.cpp:383
void showPopupSelector(Component *source, Point< int > position, const PopupItems &options, std::function< void(int)> callback, std::function< void()> cancel={ })
Shows a popup selector with options.
Definition synth_section.cpp:119
float size_ratio_
Definition synth_section.h:821
float findValue(Skin::ValueId value_id) const
Finds a value in the skin overrides or from the parent if not found locally.
Definition synth_section.cpp:18
bool isActive() const
Checks if the section is currently active.
Definition synth_section.h:683
void addOpenGlComponent(OpenGlComponent *open_gl_component, bool to_beginning=false)
Definition synth_section.cpp:489
virtual void sliderValueChanged(Slider *moved_slider) override
Called when a slider value changes. Updates the synth parameter accordingly.
Definition synth_section.cpp:391
const SynthSection * parent_
Definition synth_section.h:814
void setSkinOverride(Skin::SectionOverride skin_override)
Definition synth_section.h:303
virtual void initOpenGlComponents(OpenGlWrapper &open_gl)
Initializes all OpenGL components in this section and sub-sections.
Definition synth_section.cpp:349
Listener interface for receiving slider events such as mouse interactions, modulation changes,...
Definition synth_slider.h:347
A specialized slider with extended functionality for modulation, parameter control,...
Definition synth_slider.h:314
void setShowPopupOnHover(bool show)
Definition synth_slider.h:629
bool has_parameter_assignment_
Definition synth_slider.h:736
bool isModulationBypassed() const
Definition synth_slider.h:580
virtual void mouseDown(const MouseEvent &e) override
Mouse event overrides for custom behavior.
Definition synth_slider.cpp:234
float findValue(Skin::ValueId value_id) const override
Definition synth_slider.h:681
Rectangle< int > getModulationMeterBounds() const
Definition synth_slider.cpp:673
static constexpr float kLinearWidthPercent
Definition synth_slider.h:341
void setPopupPrefix(String prefix)
Definition synth_slider.h:633
void setTextEntrySizePercent(float width_percent, float height_percent)
Definition synth_slider.h:613
virtual void mouseExit(const MouseEvent &e) override
Definition synth_slider.cpp:327
void handlePopupResult(int result)
Definition synth_slider.cpp:703
Component * getExtraModulationTarget()
Definition synth_slider.h:653
virtual void mouseUp(const MouseEvent &e) override
Definition synth_slider.cpp:302
bool isModulationStereo() const
Definition synth_slider.h:576
void setPopupPlacement(BubbleComponent::BubblePlacement placement)
Definition synth_slider.h:515
SynthGuiInterface * synth_interface_
Definition synth_slider.h:766
BubbleComponent::BubblePlacement getModulationPlacement()
Definition synth_slider.h:531
std::vector< SliderListener * > slider_listeners_
Definition synth_slider.h:769
float getKnobSizeScale() const override
Definition synth_slider.h:641
@ kClearMidiLearn
Definition synth_slider.h:320
@ kManualEntry
Definition synth_slider.h:322
@ kArmMidiLearn
Definition synth_slider.h:319
bool isModulationBipolar() const
Definition synth_slider.h:572
A container managing a fixed number of ModulationConnections.
Definition synth_types.h:87
ModulationConnection * atIndex(int index)
Retrieves a ModulationConnection by index.
Definition synth_types.h:114
static const ValueDetails & getDetails(const std::string &name)
Definition synth_parameters.h:200
static std::string getDisplayName(const std::string &name)
Definition synth_parameters.h:212
force_inline poly_float value() const
Returns the current status value.
Definition synth_module.h:49
force_inline poly_float clamp(poly_float value, mono_float min, mono_float max)
Clamps each lane of a vector to [min, max].
Definition poly_utils.h:306
force_inline poly_float interpolate(poly_float from, poly_float to, mono_float t)
Performs a linear interpolation between two poly_floats using a scalar t in [0..1].
Definition poly_utils.h:182
std::map< std::string, Output * > output_map
Maps parameter names to Output pointers, representing output signals from various modules.
Definition synth_types.h:229
constexpr int kMaxModulationConnections
Maximum number of modulation connections allowed.
Definition synth_constants.h:49
float mono_float
Definition common.h:33
A helper struct containing references to OpenGL context, shaders, and display scale.
Definition shaders.h:174
A hierarchical structure of popup menu items for a selector component.
Definition synth_section.h:29
void addItem(int sub_id, const std::string &sub_name, bool sub_selected=false)
Adds a new item as a submenu entry.
Definition synth_section.h:51
A structure representing a single modulation connection between a modulation source and a destination...
Definition synth_types.h:30
std::unique_ptr< ModulationConnectionProcessor > modulation_processor
Processor applying scaling/mapping.
Definition synth_types.h:75
std::string destination_name
The name of the destination parameter.
Definition synth_types.h:74
std::string source_name
The name of the modulation source.
Definition synth_types.h:73
Holds and manages a buffer of samples (poly_float) for a Processor's output.
Definition processor.h:35
Holds metadata about a single parameter (control) in the Vital synthesizer.
Definition synth_parameters.h:23
mono_float max
Maximum parameter value.
Definition synth_parameters.h:41
@ kLinear
Parameter scales linearly between min and max.
Definition synth_parameters.h:30
@ kIndexed
Parameter steps through discrete indexed values.
Definition synth_parameters.h:29
mono_float min
Minimum parameter value.
Definition synth_parameters.h:40
ValueScale value_scale
The scaling mode of the parameter value.
Definition synth_parameters.h:45
Represents a vector of floating-point values using SIMD instructions.
Definition poly_values.h:600
simd_type value
The underlying SIMD register for float.
Definition poly_values.h:1112