19 constexpr int kBlendLabelWidth = 30;
26 int getNumStyles(
int int_model) {
56 std::string getStyleName(
int int_model,
int style) {
88 SynthSection(name), current_model_(0), current_style_(0), specify_input_(false) {
90 std::string number = suffix.toStdString();
91 model_name_ =
"filter_" + number +
"_model";
92 style_name_ =
"filter_" + number +
"_style";
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());
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: ");
109 mix_ = std::make_unique<SynthSlider>(
"filter_" + number +
"_mix");
110 addSlider(mix_.get());
111 mix_->setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
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: ");
121 preset_selector_ = std::make_unique<PresetSelector>();
122 addSubSection(preset_selector_.get());
123 preset_selector_->addListener(
this);
124 setPresetSelector(preset_selector_.get());
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: ");
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: ");
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: ");
149 drive_ = std::make_unique<SynthSlider>(
"filter_" + number +
"_drive");
150 addSlider(drive_.get());
151 drive_->setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
153 formant_resonance_ = std::make_unique<SynthSlider>(
"filter_" + number +
"_formant_resonance");
154 addSlider(formant_resonance_.get());
155 formant_resonance_->setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
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();
163 blend_transpose_ = std::make_unique<SynthSlider>(
"filter_" + number +
"_blend_transpose");
164 addSlider(blend_transpose_.get());
165 blend_transpose_->setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
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();
173 filter_on_ = std::make_unique<SynthButton>(
"filter_" + number +
"_on");
174 addButton(filter_on_.get());
175 setActivator(filter_on_.get());
177 filter_label_1_ = std::make_unique<PlainTextComponent>(
"label1",
"DRIVE");
178 addOpenGlComponent(filter_label_1_.get());
180 filter_label_2_ = std::make_unique<PlainTextComponent>(
"label2",
"KEY TRK");
181 addOpenGlComponent(filter_label_2_.get());
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);
194 filter_response_ = std::make_unique<FilterResponse>(suffix, mono_modulations);
197 preset_selector_->setTextComponent(
true);
207 std::string number = std::to_string(index);
208 filter_response_ = std::make_unique<FilterResponse>(index, mono_modulations, poly_modulations);
212 cutoff_->setExtraModulationTarget(filter_response_.get());
215 osc1_input_ = std::make_unique<OpenGlToggleButton>(
"OSC1");
216 addAndMakeVisible(osc1_input_.get());
218 osc1_input_->addListener(
this);
219 osc1_input_->setText(
"OSC1");
222 osc2_input_ = std::make_unique<OpenGlToggleButton>(
"OSC2");
223 addAndMakeVisible(osc2_input_.get());
225 osc2_input_->addListener(
this);
226 osc2_input_->setText(
"OSC2");
229 osc3_input_ = std::make_unique<OpenGlToggleButton>(
"OSC3");
230 addAndMakeVisible(osc3_input_.get());
232 osc3_input_->addListener(
this);
233 osc3_input_->setText(
"OSC3");
236 sample_input_ = std::make_unique<OpenGlToggleButton>(
"SMP");
237 addAndMakeVisible(sample_input_.get());
239 sample_input_->addListener(
this);
240 sample_input_->setText(
"SMP");
243 filter_input_ = std::make_unique<SynthButton>(
"filter_" + number +
"_filter_input");
245 filter_input_->setText(String(
"FIL") + String(3 - index));
247 specify_input_ =
true;
249 preset_selector_->setTextComponent(
false);
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());
268 cutoff_->toFront(
false);
269 resonance_->toFront(
false);
270 formant_x_->toFront(
false);
271 formant_y_->toFront(
false);
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_) {
291 g.fillRect(preset_selector_->getBounds());
294 morph_y = blend_label_padding_y;
295 blend_height = filter_response_->getY();
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;
304 Rectangle<float> left_morph_bounds(left_morph_x, morph_y, morph_width, morph_height);
306 g.fillPath(left_morph, left_morph.getTransformToScaleToFit(left_morph_bounds,
true));
308 Rectangle<float> right_morph_bounds(right_morph_x, morph_y, morph_width, morph_height);
310 g.fillPath(right_morph, right_morph.getTransformToScaleToFit(right_morph_bounds,
true));
318 int blend_label_width =
size_ratio_ * kBlendLabelWidth;
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;
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);
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);
338 float component_width = getWidth() / 5.0f;
339 int knob_y = getHeight() - knob_section_height;
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;
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);
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);
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);
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() });
368 int blend_label_width =
size_ratio_ * kBlendLabelWidth;
371 Rectangle<int> bounds = getLocalBounds().withLeft(title_width);
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;
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);
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() });
394 preset_selector_->setBounds(title_width + widget_margin, widget_margin,
395 knob_area_width - 2 * widget_margin, knob_section_height - 2 * widget_margin);
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());
417 filter_label_1_->setTextSize(label_height);
418 filter_label_2_->setTextSize(label_height);
424 filter_label_1_->setColor(body_text);
425 filter_label_2_->setColor(body_text);
431 if (clicked_button == filter_input_.get()) {
432 if (filter_input_->getToggleState()) {
433 for (
Listener* listener : listeners_)
434 listener->filterSerialSelected(
this);
438 else if (clicked_button == osc1_input_.get()) {
439 for (
Listener* listener : listeners_)
440 listener->oscInputToggled(
this, 0, clicked_button->getToggleState());
442 else if (clicked_button == osc2_input_.get()) {
443 for (
Listener* listener : listeners_)
444 listener->oscInputToggled(
this, 1, clicked_button->getToggleState());
446 else if (clicked_button == osc3_input_.get()) {
447 for (
Listener* listener : listeners_)
448 listener->oscInputToggled(
this, 2, clicked_button->getToggleState());
450 else if (clicked_button == sample_input_.get()) {
451 for (
Listener* listener : listeners_)
452 listener->sampleInputToggled(
this, clicked_button->getToggleState());
460 current_model_ = std::round(controls[model_name_]->value());
461 current_style_ = std::round(controls[style_name_]->value());
465 filter_response_->setModel(model);
466 filter_response_->setStyle(current_style_);
474 if (current_style_ < 0) {
476 current_style_ = getNumStyles(current_model_) - 1;
479 notifyFilterChange();
485 if (current_style_ >= getNumStyles(current_model_)) {
490 notifyFilterChange();
500 sub_options.
selected = i == current_model_;
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_);
509 Point<int> position(getWidth(), preset_selector_->getY());
511 position = Point<int>(preset_selector_->getRight() -
getDualPopupWidth(), preset_selector_->getBottom());
520 int num_styles = getNumStyles(i);
521 if (menu_id - current_id < num_styles) {
523 current_style_ = menu_id - current_id;
525 notifyFilterChange();
529 current_id += num_styles;
535 if (oscillator_index == 0)
536 osc1_input_->setToggleState(input, dontSendNotification);
537 else if (oscillator_index == 1)
538 osc2_input_->setToggleState(input, dontSendNotification);
540 osc3_input_->setToggleState(input, dontSendNotification);
545 sample_input_->setToggleState(input, dontSendNotification);
548void FilterSection::showModelKnobs() {
551 filter_response_->setModel(model);
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);
562 blend_transpose_->setVisible(comb);
564 cutoff_->setVisible(!formant);
565 resonance_->setVisible(!formant);
566 keytrack_->setVisible(!formant);
567 blend_->setVisible(!formant || vocal_tract);
568 drive_->setVisible(!formant && !comb);
571void FilterSection::setFilterText() {
573 std::string style = getStyleName(current_model_, current_style_);
577void FilterSection::setLabelText() {
580 filter_label_1_->setText(
"PEAK");
581 filter_label_2_->setText(
"SPREAD");
584 filter_label_2_->setText(
"KEY TRK");
586 filter_label_1_->setText(
"CUT");
588 filter_label_1_->setText(
"DRIVE");
592void FilterSection::notifyFilterChange() {
594 filter_response_->setStyle(current_style_);
608 if (filter_response_)
609 filter_response_->setActive(active);
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
@ 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.
Declares the SynthSlider and related classes, providing various slider styles and functionality in th...