Vital
Loading...
Searching...
No Matches
wave_source_editor.cpp
Go to the documentation of this file.
2
3#include "skin.h"
5#include "synth_constants.h"
6#include "shaders.h"
7#include "utils.h"
8
9namespace {
16 void waveSourceCallback(int result, WaveSourceEditor* editor) {
17 if (editor == nullptr)
18 return;
19
20 if (result == WaveSourceEditor::kClear)
21 editor->clear();
22 else if (result == WaveSourceEditor::kFlipHorizontal)
23 editor->flipHorizontal();
24 else if (result == WaveSourceEditor::kFlipVertical)
25 editor->flipVertical();
26 // kInitSaw can be implemented if needed.
27 }
28}
29
31 OpenGlLineRenderer(size, true),
32 grid_lines_(2 * WavetableComponentOverlay::kMaxGrid, Shaders::kColorFragment),
33 grid_circles_(kNumCircles, Shaders::kCircleFragment), hover_circle_(Shaders::kCircleFragment), editing_line_(2) {
34 grid_lines_.setTargetComponent(this);
35 grid_circles_.setTargetComponent(this);
36 hover_circle_.setTargetComponent(this);
37 hover_circle_.setQuad(0, -2.0f, -2.0f, 0.0f, 0.0f);
38 addAndMakeVisible(&editing_line_);
39 editing_line_.setInterceptsMouseClicks(false, false);
40
41 setEditable(false);
42 setFit(true);
43 horizontal_grid_ = 0;
44 vertical_grid_ = 0;
45 editing_ = false;
46 dragging_audio_file_ = false;
47
48 values_ = std::make_unique<float[]>(size);
49
50 current_mouse_position_ = Point<int>(INT_MAX / 2, INT_MAX / 2);
51}
52
55
57 g.fillAll(findColour(Skin::kWidgetBackground, true));
58
59 Colour lighten = findColour(Skin::kLightenScreen, true);
60 grid_lines_.setColor(lighten);
61 grid_circles_.setColor(lighten.withMultipliedAlpha(0.5f));
62 hover_circle_.setColor(lighten);
63 editing_line_.setColor(lighten);
64}
65
67 float width = getWidth();
68 float line_width = findValue(Skin::kWidgetLineWidth);
69 setLineWidth(line_width);
70 editing_line_.setLineWidth(line_width);
71
72 int num_points = numPoints();
73 for (int i = 0; i < numPoints(); ++i)
74 setXAt(i, i * width / (num_points - 1.0f));
75
76 setLineValues();
77
79 editing_line_.setBounds(getLocalBounds());
80 setGridPositions();
81}
82
83void WaveSourceEditor::mouseDown(const MouseEvent& e) {
84 current_mouse_position_ = e.getPosition();
85 if (e.mods.isPopupMenu()) {
86 PopupItems options;
87 options.addItem(kClear, "Clear");
88 options.addItem(kFlipVertical, "Flip Vertical");
89 options.addItem(kFlipHorizontal, "Flip Horizontal");
90
91 SynthSection* parent = findParentComponentOfClass<SynthSection>();
92 parent->showPopupSelector(this, e.getPosition(), options,
93 [=](int selection) { waveSourceCallback(selection, this); });
94 }
95 else {
96 last_edit_position_ = getSnappedPoint(current_mouse_position_);
97 setHoverPosition();
98 changeValues(e);
99 editing_line_.setXAt(0, last_edit_position_.x);
100 editing_line_.setYAt(0, last_edit_position_.y);
101 editing_line_.setXAt(1, current_mouse_position_.x);
102 editing_line_.setYAt(1, current_mouse_position_.y);
103 editing_ = true;
104 }
105}
106
107void WaveSourceEditor::mouseUp(const MouseEvent& e) {
108 editing_ = false;
109 current_mouse_position_ = e.getPosition();
110 int index = getHoveredIndex(snapToGrid(current_mouse_position_));
111
112 for (Listener* listener : listeners_)
113 listener->valuesChanged(index, index, true);
114}
115
116void WaveSourceEditor::mouseMove(const MouseEvent& e) {
117 current_mouse_position_ = e.getPosition();
118 setHoverPosition();
119}
120
121void WaveSourceEditor::mouseDrag(const MouseEvent& e) {
122 current_mouse_position_ = e.getPosition();
123 changeValues(e);
124 last_edit_position_ = snapToGrid(current_mouse_position_);
125 setHoverPosition();
126 editing_line_.setXAt(0, last_edit_position_.x);
127 editing_line_.setYAt(0, last_edit_position_.y);
128 editing_line_.setXAt(1, current_mouse_position_.x);
129 editing_line_.setYAt(1, current_mouse_position_.y);
130}
131
132void WaveSourceEditor::mouseExit(const MouseEvent& e) {
133 current_mouse_position_ = Point<int>(-getWidth(), 0);
134 setHoverPosition();
135}
136
137int WaveSourceEditor::getHoveredIndex(Point<int> position) {
138 int index = floorf(numPoints() * (1.0f * position.x) / getWidth());
139 return vital::utils::iclamp(index, 0, numPoints() - 1);
140}
141
142float WaveSourceEditor::getSnapRadius() {
143 static float kGridProximity = 0.2f;
144
145 if (horizontal_grid_ == 0 || vertical_grid_ == 0)
146 return 0.0f;
147
148 float grid_cell_width = getWidth() / (1.0f * horizontal_grid_);
149 float grid_cell_height = getHeight() / (1.0f * vertical_grid_);
150 return kGridProximity * std::min(grid_cell_height, grid_cell_width);
151}
152
153void WaveSourceEditor::setLineValues() {
154 float adjust = getHeight() / 2.0f;
155 for (int i = 0; i < numPoints(); ++i)
156 setYAt(i, adjust * (1.0f - values_[i]));
157}
158
159void WaveSourceEditor::changeValues(const MouseEvent& e) {
160 Point<int> mouse_position = snapToGrid(e.getPosition());
161 int from_index = getHoveredIndex(last_edit_position_);
162 int selected_index = getHoveredIndex(mouse_position);
163
164 float x = mouse_position.x;
165 float y = mouse_position.y;
166 float x_delta = last_edit_position_.x - x;
167 float y_delta = last_edit_position_.y - y;
168 float slope = y_delta == 0 ? 0 : y_delta / x_delta;
169 float next_x = getWidth() * (1.0f * selected_index) / numPoints();
170 int direction = -1;
171
172 if (selected_index < from_index) {
173 direction = 1;
174 next_x += getWidth() * 1.0f / numPoints();
175 }
176 float inc_x = next_x - x;
177
178 for (int index = selected_index; index != from_index + direction; index += direction) {
179 if (index >= 0 && index < numPoints()) {
180 float new_value = -2.0f * y / getHeight() + 1.0f;
181 values_[index] = vital::utils::clamp(new_value, -1.0f, 1.0f);
182 }
183
184 y += inc_x * slope;
185 inc_x = direction * getWidth() * 1.0f / numPoints();
186 }
187
188 setLineValues();
189
190 int min_index = std::min(from_index, selected_index);
191 int max_index = std::max(from_index, selected_index);
192
193 for (Listener* listener : listeners_)
194 listener->valuesChanged(min_index, max_index, false);
195}
196
197Point<int> WaveSourceEditor::getSnappedPoint(Point<int> input) {
198 if (horizontal_grid_ == 0 || vertical_grid_ == 0)
199 return input;
200
201 float x = horizontal_grid_ * (1.0f * input.x) / getWidth();
202 float y = vertical_grid_ * (1.0f * input.y) / getHeight();
203 float snapped_x = getWidth() * std::roundf(x) / horizontal_grid_;
204 float snapped_y = getHeight() * std::roundf(y) / vertical_grid_;
205 return Point<int>(snapped_x, snapped_y);
206}
207
208Point<int> WaveSourceEditor::snapToGrid(Point<int> input) {
209 Point<int> snapped = getSnappedPoint(input);
210
211 if (input.getDistanceFrom(snapped) > getSnapRadius())
212 return last_edit_position_;
213
214 return snapped;
215}
216
217void WaveSourceEditor::loadWaveform(float* waveform) {
218 for (int i = 0; i < numPoints(); ++i)
219 values_[i] = waveform[i];
220
221 setLineValues();
222}
223
224void WaveSourceEditor::setEditable(bool editable) {
225 setInterceptsMouseClicks(editable, editable);
226 editable_ = editable;
227}
228
229void WaveSourceEditor::setGrid(int horizontal, int vertical) {
230 horizontal_grid_ = horizontal;
231 vertical_grid_ = vertical;
232 setGridPositions();
233}
234
235void WaveSourceEditor::setGridPositions() {
236 float pixel_height = 2.0f / getHeight();
237 float pixel_width = 2.0f / getWidth();
238
239 int grid_index = 0;
240 for (int i = 1; i < horizontal_grid_; ++i) {
241 grid_lines_.setQuad(grid_index, i * 2.0f / horizontal_grid_ - 1.0f, -1.0f, pixel_width, 2.0f);
242 grid_index++;
243 }
244
245 for (int i = 1; i < vertical_grid_; ++i) {
246 grid_lines_.setQuad(grid_index, -1.0f, i * 2.0f / vertical_grid_ - 1.0f, 2.0f, pixel_height);
247 grid_index++;
248 }
249 grid_lines_.setNumQuads(grid_index);
250
251 int circle_index = 0;
252 float circle_radius_x = getSnapRadius() * 2.0f / getWidth();
253 float circle_radius_y = getSnapRadius() * 2.0f / getHeight();
254 for (int h = 0; h <= horizontal_grid_; ++h) {
255 for (int v = 0; v <= vertical_grid_; ++v) {
256 float x = h * 2.0f / horizontal_grid_ - 1.0f;
257 float y = v * 2.0f / vertical_grid_ - 1.0f;
258
259 grid_circles_.setQuad(circle_index, x - circle_radius_x, y - circle_radius_y,
260 circle_radius_x * 2.0f, circle_radius_y * 2.0f);
261 circle_index++;
262 }
263 }
264
265 grid_circles_.setNumQuads(circle_index);
266}
267
268
269void WaveSourceEditor::setHoverPosition() {
270 float circle_radius_x = getSnapRadius() * 2.0f / getWidth();
271 float circle_radius_y = getSnapRadius() * 2.0f / getHeight();
272
273 Point<int> position;
274 if (editing_)
275 position = last_edit_position_;
276 else
277 position = getSnappedPoint(current_mouse_position_);
278 hover_circle_.setQuad(0, position.x * 2.0f / getWidth() - 1.0f - circle_radius_x,
279 1.0f - position.y * 2.0f / getHeight() - circle_radius_y,
280 2.0f * circle_radius_x, 2.0f * circle_radius_y);
281}
282
283void WaveSourceEditor::audioFileLoaded(const File& file) {
284 dragging_audio_file_ = false;
285}
286
287void WaveSourceEditor::fileDragEnter(const StringArray& files, int x, int y) {
288 dragging_audio_file_ = true;
289}
290
291void WaveSourceEditor::fileDragExit(const StringArray& files) {
292 dragging_audio_file_ = false;
293}
294
296 for (int index = 0; index < numPoints(); ++index)
297 values_[index] = 0.0f;
298
299 setLineValues();
300
301 for (Listener* listener : listeners_)
302 listener->valuesChanged(0, numPoints() - 1, true);
303}
304
306 for (int index = 0; index < numPoints(); ++index)
307 values_[index] = -values_[index];
308
309 setLineValues();
310
311 for (Listener* listener : listeners_)
312 listener->valuesChanged(0, numPoints() - 1, true);
313}
314
316 for (int index = 0; index < numPoints() / 2; ++index) {
317 int other_index = numPoints() - index - 1;
318 float other_value = values_[other_index];
319 values_[other_index] = values_[index];
320 values_[index] = other_value;
321 }
322
323 setLineValues();
324
325 for (Listener* listener : listeners_)
326 listener->valuesChanged(0, numPoints() - 1, true);
327}
virtual void resized() override
Called when the component is resized.
Definition open_gl_component.cpp:121
float findValue(Skin::ValueId value_id)
Finds a float value from the skin associated with this component's parent.
Definition open_gl_component.cpp:173
A component for rendering lines with optional filling and boost effects using OpenGL.
Definition open_gl_line_renderer.h:16
force_inline void setYAt(int index, float val)
Sets the y-coordinate of a point, marking data as dirty.
Definition open_gl_line_renderer.h:98
force_inline void setXAt(int index, float val)
Sets the x-coordinate of a point, marking data as dirty.
Definition open_gl_line_renderer.h:105
force_inline void setFit(bool fit)
Enables fitting the line inside the available area.
Definition open_gl_line_renderer.h:141
force_inline void setLineWidth(float width)
Sets the line width in pixels.
Definition open_gl_line_renderer.h:66
force_inline void setColor(Colour color)
Sets the line color.
Definition open_gl_line_renderer.h:63
force_inline int numPoints() const
Gets the number of points in the line.
Definition open_gl_line_renderer.h:186
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
void setNumQuads(int num_quads)
Sets how many quads will actually be drawn (up to max_quads).
Definition open_gl_multi_quad.h:92
force_inline void setColor(Colour color)
Sets the base color for the quads.
Definition open_gl_multi_quad.h:102
Manages and provides access to vertex and fragment shaders used by the OpenGL rendering pipeline.
Definition shaders.h:19
@ kWidgetLineWidth
Definition skin.h:105
@ kWidgetBackground
Definition skin.h:173
@ kLightenScreen
Definition skin.h:141
Base class for all synthesizer sections, providing UI layout, painting, and interaction logic.
Definition synth_section.h:193
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
Interface for receiving notifications about waveform modifications.
Definition wave_source_editor.h:26
A graphical editor for manipulating a single-cycle waveform's sample values.
Definition wave_source_editor.h:20
void mouseUp(const MouseEvent &e) override
Handles a mouse up event to finalize editing and notify listeners.
Definition wave_source_editor.cpp:107
void flipHorizontal()
Flips the waveform horizontally, reversing its sample order.
Definition wave_source_editor.cpp:315
void resized() override
Adjusts layout and internal structures on component resize.
Definition wave_source_editor.cpp:66
void setGrid(int horizontal, int vertical)
Defines a grid overlay for snapping points.
Definition wave_source_editor.cpp:229
WaveSourceEditor(int size)
Constructs a WaveSourceEditor with a given waveform size.
Definition wave_source_editor.cpp:30
void loadWaveform(float *waveform)
Loads a complete waveform into the editor.
Definition wave_source_editor.cpp:217
void mouseMove(const MouseEvent &e) override
Handles a mouse move event to update the hover position.
Definition wave_source_editor.cpp:116
void clear()
Clears the entire waveform to zero.
Definition wave_source_editor.cpp:295
void fileDragEnter(const StringArray &files, int x, int y) override
Called when dragging audio files enters the component.
Definition wave_source_editor.cpp:287
void mouseDrag(const MouseEvent &e) override
Handles a mouse drag event to continuously update waveform samples.
Definition wave_source_editor.cpp:121
void paintBackground(Graphics &g) override
Paints the background of the waveform editor.
Definition wave_source_editor.cpp:56
void setEditable(bool editable)
Enables or disables editing of the waveform.
Definition wave_source_editor.cpp:224
void fileDragExit(const StringArray &files) override
Called when dragging audio files leaves the component.
Definition wave_source_editor.cpp:291
void mouseExit(const MouseEvent &e) override
Handles a mouse exit event to clear the hover indication.
Definition wave_source_editor.cpp:132
virtual ~WaveSourceEditor()
Destructor.
Definition wave_source_editor.cpp:53
void flipVertical()
Flips the waveform vertically, inverting all sample values.
Definition wave_source_editor.cpp:305
void mouseDown(const MouseEvent &e) override
Handles a mouse down event to start editing or show the context menu.
Definition wave_source_editor.cpp:83
void audioFileLoaded(const File &file) override
Called when an audio file is dropped and loaded successfully.
Definition wave_source_editor.cpp:283
@ kClear
Definition wave_source_editor.h:53
@ kFlipHorizontal
Definition wave_source_editor.h:51
@ kFlipVertical
Definition wave_source_editor.h:52
A base overlay component for editing and interacting with a wavetable component's parameters.
Definition wavetable_component_overlay.h:22
force_inline int iclamp(int value, int min_val, int max_val)
Clamps an integer between [min_val, max_val].
Definition utils.h:250
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
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
Provides various utility functions, classes, and constants for audio, math, and general-purpose opera...