Vital
Loading...
Searching...
No Matches
filter_response.cpp
Go to the documentation of this file.
1#include "filter_response.h"
2
3#include "comb_filter.h"
4#include "skin.h"
5#include "synth_constants.h"
6#include "shaders.h"
7#include "synth_slider.h"
8#include "utils.h"
9
10namespace {
17 FilterResponse::FilterShader getShaderForModel(vital::constants::FilterModel model, int style) {
18 switch (model) {
23 if (feedback_style == vital::CombFilter::kComb)
25 if (feedback_style == vital::CombFilter::kPositiveFlange)
28 }
41 default:
42 VITAL_ASSERT(false);
44 }
45 }
46
53 switch (shader) {
74 default:
75 VITAL_ASSERT(false);
77 }
78 }
79
86 std::pair<vital::Output*, vital::Output*> getOutputs(const vital::output_map& mono_modulations,
87 const std::string& name) {
88 return {
89 mono_modulations.at(name),
90 nullptr
91 };
92 }
93
101 std::pair<vital::Output*, vital::Output*> getOutputs(const vital::output_map& mono_modulations,
102 const vital::output_map& poly_modulations,
103 const std::string& name) {
104 return {
105 mono_modulations.at(name),
106 poly_modulations.at(name)
107 };
108 }
109} // namespace
110
111FilterResponse::FilterResponse() : OpenGlLineRenderer(kResolution), phaser_filter_(false) {
112 setFill(true);
113 setFillCenter(-1.0f);
114 active_ = false;
115 animate_ = false;
116 mix_ = 1.0f;
117 current_resonance_value_ = 0.0;
118 current_cutoff_value_ = 0.0;
119 current_formant_x_value_ = 0.0;
120 current_formant_y_value_ = 0.0;
121
122 cutoff_slider_ = nullptr;
123 resonance_slider_ = nullptr;
124 formant_x_slider_ = nullptr;
125 formant_y_slider_ = nullptr;
126 filter_mix_slider_ = nullptr;
127 blend_slider_ = nullptr;
128 transpose_slider_ = nullptr;
129 formant_transpose_slider_ = nullptr;
130 formant_resonance_slider_ = nullptr;
131 formant_spread_slider_ = nullptr;
132 last_filter_style_ = 0;
133 last_filter_model_ = static_cast<vital::constants::FilterModel>(0);
134 filter_model_ = static_cast<vital::constants::FilterModel>(0);
135
136 line_data_ = std::make_unique<float[]>(2 * kResolution);
137 line_buffer_ = 0;
138 response_buffer_ = 0;
139 vertex_array_object_ = 0;
140
141 for (int i = 0; i < kResolution; ++i) {
142 float t = i / (kResolution - 1.0f);
143 line_data_[2 * i] = 2.0f * t - 1.0f;
144 line_data_[2 * i + 1] = (i / kCombAlternatePeriod) % 2;
145 }
146
147 analog_filter_.setSampleRate(kDefaultVisualSampleRate);
148 comb_filter_.setSampleRate(kDefaultVisualSampleRate);
149 digital_filter_.setSampleRate(kDefaultVisualSampleRate);
150 diode_filter_.setSampleRate(kDefaultVisualSampleRate);
151 dirty_filter_.setSampleRate(kDefaultVisualSampleRate);
152 formant_filter_.setSampleRate(kDefaultVisualSampleRate);
153 ladder_filter_.setSampleRate(kDefaultVisualSampleRate);
154 phaser_filter_.setSampleRate(kDefaultVisualSampleRate);
155}
156
157FilterResponse::FilterResponse(String suffix, const vital::output_map& mono_modulations) : FilterResponse() {
158 std::string prefix = std::string("filter_") + suffix.toStdString() +"_";
159
160 filter_mix_outputs_ = getOutputs(mono_modulations, prefix + "mix");
161 midi_cutoff_outputs_ = getOutputs(mono_modulations, prefix + "cutoff");
162 resonance_outputs_ = getOutputs(mono_modulations, prefix + "resonance");
163 blend_outputs_ = getOutputs(mono_modulations, prefix + "blend");
164 transpose_outputs_ = getOutputs(mono_modulations, prefix + "blend_transpose");
165 interpolate_x_outputs_ = getOutputs(mono_modulations, prefix + "formant_x");
166 interpolate_y_outputs_ = getOutputs(mono_modulations, prefix + "formant_y");
167 formant_transpose_outputs_ = getOutputs(mono_modulations, prefix + "formant_transpose");
168 formant_resonance_outputs_ = getOutputs(mono_modulations, prefix + "formant_resonance");
169 formant_spread_outputs_ = getOutputs(mono_modulations, prefix + "formant_spread");
170}
171
172FilterResponse::FilterResponse(int index, const vital::output_map& mono_modulations,
173 const vital::output_map& poly_modulations) : FilterResponse() {
174 std::string prefix = std::string("filter_") + std::to_string(index) + "_";
175
176 filter_mix_outputs_ = getOutputs(mono_modulations, poly_modulations, prefix + "mix");
177 midi_cutoff_outputs_ = getOutputs(mono_modulations, poly_modulations, prefix + "cutoff");
178 resonance_outputs_ = getOutputs(mono_modulations, poly_modulations, prefix + "resonance");
179 blend_outputs_ = getOutputs(mono_modulations, poly_modulations, prefix + "blend");
180 transpose_outputs_ = getOutputs(mono_modulations, poly_modulations, prefix + "blend_transpose");
181 interpolate_x_outputs_ = getOutputs(mono_modulations, poly_modulations, prefix + "formant_x");
182 interpolate_y_outputs_ = getOutputs(mono_modulations, poly_modulations, prefix + "formant_y");
183 formant_transpose_outputs_ = getOutputs(mono_modulations, poly_modulations, prefix + "formant_transpose");
184 formant_resonance_outputs_ = getOutputs(mono_modulations, poly_modulations, prefix + "formant_resonance");
185 formant_spread_outputs_ = getOutputs(mono_modulations, poly_modulations, prefix + "formant_spread");
186}
187
189
192
193 const GLchar* varyings[] = { "response_out" };
194 open_gl.context.extensions.glGenVertexArrays(1, &vertex_array_object_);
195 open_gl.context.extensions.glBindVertexArray(vertex_array_object_);
196
197 GLsizeiptr data_size = static_cast<GLsizeiptr>(kResolution * sizeof(float));
198 open_gl.context.extensions.glGenBuffers(1, &line_buffer_);
199 open_gl.context.extensions.glBindBuffer(GL_ARRAY_BUFFER, line_buffer_);
200 open_gl.context.extensions.glBufferData(GL_ARRAY_BUFFER, 2 * data_size, line_data_.get(), GL_STATIC_DRAW);
201
202 open_gl.context.extensions.glGenBuffers(1, &response_buffer_);
203 open_gl.context.extensions.glBindBuffer(GL_ARRAY_BUFFER, response_buffer_);
204 open_gl.context.extensions.glBufferData(GL_ARRAY_BUFFER, data_size, nullptr, GL_STATIC_READ);
205
206 for (int i = 0; i < kNumFilterShaders; ++i) {
207 Shaders::VertexShader vertex_shader = getVertexShader(static_cast<FilterShader>(i));
208 OpenGLShaderProgram* shader = open_gl.shaders->getShaderProgram(vertex_shader, Shaders::kColorFragment, varyings);
209 shaders_[i].shader = shader;
210
211 shader->use();
212 shaders_[i].position = getAttribute(open_gl, *shader, "position");
213
214 shaders_[i].mix = getUniform(open_gl, *shader, "mix");
215 shaders_[i].midi_cutoff = getUniform(open_gl, *shader, "midi_cutoff");
216 shaders_[i].resonance = getUniform(open_gl, *shader, "resonance");
217 shaders_[i].drive = getUniform(open_gl, *shader, "drive");
218 shaders_[i].db24 = getUniform(open_gl, *shader, "db24");
219
220 shaders_[i].formant_cutoff = getUniform(open_gl, *shader, "formant_cutoff");
221 shaders_[i].formant_resonance = getUniform(open_gl, *shader, "formant_resonance");
222 shaders_[i].formant_spread = getUniform(open_gl, *shader, "formant_spread");
223 shaders_[i].formant_low = getUniform(open_gl, *shader, "low");
224 shaders_[i].formant_band = getUniform(open_gl, *shader, "band");
225 shaders_[i].formant_high = getUniform(open_gl, *shader, "high");
226
227 for (int s = 0; s < FilterResponseShader::kMaxStages; ++s) {
228 String stage = String("stage") + String(s);
229 shaders_[i].stages[s] = getUniform(open_gl, *shader, stage.toRawUTF8());
230 }
231 }
232}
233
234void FilterResponse::render(OpenGlWrapper& open_gl, bool animate) {
235 animate_ = animate;
236 drawFilterResponse(open_gl);
237 renderCorners(open_gl, animate);
238}
239
242
243 open_gl.context.extensions.glDeleteBuffers(1, &line_buffer_);
244 open_gl.context.extensions.glDeleteBuffers(1, &response_buffer_);
245
246 vertex_array_object_ = 0;
247 line_buffer_ = 0;
248 response_buffer_ = 0;
249
250 for (int i = 0; i < kNumFilterShaders; ++i) {
251 shaders_[i].shader = nullptr;
252 shaders_[i].position = nullptr;
253
254 shaders_[i].mix = nullptr;
255 shaders_[i].midi_cutoff = nullptr;
256 shaders_[i].resonance = nullptr;
257 shaders_[i].drive = nullptr;
258 shaders_[i].db24 = nullptr;
259
260 shaders_[i].formant_cutoff = nullptr;
261 shaders_[i].formant_resonance = nullptr;
262 shaders_[i].formant_spread = nullptr;
263 shaders_[i].formant_low = nullptr;
264 shaders_[i].formant_band = nullptr;
265 shaders_[i].formant_high = nullptr;
266
267 for (int s = 0; s < FilterResponseShader::kMaxStages; ++s)
268 shaders_[i].stages[s] = nullptr;
269 }
270}
271
273 g.fillAll(findColour(Skin::kWidgetBackground, true));
274
275 line_left_color_ = findColour(Skin::kWidgetPrimary1, true);
276 line_right_color_ = findColour(Skin::kWidgetPrimary2, true);
277 line_disabled_color_ = findColour(Skin::kWidgetPrimaryDisabled, true);
278 fill_left_color_ = findColour(Skin::kWidgetSecondary1, true);
279 fill_right_color_ = findColour(Skin::kWidgetSecondary2, true);
280 fill_disabled_color_ = findColour(Skin::kWidgetSecondaryDisabled, true);
281}
282
283void FilterResponse::setFilterSettingsFromPosition(Point<int> position) {
284 Point<int> delta = position - last_mouse_position_;
285 last_mouse_position_ = position;
286 double width = getWidth();
287 double height = getHeight();
288 current_cutoff_value_ += delta.x * cutoff_slider_->getRange().getLength() / width;
289 current_formant_x_value_ += delta.x * formant_x_slider_->getRange().getLength() / width;
290 current_resonance_value_ -= delta.y * resonance_slider_->getRange().getLength() / height;
291 current_formant_y_value_ -= delta.y * formant_y_slider_->getRange().getLength() / height;
292 current_cutoff_value_ = cutoff_slider_->getRange().clipValue(current_cutoff_value_);
293 current_formant_x_value_ = formant_x_slider_->getRange().clipValue(current_formant_x_value_);
294 current_resonance_value_ = resonance_slider_->getRange().clipValue(current_resonance_value_);
295 current_formant_y_value_ = formant_y_slider_->getRange().clipValue(current_formant_y_value_);
296
297 if (filter_model_ == vital::constants::kFormant) {
298 formant_x_slider_->setValue(current_formant_x_value_);
299 formant_x_slider_->showPopup(true);
300 formant_y_slider_->setValue(current_formant_y_value_);
301 formant_y_slider_->showPopup(false);
302 }
303 else {
304 cutoff_slider_->setValue(current_cutoff_value_);
305 cutoff_slider_->showPopup(true);
306 resonance_slider_->setValue(current_resonance_value_);
307 resonance_slider_->showPopup(false);
308 }
309}
310
311void FilterResponse::mouseDown(const MouseEvent& e) {
312 last_mouse_position_ = e.getPosition();
313 current_resonance_value_ = resonance_slider_->getValue();
314 current_cutoff_value_ = cutoff_slider_->getValue();
315 current_formant_x_value_ = formant_x_slider_->getValue();
316 current_formant_y_value_ = formant_y_slider_->getValue();
317
318 if (filter_model_ == vital::constants::kFormant) {
319 formant_x_slider_->showPopup(true);
320 formant_y_slider_->showPopup(false);
321 }
322 else {
323 cutoff_slider_->showPopup(true);
324 resonance_slider_->showPopup(false);
325 }
326}
327
328void FilterResponse::mouseDrag(const MouseEvent& e) {
329 setFilterSettingsFromPosition(e.getPosition());
330}
331
332void FilterResponse::mouseExit(const MouseEvent& e) {
333 cutoff_slider_->hidePopup(true);
334 resonance_slider_->hidePopup(false);
335 OpenGlLineRenderer::mouseExit(e);
336}
337
338void FilterResponse::mouseWheelMove(const MouseEvent& e, const MouseWheelDetails& wheel) {
339 MouseWheelDetails horizontal_details = wheel;
340 horizontal_details.deltaY = 0.0f;
341 MouseWheelDetails vertical_details = wheel;
342 vertical_details.deltaX = 0.0f;
343
344 if (filter_model_ == vital::constants::kFormant) {
345 formant_x_slider_->mouseWheelMove(e, horizontal_details);
346 formant_y_slider_->mouseWheelMove(e, vertical_details);
347 }
348 else {
349 cutoff_slider_->mouseWheelMove(e, horizontal_details);
350 resonance_slider_->mouseWheelMove(e, vertical_details);
351 }
352}
353
354inline vital::poly_float FilterResponse::getOutputsTotal(
355 std::pair<vital::Output*, vital::Output*> outputs, vital::poly_float default_value) {
356 if (!active_ || !animate_ || !outputs.first->owner->enabled())
357 return default_value;
358 if (outputs.second == nullptr || num_voices_readout_ == nullptr || num_voices_readout_->value()[0] <= 0.0f)
359 return outputs.first->trigger_value;
360 return outputs.first->trigger_value + outputs.second->trigger_value;
361}
362
363bool FilterResponse::setupFilterState(vital::constants::FilterModel model) {
364 vital::poly_float midi_cutoff = getOutputsTotal(midi_cutoff_outputs_, cutoff_slider_->getValue());
366 vital::poly_float mix = getOutputsTotal(filter_mix_outputs_, filter_mix_slider_->getValue());
367 mix = vital::utils::clamp(mix, 0.0f, 1.0f);
368 vital::poly_float resonance_percent = getOutputsTotal(resonance_outputs_, resonance_slider_->getValue());
369 vital::poly_float pass_blend = getOutputsTotal(blend_outputs_, blend_slider_->getValue());
370 pass_blend = vital::utils::clamp(pass_blend, 0.0f, 2.0f);
371 vital::poly_float transpose = getOutputsTotal(transpose_outputs_, transpose_slider_->getValue());
372 vital::poly_float interpolate_x = getOutputsTotal(interpolate_x_outputs_, formant_x_slider_->getValue());
373 vital::poly_float interpolate_y = getOutputsTotal(interpolate_y_outputs_, formant_y_slider_->getValue());
374
375 if (model == vital::constants::kFormant) {
376 transpose = getOutputsTotal(formant_transpose_outputs_, formant_transpose_slider_->getValue());
377 resonance_percent = getOutputsTotal(formant_resonance_outputs_, formant_resonance_slider_->getValue());
378 pass_blend = getOutputsTotal(formant_spread_outputs_, formant_spread_slider_->getValue());
379 }
380
383 equal = equal & vital::poly_float::equal(mix_, mix);
384 equal = equal & vital::poly_float::equal(filter_state_.resonance_percent, resonance_percent);
385 equal = equal & vital::poly_float::equal(filter_state_.pass_blend, pass_blend);
386 equal = equal & vital::poly_float::equal(filter_state_.transpose, transpose);
387 equal = equal & vital::poly_float::equal(filter_state_.interpolate_x, interpolate_x);
388 equal = equal & vital::poly_float::equal(filter_state_.interpolate_y, interpolate_y);
389
390 filter_state_.midi_cutoff = midi_cutoff;
391 mix_ = mix;
392 filter_state_.resonance_percent = resonance_percent;
393 filter_state_.pass_blend = pass_blend;
394 filter_state_.transpose = transpose;
395 filter_state_.interpolate_x = interpolate_x;
396 filter_state_.interpolate_y = interpolate_y;
397
398 bool new_type = last_filter_model_ != model || last_filter_style_ != filter_state_.style;
399 last_filter_style_ = filter_state_.style;
400 last_filter_model_ = model;
401
402 return (~equal).anyMask() || new_type;
403}
404
405bool FilterResponse::isStereoState() {
413 vital::utils::swapStereo(filter_state_.pass_blend));
415 vital::utils::swapStereo(filter_state_.transpose));
420
421 return (~equal).anyMask();
422}
423
424void FilterResponse::loadShader(FilterShader shader, vital::constants::FilterModel model, int index) {
425 if (model == vital::constants::kAnalog) {
426 analog_filter_.setupFilter(filter_state_);
427 shaders_[shader].shader->use();
428 float resonance = vital::utils::clamp(analog_filter_.getResonance()[index], 0.0f, 2.0f);
429 shaders_[shader].midi_cutoff->set(filter_state_.midi_cutoff[index]);
430 shaders_[shader].resonance->set(resonance);
431 shaders_[shader].drive->set(analog_filter_.getDrive()[index]);
432 shaders_[shader].db24->set(filter_state_.style != vital::SynthFilter::k12Db ? 1.0f : 0.0f);
433
434 shaders_[shader].stages[0]->set(analog_filter_.getLowAmount()[index]);
435 shaders_[shader].stages[1]->set(analog_filter_.getBandAmount()[index]);
436 shaders_[shader].stages[2]->set(analog_filter_.getHighAmount()[index]);
437 shaders_[shader].stages[3]->set(analog_filter_.getLowAmount24(filter_state_.style)[index]);
438 shaders_[shader].stages[4]->set(analog_filter_.getHighAmount24(filter_state_.style)[index]);
439 }
440 else if (model == vital::constants::kComb) {
441 comb_filter_.setupFilter(filter_state_);
442 shaders_[shader].shader->use();
443 float resonance = vital::utils::clamp(comb_filter_.getResonance()[index], -0.99f, 0.99f);
444 shaders_[shader].midi_cutoff->set(filter_state_.midi_cutoff[index]);
445 shaders_[shader].resonance->set(resonance);
446 shaders_[shader].drive->set(comb_filter_.getDrive()[index]);
447
448 shaders_[shader].stages[0]->set(comb_filter_.getLowAmount()[index]);
449 shaders_[shader].stages[1]->set(comb_filter_.getHighAmount()[index]);
450 shaders_[shader].stages[2]->set(comb_filter_.getFilterMidiCutoff()[index]);
451 shaders_[shader].stages[3]->set(comb_filter_.getFilter2MidiCutoff()[index]);
452 }
453 else if (model == vital::constants::kDigital) {
454 digital_filter_.setupFilter(filter_state_);
455 shaders_[shader].shader->use();
456 float resonance = vital::utils::clamp(digital_filter_.getResonance()[index], 0.0f, 2.0f);
457 shaders_[shader].midi_cutoff->set(digital_filter_.getMidiCutoff()[index]);
458 shaders_[shader].resonance->set(resonance);
459 shaders_[shader].drive->set(digital_filter_.getDrive()[index]);
460 shaders_[shader].db24->set(filter_state_.style != vital::SynthFilter::k12Db ? 1.0f : 0.0f);
461
462 shaders_[shader].stages[0]->set(digital_filter_.getLowAmount()[index]);
463 shaders_[shader].stages[1]->set(digital_filter_.getBandAmount()[index]);
464 shaders_[shader].stages[2]->set(digital_filter_.getHighAmount()[index]);
465 shaders_[shader].stages[3]->set(digital_filter_.getLowAmount24(filter_state_.style)[index]);
466 shaders_[shader].stages[4]->set(digital_filter_.getHighAmount24(filter_state_.style)[index]);
467 }
468 else if (model == vital::constants::kDiode) {
469 diode_filter_.setupFilter(filter_state_);
470 shaders_[shader].shader->use();
471 shaders_[shader].midi_cutoff->set(filter_state_.midi_cutoff[index]);
472 shaders_[shader].resonance->set(diode_filter_.getResonance()[index]);
473 shaders_[shader].drive->set(diode_filter_.getDrive()[index]);
474 shaders_[shader].db24->set(diode_filter_.getHighPassAmount()[index]);
475 shaders_[shader].stages[0]->set(diode_filter_.getHighPassRatio()[index]);
476 }
477 else if (model == vital::constants::kDirty) {
478 dirty_filter_.setupFilter(filter_state_);
479 shaders_[shader].shader->use();
480 float resonance = vital::utils::clamp(dirty_filter_.getResonance()[index], 0.0f, 2.0f);
481 shaders_[shader].midi_cutoff->set(filter_state_.midi_cutoff[index]);
482 shaders_[shader].resonance->set(resonance);
483 shaders_[shader].drive->set(dirty_filter_.getDrive()[index]);
484 shaders_[shader].db24->set(filter_state_.style != vital::SynthFilter::k12Db ? 1.0f : 0.0f);
485
486 shaders_[shader].stages[0]->set(dirty_filter_.getLowAmount()[index]);
487 shaders_[shader].stages[1]->set(dirty_filter_.getBandAmount()[index]);
488 shaders_[shader].stages[2]->set(dirty_filter_.getHighAmount()[index]);
489 shaders_[shader].stages[3]->set(dirty_filter_.getLowAmount24(filter_state_.style)[index]);
490 shaders_[shader].stages[4]->set(dirty_filter_.getHighAmount24(filter_state_.style)[index]);
491 }
492 else if (model == vital::constants::kFormant) {
493 shaders_[shader].shader->use();
494
495 vital::DigitalSvf* formant0 = formant_filter_.getFormant(0);
496 vital::DigitalSvf* formant1 = formant_filter_.getFormant(1);
497 vital::DigitalSvf* formant2 = formant_filter_.getFormant(2);
498 vital::DigitalSvf* formant3 = formant_filter_.getFormant(3);
499
500 formant_filter_.setupFilter(filter_state_);
501 shaders_[shader].formant_cutoff->set(formant0->getMidiCutoff()[index], formant1->getMidiCutoff()[index],
502 formant2->getMidiCutoff()[index], formant3->getMidiCutoff()[index]);
503 shaders_[shader].formant_resonance->set(formant0->getResonance()[index], formant1->getResonance()[index],
504 formant2->getResonance()[index], formant3->getResonance()[index]);
505 vital::poly_float drive0 = formant0->getDrive();
506 vital::poly_float drive1 = formant1->getDrive();
507 vital::poly_float drive2 = formant2->getDrive();
508 vital::poly_float drive3 = formant3->getDrive();
509 vital::poly_float low0 = formant0->getLowAmount() * drive0;
510 vital::poly_float low1 = formant1->getLowAmount() * drive1;
511 vital::poly_float low2 = formant2->getLowAmount() * drive2;
512 vital::poly_float low3 = formant3->getLowAmount() * drive3;
513 vital::poly_float band0 = formant0->getBandAmount() * drive0;
514 vital::poly_float band1 = formant1->getBandAmount() * drive1;
515 vital::poly_float band2 = formant2->getBandAmount() * drive2;
516 vital::poly_float band3 = formant3->getBandAmount() * drive3;
517 vital::poly_float high0 = formant0->getHighAmount() * drive0;
518 vital::poly_float high1 = formant1->getHighAmount() * drive1;
519 vital::poly_float high2 = formant2->getHighAmount() * drive2;
520 vital::poly_float high3 = formant3->getHighAmount() * drive3;
521
522 shaders_[shader].formant_low->set(low0[index], low1[index], low2[index], low3[index]);
523 shaders_[shader].formant_band->set(band0[index], band1[index], band2[index], band3[index]);
524 shaders_[shader].formant_high->set(high0[index], high1[index], high2[index], high3[index]);
525 }
526 else if (model == vital::constants::kLadder) {
527 ladder_filter_.setupFilter(filter_state_);
528 shaders_[shader].shader->use();
529 shaders_[shader].midi_cutoff->set(filter_state_.midi_cutoff[index]);
530 shaders_[shader].resonance->set(ladder_filter_.getResonance()[index]);
531 shaders_[shader].drive->set(ladder_filter_.getDrive()[index]);
532
533 for (int s = 0; s < FilterResponseShader::kMaxStages; ++s)
534 shaders_[shader].stages[s]->set(ladder_filter_.getStageScale(s)[index]);
535 }
536 else if (model == vital::constants::kPhase) {
537 phaser_filter_.setupFilter(filter_state_);
538 shaders_[shader].shader->use();
539 shaders_[shader].midi_cutoff->set(filter_state_.midi_cutoff[index]);
540 shaders_[shader].resonance->set(phaser_filter_.getResonance()[index]);
541 shaders_[shader].db24->set(filter_state_.style != vital::SynthFilter::k12Db ? 1.0f : 0.0f);
542
543 shaders_[shader].stages[0]->set(phaser_filter_.getPeak1Amount()[index]);
544 shaders_[shader].stages[1]->set(phaser_filter_.getPeak3Amount()[index]);
545 shaders_[shader].stages[2]->set(phaser_filter_.getPeak5Amount()[index]);
546 }
547
548 shaders_[shader].mix->set(mix_[index]);
549}
550
551void FilterResponse::bind(FilterShader shader, OpenGLContext& open_gl_context) {
552 open_gl_context.extensions.glBindVertexArray(vertex_array_object_);
553 open_gl_context.extensions.glBindBuffer(GL_ARRAY_BUFFER, line_buffer_);
554
555 OpenGLShaderProgram::Attribute* position = shaders_[shader].position.get();
556 open_gl_context.extensions.glVertexAttribPointer(position->attributeID, 2, GL_FLOAT,
557 GL_FALSE, 2 * sizeof(float), nullptr);
558 open_gl_context.extensions.glEnableVertexAttribArray(position->attributeID);
559
560 open_gl_context.extensions.glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, response_buffer_);
561}
562
563void FilterResponse::unbind(FilterShader shader, OpenGLContext& open_gl_context) {
564 OpenGLShaderProgram::Attribute* position = shaders_[shader].position.get();
565 open_gl_context.extensions.glDisableVertexAttribArray(position->attributeID);
566 open_gl_context.extensions.glBindBuffer(GL_ARRAY_BUFFER, 0);
567 open_gl_context.extensions.glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0);
568}
569
570void FilterResponse::drawFilterResponse(OpenGlWrapper& open_gl) {
571 vital::constants::FilterModel model = filter_model_;
572 bool new_response = setupFilterState(model);
573 new_response = new_response || isStereoState();
574
575 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
576 glEnable(GL_BLEND);
577 setViewPort(open_gl);
578
579 Colour color_line = line_right_color_;
580 Colour color_fill_to = fill_right_color_;
581 float fill_fade = findValue(Skin::kWidgetFillFade);
582 Colour color_fill_from = color_fill_to.withMultipliedAlpha(1.0f - fill_fade);
583
586
587 FilterShader shader = getShaderForModel(model, filter_state_.style);
588
589 if (active_) {
590 if (new_response) {
591 bind(shader, open_gl.context);
592 loadShader(shader, model, 1);
593 renderLineResponse(open_gl);
594 }
595
596 setFillColors(color_fill_from, color_fill_to);
597 setColor(color_line);
598 OpenGlLineRenderer::render(open_gl, animate_);
599 }
600
601 color_line = line_left_color_;
602 color_fill_to = fill_left_color_;
603 if (!active_) {
604 color_line = line_disabled_color_;
605 color_fill_to = fill_disabled_color_;
606 }
607 color_fill_from = color_fill_to.withMultipliedAlpha(1.0f - fill_fade);
608
609 if (new_response) {
610 bind(shader, open_gl.context);
611 loadShader(shader, model, 0);
612 renderLineResponse(open_gl);
613 }
614
615 setFillColors(color_fill_from, color_fill_to);
616 setColor(color_line);
617 OpenGlLineRenderer::render(open_gl, animate_);
618
619 unbind(shader, open_gl.context);
620 glDisable(GL_BLEND);
621 checkGlError();
622}
623
624void FilterResponse::renderLineResponse(OpenGlWrapper& open_gl) {
625 glEnable(GL_BLEND);
626 open_gl.context.extensions.glBeginTransformFeedback(GL_POINTS);
627 glDrawArrays(GL_POINTS, 0, kResolution);
628 open_gl.context.extensions.glEndTransformFeedback();
629
630 void* buffer = open_gl.context.extensions.glMapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0,
631 kResolution * sizeof(float), GL_MAP_READ_BIT);
632
633 float* response_data = (float*)buffer;
634 float width = getWidth();
635 float y_adjust = getHeight() / 2.0f;
636 for (int i = 0; i < kResolution; ++i) {
637 setXAt(i, width * i / (kResolution - 1.0f));
638 setYAt(i, y_adjust * (1.0f - response_data[i]));
639 }
640
641 open_gl.context.extensions.glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
642 glDisable(GL_BLEND);
643}
Displays the frequency response of various filter models in Vital.
Definition filter_response.h:26
void mouseDrag(const MouseEvent &e) override
Handles mouse drag events, allowing interactive adjustment of filter parameters.
Definition filter_response.cpp:328
void destroy(OpenGlWrapper &open_gl) override
Destroys the OpenGL resources used by this component.
Definition filter_response.cpp:240
void mouseDown(const MouseEvent &e) override
Handles mouse down events, initiating user interaction with filter parameters.
Definition filter_response.cpp:311
FilterShader
An enumeration of different filter shader programs used for rendering.
Definition filter_response.h:43
@ kLadder
Definition filter_response.h:46
@ kPositiveFlange
Definition filter_response.h:51
@ kComb
Definition filter_response.h:50
@ kDigital
Definition filter_response.h:47
@ kNumFilterShaders
Definition filter_response.h:54
@ kDiode
Definition filter_response.h:48
@ kNegativeFlange
Definition filter_response.h:52
@ kFormant
Definition filter_response.h:49
@ kPhase
Definition filter_response.h:53
@ kDirty
Definition filter_response.h:45
@ kAnalog
Definition filter_response.h:44
void paintBackground(Graphics &g) override
Draws the background of the component (e.g. coloring, grid lines).
Definition filter_response.cpp:272
virtual ~FilterResponse()
Destructor.
Definition filter_response.cpp:188
void mouseWheelMove(const MouseEvent &e, const MouseWheelDetails &wheel) override
Handles mouse wheel events for fine-tuning filter parameters.
Definition filter_response.cpp:338
void mouseExit(const MouseEvent &e) override
Handles mouse exit events, finalizing interactive changes and hiding tooltips.
Definition filter_response.cpp:332
void render(OpenGlWrapper &open_gl, bool animate) override
Renders the filter response curve to the screen.
Definition filter_response.cpp:234
FilterResponse(String suffix, const vital::output_map &mono_modulations)
Constructs a FilterResponse given a suffix and modulation outputs for a single filter configuration.
Definition filter_response.cpp:157
static constexpr int kResolution
Number of points used for drawing the filter response curve.
Definition filter_response.h:29
void init(OpenGlWrapper &open_gl) override
Initializes the OpenGL resources for the filter response visualization.
Definition filter_response.cpp:190
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
static std::unique_ptr< OpenGLShaderProgram::Attribute > getAttribute(const OpenGlWrapper &open_gl, const OpenGLShaderProgram &program, const char *name)
Retrieves an attribute from the shader program if it exists.
Definition open_gl_component.h:79
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
force_inline void checkGlError()
Checks for and asserts that there are no OpenGL errors.
Definition open_gl_component.h:231
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
static std::unique_ptr< OpenGLShaderProgram::Uniform > getUniform(const OpenGlWrapper &open_gl, const OpenGLShaderProgram &program, const char *name)
Retrieves a uniform from the shader program if it exists.
Definition open_gl_component.h:64
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
virtual void render(OpenGlWrapper &open_gl, bool animate) override
Renders the line using OpenGL.
Definition open_gl_line_renderer.cpp:464
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 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
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
VertexShader
An enumeration of all available vertex shaders.
Definition shaders.h:27
@ kNumVertexShaders
Definition shaders.h:49
@ kDirtyFilterResponseVertex
Definition shaders.h:40
@ kDigitalFilterResponseVertex
Definition shaders.h:38
@ kDiodeFilterResponseVertex
Definition shaders.h:39
@ kAnalogFilterResponseVertex
Definition shaders.h:34
@ kFormantFilterResponseVertex
Definition shaders.h:41
@ kPhaserFilterResponseVertex
Definition shaders.h:43
@ kLadderFilterResponseVertex
Definition shaders.h:42
@ kPositiveFlangeFilterResponseVertex
Definition shaders.h:36
@ kCombFilterResponseVertex
Definition shaders.h:35
@ kNegativeFlangeFilterResponseVertex
Definition shaders.h:37
@ kColorFragment
Definition shaders.h:63
OpenGLShaderProgram * getShaderProgram(VertexShader vertex_shader, FragmentShader fragment_shader, const GLchar **varyings=nullptr)
Retrieves or creates an OpenGLShaderProgram from a given vertex and fragment shader pair.
Definition shaders.cpp:953
@ kWidgetLineWidth
Definition skin.h:105
@ kWidgetFillCenter
Definition skin.h:107
@ kWidgetFillFade
Definition skin.h:108
@ kWidgetPrimaryDisabled
Definition skin.h:167
@ kWidgetPrimary2
Definition skin.h:166
@ kWidgetPrimary1
Definition skin.h:165
@ kWidgetBackground
Definition skin.h:173
@ kWidgetSecondary1
Definition skin.h:168
@ kWidgetSecondaryDisabled
Definition skin.h:170
@ kWidgetSecondary2
Definition skin.h:169
void hidePopup(bool primary)
Definition synth_slider.cpp:640
void showPopup(bool primary)
Definition synth_slider.cpp:635
virtual void mouseWheelMove(const MouseEvent &e, const MouseWheelDetails &wheel) override
Definition synth_slider.cpp:346
poly_float getResonance()
Getter for the feedback parameter controlling comb/flange feedback amount.
Definition comb_filter.h:172
poly_float getHighAmount()
Getter for the high-frequency gain used in filter blending.
Definition comb_filter.h:186
void setupFilter(const FilterState &filter_state) override
Sets up the CombFilter state based on a FilterState struct.
Definition comb_filter.cpp:229
poly_float getFilterMidiCutoff()
Getter for the primary filter MIDI cutoff.
Definition comb_filter.h:193
poly_float getLowAmount()
Getter for the low-frequency gain used in filter blending.
Definition comb_filter.h:179
poly_float getFilter2MidiCutoff()
Getter for the secondary filter MIDI cutoff (used in band-spread style).
Definition comb_filter.h:200
FeedbackStyle
Types of feedback for the comb filter (comb, positive/negative flange).
Definition comb_filter.h:24
@ kComb
Standard comb filtering.
Definition comb_filter.h:25
@ kPositiveFlange
Positive flanging effect.
Definition comb_filter.h:26
poly_float getDrive()
Getter for the drive (scale) parameter controlling input amplitude scaling.
Definition comb_filter.h:165
static FeedbackStyle getFeedbackStyle(int style)
Converts an integer to a valid FeedbackStyle, wrapping around kNumFeedbackStyles.
Definition comb_filter.h:47
A state-variable filter (SVF) implementation, supporting multiple filter types (12/24 dB,...
Definition digital_svf.h:17
poly_float getMidiCutoff() const
Retrieves the current MIDI-based cutoff frequency.
Definition digital_svf.h:345
poly_float getResonance() const
Retrieves the current resonance value (inverted if needed).
Definition digital_svf.h:351
void setupFilter(const FilterState &filter_state) override
Configures this SVF based on a FilterState (cutoff, resonance, style, etc.).
Definition digital_svf.cpp:237
poly_float getBandAmount() const
Retrieves the current band-frequency mix portion.
Definition digital_svf.h:363
poly_float getHighAmount24(int style) const
Helper for a 24 dB filter style that may swap low/high in a dual notch band.
Definition digital_svf.h:389
poly_float getLowAmount24(int style) const
Helper for a 24 dB filter style that may swap low/high in a dual notch band.
Definition digital_svf.h:377
poly_float getHighAmount() const
Retrieves the current high-frequency mix portion.
Definition digital_svf.h:369
poly_float getLowAmount() const
Retrieves the current low-frequency mix portion.
Definition digital_svf.h:357
poly_float getDrive() const
Retrieves the final drive (post drive compensation) used in the filter.
Definition digital_svf.h:339
poly_float getHighPassAmount()
Gets the current high-pass amount.
Definition diode_filter.h:137
poly_float getHighPassRatio()
Gets the current high-pass ratio.
Definition diode_filter.h:131
poly_float getResonance()
Gets the current resonance setting of the filter.
Definition diode_filter.h:119
void setupFilter(const FilterState &filter_state) override
Sets up filter parameters from the given FilterState.
Definition diode_filter.cpp:131
poly_float getDrive()
Gets the current drive setting of the filter.
Definition diode_filter.h:125
force_inline poly_float getBandAmount()
Gets the band-pass blend amount.
Definition dirty_filter.h:180
force_inline poly_float getLowAmount()
Gets the low-pass blend amount.
Definition dirty_filter.h:175
force_inline poly_float getResonance()
Gets the current resonance value adjusted by coefficient and drive.
Definition dirty_filter.h:158
void setupFilter(const FilterState &filter_state) override
Sets up the filter's internal state (cutoff, resonance, drive, blend) from the given FilterState.
Definition dirty_filter.cpp:396
force_inline poly_float getDrive()
Gets the current drive value adjusted based on resonance.
Definition dirty_filter.h:166
force_inline poly_float getLowAmount24(int style)
For 24 dB mode, gets the low amount depending on filter style.
Definition dirty_filter.h:190
force_inline poly_float getHighAmount()
Gets the high-pass blend amount.
Definition dirty_filter.h:185
force_inline poly_float getHighAmount24(int style)
For 24 dB mode, gets the high amount depending on filter style.
Definition dirty_filter.h:199
void setupFilter(const FilterState &filter_state) override
Configures this FormantFilter (and associated DigitalSvf instances) from a FilterState.
Definition formant_filter.cpp:236
DigitalSvf * getFormant(int index)
Gets a pointer to the underlying DigitalSvf filter for a specific formant index.
Definition formant_filter.h:84
poly_float getResonance()
Retrieves the current resonance setting.
Definition ladder_filter.h:125
poly_float getStageScale(int index)
Retrieves the scale value for one of the filter’s output stages.
Definition ladder_filter.h:132
void setupFilter(const FilterState &filter_state) override
Configures the filter parameters based on a FilterState.
Definition ladder_filter.cpp:126
poly_float getDrive()
Retrieves the current drive setting.
Definition ladder_filter.h:119
poly_float getPeak1Amount()
Gets the current peak1 (lowest peak cluster) mix amount.
Definition phaser_filter.h:120
poly_float getResonance()
Gets the current resonance value.
Definition phaser_filter.h:108
poly_float getPeak3Amount()
Gets the current peak3 (second peak cluster) mix amount.
Definition phaser_filter.h:126
poly_float getPeak5Amount()
Gets the current peak5 (highest peak cluster) mix amount.
Definition phaser_filter.h:132
void setupFilter(const FilterState &filter_state) override
Sets up the filter parameters (resonance, drive, peaks) based on the FilterState.
Definition phaser_filter.cpp:71
poly_float getLowAmount24(int style)
For 24 dB modes, gets the low amount depending on the style.
Definition sallen_key_filter.h:172
poly_float getLowAmount()
Gets the low-pass blend amount.
Definition sallen_key_filter.h:157
poly_float getDrive()
Gets the current drive value.
Definition sallen_key_filter.h:152
poly_float getHighAmount24(int style)
For 24 dB modes, gets the high amount depending on the style.
Definition sallen_key_filter.h:181
poly_float getHighAmount()
Gets the high-pass blend amount.
Definition sallen_key_filter.h:167
void setupFilter(const FilterState &filter_state) override
Sets up the filter parameters (cutoff, resonance, drive) and mode from a given FilterState.
Definition sallen_key_filter.cpp:320
poly_float getBandAmount()
Gets the band-pass blend amount.
Definition sallen_key_filter.h:162
poly_float getResonance()
Gets the current resonance value.
Definition sallen_key_filter.h:147
force_inline poly_float value() const
Returns the current status value.
Definition synth_module.h:49
poly_float transpose
Transpose in semitones (applied to midi_cutoff)
Definition synth_filter.h:120
poly_float interpolate_x
Interpolation X coordinate (e.g., for formant filters)
Definition synth_filter.h:118
poly_float interpolate_y
Interpolation Y coordinate (e.g., for formant filters)
Definition synth_filter.h:119
poly_float pass_blend
Blend parameter in [0..2], controlling pass type.
Definition synth_filter.h:117
int style
Filter style enum (e.g., k12Db, k24Db)
Definition synth_filter.h:116
poly_float midi_cutoff
MIDI note-based cutoff value.
Definition synth_filter.h:110
poly_float resonance_percent
Resonance parameter in [0..1].
Definition synth_filter.h:112
@ k12Db
Definition synth_filter.h:75
#define VITAL_ASSERT(x)
Definition common.h:11
cr::Value resonance
Resonance factor for this formant.
Definition formant_filter.cpp:18
cr::Value midi_cutoff
MIDI note for the filter's cutoff.
Definition formant_filter.cpp:19
const poly_mask kFullMask
A mask covering all lanes of a poly_float vector.
Definition synth_constants.h:257
FilterModel
Identifiers for different filter models available in Vital’s filters.
Definition synth_constants.h:194
@ kDirty
Definition synth_constants.h:196
@ 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
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 max(poly_float left, poly_float right)
Returns the maximum of two poly_floats lane-by-lane.
Definition poly_utils.h:327
force_inline bool equal(poly_float left, poly_float right)
Checks if two poly_floats are equal lane-by-lane. Returns true if all lanes match.
Definition poly_utils.h:341
force_inline poly_float swapStereo(poly_float value)
Swaps the left and right channels of a stereo poly_float.
Definition poly_utils.h:411
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
OpenGLContext & context
The OpenGLContext for current rendering.
Definition shaders.h:181
Shaders * shaders
Pointer to the Shaders instance providing compiled shaders.
Definition shaders.h:182
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
Represents a vector of integer values using SIMD instructions.
Definition poly_values.h:56
Declares the SynthSlider and related classes, providing various slider styles and functionality in th...
Provides various utility functions, classes, and constants for audio, math, and general-purpose opera...