Vital
Loading...
Searching...
No Matches
filter_section.cpp
Go to the documentation of this file.
1#include "filter_section.h"
2
3#include "skin.h"
4#include "digital_svf.h"
5#include "filter_response.h"
6#include "preset_selector.h"
7#include "fonts.h"
8#include "synth_button.h"
9#include "synth_constants.h"
10#include "synth_slider.h"
11#include "synth_strings.h"
12#include "synth_gui_interface.h"
13#include "text_look_and_feel.h"
14
15namespace {
19 constexpr int kBlendLabelWidth = 30;
20
26 int getNumStyles(int int_model) {
28 switch (model) {
30 return 5;
32 return 5;
34 return 5;
36 return 5;
38 return 2;
40 return 2;
42 return 6;
44 return 2;
45 default:
46 return 0;
47 }
48 }
49
56 std::string getStyleName(int int_model, int style) {
58 switch (model) {
63 return strings::kFilterStyleNames[style];
65 return strings::kDiodeStyleNames[style];
67 // Special handling for formant styles
69 return "The Mouth";
70 else if (style == vital::FormantFilter::kAIUO)
71 return "AIUO";
72 else
73 return "AOIE";
75 return strings::kCombStyleNames[style];
77 if (style)
78 return "Negative";
79 else
80 return "Positive";
81 default:
82 return "";
83 }
84 }
85}
86
87FilterSection::FilterSection(String name, String suffix) :
88 SynthSection(name), current_model_(0), current_style_(0), specify_input_(false) {
89 // Internal construction of the filter section common elements.
90 std::string number = suffix.toStdString();
91 model_name_ = "filter_" + number + "_model";
92 style_name_ = "filter_" + number + "_style";
93
94 cutoff_ = std::make_unique<SynthSlider>("filter_" + number + "_cutoff");
95 addSlider(cutoff_.get());
96 cutoff_->setSliderStyle(Slider::LinearBar);
97 cutoff_->setPopupPlacement(BubbleComponent::below);
98 cutoff_->setModulationPlacement(BubbleComponent::above);
99 cutoff_->setPopupPrefix("Cutoff: ");
100 setSliderHasHzAlternateDisplay(cutoff_.get());
101
102 formant_x_ = std::make_unique<SynthSlider>("filter_" + number + "_formant_x");
103 addSlider(formant_x_.get());
104 formant_x_->setSliderStyle(Slider::LinearBar);
105 formant_x_->setPopupPlacement(BubbleComponent::below);
106 formant_x_->setModulationPlacement(BubbleComponent::above);
107 formant_x_->setPopupPrefix("Formant X: ");
108
109 mix_ = std::make_unique<SynthSlider>("filter_" + number + "_mix");
110 addSlider(mix_.get());
111 mix_->setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
112
113 blend_ = std::make_unique<SynthSlider>("filter_" + number + "_blend");
114 addSlider(blend_.get());
115 blend_->snapToValue(true, 1.0);
116 blend_->setBipolar(true);
117 blend_->setSliderStyle(Slider::LinearBar);
118 blend_->setPopupPlacement(BubbleComponent::above);
119 blend_->setPopupPrefix("Blend: ");
120
121 preset_selector_ = std::make_unique<PresetSelector>();
122 addSubSection(preset_selector_.get());
123 preset_selector_->addListener(this);
124 setPresetSelector(preset_selector_.get());
125 setFilterText();
126
127 formant_transpose_ = std::make_unique<SynthSlider>("filter_" + number + "_formant_transpose");
128 addSlider(formant_transpose_.get());
129 formant_transpose_->snapToValue(true, 0.0);
130 formant_transpose_->setBipolar(true);
131 formant_transpose_->setSliderStyle(Slider::LinearBar);
132 formant_transpose_->setPopupPlacement(BubbleComponent::above);
133 formant_transpose_->setPopupPrefix("Formant Transpose: ");
134
135 resonance_ = std::make_unique<SynthSlider>("filter_" + number + "_resonance");
136 addSlider(resonance_.get());
137 resonance_->setSliderStyle(Slider::LinearBarVertical);
138 resonance_->setPopupPlacement(BubbleComponent::right);
139 resonance_->setModulationPlacement(BubbleComponent::left);
140 resonance_->setPopupPrefix("Resonance: ");
141
142 formant_y_ = std::make_unique<SynthSlider>("filter_" + number + "_formant_y");
143 addSlider(formant_y_.get());
144 formant_y_->setSliderStyle(Slider::LinearBarVertical);
145 formant_y_->setPopupPlacement(BubbleComponent::right);
146 formant_y_->setModulationPlacement(BubbleComponent::left);
147 formant_y_->setPopupPrefix("Formant Y: ");
148
149 drive_ = std::make_unique<SynthSlider>("filter_" + number + "_drive");
150 addSlider(drive_.get());
151 drive_->setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
152
153 formant_resonance_ = std::make_unique<SynthSlider>("filter_" + number + "_formant_resonance");
154 addSlider(formant_resonance_.get());
155 formant_resonance_->setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
156
157 formant_spread_ = std::make_unique<SynthSlider>("filter_" + number + "_formant_spread");
158 addSlider(formant_spread_.get());
159 formant_spread_->setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
160 formant_spread_->snapToValue(true, 0.0);
161 formant_spread_->setBipolar();
162
163 blend_transpose_ = std::make_unique<SynthSlider>("filter_" + number + "_blend_transpose");
164 addSlider(blend_transpose_.get());
165 blend_transpose_->setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
166
167 keytrack_ = std::make_unique<SynthSlider>("filter_" + number + "_keytrack");
168 addSlider(keytrack_.get());
169 keytrack_->setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
170 keytrack_->snapToValue(true, 0.0);
171 keytrack_->setBipolar();
172
173 filter_on_ = std::make_unique<SynthButton>("filter_" + number + "_on");
174 addButton(filter_on_.get());
175 setActivator(filter_on_.get());
176
177 filter_label_1_ = std::make_unique<PlainTextComponent>("label1", "DRIVE");
178 addOpenGlComponent(filter_label_1_.get());
179
180 filter_label_2_ = std::make_unique<PlainTextComponent>("label2", "KEY TRK");
181 addOpenGlComponent(filter_label_2_.get());
182
183 formant_x_->setVisible(false);
184 formant_y_->setVisible(false);
185 formant_transpose_->setVisible(false);
186 formant_resonance_->setVisible(false);
187 formant_spread_->setVisible(false);
188 blend_transpose_->setVisible(false);
189}
190
191FilterSection::FilterSection(String suffix, const vital::output_map& mono_modulations) :
192 FilterSection("FILTER", suffix) {
193 // Construct a filter section with a filter response for mono modulations.
194 filter_response_ = std::make_unique<FilterResponse>(suffix, mono_modulations);
195 addOpenGlComponent(filter_response_.get());
197 preset_selector_->setTextComponent(true);
199}
200
201FilterSection::FilterSection(int index, const vital::output_map& mono_modulations,
202 const vital::output_map& poly_modulations) :
203 FilterSection("FILTER " + String(index), String(index)) {
204 // Construct a filter section for a given index, using both mono and poly modulations.
205 setSidewaysHeading(false);
206
207 std::string number = std::to_string(index);
208 filter_response_ = std::make_unique<FilterResponse>(index, mono_modulations, poly_modulations);
209 addOpenGlComponent(filter_response_.get());
211
212 cutoff_->setExtraModulationTarget(filter_response_.get());
213
214 // Add oscillator and sample input toggle buttons
215 osc1_input_ = std::make_unique<OpenGlToggleButton>("OSC1");
216 addAndMakeVisible(osc1_input_.get());
217 addOpenGlComponent(osc1_input_->getGlComponent());
218 osc1_input_->addListener(this);
219 osc1_input_->setText("OSC1");
220 osc1_input_->setLookAndFeel(TextLookAndFeel::instance());
221
222 osc2_input_ = std::make_unique<OpenGlToggleButton>("OSC2");
223 addAndMakeVisible(osc2_input_.get());
224 addOpenGlComponent(osc2_input_->getGlComponent());
225 osc2_input_->addListener(this);
226 osc2_input_->setText("OSC2");
227 osc2_input_->setLookAndFeel(TextLookAndFeel::instance());
228
229 osc3_input_ = std::make_unique<OpenGlToggleButton>("OSC3");
230 addAndMakeVisible(osc3_input_.get());
231 addOpenGlComponent(osc3_input_->getGlComponent());
232 osc3_input_->addListener(this);
233 osc3_input_->setText("OSC3");
234 osc3_input_->setLookAndFeel(TextLookAndFeel::instance());
235
236 sample_input_ = std::make_unique<OpenGlToggleButton>("SMP");
237 addAndMakeVisible(sample_input_.get());
238 addOpenGlComponent(sample_input_->getGlComponent());
239 sample_input_->addListener(this);
240 sample_input_->setText("SMP");
241 sample_input_->setLookAndFeel(TextLookAndFeel::instance());
242
243 filter_input_ = std::make_unique<SynthButton>("filter_" + number + "_filter_input");
244 addButton(filter_input_.get());
245 filter_input_->setText(String("FIL") + String(3 - index));
246 filter_input_->setLookAndFeel(TextLookAndFeel::instance());
247 specify_input_ = true;
248
249 preset_selector_->setTextComponent(false);
251}
252
254
256 // Link sliders to the FilterResponse so that the graph updates accordingly.
257 filter_response_->setCutoffSlider(cutoff_.get());
258 filter_response_->setResonanceSlider(resonance_.get());
259 filter_response_->setFormantXSlider(formant_x_.get());
260 filter_response_->setFormantYSlider(formant_y_.get());
261 filter_response_->setBlendSlider(blend_.get());
262 filter_response_->setTransposeSlider(blend_transpose_.get());
263 filter_response_->setFormantTransposeSlider(formant_transpose_.get());
264 filter_response_->setFormantResonanceSlider(formant_resonance_.get());
265 filter_response_->setFormantSpreadSlider(formant_spread_.get());
266 filter_response_->setFilterMixSlider(mix_.get());
267
268 cutoff_->toFront(false);
269 resonance_->toFront(false);
270 formant_x_->toFront(false);
271 formant_y_->toFront(false);
272}
273
275 // Paint the background and relevant labels, blend morph icons, etc.
277 setLabelFont(g);
278 drawLabelForComponent(g, TRANS("MIX"), mix_.get());
279
280 int title_width = getTitleWidth();
281 int blend_label_padding_y = size_ratio_ * kBlendLabelPaddingY;
282
283 drawLabelBackgroundForComponent(g, drive_.get());
284 drawLabelBackgroundForComponent(g, keytrack_.get());
285
286 int widget_margin = findValue(Skin::kWidgetMargin);
287 int blend_height = filter_response_->getY() - title_width + widget_margin;
288 int morph_y = title_width - widget_margin + blend_label_padding_y;
289 if (!specify_input_) {
290 g.setColour(findColour(Skin::kBody, true));
291 g.fillRect(preset_selector_->getBounds());
292 drawTextComponentBackground(g, preset_selector_->getBounds(), true);
293 drawLabelForComponent(g, TRANS("MODE"), preset_selector_.get(), true);
294 morph_y = blend_label_padding_y;
295 blend_height = filter_response_->getY();
296 }
297
298 g.setColour(findColour(Skin::kBodyText, true));
299 int morph_width = size_ratio_ * kBlendLabelWidth;
300 int morph_height = blend_height - 2 * blend_label_padding_y;
301 int left_morph_x = blend_->getX() - morph_width + widget_margin;
302 int right_morph_x = getWidth() - morph_width;
303
304 Rectangle<float> left_morph_bounds(left_morph_x, morph_y, morph_width, morph_height);
305 Path left_morph = getLeftMorphPath();
306 g.fillPath(left_morph, left_morph.getTransformToScaleToFit(left_morph_bounds, true));
307
308 Rectangle<float> right_morph_bounds(right_morph_x, morph_y, morph_width, morph_height);
309 Path right_morph = getRightMorphPath();
310 g.fillPath(right_morph, right_morph.getTransformToScaleToFit(right_morph_bounds, true));
311}
312
314 // Lays out controls in a top-bottom orientation.
315 int title_width = getTitleWidth();
316 int knob_section_height = getKnobSectionHeight();
317 int slider_width = getSliderWidth();
318 int blend_label_width = size_ratio_ * kBlendLabelWidth;
319 int widget_margin = getWidgetMargin();
320
321 int slider_overlap = getSliderOverlap();
322 int slider_overlap_space = getSliderOverlapWithSpace();
323 int response_width = getWidth() - slider_width + slider_overlap + slider_overlap_space - 2 * widget_margin;
324 int response_y = title_width + slider_width - slider_overlap_space - slider_overlap;
325 int response_height = getHeight() - 2 * slider_width -
326 title_width - knob_section_height + 2 * slider_overlap_space + 2 * slider_overlap;
327
328 int blend_y = title_width - slider_overlap;
329 blend_->setBounds(blend_label_width - widget_margin, blend_y,
330 getWidth() - 2 * (blend_label_width - widget_margin), slider_width);
331
332 filter_response_->setBounds(widget_margin, response_y, response_width, response_height);
333 int resonance_x = filter_response_->getRight() - slider_overlap + widget_margin;
334 resonance_->setBounds(resonance_x, response_y - widget_margin, slider_width, response_height + 2 * widget_margin);
335 int cutoff_y = filter_response_->getBottom() - slider_overlap + widget_margin;
336 cutoff_->setBounds(0, cutoff_y, response_width + 2 * widget_margin, slider_width);
337
338 float component_width = getWidth() / 5.0f;
339 int knob_y = getHeight() - knob_section_height;
340
341 int inputs_width = 2 * component_width;
342 int internal_margin = widget_margin / 2;
343 int input_width = (inputs_width - 2 * widget_margin - internal_margin) / 2;
344 float input_height = (knob_section_height - 2 * (widget_margin + internal_margin)) / 3.0f;
345 int osc_y = knob_y + widget_margin;
346
347 osc1_input_->setBounds(widget_margin, osc_y, input_width, input_height);
348 osc2_input_->setBounds(inputs_width - widget_margin - input_width, osc_y, input_width, input_height);
349
350 int other_y = knob_y + (knob_section_height - input_height) / 2.0f;
351 osc3_input_->setBounds(widget_margin, other_y, input_width, input_height);
352 sample_input_->setBounds(inputs_width - widget_margin - input_width, other_y, input_width, input_height);
353
354 int filter_x = (inputs_width - input_width) / 2;
355 int filter_y = getHeight() - input_height - widget_margin;
356 filter_input_->setBounds(filter_x, filter_y, input_width, input_height);
357
358 int knobs_x = 2.0f * component_width - widget_margin;
359 Rectangle<int> knobs_area(knobs_x, knob_y, getWidth() - knobs_x, knob_section_height);
360 placeKnobsInArea(knobs_area, { drive_.get(), mix_.get(), keytrack_.get() });
361}
362
364 // Lays out controls in a left-right orientation.
365 int title_width = getTitleWidth();
366 int knob_section_height = getKnobSectionHeight();
367 int slider_width = getSliderWidth();
368 int blend_label_width = size_ratio_ * kBlendLabelWidth;
369 int widget_margin = getWidgetMargin();
370
371 Rectangle<int> bounds = getLocalBounds().withLeft(title_width);
372 Rectangle<int> widget_bounds = getDividedAreaUnbuffered(bounds, 2, 1, widget_margin);
373
374 int slider_overlap = getSliderOverlapWithSpace();
375 int response_x = widget_bounds.getX();
376 int response_area_width = getWidth() - response_x;
377 int response_width = response_area_width - slider_width + 2 * slider_overlap;
378 int response_y = slider_width - 2 * slider_overlap;
379 int response_height = getHeight() - 2 * slider_width + 4 * slider_overlap;
380
381 filter_response_->setBounds(response_x, response_y, response_width, response_height);
382 blend_->setBounds(response_x + blend_label_width - 2 * widget_margin, -slider_overlap,
383 response_area_width - 2 * (blend_label_width - widget_margin), slider_width);
384 resonance_->setBounds(getWidth() - slider_width + slider_overlap, response_y - widget_margin, slider_width,
385 response_height + 2 * widget_margin);
386 cutoff_->setBounds(response_x - widget_margin, filter_response_->getBottom() - slider_overlap,
387 response_width + 2 * widget_margin, slider_width);
388
389 int knob_area_width = response_x - title_width;
390 int knobs_y = getHeight() - knob_section_height;
391 placeKnobsInArea(Rectangle<int>(title_width, knobs_y, knob_area_width, knob_section_height),
392 { drive_.get(), mix_.get(), keytrack_.get() });
393
394 preset_selector_->setBounds(title_width + widget_margin, widget_margin,
395 knob_area_width - 2 * widget_margin, knob_section_height - 2 * widget_margin);
396}
397
400
401 if (specify_input_)
403 else
405
406 // Positioning formant and special sliders, setting label fonts, etc.
407 formant_x_->setBounds(cutoff_->getBounds());
408 formant_y_->setBounds(resonance_->getBounds());
409 formant_transpose_->setBounds(blend_->getBounds());
410 formant_resonance_->setBounds(drive_->getBounds());
411 formant_spread_->setBounds(keytrack_->getBounds());
412 blend_transpose_->setBounds(drive_->getBounds());
413
414 filter_label_1_->setFontType(PlainTextComponent::kRegular);
415 filter_label_2_->setFontType(PlainTextComponent::kRegular);
416 float label_height = findValue(Skin::kLabelHeight);
417 filter_label_1_->setTextSize(label_height);
418 filter_label_2_->setTextSize(label_height);
419
420 filter_label_1_->setBounds(getLabelBackgroundBounds(drive_.get()));
421 filter_label_2_->setBounds(getLabelBackgroundBounds(keytrack_.get()));
422
423 Colour body_text = findColour(Skin::kBodyText, true);
424 filter_label_1_->setColor(body_text);
425 filter_label_2_->setColor(body_text);
426}
427
428void FilterSection::buttonClicked(Button* clicked_button) {
429 // Handle button clicks, including toggling filter serial mode, osc/sample inputs, etc.
430 // Notify listeners as necessary.
431 if (clicked_button == filter_input_.get()) {
432 if (filter_input_->getToggleState()) {
433 for (Listener* listener : listeners_)
434 listener->filterSerialSelected(this);
435 }
436 SynthSection::buttonClicked(clicked_button);
437 }
438 else if (clicked_button == osc1_input_.get()) {
439 for (Listener* listener : listeners_)
440 listener->oscInputToggled(this, 0, clicked_button->getToggleState());
441 }
442 else if (clicked_button == osc2_input_.get()) {
443 for (Listener* listener : listeners_)
444 listener->oscInputToggled(this, 1, clicked_button->getToggleState());
445 }
446 else if (clicked_button == osc3_input_.get()) {
447 for (Listener* listener : listeners_)
448 listener->oscInputToggled(this, 2, clicked_button->getToggleState());
449 }
450 else if (clicked_button == sample_input_.get()) {
451 for (Listener* listener : listeners_)
452 listener->sampleInputToggled(this, clicked_button->getToggleState());
453 }
454 else
455 SynthSection::buttonClicked(clicked_button);
456}
457
459 // Set the filter model and style values from a control map and update the UI.
460 current_model_ = std::round(controls[model_name_]->value());
461 current_style_ = std::round(controls[style_name_]->value());
462 setFilterText();
463
464 vital::constants::FilterModel model = static_cast<vital::constants::FilterModel>(current_model_);
465 filter_response_->setModel(model);
466 filter_response_->setStyle(current_style_);
467 showModelKnobs();
468 setLabelText();
469}
470
472 // Cycle to the previous filter model/style combination and notify of changes.
473 current_style_--;
474 if (current_style_ < 0) {
475 current_model_ = (current_model_ + vital::constants::kNumFilterModels - 1) % vital::constants::kNumFilterModels;
476 current_style_ = getNumStyles(current_model_) - 1;
477 }
478 showModelKnobs();
479 notifyFilterChange();
480}
481
483 // Cycle to the next filter model/style combination and notify of changes.
484 current_style_++;
485 if (current_style_ >= getNumStyles(current_model_)) {
486 current_style_ = 0;
487 current_model_ = (current_model_ + 1) % vital::constants::kNumFilterModels;
488 }
489 showModelKnobs();
490 notifyFilterChange();
491}
492
493void FilterSection::textMouseDown(const MouseEvent& e) {
494 // Handle popup menu for selecting models and styles from text interaction.
495 PopupItems options;
496
497 int index = 1;
498 for (int i = 0; i < vital::constants::kNumFilterModels; ++i) {
500 sub_options.selected = i == current_model_;
501
502 int num_styles = getNumStyles(i);
503 for (int style = 0; style < num_styles; ++style)
504 sub_options.addItem(index++, getStyleName(i, style), style == current_style_);
505
506 options.addItem(sub_options);
507 }
508
509 Point<int> position(getWidth(), preset_selector_->getY());
510 if (!specify_input_)
511 position = Point<int>(preset_selector_->getRight() - getDualPopupWidth(), preset_selector_->getBottom());
513 options, [=](int selection) { setFilterSelected(selection); });
514}
515
517 // Set the filter model/style from a given menu ID selection.
518 int current_id = 1;
519 for (int i = 0; i < vital::constants::kNumFilterModels; ++i) {
520 int num_styles = getNumStyles(i);
521 if (menu_id - current_id < num_styles) {
522 current_model_ = i;
523 current_style_ = menu_id - current_id;
524 showModelKnobs();
525 notifyFilterChange();
526 return;
527 }
528
529 current_id += num_styles;
530 }
531}
532
533void FilterSection::setOscillatorInput(int oscillator_index, bool input) {
534 // Set the toggle state of the specified oscillator input.
535 if (oscillator_index == 0)
536 osc1_input_->setToggleState(input, dontSendNotification);
537 else if (oscillator_index == 1)
538 osc2_input_->setToggleState(input, dontSendNotification);
539 else
540 osc3_input_->setToggleState(input, dontSendNotification);
541}
542
544 // Set the toggle state of the sample input.
545 sample_input_->setToggleState(input, dontSendNotification);
546}
547
548void FilterSection::showModelKnobs() {
549 // Show or hide knobs depending on the current filter model and style.
550 vital::constants::FilterModel model = static_cast<vital::constants::FilterModel>(current_model_);
551 filter_response_->setModel(model);
552
553 bool formant = model == vital::constants::kFormant;
554 bool vocal_tract = formant && current_style_ == vital::FormantFilter::kVocalTract;
555 bool comb = model == vital::constants::kComb;
556 formant_x_->setVisible(formant);
557 formant_y_->setVisible(formant);
558 formant_transpose_->setVisible(formant && !vocal_tract);
559 formant_resonance_->setVisible(formant);
560 formant_spread_->setVisible(formant);
561
562 blend_transpose_->setVisible(comb);
563
564 cutoff_->setVisible(!formant);
565 resonance_->setVisible(!formant);
566 keytrack_->setVisible(!formant);
567 blend_->setVisible(!formant || vocal_tract);
568 drive_->setVisible(!formant && !comb);
569}
570
571void FilterSection::setFilterText() {
572 // Update the preset selector text to match the current model/style.
573 std::string style = getStyleName(current_model_, current_style_);
574 preset_selector_->setText(strings::kFilterModelNames[current_model_], ":", style);
575}
576
577void FilterSection::setLabelText() {
578 // Update the parameter labels depending on the current filter model.
579 if (current_model_ == vital::constants::kFormant) {
580 filter_label_1_->setText("PEAK");
581 filter_label_2_->setText("SPREAD");
582 }
583 else {
584 filter_label_2_->setText("KEY TRK");
585 if (current_model_ == vital::constants::kComb)
586 filter_label_1_->setText("CUT");
587 else
588 filter_label_1_->setText("DRIVE");
589 }
590}
591
592void FilterSection::notifyFilterChange() {
593 // Notify parent and FilterResponse of filter changes and update the synth values.
594 filter_response_->setStyle(current_style_);
595 filter_response_->setModel(static_cast<vital::constants::FilterModel>(current_model_));
596 setFilterText();
597 setLabelText();
598
599 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
600 if (parent) {
601 parent->getSynth()->valueChangedInternal(model_name_, current_model_);
602 parent->getSynth()->valueChangedInternal(style_name_, current_style_);
603 }
604}
605
606void FilterSection::setActive(bool active) {
608 if (filter_response_)
609 filter_response_->setActive(active);
610}
611
613 // Returns a Path object representing the left morph icon for the current filter model/style.
614 if (current_model_ == vital::constants::kPhase)
615 return Paths::phaser1();
616 if (current_model_ == vital::constants::kFormant)
617 return Paths::leftArrow();
618 if (current_style_ == vital::SynthFilter::kDualNotchBand || current_style_ == vital::SynthFilter::kBandPeakNotch)
619 return Paths::bandPass();
620 if (current_model_ == vital::constants::kComb && current_style_)
621 return Paths::narrowBand();
622
623 return Paths::lowPass();
624}
625
627 // Returns a Path object representing the right morph icon for the current filter model/style.
628 if (current_model_ == vital::constants::kPhase)
629 return Paths::phaser3();
630 if (current_model_ == vital::constants::kFormant)
631 return Paths::rightArrow();
632 if (current_style_ == vital::SynthFilter::kDualNotchBand || current_style_ == vital::SynthFilter::kBandPeakNotch)
633 return Paths::notch();
634 if (current_model_ == vital::constants::kComb && current_style_)
635 return Paths::wideBand();
636 if (current_model_ == vital::constants::kDiode)
637 return Paths::bandPass();
638 return Paths::highPass();
639}
An interface for objects that need to respond to changes in the FilterSection.
Definition filter_section.h:39
A graphical user interface component representing a filter section in the synthesizer.
Definition filter_section.h:25
void setSampleInput(bool input)
Sets the sample input state.
Definition filter_section.cpp:543
void positionTopBottom()
Positions UI elements in a top-bottom orientation.
Definition filter_section.cpp:313
void textMouseDown(const MouseEvent &e) override
Called when the user interacts with the text component of the preset selector.
Definition filter_section.cpp:493
void resized() override
Called when the component is resized.
Definition filter_section.cpp:398
void setFilterResponseSliders()
Connects the filter's sliders to the filter response display.
Definition filter_section.cpp:255
void positionLeftRight()
Positions UI elements in a left-right orientation.
Definition filter_section.cpp:363
Path getLeftMorphPath()
Returns a path that represents the left morph shape icon based on the current model/style.
Definition filter_section.cpp:612
FilterSection(String suffix, const vital::output_map &mono_modulations)
Constructs a FilterSection with a given suffix for parameter naming, using only mono modulations.
Definition filter_section.cpp:191
void setAllValues(vital::control_map &controls) override
Sets all parameter values for this filter section.
Definition filter_section.cpp:458
void paintBackground(Graphics &g) override
Paints the background of the filter section.
Definition filter_section.cpp:274
~FilterSection()
Destructor.
Definition filter_section.cpp:253
Path getRightMorphPath()
Returns a path that represents the right morph shape icon based on the current model/style.
Definition filter_section.cpp:626
void buttonClicked(Button *clicked_button) override
Called when a button is clicked.
Definition filter_section.cpp:428
static constexpr int kBlendLabelPaddingY
Vertical padding used for label placement relative to the blend slider.
Definition filter_section.h:30
void nextClicked() override
Called when the next button on the preset selector is clicked.
Definition filter_section.cpp:482
void setFilterSelected(int menu_id)
Sets the filter model and style based on a given menu ID.
Definition filter_section.cpp:516
void prevClicked() override
Called when the previous button on the preset selector is clicked.
Definition filter_section.cpp:471
void setActive(bool active) override
Sets the active state of this filter section.
Definition filter_section.cpp:606
void setOscillatorInput(int oscillator_index, bool input)
Sets the oscillator input state for a given oscillator index.
Definition filter_section.cpp:533
static Path highPass()
Definition paths.h:853
static Path leftArrow()
Definition paths.h:880
static Path notch()
Definition paths.h:949
static Path rightArrow()
Definition paths.h:894
static Path narrowBand()
Definition paths.h:965
static Path phaser1()
Definition paths.h:908
static Path phaser3()
Definition paths.h:922
static Path bandPass()
Definition paths.h:864
static Path lowPass(float line_thickness=0.1f)
Definition paths.h:842
static Path wideBand()
Definition paths.h:981
@ kRegular
Definition open_gl_image_component.h:310
@ kWidgetMargin
Definition skin.h:103
@ kLabelHeight
Definition skin.h:72
@ kBodyText
Definition skin.h:133
@ kBody
Definition skin.h:129
@ kFxFilter
Definition skin.h:51
@ kFilter
Definition skin.h:38
void valueChangedInternal(const std::string &name, vital::mono_float value)
Handles internal value changes, updating the parameter and optionally notifying the host.
Definition synth_base.cpp:54
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
Base class for all synthesizer sections, providing UI layout, painting, and interaction logic.
Definition synth_section.h:193
void drawTextComponentBackground(Graphics &g, Rectangle< int > bounds, bool extend_to_label)
Draws a background for a text component area.
Definition synth_section.cpp:319
virtual void buttonClicked(Button *clicked_button) override
Called when a button is clicked. Updates the synth parameter accordingly.
Definition synth_section.cpp:398
float getSliderWidth()
Definition synth_section.cpp:637
void placeKnobsInArea(Rectangle< int > area, std::vector< Component * > knobs)
Definition synth_section.cpp:601
int getDualPopupWidth() const
Definition synth_section.h:767
void setSidewaysHeading(bool sideways)
Definition synth_section.h:771
virtual void resized() override
Called when the component is resized. Arranges layout of child components.
Definition synth_section.cpp:35
virtual void setActive(bool active)
Sets the active state of this section and sub-sections.
Definition synth_section.cpp:806
void drawLabelForComponent(Graphics &g, String text, Component *component, bool text_component=false)
Draws a label for a given component.
Definition synth_section.h:548
void drawLabelBackgroundForComponent(Graphics &g, Component *component)
Draws label background for a specific component.
Definition synth_section.cpp:764
void setLabelFont(Graphics &g)
Sets the Graphics context font and color for labels.
Definition synth_section.cpp:740
float getSliderOverlap()
Definition synth_section.cpp:641
float size_ratio_
Definition synth_section.h:821
void addButton(OpenGlToggleButton *button, bool show=true)
Definition synth_section.cpp:428
Rectangle< int > getLabelBackgroundBounds(Rectangle< int > bounds, bool text_component=false)
Gets the background bounds for a label.
Definition synth_section.cpp:782
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
virtual void paintBackground(Graphics &g)
Paints the background of the section. Calls paintContainer, heading, knobs, children.
Definition synth_section.cpp:77
float getKnobSectionHeight()
Definition synth_section.cpp:633
void showDualPopupSelector(Component *source, Point< int > position, int width, const PopupItems &options, std::function< void(int)> callback)
Shows a dual popup selector for hierarchical selection.
Definition synth_section.cpp:126
void addOpenGlComponent(OpenGlComponent *open_gl_component, bool to_beginning=false)
Definition synth_section.cpp:489
Rectangle< int > getDividedAreaUnbuffered(Rectangle< int > full_area, int num_sections, int section, int buffer)
Divides an area into equal sections without extra buffering, returns the specified section.
Definition synth_section.cpp:768
float getSliderOverlapWithSpace()
Definition synth_section.cpp:648
float getTitleWidth()
Definition synth_section.cpp:629
void setSkinOverride(Skin::SectionOverride skin_override)
Definition synth_section.h:303
float getWidgetMargin()
Definition synth_section.cpp:676
static TextLookAndFeel * instance()
Singleton instance access.
Definition text_look_and_feel.h:106
@ kVocalTract
Extended style: Vocal tract modeling.
Definition formant_filter.h:28
@ kAIUO
Style 2: Typically blending vowels A, I, U, O.
Definition formant_filter.h:26
@ kBandPeakNotch
Definition synth_filter.h:79
@ kDualNotchBand
Definition synth_filter.h:78
const std::string kFilterModelNames[]
Names of different filter models (analog, dirty, ladder, etc.).
Definition synth_strings.h:197
const std::string kDiodeStyleNames[]
Special diode filter style names.
Definition synth_strings.h:127
const std::string kFilterStyleNames[]
Filter style names for visualizing different filter slopes and blends.
Definition synth_strings.h:115
const std::string kCombStyleNames[]
Names for comb and flanger filter styles.
Definition synth_strings.h:136
FilterModel
Identifiers for different filter models available in Vital’s filters.
Definition synth_constants.h:194
@ kDirty
Definition synth_constants.h:196
@ kNumFilterModels
Definition synth_constants.h:203
@ kDigital
Definition synth_constants.h:198
@ kAnalog
Definition synth_constants.h:195
@ kPhase
Definition synth_constants.h:202
@ kFormant
Definition synth_constants.h:200
@ kDiode
Definition synth_constants.h:199
@ kLadder
Definition synth_constants.h:197
@ kComb
Definition synth_constants.h:201
std::map< std::string, Output * > output_map
Maps parameter names to Output pointers, representing output signals from various modules.
Definition synth_types.h:229
std::map< std::string, Value * > control_map
Maps parameter names to Value pointers representing synth control parameters.
Definition synth_types.h:214
Declares the PresetSelector class which provides a UI component for selecting presets.
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
bool selected
Whether this item is currently selected.
Definition synth_section.h:32
Declares classes for OpenGL-based buttons used in the Vital synth UI.
Declares the SynthSlider and related classes, providing various slider styles and functionality in th...