1 /*
2 * Dragonfly Reverb, copyright (c) 2019 Michael Willis, Rob van den Berg
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 3 of
7 * the License, or any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * For a full copy of the GNU General Public License see the LICENSE file.
15 */
16
17 #include "DistrhoUI.hpp"
18 #include "DragonflyVersion.h"
19 #include "DSP.hpp"
20 #include "UI.hpp"
21 #include "Artwork.hpp"
22 #include "DistrhoPluginInfo.h"
23 #include <array>
24 #include <vector>
25 #include <math.h>
26 #include <string>
27
28 START_NAMESPACE_DISTRHO
29
30 namespace Art = Artwork;
31 using DGL::Color;
32
33 static const int knobx[] = {185, 260, 680, 755, 830};
34 static const int knoby[] = {15, 130, 245};
35
36 // -----------------------------------------------------------------------------------------------------------
DragonflyReverbUI()37 DragonflyReverbUI::DragonflyReverbUI()
38 : DragonflyReverbAbstractUI ( Art::backgroundWidth, Art::backgroundHeight, PARAMS, Art::knobData, Art::knobWidth, Art::knobHeight),
39 fImgBackground ( Art::backgroundData, Art::backgroundWidth, Art::backgroundHeight, GL_BGRA ),
40 fImgTabOff ( Art::tab_offData, Art::tab_offWidth, Art::tab_offHeight, GL_BGR ),
41 fImgTabOn ( Art::tab_onData, Art::tab_onWidth,Art::tab_onHeight, GL_BGR ),
42 fImgQuestion ( Art::questionData, Art::questionWidth, Art::questionHeight, GL_BGRA )
43 {
44 currentBank = DEFAULT_BANK;
45 for (int b = 0; b < NUM_BANKS; b++)
46 {
47 currentPreset[b] = DEFAULT_PRESET;
48 }
49
50 displayAbout = false;
51
52 fKnobSize = createLabelledKnob(¶ms[paramSize], "%2.0f m", knobx[0], knoby[1]);
53 fKnobWidth = createLabelledKnob(¶ms[paramWidth], "%3.0f%%", knobx[1], knoby[1]);
54 fKnobPredelay = createLabelledKnob(¶ms[paramPredelay], "%2.0f ms", knobx[0], knoby[2]);
55 fKnobDecay = createLabelledKnob(¶ms[paramDecay], "%2.1f s", knobx[1], knoby[2]);
56
57 fKnobDiffuse = createLabelledKnob(¶ms[paramDiffuse], "%2.0f%%", knobx[2], knoby[0]);
58 fKnobSpin = createLabelledKnob(¶ms[paramSpin], "%2.2f Hz", knobx[3], knoby[0]);
59 fKnobWander = createLabelledKnob(¶ms[paramWander], "%2.0f%%", knobx[4], knoby[0]);
60
61 fKnobInHighCut = createLabelledKnob(¶ms[paramInHighCut], "%5.0f Hz", knobx[2], knoby[1]);
62 fKnobEarlyDamp = createLabelledKnob(¶ms[paramEarlyDamp], "%5.0f Hz", knobx[3], knoby[1]);
63 fKnobLateDamp = createLabelledKnob(¶ms[paramLateDamp], "%5.0f Hz", knobx[4], knoby[1]);
64
65 fKnobInLowCut = createLabelledKnob(¶ms[paramInLowCut], "%4.0f Hz", knobx[2], knoby[2]);
66 fKnobBoost = createLabelledKnob(¶ms[paramBoost], "%1.0f%%", knobx[3], knoby[2]);
67 fKnobBoostLPF = createLabelledKnob(¶ms[paramBoostLPF], "%4.0f Hz", knobx[4], knoby[2]);
68
69 fSliderDry_level = new ImageSlider ( this, Image ( Art::sliderData, Art::sliderWidth, Art::sliderHeight, GL_BGRA ) );
70 fSliderDry_level->setId ( paramDry );
71 fSliderDry_level->setStartPos ( 17, 157 );
72 fSliderDry_level->setEndPos ( 17, 317 );
73 fSliderDry_level->setRange ( 0.0f, 100.0f );
74 fSliderDry_level->setInverted ( true );
75 fSliderDry_level->setCallback ( this );
76
77 fSliderEarly_level = new ImageSlider ( this, Image ( Art::sliderData, Art::sliderWidth, Art::sliderHeight, GL_BGRA ) );
78 fSliderEarly_level->setId ( paramEarly );
79 fSliderEarly_level->setStartPos ( 57, 157 );
80 fSliderEarly_level->setEndPos ( 57, 317 );
81 fSliderEarly_level->setRange ( 0.0f, 100.0f );
82 fSliderEarly_level->setInverted ( true );
83 fSliderEarly_level->setCallback ( this );
84
85 fSliderEarlySend = new ImageSlider ( this, Image ( Art::sliderData, Art::sliderWidth, Art::sliderHeight, GL_BGRA ) );
86 fSliderEarlySend->setId ( paramEarlySend );
87 fSliderEarlySend->setStartPos ( 97, 157 );
88 fSliderEarlySend->setEndPos ( 97, 317 );
89 fSliderEarlySend->setRange ( 0.0f, 100.0f );
90 fSliderEarlySend->setInverted ( true );
91 fSliderEarlySend->setCallback ( this );
92
93 fSliderLate_level = new ImageSlider ( this, Image ( Art::sliderData, Art::sliderWidth, Art::sliderHeight, GL_BGRA ) );
94 fSliderLate_level->setId ( paramLate );
95 fSliderLate_level->setStartPos ( 137, 157 );
96 fSliderLate_level->setEndPos ( 137, 317 );
97 fSliderLate_level->setRange ( 0.0f, 100.0f );
98 fSliderLate_level->setInverted ( true );
99 fSliderLate_level->setCallback ( this );
100
101 rectSliders[0].setPos ( 17, 157 );
102 rectSliders[0].setSize ( 26, 160 );
103 rectSliders[1].setPos ( 57, 157 );
104 rectSliders[1].setSize ( 26, 160 );
105 rectSliders[2].setPos ( 97, 157 );
106 rectSliders[2].setSize ( 26, 160 );
107 rectSliders[3].setPos ( 137, 157 );
108 rectSliders[3].setSize ( 26, 160 );
109
110 rectDisplay.setPos ( 355, 126 );
111 rectDisplay.setSize ( 305, 207 );
112
113 for ( int i = 0; i < NUM_BANKS; ++i)
114 {
115 rectBanks[i].setPos ( 350, 5 + (i * 21) );
116 rectBanks[i].setSize ( 95, 24 );
117 }
118
119 for ( int i = 0; i < PRESETS_PER_BANK; ++i)
120 {
121 rectPresets[i].setPos( 460, 5 + (i * 21) );
122 rectPresets[i].setSize( 150, 21 );
123 }
124
125 rectAbout.setPos ( 635, 130 );
126 rectAbout.setSize ( 20, 20 );
127
128 AbstractDSP *dsp = new DragonflyReverbDSP(SPECTROGRAM_SAMPLE_RATE);
129 spectrogram = new Spectrogram(this, &nanoText, &rectDisplay, dsp);
130 spectrogram->setAbsolutePos (355, 126);
131 }
132
133 /**
134 A parameter has changed on the plugin side
135 This is called by the host to inform the UI about parameter changes.
136 */
parameterChanged(uint32_t index,float value)137 void DragonflyReverbUI::parameterChanged ( uint32_t index, float value )
138 {
139 displayAbout = false;
140
141 switch ( index )
142 {
143 case paramDry: fSliderDry_level->setValue ( value ); break;
144 case paramEarly: fSliderEarly_level->setValue ( value ); break;
145 case paramEarlySend: fSliderEarlySend->setValue ( value ); break;
146 case paramLate: fSliderLate_level->setValue ( value ); break;
147
148 case paramSize: fKnobSize->setValue ( value ); break;
149 case paramWidth: fKnobWidth->setValue ( value ); break;
150 case paramPredelay: fKnobPredelay->setValue ( value ); break;
151 case paramDecay: fKnobDecay->setValue ( value ); break;
152
153 case paramDiffuse: fKnobDiffuse->setValue ( value ); break;
154 case paramSpin: fKnobSpin->setValue ( value ); break;
155 case paramWander: fKnobWander->setValue ( value ); break;
156
157 case paramInHighCut: fKnobInHighCut->setValue ( value ); break;
158 case paramEarlyDamp: fKnobEarlyDamp->setValue ( value ); break;
159 case paramLateDamp: fKnobLateDamp->setValue ( value ); break;
160
161 case paramInLowCut: fKnobInLowCut->setValue ( value ); break;
162 case paramBoost: fKnobBoost->setValue ( value ); break;
163 case paramBoostLPF: fKnobBoostLPF->setValue ( value ); break;
164 }
165
166 if (index != paramDry) {
167 spectrogram->setParameterValue(index, value);
168 }
169 }
170
stateChanged(const char * key,const char * value)171 void DragonflyReverbUI::stateChanged(const char* key, const char* value)
172 {
173 if (std::strcmp(key, "preset") == 0) {
174 for (int b = 0; b < NUM_BANKS; b++) {
175 for (int p = 0; p < PRESETS_PER_BANK; p++) {
176 if (std::strcmp(value, banks[b].presets[p].name) == 0) {
177 currentBank = b;
178 currentPreset[currentBank] = p;
179 }
180 }
181 }
182
183 updatePresetDefaults();
184 }
185
186 repaint();
187 }
188
189 /* ----------------------------------------------------------------------------------------------------------
190 * Widget Callbacks
191 *----------------------------------------------------------------------------------------------------------*/
192
imageKnobDragStarted(ImageKnob * knob)193 void DragonflyReverbUI::imageKnobDragStarted ( ImageKnob* knob )
194 {
195 editParameter ( knob->getId(), true );
196 }
197
imageKnobDragFinished(ImageKnob * knob)198 void DragonflyReverbUI::imageKnobDragFinished ( ImageKnob* knob )
199 {
200 editParameter ( knob->getId(), false );
201 }
202
imageKnobValueChanged(ImageKnob * knob,float value)203 void DragonflyReverbUI::imageKnobValueChanged ( ImageKnob* knob, float value )
204 {
205 setParameterValue ( knob->getId(),value );
206 spectrogram->setParameterValue ( knob->getId(), value );
207 }
208
imageSliderDragStarted(ImageSlider * slider)209 void DragonflyReverbUI::imageSliderDragStarted ( ImageSlider* slider )
210 {
211 editParameter ( slider->getId(), true );
212 }
213
imageSliderDragFinished(ImageSlider * slider)214 void DragonflyReverbUI::imageSliderDragFinished ( ImageSlider* slider )
215 {
216 editParameter ( slider->getId(), false );
217 }
218
imageSliderValueChanged(ImageSlider * slider,float value)219 void DragonflyReverbUI::imageSliderValueChanged ( ImageSlider* slider, float value )
220 {
221 int SliderID = slider->getId();
222 setParameterValue ( SliderID,value );
223 spectrogram->setParameterValue ( SliderID, value );
224 }
225
onMouse(const MouseEvent & ev)226 bool DragonflyReverbUI::onMouse ( const MouseEvent& ev )
227 {
228 if ( ev.button != 1 )
229 return false;
230 if ( ev.press )
231 {
232 if ( displayAbout )
233 {
234 displayAbout = false;
235 repaint();
236 return false;
237 }
238 else
239 {
240 bool presetClicked = false;
241
242 for (int row = 0; row < NUM_BANKS; row++)
243 {
244 if (rectBanks[row].contains ( ev.pos ))
245 {
246 currentBank = row;
247 presetClicked = true;
248 }
249 }
250
251 for (int row = 0; row < PRESETS_PER_BANK; row++)
252 {
253 if (rectPresets[row].contains ( ev.pos ))
254 {
255 currentPreset[currentBank] = row;
256 presetClicked = true;
257 }
258 }
259
260 if (presetClicked)
261 {
262 setState("preset", banks[currentBank].presets[currentPreset[currentBank]].name);
263 updatePresetDefaults();
264
265 const float *preset = banks[currentBank].presets[currentPreset[currentBank]].params;
266
267 fKnobSize->setValue ( preset[paramSize] );
268 fKnobWidth->setValue ( preset[paramWidth] );
269 fKnobPredelay->setValue ( preset[paramPredelay] );
270 fKnobDecay->setValue ( preset[paramDecay] );
271
272 fKnobDiffuse->setValue ( preset[paramDiffuse] );
273 fKnobSpin->setValue ( preset[paramSpin] );
274 fKnobWander->setValue ( preset[paramWander] );
275
276 fKnobInHighCut->setValue ( preset[paramInHighCut] );
277 fKnobEarlyDamp->setValue ( preset[paramEarlyDamp] );
278 fKnobLateDamp->setValue ( preset[paramLateDamp] );
279
280 fKnobInLowCut->setValue ( preset[paramInLowCut] );
281 fKnobBoost->setValue ( preset[paramBoost] );
282 fKnobBoostLPF->setValue ( preset[paramBoostLPF] );
283
284 for ( uint32_t i = 0; i < paramCount; i++ ) {
285 // Don't set sliders
286 if (i != paramDry && i != paramEarly && i != paramEarlySend && i != paramLate) {
287 setParameterValue ( i, preset[i] );
288 spectrogram->setParameterValue(i, preset[i]);
289 }
290 }
291
292 repaint();
293 return true;
294 }
295
296 if ( rectAbout.contains ( ev.pos ) )
297 {
298 displayAbout = true;
299 repaint();
300 return true;
301 }
302 }
303 }
304 return false;
305 }
306
onDisplay()307 void DragonflyReverbUI::onDisplay()
308 {
309 fImgBackground.draw();
310
311 float r,g,b;
312 r = 230.0f / 256;
313 g = 230.0f / 256;
314 b = 230.0f / 256;
315
316 // print parameters
317 nanoText.beginFrame ( this );
318 nanoText.fontSize ( 15 );
319 nanoText.textAlign ( NanoVG::ALIGN_CENTER|NanoVG::ALIGN_MIDDLE );
320
321 nanoText.fillColor ( Color ( r, g, b ) );
322
323 char strBuf[32+1];
324 strBuf[32] = '\0';
325
326 std::snprintf ( strBuf, 32, "%i%%", int ( fSliderDry_level->getValue() ) );
327 nanoText.textBox ( 17 - 2, 330, 35.0f, strBuf, nullptr );
328 std::snprintf ( strBuf, 32, "%i%%", int ( fSliderEarly_level->getValue() ) );
329 nanoText.textBox ( 57 - 2, 330, 35.0f, strBuf, nullptr );
330 std::snprintf ( strBuf, 32, "%i%%", int ( fSliderEarlySend->getValue() ) );
331 nanoText.textBox ( 97 - 2, 330, 35.0f, strBuf, nullptr );
332 std::snprintf ( strBuf, 32, "%i%%", int ( fSliderLate_level->getValue() ) );
333 nanoText.textBox (137 - 2, 330, 35.0f, strBuf, nullptr );
334
335 // print labels;
336 nanoText.fillColor ( Color ( 0.90f, 0.95f, 1.00f ) );
337 nanoText.fontSize ( 14 );
338 nanoText.textBox ( 10, 130, 40, "Dry\nLevel", nullptr );
339 nanoText.textBox ( 50, 130, 40, "Early\nLevel", nullptr );
340 nanoText.textBox ( 90, 130, 40, "Early\nSend", nullptr );
341 nanoText.textBox ( 130, 130, 40, "Late\nLevel", nullptr );
342
343 nanoText.endFrame();
344
345 //draw faders
346 r = 98.0f/255.f;
347 g = 130.0f/255.f;
348 b = 161.0f/255.f;
349 glColor4f ( r, g, b, 1.0f );
350 uint dry = ( float ( fSliderDry_level->getValue() ) / 100.0 ) * 160.0 + 1.0f;
351 uint early = ( float ( fSliderEarly_level->getValue() ) / 100.0 ) * 160.0 + 1.0f;
352 uint early_send = ( float ( fSliderEarlySend->getValue() ) / 100.0 ) * 160.0 + 1.0f;
353 uint late = ( float ( fSliderLate_level->getValue() ) / 100.0 ) * 160.0 + 1.0f;
354
355 rectSliders[0].setHeight ( dry );
356 rectSliders[0].setY ( 118 + 200 - dry );
357
358 rectSliders[1].setHeight ( early );
359 rectSliders[1].setY ( 118 + 200 - early );
360
361 rectSliders[2].setHeight ( early_send );
362 rectSliders[2].setY ( 118 + 200 - early_send );
363
364 rectSliders[3].setHeight ( late );
365 rectSliders[3].setY ( 118 + 200 - late );
366
367 if ( dry > 1 )
368 rectSliders[0].draw();
369 if ( early > 1 )
370 rectSliders[1].draw();
371 if ( early_send > 1 )
372 rectSliders[2].draw();
373 if ( late > 1 )
374 rectSliders[3].draw();
375
376 glColor4f ( 1.0f,1.0f,1.0f,1.0f );
377
378 nanoText.beginFrame ( this );
379 nanoText.fontSize ( 15 );
380 nanoText.textAlign ( NanoVG::ALIGN_RIGHT | NanoVG::ALIGN_TOP );
381
382 Color bright = Color ( 0.90f, 0.95f, 1.00f );
383 Color dim = Color ( 0.65f, 0.65f, 0.65f );
384
385 for (int row = 0; row < NUM_BANKS; row ++)
386 {
387 DGL::Rectangle<int> bank = rectBanks[row];
388 if (currentBank == row) {
389 fImgTabOn.drawAt ( bank.getX(), bank.getY() );
390 nanoText.fillColor ( bright );
391 } else {
392 fImgTabOff.drawAt ( bank.getX(), bank.getY() );
393 nanoText.fillColor ( dim );
394 }
395
396 nanoText.textBox ( bank.getX(), bank.getY() + 4, bank.getWidth(), banks[row].name, nullptr );
397 }
398
399
400 nanoText.textAlign ( NanoVG::ALIGN_LEFT | NanoVG::ALIGN_TOP );
401
402 for (int row = 0; row < PRESETS_PER_BANK; row ++)
403 {
404 DGL::Rectangle<int> presetRect = rectPresets[row];
405 nanoText.fillColor( row == currentPreset[currentBank] ? bright : dim );
406 nanoText.textBox ( presetRect.getX(), presetRect.getY() + 4, presetRect.getWidth(), banks[currentBank].presets[row].name, nullptr );
407 }
408
409 nanoText.endFrame();
410
411 if (displayAbout) {
412 spectrogram->hide();
413 nanoText.beginFrame ( this );
414 nanoText.fontSize ( 15 );
415 nanoText.textAlign ( NanoVG::ALIGN_LEFT|NanoVG::ALIGN_TOP );
416
417 r = 230.0f / 256;
418 g = 230.0f / 256;
419 b = 230.0f / 256;
420 nanoText.fillColor ( Color ( r, g, b ) );
421
422 int x = rectDisplay.getX() + 5;
423 int y = rectDisplay.getY() + 5;
424 int w = rectDisplay.getWidth() - 10;
425
426 char textBuffer[400];
427
428 std::snprintf(textBuffer, 400,
429 "Dragonfly Room Reverb is a free audio effect\n"
430 "based on Freeverb3 ProG.\n\n"
431 "Version: %d.%d.%d%s License: GPL 3+\n\n"
432 "• Michael Willis - Plugin Development\n"
433 "• James Peters - Quality Assurance\n"
434 "• Chris Share - Technical Writer\n"
435 "• Teru Kamogashira - Freeverb3\n"
436 "• \"falkTX\" Coelho - Distrho Plugin Framework",
437 MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION, VERSION_SUFFIX
438 );
439
440 nanoText.textBox ( x, y, w, textBuffer, nullptr );
441 nanoText.endFrame();
442 }
443 else
444 {
445 spectrogram->show();
446 glColor4f ( 1.0f,1.0f,1.0f,1.0f );
447 fImgQuestion.drawAt ( rectAbout.getX(), rectAbout.getY() );
448 }
449
450 }
451
uiIdle()452 void DragonflyReverbUI::uiIdle() {
453 spectrogram->uiIdle();
454 }
455
updatePresetDefaults()456 void DragonflyReverbUI::updatePresetDefaults() {
457 const float *preset = banks[currentBank].presets[currentPreset[currentBank]].params;
458
459 fKnobSize->setDefault ( preset[paramSize] );
460 fKnobWidth->setDefault ( preset[paramWidth] );
461 fKnobPredelay->setDefault ( preset[paramPredelay] );
462 fKnobDecay->setDefault ( preset[paramDecay] );
463
464 fKnobDiffuse->setDefault ( preset[paramDiffuse] );
465 fKnobSpin->setDefault ( preset[paramSpin] );
466 fKnobWander->setDefault ( preset[paramWander] );
467
468 fKnobInHighCut->setDefault ( preset[paramInHighCut] );
469 fKnobEarlyDamp->setDefault ( preset[paramEarlyDamp] );
470 fKnobLateDamp->setDefault ( preset[paramLateDamp] );
471
472 fKnobInLowCut->setDefault ( preset[paramInLowCut] );
473 fKnobBoost->setDefault ( preset[paramBoost] );
474 fKnobBoostLPF->setDefault ( preset[paramBoostLPF] );
475 }
476
477 /* ------------------------------------------------------------------------------------------------------------
478 * UI entry point, called by DPF to create a new UI instance. */
479
createUI()480 UI* createUI()
481 {
482 return new DragonflyReverbUI();
483 }
484
485 // -----------------------------------------------------------------------------------------------------------
486
487 END_NAMESPACE_DISTRHO
488