Vital
Loading...
Searching...
No Matches
modulation_meter.cpp
Go to the documentation of this file.
1#include "modulation_meter.h"
2
5#include "shaders.h"
6#include "synth_section.h"
7#include "synth_slider.h"
9
11 const SynthSlider* slider, OpenGlMultiQuad* quads, int index) :
12 mono_total_(mono_total), poly_total_(poly_total), destination_(slider),
13 quads_(quads), index_(index), current_value_(0.0), mod_percent_(0.0) {
14
15 rotary_ = destination_->isRotary() && !destination_->isTextOrCurve();
16
17 // If vertical bar or text/curve slider style, rotate coordinates in the multi-quad.
18 if (destination_->getSliderStyle() == Slider::LinearBarVertical || destination_->isTextOrCurve())
19 quads->setRotatedCoordinates(index, -1.0f, -1.0f, 2.0f, 2.0f);
20
21 setInterceptsMouseClicks(false, false);
22 updateDrawing(false);
23}
24
26
28 SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
29 if (parent) {
30 // Check if the parameter is modulated by any source.
31 std::vector<vital::ModulationConnection*> connections;
32 connections = parent->getSynth()->getSourceConnections(getName().toStdString());
33 setModulated(!connections.empty());
34 }
35
36 if (isVisible())
37 setVertices();
38 else
39 collapseVertices();
40}
41
42void ModulationMeter::setActive(bool active) {
43 if (active)
44 setVertices();
45 else
46 collapseVertices();
47}
48
49Rectangle<float> ModulationMeter::getMeterBounds() {
50 float width = getWidth();
51 float height = getHeight();
52
53 // If not rotary and not text/curve, it's a linear slider: calculate a linear meter area.
54 if (!destination_->isRotary() && !destination_->isTextOrCurve()) {
55 SynthSection* parent = findParentComponentOfClass<SynthSection>();
56 int widget_margin = parent->getWidgetMargin();
57
58 int total_width = destination_->isHorizontal() ? destination_->getHeight() : destination_->getWidth();
59 int extra = total_width % 2;
60 int slider_width = std::floor(SynthSlider::kLinearWidthPercent * total_width * 0.5f) * 2.0f + extra;
61
62 int inner_area = (total_width - slider_width) / 2;
63 int outer_area = inner_area - widget_margin;
64 int meter_width = SynthSlider::kLinearModulationPercent * total_width;
65 int border = std::max<int>(1, (widget_margin - meter_width) * 0.5f);
66
67 if (destination_->isHorizontal()) {
68 // Horizontal slider: meter goes vertically in the slider track.
69 return Rectangle<float>(0.0f, outer_area + border, width, inner_area - outer_area - 2.0f * border);
70 }
71
72 // Vertical slider: meter goes horizontally in the slider track.
73 return Rectangle<float>(outer_area + border, 0.0f, inner_area - outer_area - 2.0f * border, height);
74 }
75 else if (!destination_->isTextOrCurve()) {
76 // Rotary knob style
77 float knob_scale = destination_->getKnobSizeScale();
78 float meter_width = destination_->findValue(Skin::kKnobModMeterArcSize) * knob_scale;
79 meter_width += destination_->findValue(Skin::kKnobModMeterArcThickness) * (1.0f - knob_scale);
80 float offset = destination_->findValue(Skin::kKnobOffset);
81
82 float center_x = getWidth() * 0.5f;
83 float center_y = getHeight() * 0.5f;
84 return Rectangle<float>(center_x - meter_width * 0.5f, center_y - meter_width * 0.5f + offset,
85 meter_width, meter_width);
86 }
87
88 // For text or curve style, use the entire area.
89 return getLocalBounds().toFloat();
90}
91
92void ModulationMeter::setVertices() {
93 Rectangle<int> parent_bounds = getParentComponent()->getBounds();
94 Rectangle<int> bounds = getBounds();
95 Rectangle<float> meter_bounds = getMeterBounds();
96 float left = bounds.getX() + meter_bounds.getX();
97 float right = bounds.getX() + meter_bounds.getRight();
98 float top = parent_bounds.getHeight() - (bounds.getY() + meter_bounds.getY());
99 float bottom = parent_bounds.getHeight() - (bounds.getY() + meter_bounds.getBottom());
100
101 left_ = 2.0f * left / parent_bounds.getWidth() - 1.0f;
102 right_ = 2.0f * right / parent_bounds.getWidth() - 1.0f;
103 top_ = 2.0f * top / parent_bounds.getHeight() - 1.0f;
104 bottom_ = 2.0f * bottom / parent_bounds.getHeight() - 1.0f;
105 quads_->setQuad(index_, left_, bottom_, right_ - left_, top_ - bottom_);
106}
107
108void ModulationMeter::collapseVertices() {
109 left_ = right_ = top_ = bottom_= 0.0f;
110
111 quads_->setQuad(index_, left_, bottom_, right_ - left_, top_ - bottom_);
112 mod_percent_ = 0.0f;
113}
114
116 Rectangle<float> meter_bounds = getMeterBounds();
117 if (rotary_)
118 meter_bounds.expand(2.0f, 2.0f);
119
120 float width = getWidth();
121 float height = getHeight();
122 float left = 2.0f * meter_bounds.getX() / width - 1.0f;
123 float bottom = 1.0f - 2.0f * meter_bounds.getBottom() / height;
124
125 bool vertical_bar = destination_->getSliderStyle() == Slider::LinearBarVertical || destination_->isTextOrCurve();
126 if (vertical_bar)
127 quad.setRotatedCoordinates(0, -1.0f, -1.0f, 2.0f, 2.0f);
128 else
129 quad.setCoordinates(0, -1.0f, -1.0f, 2.0f, 2.0f);
130
131 if (rotary_)
132 quad.setQuad(0, left, bottom, 2.0f * meter_bounds.getWidth() / width, 2.0f * meter_bounds.getHeight() / height);
133 else if (vertical_bar) {
134 float thickness = 2.0f / width;
135 quad.setQuad(0, left, bottom, thickness, 2.0f * meter_bounds.getHeight() / height);
136 }
137 else {
138 float thickness = 2.0f / height;
139 quad.setQuad(0, left, bottom + 2.0f * meter_bounds.getHeight() / height - thickness,
140 2.0f * meter_bounds.getWidth() / width, thickness);
141 }
142}
143
145 if (mono_total_) {
146 current_value_ = mono_total_->trigger_value;
147 if (poly_total_ && use_poly)
148 current_value_ += poly_total_->trigger_value;
149 }
150
151 float range = destination_->getMaximum() - destination_->getMinimum();
152 vital::poly_float value = (current_value_ - destination_->getMinimum()) * (1.0f / range);
153 mod_percent_ = vital::utils::clamp(value, 0.0f, 1.0f);
154 float knob_percent = (destination_->getValue() - destination_->getMinimum()) / range;
155
156 vital::poly_float min_percent = vital::utils::min(mod_percent_, knob_percent);
157 vital::poly_float max_percent = vital::utils::max(mod_percent_, knob_percent);
158
159 quads_->setQuad(index_, left_, bottom_, right_ - left_, top_ - bottom_);
160
161 // Convert to angles if rotary
162 if (rotary_) {
163 if (&destination_->getLookAndFeel() == TextLookAndFeel::instance()) {
164 min_percent = vital::utils::interpolate(-vital::kPi, 0.0f, min_percent);
165 max_percent = vital::utils::interpolate(-vital::kPi, 0.0f, max_percent);
166 }
167 else {
168 float angle = SynthSlider::kRotaryAngle;
169 min_percent = vital::utils::interpolate(-angle, angle, min_percent);
170 max_percent = vital::utils::interpolate(-angle, angle, max_percent);
171 }
172 }
173
174 quads_->setShaderValue(index_, min_percent[0], 0);
175 quads_->setShaderValue(index_, max_percent[0], 1);
176 quads_->setShaderValue(index_, min_percent[1], 2);
177 quads_->setShaderValue(index_, max_percent[1], 3);
178}
179
180void ModulationMeter::setModulationAmountQuad(OpenGlQuad& quad, float amount, bool bipolar) {
181 float range = destination_->getMaximum() - destination_->getMinimum();
182 float knob_percent = (destination_->getValue() - destination_->getMinimum()) / range;
183
184 float min_percent = std::min(knob_percent + amount, knob_percent);
185 float max_percent = std::max(knob_percent + amount, knob_percent);
186 if (bipolar) {
187 min_percent = std::min(knob_percent + amount * 0.5f, knob_percent - amount * 0.5f);
188 max_percent = std::max(knob_percent + amount * 0.5f, knob_percent - amount * 0.5f);
189 }
190
191 // Convert to angles if rotary
192 if (rotary_) {
193 if (&destination_->getLookAndFeel() == TextLookAndFeel::instance()) {
194 min_percent = vital::utils::interpolate(-vital::kPi, 0.0f, min_percent);
195 max_percent = vital::utils::interpolate(-vital::kPi, 0.0f, max_percent);
196 }
197 else {
198 float angle = SynthSlider::kRotaryAngle;
199 min_percent = vital::utils::interpolate(-angle, angle, min_percent);
200 max_percent = vital::utils::interpolate(-angle, angle, max_percent);
201 min_percent = std::max(-angle, min_percent);
202 max_percent = std::min(angle, max_percent);
203 }
204 }
205
206 quad.setShaderValue(0, min_percent, 0);
207 quad.setShaderValue(0, max_percent, 1);
208 quad.setShaderValue(0, min_percent, 2);
209 quad.setShaderValue(0, max_percent, 3);
210}
void resized() override
Called when the component is resized.
Definition modulation_meter.cpp:27
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
void updateDrawing(bool use_poly)
Updates the drawing of the modulation meter, recalculating mod percent and visuals.
Definition modulation_meter.cpp:144
void setActive(bool active)
Sets whether the modulation meter is active (visible and updating).
Definition modulation_meter.cpp:42
ModulationMeter(const vital::Output *mono_total, const vital::Output *poly_total, const SynthSlider *slider, OpenGlMultiQuad *quads, int index)
Constructs a ModulationMeter.
Definition modulation_meter.cpp:10
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
virtual ~ModulationMeter()
Destructor.
Definition modulation_meter.cpp:25
A component for rendering multiple quads using OpenGL, with customizable colors, rounding,...
Definition open_gl_multi_quad.h:16
void setShaderValue(int i, float shader_value, int value_index=0)
Sets a shader value for all four vertices of a quad.
Definition open_gl_multi_quad.h:254
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 setCoordinates(int i, float x, float y, float w, float h)
Sets coordinates for a quad in normalized device space.
Definition open_gl_multi_quad.h:234
void setRotatedCoordinates(int i, float x, float y, float w, float h)
Sets rotated coordinates for a quad, adjusting its texture mapping.
Definition open_gl_multi_quad.h:217
A convenience class for a single quad rendered via OpenGL.
Definition open_gl_multi_quad.h:447
bool isTextOrCurve() const
Definition synth_slider.h:105
static constexpr float kRotaryAngle
The default rotary arc angle used for rotary sliders.
Definition synth_slider.h:55
@ kKnobModMeterArcThickness
Definition skin.h:96
@ kKnobOffset
Definition skin.h:97
@ kKnobModMeterArcSize
Definition skin.h:95
std::vector< vital::ModulationConnection * > getSourceConnections(const std::string &source)
Returns all modulation connections from a particular source.
Definition synth_base.cpp:236
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
float getWidgetMargin()
Definition synth_section.cpp:676
A specialized slider with extended functionality for modulation, parameter control,...
Definition synth_slider.h:314
float findValue(Skin::ValueId value_id) const override
Definition synth_slider.h:681
static constexpr float kLinearWidthPercent
Definition synth_slider.h:341
static constexpr float kLinearModulationPercent
Definition synth_slider.h:343
float getKnobSizeScale() const override
Definition synth_slider.h:641
static TextLookAndFeel * instance()
Singleton instance access.
Definition text_look_and_feel.h:106
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 min(poly_float left, poly_float right)
Returns the minimum of two poly_floats lane-by-lane.
Definition poly_utils.h:334
force_inline poly_float max(poly_float left, poly_float right)
Returns the maximum of two poly_floats lane-by-lane.
Definition poly_utils.h:327
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
constexpr mono_float kPi
Pi constant.
Definition common.h:36
Holds and manages a buffer of samples (poly_float) for a Processor's output.
Definition processor.h:35
poly_float trigger_value
Trigger values for voices.
Definition processor.h:116
Represents a vector of floating-point values using SIMD instructions.
Definition poly_values.h:600
Declares the SynthSlider and related classes, providing various slider styles and functionality in th...