Vital
Loading...
Searching...
No Matches
envelope_editor.cpp
Go to the documentation of this file.
1#include "envelope_editor.h"
2
3#include "fonts.h"
4#include "skin.h"
5#include "shaders.h"
7#include "utils.h"
8
9namespace {
15 String formatTime(float time) {
16 if (time < 1.0f) {
17 int ms_value = time * vital::kMsPerSec;
18 return String(ms_value) + "ms";
19 }
20
21 float sec_value = int(time * 10.0f) / 10.0f;
22 return String(sec_value) + "s";
23 }
24
25 constexpr float kMinWindowSize = 0.125f;
26 constexpr float kMaxWindowSize = 64.0f;
27}
28
30 const String& prefix,
31 const vital::output_map& mono_modulations,
32 const vital::output_map& poly_modulations) : OpenGlLineRenderer(kTotalPoints),
33 drag_circle_(Shaders::kCircleFragment),
34 hover_circle_(Shaders::kRingFragment),
35 grid_lines_(kMaxGridLines), sub_grid_lines_(kMaxGridLines),
36 position_circle_(Shaders::kRingFragment),
37 point_circles_(kNumSections, Shaders::kRingFragment),
38 power_circles_(kNumSections, Shaders::kCircleFragment) {
39 addAndMakeVisible(drag_circle_);
40 addAndMakeVisible(hover_circle_);
41 addAndMakeVisible(grid_lines_);
42 addAndMakeVisible(sub_grid_lines_);
43 addAndMakeVisible(position_circle_);
44 addAndMakeVisible(point_circles_);
45 addAndMakeVisible(power_circles_);
46 hover_circle_.setThickness(1.0f);
47
48 for (int i = 0; i < kMaxTimesShown; ++i) {
49 times_[i] = std::make_unique<PlainTextComponent>("Time", "");
50 times_[i]->setJustification(Justification::centredLeft);
51 times_[i]->setScissor(true);
52 addAndMakeVisible(times_[i].get());
53 }
54
56 parent_ = nullptr;
57 delay_hover_ = false;
58 attack_hover_ = false;
59 hold_hover_ = false;
60 sustain_hover_ = false;
61 release_hover_ = false;
62 attack_power_hover_ = false;
63 decay_power_hover_ = false;
64 release_power_hover_ = false;
65 mouse_down_ = false;
66
67 animate_ = false;
68 reset_positions_ = true;
69 size_ratio_ = 1.0f;
70 window_time_ = 4.0f;
71
72 current_position_alpha_ = 0.0f;
73 last_phase_ = 0.0f;
74
75 envelope_phase_ = nullptr;
76
77 attack_slider_ = nullptr;
78 attack_power_slider_ = nullptr;
79 decay_slider_ = nullptr;
80 decay_power_slider_ = nullptr;
81 sustain_slider_ = nullptr;
82 release_slider_ = nullptr;
83 release_power_slider_ = nullptr;
84
85 delay_outputs_ = getOutputs(mono_modulations, poly_modulations, prefix + "_delay");
86 attack_outputs_ = getOutputs(mono_modulations, poly_modulations, prefix + "_attack");
87 hold_outputs_ = getOutputs(mono_modulations, poly_modulations, prefix + "_hold");
88 decay_outputs_ = getOutputs(mono_modulations, poly_modulations, prefix + "_decay");
89 sustain_outputs_ = getOutputs(mono_modulations, poly_modulations, prefix + "_sustain");
90 release_outputs_ = getOutputs(mono_modulations, poly_modulations, prefix + "_release");
91
92 setFill(true);
93 setFillCenter(-1.0f);
94}
95
97
101
102 setColors();
103}
104
106 parent_ = findParentComponentOfClass<SynthGuiInterface>();
107
108 if (envelope_phase_ == nullptr && parent_)
109 envelope_phase_ = parent_->getSynth()->getStatusOutput(getName().toStdString() + "_phase");
110
111 if (parent_) {
112 setColors();
113 setTimePositions();
114 }
115
117}
118
119void EnvelopeEditor::pickHoverPosition(Point<float> position) {
120 position.x = unpadX(position.x);
121 position.y = unpadY(position.y);
122 float delay_x = getSliderDelayX();
123 float attack_x = getSliderAttackX();
124 float hold_x = getSliderHoldX();
125 float decay_x = getSliderDecayX();
126 float sustain_y = getSliderSustainY();
127 float release_x = getSliderReleaseX();
128
129 Point<float> delay_point(delay_x, getHeight());
130 Point<float> attack_power_point((delay_x + attack_x) / 2.0f, getSliderAttackValue(0.5f));
131 Point<float> top_point(attack_x, 0);
132 Point<float> hold_point(hold_x, 0);
133 Point<float> decay_power_point((hold_x + decay_x) / 2.0f, getSliderDecayValue(0.5f));
134 Point<float> sustain_point(decay_x, sustain_y);
135 Point<float> release_power_point((decay_x + release_x) / 2.0f, getSliderReleaseValue(0.5f));
136 Point<float> release_point(release_x, getHeight());
137
138 std::vector<Point<float>> points = { top_point, sustain_point, release_point };
139 if (delay_x > 0.0f)
140 points.push_back(delay_point);
141 if (hold_x > attack_x)
142 points.push_back(hold_point);
143 if (release_x - decay_x > kMinPointDistanceForPower && sustain_y < getHeight())
144 points.push_back(release_power_point);
145 if (decay_x - attack_x > kMinPointDistanceForPower && sustain_y > 0)
146 points.push_back(decay_power_point);
147 if (attack_x - delay_x > kMinPointDistanceForPower)
148 points.push_back(attack_power_point);
149
150 float closest_distance_squared = getHeight() * getHeight();
151 for (const Point<float>& point : points) {
152 float distance = position.getDistanceSquaredFrom(point);
153 closest_distance_squared = std::min(closest_distance_squared, distance);
154 }
155
156 bool release_hover = position.getDistanceSquaredFrom(release_point) <= closest_distance_squared;
157 bool sustain_hover = position.getDistanceSquaredFrom(sustain_point) <= closest_distance_squared;
158 bool attack_hover = position.getDistanceSquaredFrom(top_point) <= closest_distance_squared;
159 bool delay_hover = position.getDistanceSquaredFrom(delay_point) == closest_distance_squared;
160 bool hold_hover = hold_x > attack_x && position.getDistanceSquaredFrom(hold_point) == closest_distance_squared;
161 bool release_power_hover = position.getDistanceSquaredFrom(release_power_point) == closest_distance_squared;
162 bool decay_power_hover = position.getDistanceSquaredFrom(decay_power_point) == closest_distance_squared;
163 bool attack_power_hover = position.getDistanceSquaredFrom(attack_power_point) == closest_distance_squared;
164
165 if (delay_hover != delay_hover_ || attack_hover != attack_hover_ || hold_hover != hold_hover_ ||
166 sustain_hover != sustain_hover_ || release_hover != release_hover_ ||
167 attack_power_hover != attack_power_hover_ || decay_power_hover != decay_power_hover_ ||
168 release_power_hover != release_power_hover_) {
169 delay_hover_ = delay_hover;
170 attack_hover_ = attack_hover;
171 hold_hover_ = hold_hover;
172 sustain_hover_ = sustain_hover;
173 release_hover_ = release_hover;
174 attack_power_hover_ = attack_power_hover;
175 decay_power_hover_ = decay_power_hover;
176 release_power_hover_ = release_power_hover;
178 }
179}
180
181void EnvelopeEditor::mouseMove(const MouseEvent& e) {
182 Point<float> position = e.getPosition().toFloat();
183 pickHoverPosition(position);
184}
185
186void EnvelopeEditor::mouseExit(const MouseEvent& e) {
187 delay_hover_ = false;
188 attack_hover_ = false;
189 hold_hover_ = false;
190 sustain_hover_ = false;
191 release_hover_ = false;
192 attack_power_hover_ = false;
193 decay_power_hover_ = false;
194 release_power_hover_ = false;
196}
197
198void EnvelopeEditor::mouseDown(const MouseEvent& e) {
199 mouse_down_ = true;
200 last_edit_position_ = e.position;
202}
203
204void EnvelopeEditor::mouseDrag(const MouseEvent& e) {
205 float delta_power = (last_edit_position_.y - e.position.y) * kPowerMouseMultiplier;
206 last_edit_position_ = e.position;
207
208 if (delay_hover_)
209 setDelayX(last_edit_position_.x);
210 else if (release_hover_)
211 setReleaseX(last_edit_position_.x);
212 else if (sustain_hover_) {
213 setDecayX(last_edit_position_.x);
214 setSustainY(last_edit_position_.y);
215 }
216 else if (attack_hover_)
217 setAttackX(last_edit_position_.x);
218 else if (hold_hover_)
219 setHoldX(last_edit_position_.x);
220 else if (attack_power_hover_)
221 setAttackPower(attack_power_slider_->getValue() + delta_power);
222 else if (decay_power_hover_)
223 setDecayPower(decay_power_slider_->getValue() + delta_power);
224 else if (release_power_hover_)
225 setReleasePower(release_power_slider_->getValue() + delta_power);
226
228}
229
230void EnvelopeEditor::mouseDoubleClick(const MouseEvent& e) {
231 if (attack_power_hover_)
232 setAttackPower(0.0f);
233 else if (decay_power_hover_)
234 setDecayPower(0.0f);
235 else if (release_power_hover_)
236 setReleasePower(0.0f);
237}
238
239void EnvelopeEditor::mouseUp(const MouseEvent& e) {
240 mouse_down_ = false;
242}
243
244void EnvelopeEditor::mouseWheelMove(const MouseEvent& e, const MouseWheelDetails& wheel) {
245 static constexpr float kMouseWheelSensitivity = 1.0f;
246
247 zoom(std::pow(2.0f, -kMouseWheelSensitivity * wheel.deltaY));
248}
249
250void EnvelopeEditor::magnifyZoom(Point<float> delta) {
251 static constexpr float kMouseWheelSensitivity = 0.02f;
252
253 zoom(std::pow(2.0f, kMouseWheelSensitivity * delta.y));
254}
255
257 static constexpr float kResetBuffer = 0.25f;
258
259 window_time_ = (1.0f + kResetBuffer) * getSliderReleaseX() * window_time_ / getWidth();
260 window_time_ = std::max(std::min(window_time_, kMaxWindowSize), kMinWindowSize);
261 setTimePositions();
263}
264
265void EnvelopeEditor::zoom(float amount) {
266 window_time_ *= amount;
267 window_time_ = std::max(std::min(window_time_, kMaxWindowSize), kMinWindowSize);
268 setTimePositions();
270}
271
275
276inline float EnvelopeEditor::getSliderDelayX() {
277 if (delay_slider_ == nullptr)
278 return 0.0f;
279
280 float time = delay_slider_->getAdjustedValue(delay_slider_->getValue());
281 return getWidth() * time / window_time_;
282}
283
284inline float EnvelopeEditor::getSliderAttackX() {
285 if (attack_slider_ == nullptr)
286 return 0.0f;
287
288 float time = attack_slider_->getAdjustedValue(attack_slider_->getValue());
289 return getSliderDelayX() + getWidth() * time / window_time_;
290}
291
292inline float EnvelopeEditor::getSliderHoldX() {
293 if (hold_slider_ == nullptr)
294 return 0.0f;
295
296 float time = hold_slider_->getAdjustedValue(hold_slider_->getValue());
297 return getSliderAttackX() + getWidth() * time / window_time_;
298}
299
300float EnvelopeEditor::getSliderDecayX() {
301 if (decay_slider_ == nullptr)
302 return 0.0f;
303
304 float time = decay_slider_->getAdjustedValue(decay_slider_->getValue());
305 return getSliderHoldX() + getWidth() * time / window_time_;
306}
307
308float EnvelopeEditor::getSliderSustainY() {
309 if (sustain_slider_ == nullptr)
310 return 0.0f;
311
312 float percent = sustain_slider_->valueToProportionOfLength(sustain_slider_->getValue());
313 return getHeight() * (1.0f - percent);
314}
315
316float EnvelopeEditor::getSliderReleaseX() {
317 if (release_slider_ == nullptr)
318 return 0.0f;
319
320 float time = release_slider_->getAdjustedValue(release_slider_->getValue());
321 return getSliderDecayX() + getWidth() * time / window_time_;
322}
323
324inline float EnvelopeEditor::getDelayTime(int index) {
325 vital::poly_float delays = getOutputsTotal(delay_outputs_, delay_slider_->getValue());
326 return delay_slider_->getAdjustedValue(std::max<float>(0.0f, delays[index]));
327}
328
329inline float EnvelopeEditor::getAttackTime(int index) {
330 vital::poly_float attacks = getOutputsTotal(attack_outputs_, attack_slider_->getValue());
331 return attack_slider_->getAdjustedValue(std::max<float>(0.0f, attacks[index]));
332}
333
334inline float EnvelopeEditor::getHoldTime(int index) {
335 vital::poly_float holds = getOutputsTotal(hold_outputs_, hold_slider_->getValue());
336 return hold_slider_->getAdjustedValue(std::max<float>(0.0f, holds[index]));
337}
338
339inline float EnvelopeEditor::getDecayTime(int index) {
340 vital::poly_float decays = getOutputsTotal(decay_outputs_, decay_slider_->getValue());
341 return decay_slider_->getAdjustedValue(std::max<float>(0.0f, decays[index]));
342}
343
344inline float EnvelopeEditor::getReleaseTime(int index) {
345 vital::poly_float releases = getOutputsTotal(release_outputs_, release_slider_->getValue());
346 return release_slider_->getAdjustedValue(std::max<float>(0.0f, releases[index]));
347}
348
349inline float EnvelopeEditor::getDelayX(int index) {
350 if (index < 0)
351 return getSliderDelayX();
352
353 return getWidth() * getDelayTime(index) / window_time_;
354}
355
356inline float EnvelopeEditor::getAttackX(int index) {
357 if (index < 0)
358 return getSliderAttackX();
359
360 return getDelayX(index) + getWidth() * getAttackTime(index) / window_time_;
361}
362
363inline float EnvelopeEditor::getHoldX(int index) {
364 if (index < 0)
365 return getSliderHoldX();
366
367 return getAttackX(index) + getWidth() * getHoldTime(index) / window_time_;
368}
369
370float EnvelopeEditor::getDecayX(int index) {
371 if (index < 0)
372 return getSliderDecayX();
373
374 return getHoldX(index) + getWidth() * getDecayTime(index) / window_time_;
375}
376
377float EnvelopeEditor::getSustainY(int index) {
378 if (index < 0)
379 return getSliderSustainY();
380
381 vital::poly_float sustains = getOutputsTotal(sustain_outputs_, sustain_slider_->getValue());
382 float percent = sustains[index] / sustain_slider_->getRange().getLength();
383 percent = vital::utils::clamp(percent, 0.0f, 1.0f);
384 return getHeight() * (1.0f - percent);
385}
386
387float EnvelopeEditor::getReleaseX(int index) {
388 if (index < 0)
389 return getSliderReleaseX();
390
391 return getDecayX(index) + getWidth() * getReleaseTime(index) / window_time_;
392}
393
394float EnvelopeEditor::getBackupPhase(float phase, int index) {
395 static constexpr float kBackupTime = 1.0f / 50.0f;
396 static constexpr float kTotalPhase = vital::kVoiceKill - vital::kVoiceOn;
397 static constexpr float kDecayPoint = (vital::kVoiceDecay - 1.0f * vital::kVoiceOn) / kTotalPhase;
398 static constexpr float kReleasePoint = (vital::kVoiceOff - 1.0f * vital::kVoiceOn) / kTotalPhase;
399
400 float time = kBackupTime;
401 float current_phase = phase;
402
403 if (current_phase == kReleasePoint)
404 return phase;
405
406 if (current_phase > kReleasePoint) {
407 float release_time = getReleaseTime(index);
408 if (release_time <= 0.0f)
409 current_phase = kReleasePoint;
410 else {
411 float phase_delta = time / release_time;
412 float time_released = release_time * (current_phase - kReleasePoint);
413
414 current_phase = current_phase - phase_delta;
415 if (current_phase >= kReleasePoint)
416 return current_phase;
417
418 time -= time_released;
419 current_phase = std::max(current_phase, kReleasePoint);
420 }
421 }
422 if (current_phase > kDecayPoint) {
423 float decay_time = getDecayTime(index);
424 if (decay_time <= 0.0f)
425 current_phase = kDecayPoint;
426 else {
427 float phase_delta = time / decay_time;
428 float time_decayed = decay_time * (current_phase - kDecayPoint);
429
430 current_phase = current_phase - phase_delta;
431 if (current_phase >= kDecayPoint)
432 return current_phase;
433
434 time -= time_decayed;
435 current_phase = std::max(current_phase, kDecayPoint);
436 }
437 }
438 float attack_time = getAttackTime(index) + getDelayTime(index);
439 if (attack_time <= 0.0f)
440 return 0.0f;
441
442 float phase_delta = time / attack_time;
443 return std::max(0.0f, current_phase - phase_delta);
444}
445
446vital::poly_float EnvelopeEditor::getBackupPhase(vital::poly_float phase) {
447 vital::poly_float backup = 0.0f;
448 backup.set(0, getBackupPhase(phase[0], 0));
449 backup.set(1, getBackupPhase(phase[1], 1));
450 return backup;
451}
452
453float EnvelopeEditor::getEnvelopeValue(float t, float power, float start, float end) {
454 return start + (end - start) * vital::futils::powerScale(t, power);
455}
456
457inline float EnvelopeEditor::getSliderAttackValue(float t) {
458 float power = attack_power_slider_->getValue();
459 return getHeight() - getEnvelopeValue(1.0f - t, power, getHeight(), 0.0f);
460}
461
462inline float EnvelopeEditor::getSliderDecayValue(float t) {
463 float power = decay_power_slider_->getValue();
464 return getEnvelopeValue(t, power, 0.0f, getSliderSustainY());
465}
466
467inline float EnvelopeEditor::getSliderReleaseValue(float t) {
468 float power = release_power_slider_->getValue();
469 return getEnvelopeValue(t, power, getSliderSustainY(), getHeight());
470}
471
472inline float EnvelopeEditor::getAttackValue(float t, int index) {
473 if (index < 0)
474 return getSliderAttackValue(t);
475
476 float power = attack_power_slider_->getValue();
477 return getHeight() - getEnvelopeValue(1.0f - t, power, getHeight(), 0.0f);
478}
479
480inline float EnvelopeEditor::getDecayValue(float t, int index) {
481 if (index < 0)
482 return getSliderDecayValue(t);
483
484 float power = decay_power_slider_->getValue();
485 return getEnvelopeValue(t, power, 0.0f, getSustainY(index));
486}
487
488inline float EnvelopeEditor::getReleaseValue(float t, int index) {
489 if (index < 0)
490 return getSliderReleaseValue(t);
491
492 float power = release_power_slider_->getValue();
493 return getEnvelopeValue(t, power, getSustainY(index), getHeight());
494}
495
496void EnvelopeEditor::setDelayX(float x) {
497 if (delay_slider_ == nullptr)
498 return;
499
500 float time = x * window_time_ / getWidth();
501 delay_slider_->setValueFromAdjusted(time);
502}
503
504void EnvelopeEditor::setAttackX(float x) {
505 if (attack_slider_ == nullptr)
506 return;
507
508 float time = (x - getSliderDelayX()) * window_time_ / getWidth();
509 attack_slider_->setValueFromAdjusted(time);
510}
511
512void EnvelopeEditor::setHoldX(float x) {
513 if (delay_slider_ == nullptr)
514 return;
515
516 float time = (x - getSliderAttackX()) * window_time_ / getWidth();
517 hold_slider_->setValueFromAdjusted(time);
518}
519
520void EnvelopeEditor::setPower(SynthSlider* slider, float power) {
521 power = vital::utils::clamp(power, (float)slider->getMinimum(), (float)slider->getMaximum());
522 slider->setValue(power);
523}
524
525void EnvelopeEditor::setAttackPower(float power) {
526 setPower(attack_power_slider_, power);
527}
528
529void EnvelopeEditor::setDecayPower(float power) {
530 setPower(decay_power_slider_, power);
531}
532
533void EnvelopeEditor::setReleasePower(float power) {
534 setPower(release_power_slider_, power);
535}
536
537void EnvelopeEditor::setDecayX(float x) {
538 if (decay_slider_ == nullptr)
539 return;
540
541 float time = (x - getSliderHoldX()) * window_time_ / getWidth();
542 decay_slider_->setValueFromAdjusted(time);
543 window_time_ = std::max(window_time_, x * window_time_ / getWidth());
544 window_time_ = std::max(std::min(window_time_, kMaxWindowSize), kMinWindowSize);
545}
546
547void EnvelopeEditor::setSustainY(float y) {
548 if (sustain_slider_ == nullptr)
549 return;
550
551 float percent = vital::utils::clamp(1.0 - y / getHeight(), 0.0f, 1.0f);
552 sustain_slider_->setValue(sustain_slider_->proportionOfLengthToValue(percent));
553}
554
555void EnvelopeEditor::setReleaseX(float x) {
556 if (release_slider_ == nullptr)
557 return;
558
559 float time = (x - getSliderDecayX()) * window_time_ / getWidth();
560 release_slider_->setValueFromAdjusted(time);
561 window_time_ = std::max(window_time_, x * window_time_ / getWidth());
562 window_time_ = std::max(std::min(window_time_, kMaxWindowSize), kMinWindowSize);
563}
564
566 delay_slider_ = delay_slider;
567 delay_slider_->addSliderListener(this);
568}
569
571 attack_slider_ = attack_slider;
572 attack_slider_->addSliderListener(this);
573}
574
576 hold_slider_ = hold_slider;
577 hold_slider_->addSliderListener(this);
578}
579
581 attack_power_slider_ = attack_power_slider;
582 attack_power_slider_->addSliderListener(this);
583}
584
586 decay_slider_ = decay_slider;
587 decay_slider_->addSliderListener(this);
588}
589
591 decay_power_slider_ = decay_power_slider;
592 decay_power_slider_->addSliderListener(this);
593}
594
596 sustain_slider_ = sustain_slider;
597 sustain_slider_->addSliderListener(this);
598}
599
601 release_slider_ = release_slider;
602 release_slider_->addSliderListener(this);
603}
604
606 release_power_slider_ = release_power_slider;
607 release_power_slider_->addSliderListener(this);
608}
609
610inline vital::poly_float EnvelopeEditor::getOutputsTotal(
611 std::pair<vital::Output*, vital::Output*> outputs, vital::poly_float default_value) {
612 if (!animate_ || !outputs.first->owner->enabled())
613 return default_value;
614 if (num_voices_readout_ == nullptr || num_voices_readout_->value()[0] <= 0.0f)
615 return outputs.first->trigger_value;
616
617 return outputs.first->trigger_value + outputs.second->trigger_value;
618}
619
621 float delay_x = getDelayX(index);
622 float attack_x = getAttackX(index);
623 float hold_x = getHoldX(index);
624 float decay_x = getDecayX(index);
625 float release_x = getReleaseX(index);
626
627 for (int i = 0; i < kNumPointsPerSection; ++i) {
628 float t = (1.0f * i) / kNumPointsPerSection;
629 float x = vital::utils::interpolate(delay_x, attack_x, t);
630 float y = getAttackValue(t, index);
631 setXAt(i, padX(x));
632 setYAt(i, padY(y));
633 }
634
635 for (int i = 0; i < kNumPointsPerSection; ++i) {
636 float t = (1.0f * i) / kNumPointsPerSection;
637 float x = vital::utils::interpolate(attack_x, hold_x, t);
638 setXAt(i + kNumPointsPerSection, padX(x));
639 setYAt(i + kNumPointsPerSection, padY(0.0f));
640 }
641
642 for (int i = 0; i < kNumPointsPerSection; ++i) {
643 float t = (1.0f * i) / kNumPointsPerSection;
644 float x = vital::utils::interpolate(hold_x, decay_x, t);
645 float y = getDecayValue(t, index);
646 setXAt(i + 2 * kNumPointsPerSection, padX(x));
647 setYAt(i + 2 * kNumPointsPerSection, padY(y));
648 }
649
650 for (int i = 0; i <= kNumPointsPerSection; ++i) {
651 float t = (1.0f * i) / kNumPointsPerSection;
652 float x = vital::utils::interpolate(decay_x, release_x, t);
653 float y = getReleaseValue(t, index);
654 setXAt(i + 3 * kNumPointsPerSection, padX(x));
655 setYAt(i + 3 * kNumPointsPerSection, padY(y));
656 }
657}
658
659std::pair<float, float> EnvelopeEditor::getPosition(int index) {
660 float phase = envelope_phase_->value()[index];
661
662 if (envelope_phase_->isClearValue(phase) || phase < vital::kVoiceOn || phase >= vital::kVoiceKill)
663 return { -1.0f, -1.0f};
664
665 float delay_time = getDelayTime(index);
666 float attack_time = getAttackTime(index);
667 float hold_time = getHoldTime(index);
668 float decay_time = getDecayTime(index);
669 float release_time = getReleaseTime(index);
670 int stage = phase;
671 float stage_phase = phase - stage;
672
673 float time = 0.0f;
674 float value = 0.0f;
675 if (stage == vital::kVoiceOn) {
676 time = delay_time + stage_phase * attack_time;
677 value = getAttackValue(stage_phase, index);
678 }
679 else if (stage == vital::kVoiceHold) {
680 time = delay_time + attack_time + stage_phase * hold_time;
681 value = 1.0f;
682 }
683 else if (stage == vital::kVoiceDecay) {
684 time = delay_time + attack_time + hold_time + stage_phase * decay_time;
685 value = getDecayValue(stage_phase, index);
686 }
687 else if (stage == vital::kVoiceOff) {
688 time = delay_time + attack_time + hold_time + decay_time + stage_phase * release_time;
689 value = getReleaseValue(stage_phase, index);
690 }
691
692 float x = 2.0f * time / window_time_ - 1.0f;
693 float y = 1.0 - 2.0f * value / getHeight();
694 return { padOpenGlX(x), padOpenGlY(y) };
695}
696
697inline float EnvelopeEditor::padX(float x) {
698 return x * (1.0f - kPaddingX) + kPaddingX * getWidth() / 2.0f;
699}
700
701inline float EnvelopeEditor::padY(float y) {
702 return y * (1.0f - kPaddingY / 2.0f) + kPaddingY * getHeight() / 2.0f;
703}
704
705inline float EnvelopeEditor::unpadX(float x) {
706 return (x - kPaddingX * getWidth() / 2.0f) / (1.0f - kPaddingX);
707}
708
709inline float EnvelopeEditor::unpadY(float y) {
710 return (y - kPaddingY * getHeight() / 2.0f) / (1.0f - kPaddingY / 2.0f);
711}
712
713inline float EnvelopeEditor::padOpenGlX(float x) {
714 return x * (1.0f - kPaddingX);
715}
716
717inline float EnvelopeEditor::padOpenGlY(float y) {
718 return y * (1.0f - kPaddingY / 2.0f) - kPaddingY / 2.0f;
719}
720
723 drag_circle_.init(open_gl);
724 hover_circle_.init(open_gl);
725 grid_lines_.init(open_gl);
726 sub_grid_lines_.init(open_gl);
727 point_circles_.init(open_gl);
728 power_circles_.init(open_gl);
729 position_circle_.init(open_gl);
730
731 for (int i = 0; i < kMaxTimesShown; ++i)
732 times_[i]->init(open_gl);
733}
734
735void EnvelopeEditor::render(OpenGlWrapper& open_gl, bool animate) {
736 for (int i = 0; i < kMaxTimesShown; ++i)
737 times_[i]->render(open_gl, animate);
738
739 setGlPositions();
740 grid_lines_.render(open_gl, animate);
741 sub_grid_lines_.render(open_gl, animate);
742
745 vital::poly_float input_phase = envelope_phase_->value();
747 float phase_length = vital::kVoiceKill - vital::kVoiceOn;
748 vital::poly_float phase = (input_phase - vital::kVoiceOn) * (1.0f / phase_length);
749 phase = vital::utils::maskLoad(phase, 1.0f, off_mask);
750 phase = vital::utils::min(phase, 1.0f);
751
752 vital::poly_mask reset_mask = vital::poly_float::greaterThan(last_phase_, phase);
753 vital::poly_float backup_phase = getBackupPhase(phase);
754 last_phase_ = vital::utils::maskLoad(last_phase_, backup_phase, reset_mask);
755
756 if (!animate_)
757 last_phase_ = phase;
758
759 animate_ = animate;
760 bool animating = animate;
761 if (parent_)
762 animating = animating && parent_->getSynth()->isModSourceEnabled(getName().toStdString());
763
764 Colour envelope_graph_fill = fill_left_color_;
765 float fill_fade = findValue(Skin::kWidgetFillFade);
766 Colour envelope_graph_fill_fade = envelope_graph_fill.withMultipliedAlpha(1.0f - fill_fade);
767 Colour envelope_graph_fill_stereo = fill_right_color_;
768 Colour envelope_graph_fill_stereo_fade = envelope_graph_fill_stereo.withMultipliedAlpha(1.0f - fill_fade);
769
770 if (animating) {
772
773 float release_point = (vital::kVoiceOff - vital::kVoiceOn) / phase_length;
774 vital::poly_mask released_mask = vital::poly_float::greaterThan(phase, release_point);
775 released_mask = released_mask & vital::poly_float::lessThan(last_phase_, release_point) & ~reset_mask;
776 last_phase_ = vital::utils::maskLoad(last_phase_, release_point, released_mask);
777
778 last_phase_ = vital::utils::max(last_phase_, 0.0f);
779 if (!envelope_phase_->isClearValue(input_phase))
780 boostRange(last_phase_, phase, 0, kTailDecay);
781 last_phase_ = phase;
782
783 setFill(true);
787 setIndex(1);
788 setColor(line_right_color_);
789 setFillColors(envelope_graph_fill_stereo_fade, envelope_graph_fill_stereo);
790 drawLines(open_gl, false);
791
793 setIndex(0);
794 setColor(line_left_color_);
795 setFillColors(envelope_graph_fill_fade, envelope_graph_fill);
796 drawLines(open_gl, anyBoostValue());
797
798 setFill(false);
799 setBoostAmount(0.0f);
800 setFillBoostAmount(0.0f);
802 setColor(line_center_color_);
803 drawLines(open_gl, anyBoostValue());
804
805 setViewPort(open_gl);
806 drawPosition(open_gl, 1);
807 drawPosition(open_gl, 0);
808 }
809 else {
810 setBoostAmount(0.0f);
811 setFillBoostAmount(0.0f);
812 decayBoosts(0.0f);
814
815 setFill(true);
816 setColor(line_right_color_);
817 setFillColors(envelope_graph_fill_stereo_fade, envelope_graph_fill_stereo);
818 drawLines(open_gl, false);
819
820 setColor(line_left_color_);
821 setFillColors(envelope_graph_fill_fade, envelope_graph_fill);
822 drawLines(open_gl, anyBoostValue());
823
824 setFill(false);
825 setColor(line_center_color_);
826 drawLines(open_gl, anyBoostValue());
827 }
828
829 point_circles_.setColor(line_center_color_);
830 point_circles_.setAltColor(background_color_);
831 point_circles_.render(open_gl, animate);
832
833 power_circles_.setColor(line_center_color_);
834 power_circles_.render(open_gl, animate);
835
836 drag_circle_.render(open_gl, animate);
837 hover_circle_.render(open_gl, animate);
838
839 renderCorners(open_gl, animate);
840}
841
843 drag_circle_.destroy(open_gl);
844 hover_circle_.destroy(open_gl);
845 grid_lines_.destroy(open_gl);
846 sub_grid_lines_.destroy(open_gl);
847 point_circles_.destroy(open_gl);
848 power_circles_.destroy(open_gl);
849 position_circle_.destroy(open_gl);
850
851 for (int i = 0; i < kMaxTimesShown; ++i)
852 times_[i]->destroy(open_gl);
853
855}
856
857void EnvelopeEditor::setEditingCircleBounds() {
858 float width = getWidth();
859 float height = getHeight();
860 float delay_x = padOpenGlX(getSliderDelayX() * 2.0f / width - 1.0f);
861 float attack_x = padOpenGlX(getSliderAttackX() * 2.0f / width - 1.0f);
862 float hold_x = padOpenGlX(getSliderHoldX() * 2.0f / width - 1.0f);
863 float decay_x = padOpenGlX(getSliderDecayX() * 2.0f / width - 1.0f);
864 float sustain_y = padOpenGlY(1.0f - getSliderSustainY() * 2.0f / height);
865 float release_x = padOpenGlX(getSliderReleaseX() * 2.0f / width - 1.0f);
866 float bottom = padOpenGlY(-1.0f);
867 float top = padOpenGlY(1.0f);
868
869 float grab_width = kMarkerGrabRadius * size_ratio_ * 4.0f / width;
870 float grab_height = kMarkerGrabRadius * size_ratio_ * 4.0f / height;
871 float hover_width = kMarkerHoverRadius * size_ratio_ * 4.0f / width;
872 float hover_height = kMarkerHoverRadius * size_ratio_ * 4.0f / height;
873 Point<float> grab_point(-10, -10);
874 if (delay_hover_)
875 grab_point = Point<float>(delay_x, bottom);
876 else if (release_hover_)
877 grab_point = Point<float>(release_x, bottom);
878 else if (sustain_hover_)
879 grab_point = Point<float>(decay_x, sustain_y);
880 else if (attack_hover_)
881 grab_point = Point<float>(attack_x, top);
882 else if (hold_hover_)
883 grab_point = Point<float>(hold_x, top);
884 else if (attack_power_hover_)
885 grab_point = Point<float>((delay_x + attack_x) / 2.0f, 1.0f - 2.0f * padY(getSliderAttackValue(0.5f)) / height);
886 else if (decay_power_hover_)
887 grab_point = Point<float>((hold_x + decay_x) / 2.0f, 1.0f - 2.0f * padY(getSliderDecayValue(0.5f)) / height);
888 else if (release_power_hover_)
889 grab_point = Point<float>((decay_x + release_x) / 2.0f, 1.0f - 2.0f * padY(getSliderReleaseValue(0.5f)) / height);
890
891 drag_circle_.setColor(findColour(Skin::kWidgetAccent2, true));
892 if (mouse_down_) {
893 drag_circle_.setQuad(0, grab_point.x - grab_width * 0.5f, grab_point.y - grab_height * 0.5f,
894 grab_width, grab_height);
895 }
896 else
897 drag_circle_.setQuad(0, -2.0f, -2.0f, 0.0f, 0.0f);
898
899 hover_circle_.setColor(findColour(Skin::kWidgetAccent1, true));
900 hover_circle_.setQuad(0, grab_point.x - hover_width * 0.5f, grab_point.y - hover_height * 0.5f,
901 hover_width, hover_height);
902}
903
904void EnvelopeEditor::setTimePositions() {
905 static constexpr float kTimeDisplayBuffer = 0.025f;
906 static constexpr float kDrawWidth = 0.1f;
907
908 float powers = logf(window_time_) / logf(kRulerDivisionSize);
909 float current_division = floorf(powers);
910 float transition = powers - current_division;
911 float big_time_chunk = powf(kRulerDivisionSize, current_division) / 2.0f;
912 float little_time_chunk = big_time_chunk / kRulerDivisionSize;
913
914 float font_height = kTimeDisplaySize * getHeight();
915 float font_buffer = kTimeDisplayBuffer * getHeight();
916 float font_draw_height = font_height + font_buffer;
917 float font_y = getHeight() - font_draw_height;
918 float font_draw_width = getWidth() * kDrawWidth;
919
920 float width = getWidth();
921
922 float t = 1.0f - transition;
923 Colour lighten = findColour(Skin::kLightenScreen, true);
924 Colour big_color = background_color_.overlaidWith(lighten);
925 Colour little_color = background_color_.overlaidWith(lighten.withMultipliedAlpha(t * t));
926 int index = 1;
927 for (; index * little_time_chunk < window_time_ && index < kMaxTimesShown; ++index) {
928 if (index % kRulerDivisionSize)
929 times_[index]->setColor(little_color);
930 else
931 times_[index]->setColor(big_color);
932
933 float time = index * little_time_chunk;
934 int x = padX(width * time / window_time_);
935 String display = formatTime(time);
936 times_[index]->setText(display);
937 times_[index]->setVisible(true);
938 times_[index]->setBounds(x + font_buffer, font_y, font_draw_width, font_draw_height);
939 times_[index]->redrawImage(false);
940 }
941 for (; index < kMaxTimesShown; ++index)
942 times_[index]->setVisible(false);
943}
944
945void EnvelopeEditor::setGridPositions() {
946 float powers = logf(window_time_) / logf(kRulerDivisionSize);
947 float current_division = floorf(powers);
948 float transition = powers - current_division;
949 float big_time_chunk = powf(kRulerDivisionSize, current_division) / 2.0f;
950 float little_time_chunk = big_time_chunk / kRulerDivisionSize;
951
952 float width = getWidth();
953
954 float t = 1.0f - transition;
955 float line_width = 2.0f / width;
956 sub_grid_lines_.setColor(time_color_.withMultipliedAlpha(t * t));
957 int sub_index = 0;
958 for (int i = 1; i * little_time_chunk < window_time_; ++i) {
959 if (i % kRulerDivisionSize) {
960 float time = i * little_time_chunk;
961 float x = padOpenGlX(2.0f * time / window_time_ - 1.0f);
962 sub_grid_lines_.setQuad(sub_index, x, -1.0f, line_width, 2.0f);
963 sub_index++;
964 }
965 }
966 sub_grid_lines_.setNumQuads(sub_index);
967
968 int index = 0;
969 grid_lines_.setColor(time_color_);
970 for (int i = 1; i * big_time_chunk < window_time_; ++i) {
971 float time = i * big_time_chunk;
972 float x = padOpenGlX(2.0f * time / window_time_ - 1.0f);
973 grid_lines_.setQuad(index, x, -1.0f, line_width, 2.0f);
974 index++;
975 }
976 grid_lines_.setNumQuads(index);
977}
978
979void EnvelopeEditor::setPointPositions() {
980 float width = getWidth();
981 float height = getHeight();
982
983 float delay_x = padOpenGlX(getSliderDelayX() * 2.0f / width - 1.0f);
984 float attack_x = padOpenGlX(getSliderAttackX() * 2.0f / width - 1.0f);
985 float hold_x = padOpenGlX(getSliderHoldX() * 2.0f / width - 1.0f);
986 float decay_x = padOpenGlX(getSliderDecayX() * 2.0f / width - 1.0f);
987 float sustain_y = padOpenGlY(1.0f - getSliderSustainY() * 2.0f / height);
988 float release_x = padOpenGlX(getSliderReleaseX() * 2.0f / width - 1.0f);
989 float bottom = padOpenGlY(-1.0f);
990 float top = padOpenGlY(1.0f);
991
992 float marker_width = size_ratio_ * 2.0f * kMarkerWidth / width;
993 float marker_height = size_ratio_ * 2.0f * kMarkerWidth / height;
994 point_circles_.setThickness(size_ratio_ * kMarkerWidth * 0.5f * kRingThickness);
995 point_circles_.setQuad(0, attack_x - marker_width * 0.5f, top - marker_height * 0.5f, marker_width, marker_height);
996 if (hold_x == attack_x)
997 point_circles_.setQuad(1, -2.0f, -2.0f, 0.0f, 0.0f);
998 else
999 point_circles_.setQuad(1, hold_x - marker_width * 0.5f, top - marker_height * 0.5f, marker_width, marker_height);
1000
1001 point_circles_.setQuad(2, decay_x - marker_width * 0.5f, sustain_y - marker_height * 0.5f,
1002 marker_width, marker_height);
1003 point_circles_.setQuad(3, release_x - marker_width * 0.5f, bottom - marker_height * 0.5f,
1004 marker_width, marker_height);
1005
1006 float power_width = size_ratio_ * 2.0f * kPowerMarkerWidth / width;
1007 float power_height = size_ratio_ * 2.0f * kPowerMarkerWidth / height;
1008 float min_power_distance = kMinPointDistanceForPower * 2.0f / width;
1009 if (attack_x - delay_x > min_power_distance) {
1010 float power_attack_x = (delay_x + attack_x) * 0.5;
1011 float power_attack_y = padOpenGlY(1.0f - getSliderAttackValue(0.5f) * 2.0f / height);
1012 power_circles_.setQuad(0, power_attack_x - power_width * 0.5f, power_attack_y - power_height * 0.5f,
1013 power_width, power_height);
1014 }
1015 else
1016 power_circles_.setQuad(0, -2.0f, -2.0f, power_width, power_height);
1017
1018 if (decay_x - hold_x > min_power_distance && sustain_y < top) {
1019 float power_decay_x = (hold_x + decay_x) * 0.5;
1020 float power_decay_y = padOpenGlY(1.0f - getSliderDecayValue(0.5f) * 2.0f / height);
1021 power_circles_.setQuad(1, power_decay_x - power_width * 0.5f, power_decay_y - power_height * 0.5f,
1022 power_width, power_height);
1023 }
1024 else
1025 power_circles_.setQuad(1, -2.0f, -2.0f, 0.0f, 0.0f);
1026
1027 if (release_x - decay_x > min_power_distance && sustain_y > bottom) {
1028 float power_release_x = (decay_x + release_x) * 0.5;
1029 float power_release_y = padOpenGlY(1.0f - getSliderReleaseValue(0.5f) * 2.0f / height);
1030 power_circles_.setQuad(2, power_release_x - power_width * 0.5f, power_release_y - power_height * 0.5f,
1031 power_width, power_height);
1032 }
1033 else
1034 power_circles_.setQuad(2, -2.0f, -2.0f, 0.0f, 0.0f);
1035}
1036
1037void EnvelopeEditor::setGlPositions() {
1038 if (!reset_positions_)
1039 return;
1040
1041 reset_positions_ = false;
1042
1043 setEditingCircleBounds();
1044 setGridPositions();
1045 setPointPositions();
1046}
1047
1048void EnvelopeEditor::setColors() {
1049 line_left_color_ = findColour(Skin::kWidgetPrimary1, true);
1050 line_right_color_ = findColour(Skin::kWidgetPrimary2, true);
1051 line_center_color_ = findColour(Skin::kWidgetCenterLine, true);
1052 fill_left_color_ = findColour(Skin::kWidgetSecondary1, true);
1053 fill_right_color_ = findColour(Skin::kWidgetSecondary2, true);
1054 background_color_ = findColour(Skin::kWidgetBackground, true);
1055 time_color_ = findColour(Skin::kLightenScreen, true);
1056
1057 drag_circle_.setColor(findColour(Skin::kWidgetAccent2, true));
1058 hover_circle_.setColor(findColour(Skin::kWidgetAccent1, true));
1059}
1060
1061void EnvelopeEditor::drawPosition(OpenGlWrapper& open_gl, int index) {
1062 static constexpr float kMinPositionAlphaDecay = 0.9f;
1063 static constexpr float kCenterFade = 0.2f;
1064 if (envelope_phase_ == nullptr)
1065 return;
1066
1067 std::pair<float, float> position = getPosition(index);
1068 float current_alpha = current_position_alpha_[index];
1069 float x = position.first;
1070 float y = position.second;
1071 if (y > -1.0f)
1072 current_position_alpha_.set(index, 1.0f);
1073 else {
1074 float release = getOutputsTotal(release_outputs_, release_slider_->getValue())[index];
1075 release = vital::utils::max(release, 0.0f);
1076 current_position_alpha_.set(index, current_position_alpha_[index] * std::min(kMinPositionAlphaDecay, release));
1077 }
1078
1079 if (current_alpha == 0.0f)
1080 return;
1081
1082 glEnable(GL_BLEND);
1083 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
1084
1085 float current_phase = envelope_phase_->value()[index];
1086 if (current_phase <= vital::kVoiceKill && current_phase >= vital::kVoiceOn) {
1087 float width = getWidth();
1088 float height = getHeight();
1089 float marker_width = size_ratio_ * 2.0f * kMarkerWidth / width;
1090 float marker_height = size_ratio_ * 2.0f * kMarkerWidth / height;
1091
1092 position_circle_.setQuad(0, x - marker_width * 0.5f, y - marker_height * 0.5f, marker_width, marker_height);
1093 }
1094
1095 float current_position_alpha = current_position_alpha_[index];
1096 float mult = std::max(current_position_alpha, 0.0f);
1097 mult *= mult;
1098 Colour color;
1099 if (index)
1100 color = line_right_color_;
1101 else
1102 color = line_left_color_;
1103
1104 Colour alt_color = color.interpolatedWith(background_color_, kCenterFade);
1105 position_circle_.setThickness(size_ratio_ * 0.5f * kMarkerWidth * kRingThickness);
1106 position_circle_.setColor(color.withMultipliedAlpha(mult));
1107 position_circle_.setAltColor(alt_color.withMultipliedAlpha(mult));
1108 position_circle_.render(open_gl, true);
1109}
void setDecayPowerSlider(SynthSlider *decay_slider)
Definition envelope_editor.cpp:590
void parentHierarchyChanged() override
Called when the component’s parent hierarchy changes.
Definition envelope_editor.cpp:105
void mouseDrag(const MouseEvent &e) override
Definition envelope_editor.cpp:204
void mouseWheelMove(const MouseEvent &e, const MouseWheelDetails &wheel) override
Handles mouse wheel events for zooming.
Definition envelope_editor.cpp:244
void setAttackPowerSlider(SynthSlider *attack_slider)
Definition envelope_editor.cpp:580
void resetEnvelopeLine(int index)
Resets the envelope line for a given voice index.
Definition envelope_editor.cpp:620
static constexpr float kRingThickness
Thickness fraction for the marker rings.
Definition envelope_editor.h:24
void render(OpenGlWrapper &open_gl, bool animate) override
Renders the envelope using OpenGL.
Definition envelope_editor.cpp:735
static constexpr int kNumPointsPerSection
Number of points per envelope section.
Definition envelope_editor.h:51
static constexpr float kTailDecay
Decay factor for tail end animations.
Definition envelope_editor.h:32
~EnvelopeEditor()
Destructor.
Definition envelope_editor.cpp:96
void setHoldSlider(SynthSlider *hold_slider)
Definition envelope_editor.cpp:575
void setSustainSlider(SynthSlider *sustain_slider)
Definition envelope_editor.cpp:595
void init(OpenGlWrapper &open_gl) override
Initializes OpenGL resources.
Definition envelope_editor.cpp:721
void mouseMove(const MouseEvent &e) override
Mouse event callbacks for interactions.
Definition envelope_editor.cpp:181
static constexpr float kTimeDisplaySize
Display size ratio for time text.
Definition envelope_editor.h:42
void mouseDown(const MouseEvent &e) override
Definition envelope_editor.cpp:198
void setDecaySlider(SynthSlider *decay_slider)
Definition envelope_editor.cpp:585
static constexpr float kPaddingX
Horizontal padding ratio for the display.
Definition envelope_editor.h:34
EnvelopeEditor(const String &prefix, const vital::output_map &mono_modulations, const vital::output_map &poly_modulations)
Constructs an EnvelopeEditor.
Definition envelope_editor.cpp:29
static constexpr float kMarkerGrabRadius
Radius in pixels for grabbing markers.
Definition envelope_editor.h:30
static constexpr int kMaxTimesShown
Maximum number of time markers shown.
Definition envelope_editor.h:49
void mouseDoubleClick(const MouseEvent &e) override
Definition envelope_editor.cpp:230
void setReleasePowerSlider(SynthSlider *release_slider)
Definition envelope_editor.cpp:605
void guiChanged(SynthSlider *slider) override
Called when a slider value changes.
Definition envelope_editor.cpp:272
void magnifyZoom(Point< float > delta)
Zooms the envelope display via magnification (e.g., pinch gestures).
Definition envelope_editor.cpp:250
void mouseUp(const MouseEvent &e) override
Definition envelope_editor.cpp:239
void setReleaseSlider(SynthSlider *release_slider)
Definition envelope_editor.cpp:600
void pickHoverPosition(Point< float > position)
Determines hover state based on mouse position.
Definition envelope_editor.cpp:119
void setAttackSlider(SynthSlider *attack_slider)
Definition envelope_editor.cpp:570
static constexpr float kMarkerWidth
Width in pixels of the main markers (ADHSR points).
Definition envelope_editor.h:22
static constexpr float kPowerMouseMultiplier
Multiplier for mouse movements when adjusting power.
Definition envelope_editor.h:40
void magnifyReset()
Resets the magnification zoom to a default level.
Definition envelope_editor.cpp:256
static constexpr float kMinPointDistanceForPower
Minimum point distance for enabling power handle editing.
Definition envelope_editor.h:38
void setDelaySlider(SynthSlider *delay_slider)
Sets the sliders corresponding to different envelope parameters.
Definition envelope_editor.cpp:565
void resetPositions()
Flags that the positions need to be reset and recalculated.
Definition envelope_editor.h:188
void destroy(OpenGlWrapper &open_gl) override
Destroys the OpenGL resources.
Definition envelope_editor.cpp:842
void mouseExit(const MouseEvent &e) override
Definition envelope_editor.cpp:186
void paintBackground(Graphics &g) override
Paints the background of the editor.
Definition envelope_editor.cpp:98
static constexpr int kRulerDivisionSize
Division size for major time ruler lines.
Definition envelope_editor.h:45
static constexpr float kMarkerHoverRadius
Radius in pixels for hovering detection over markers.
Definition envelope_editor.h:28
static constexpr float kPaddingY
Vertical padding ratio for the display.
Definition envelope_editor.h:36
static constexpr float kPowerMarkerWidth
Width in pixels of the power markers (for adjusting curve power).
Definition envelope_editor.h:26
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
const vital::StatusOutput * num_voices_readout_
StatusOutput for voice count lookups.
Definition open_gl_component.h:258
void renderCorners(OpenGlWrapper &open_gl, bool animate, Colour color, float rounding)
Renders the corner shapes using the given color and rounding amount.
Definition open_gl_component.cpp:153
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
void setBackgroundColor(const Colour &color)
Sets the background color of the component for painting operations.
Definition open_gl_component.h:242
virtual void paintBackground(Graphics &g)
Paints a standard background for the component.
Definition open_gl_component.cpp:105
virtual void parentHierarchyChanged() override
Called when the component's parent hierarchy changes.
Definition open_gl_component.cpp:128
A component for rendering lines with optional filling and boost effects using OpenGL.
Definition open_gl_line_renderer.h:16
virtual void init(OpenGlWrapper &open_gl) override
Initializes OpenGL resources for rendering the line.
Definition open_gl_line_renderer.cpp:78
virtual void destroy(OpenGlWrapper &open_gl) override
Destroys OpenGL resources allocated by this line renderer.
Definition open_gl_line_renderer.cpp:468
force_inline void setFillCenter(float fill_center)
Sets the vertical center for the fill area.
Definition open_gl_line_renderer.h:138
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 setFill(bool fill)
Enables or disables filling below the line.
Definition open_gl_line_renderer.h:124
void decayBoosts(vital::poly_float mult)
Decays all boosts by a multiplicative factor, allowing animated damping.
Definition open_gl_line_renderer.cpp:185
force_inline void setFillBoostAmount(float boost_amount)
Sets the boost amount that affects fill thickness.
Definition open_gl_line_renderer.h:147
force_inline void setBoostAmount(float boost_amount)
Sets the boost amount that affects line thickness.
Definition open_gl_line_renderer.h:144
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 setFillColors(Colour fill_color_from, Colour fill_color_to)
Sets a gradient fill from one color to another.
Definition open_gl_line_renderer.h:132
bool anyBoostValue()
Checks if any boost value is set.
Definition open_gl_line_renderer.h:202
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
void boostRange(float *boosts, float start, float end, int buffer_vertices, float min)
Boosts a range for the given boost array.
Definition open_gl_line_renderer.cpp:128
void drawLines(OpenGlWrapper &open_gl, bool left)
Draws the line and optional fill using OpenGL.
Definition open_gl_line_renderer.cpp:386
force_inline void setIndex(int index)
Sets an index used for custom behavior (e.g., multiple line sets).
Definition open_gl_line_renderer.h:150
force_inline Colour color() const
Gets the current line color.
Definition open_gl_line_renderer.h:189
void enableBackwardBoost(bool enable)
Enables backward boost calculation for symmetrical line deformation.
Definition open_gl_line_renderer.h:183
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
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 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 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
Manages and provides access to vertex and fragment shaders used by the OpenGL rendering pipeline.
Definition shaders.h:19
@ kWidgetLineWidth
Definition skin.h:105
@ kWidgetFillCenter
Definition skin.h:107
@ kWidgetFillFade
Definition skin.h:108
@ kWidgetLineBoost
Definition skin.h:106
@ kWidgetFillBoost
Definition skin.h:109
@ kWidgetAccent1
Definition skin.h:171
@ kWidgetPrimary2
Definition skin.h:166
@ kWidgetPrimary1
Definition skin.h:165
@ kWidgetBackground
Definition skin.h:173
@ kWidgetSecondary1
Definition skin.h:168
@ kLightenScreen
Definition skin.h:141
@ kWidgetCenterLine
Definition skin.h:164
@ kWidgetSecondary2
Definition skin.h:169
@ kWidgetAccent2
Definition skin.h:172
bool isModSourceEnabled(const std::string &source)
Checks if a modulation source is currently enabled.
Definition synth_base.cpp:223
const vital::StatusOutput * getStatusOutput(const std::string &name)
Retrieves a status output by name.
Definition synth_base.cpp:262
SynthBase * getSynth()
Returns the SynthBase instance this interface is managing.
Definition synth_gui_interface.h:85
A specialized slider with extended functionality for modulation, parameter control,...
Definition synth_slider.h:314
double getAdjustedValue(double value)
Definition synth_slider.cpp:430
void addSliderListener(SliderListener *listener)
Definition synth_slider.cpp:631
void setValueFromAdjusted(double value)
Definition synth_slider.cpp:495
force_inline bool isClearValue(poly_float value) const
Checks if a given poly_float is the special "clear" value.
Definition synth_module.h:81
force_inline poly_float value() const
Returns the current status value.
Definition synth_module.h:49
force_inline mono_float powerScale(mono_float value, mono_float power)
A power-scaling function to map a linear range to a curved response.
Definition futils.h:455
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 maskLoad(poly_float zero_value, poly_float one_value, poly_mask reset_mask)
Selects between two values (zero_value or one_value) based on a mask in each lane.
Definition poly_utils.h:351
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
@ kVoiceOn
Definition common.h:77
@ kVoiceKill
Definition common.h:81
@ kVoiceDecay
Definition common.h:79
@ kVoiceHold
Definition common.h:78
@ kVoiceOff
Definition common.h:80
constexpr int kMsPerSec
Milliseconds per second.
Definition common.h:50
std::map< std::string, Output * > output_map
Maps parameter names to Output pointers, representing output signals from various modules.
Definition synth_types.h:229
A helper struct containing references to OpenGL context, shaders, and display scale.
Definition shaders.h:174
Represents a vector of floating-point values using SIMD instructions.
Definition poly_values.h:600
static force_inline mask_simd_type vector_call equal(simd_type one, simd_type two)
Compares two SIMD float registers for equality, element-wise.
Definition poly_values.h:954
static force_inline poly_mask vector_call lessThan(poly_float one, poly_float two)
Definition poly_values.h:1105
force_inline void vector_call set(size_t index, float new_value) noexcept
Sets a specific element in the SIMD register.
Definition poly_values.h:1182
static force_inline mask_simd_type vector_call greaterThan(simd_type one, simd_type two)
Compares two SIMD float registers, element-wise, for greater than.
Definition poly_values.h:971
Represents a vector of integer values using SIMD instructions.
Definition poly_values.h:56
Provides various utility functions, classes, and constants for audio, math, and general-purpose opera...