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(&params[paramSize],        "%2.0f m",  knobx[0], knoby[1]);
53   fKnobWidth       = createLabelledKnob(&params[paramWidth],       "%3.0f%%",  knobx[1], knoby[1]);
54   fKnobPredelay    = createLabelledKnob(&params[paramPredelay],    "%2.0f ms", knobx[0], knoby[2]);
55   fKnobDecay       = createLabelledKnob(&params[paramDecay],       "%2.1f s",  knobx[1], knoby[2]);
56 
57   fKnobDiffuse     = createLabelledKnob(&params[paramDiffuse],     "%2.0f%%",  knobx[2], knoby[0]);
58   fKnobSpin        = createLabelledKnob(&params[paramSpin],        "%2.2f Hz", knobx[3], knoby[0]);
59   fKnobWander      = createLabelledKnob(&params[paramWander],      "%2.0f%%",  knobx[4], knoby[0]);
60 
61   fKnobInHighCut   = createLabelledKnob(&params[paramInHighCut],   "%5.0f Hz", knobx[2], knoby[1]);
62   fKnobEarlyDamp   = createLabelledKnob(&params[paramEarlyDamp],   "%5.0f Hz", knobx[3], knoby[1]);
63   fKnobLateDamp    = createLabelledKnob(&params[paramLateDamp],    "%5.0f Hz", knobx[4], knoby[1]);
64 
65   fKnobInLowCut    = createLabelledKnob(&params[paramInLowCut],    "%4.0f Hz", knobx[2], knoby[2]);
66   fKnobBoost       = createLabelledKnob(&params[paramBoost],       "%1.0f%%",  knobx[3], knoby[2]);
67   fKnobBoostLPF    = createLabelledKnob(&params[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