1 /*
2  * DISTRHO Plugin Framework (DPF)
3  * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any purpose with
6  * or without fee is hereby granted, provided that the above copyright notice and this
7  * permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
10  * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
11  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
12  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
13  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #include "DistrhoUI.hpp"
18 
19 START_NAMESPACE_DISTRHO
20 
21 /**
22   We need the Color class from DGL.
23  */
24 using DGL_NAMESPACE::Color;
25 
26 /**
27   Smooth meters a bit.
28  */
29 static const float kSmoothMultiplier = 3.0f;
30 
31 // -----------------------------------------------------------------------------------------------------------
32 
33 class ExampleUIMeters : public UI
34 {
35 public:
36     ExampleUIMeters()
37         : UI(128, 512),
38           // default color is green
39           fColor(93, 231, 61),
40           // which is value 0
41           fColorValue(0),
42           // init meter values to 0
43           fOutLeft(0.0f),
44           fOutRight(0.0f)
45     {
46         setGeometryConstraints(32, 128, false);
47     }
48 
49 protected:
50    /* --------------------------------------------------------------------------------------------------------
51     * DSP/Plugin Callbacks */
52 
53    /**
54       A parameter has changed on the plugin side.
55       This is called by the host to inform the UI about parameter changes.
56     */
57     void parameterChanged(uint32_t index, float value) override
58     {
59         switch (index)
60         {
61         case 0: // color
62             updateColor(std::round(value));
63             break;
64 
65         case 1: // out-left
66             value = (fOutLeft * kSmoothMultiplier + value) / (kSmoothMultiplier + 1.0f);
67 
68             /**/ if (value < 0.001f) value = 0.0f;
69             else if (value > 0.999f) value = 1.0f;
70 
71             if (fOutLeft != value)
72             {
73                 fOutLeft = value;
74                 repaint();
75             }
76             break;
77 
78         case 2: // out-right
79             value = (fOutRight * kSmoothMultiplier + value) / (kSmoothMultiplier + 1.0f);
80 
81             /**/ if (value < 0.001f) value = 0.0f;
82             else if (value > 0.999f) value = 1.0f;
83 
84             if (fOutRight != value)
85             {
86                 fOutRight = value;
87                 repaint();
88             }
89             break;
90         }
91     }
92 
93    /**
94       A state has changed on the plugin side.
95       This is called by the host to inform the UI about state changes.
96     */
97     void stateChanged(const char*, const char*) override
98     {
99         // nothing here
100     }
101 
102    /* --------------------------------------------------------------------------------------------------------
103     * Widget Callbacks */
104 
105    /**
106       The NanoVG drawing function.
107     */
108     void onNanoDisplay() override
109     {
110         static const Color kColorBlack(0, 0, 0);
111         static const Color kColorRed(255, 0, 0);
112         static const Color kColorYellow(255, 255, 0);
113 
114         // get meter values
115         const float outLeft(fOutLeft);
116         const float outRight(fOutRight);
117 
118         // tell DSP side to reset meter values
119         setState("reset", "");
120 
121         // useful vars
122         const float halfWidth        = static_cast<float>(getWidth())/2;
123         const float redYellowHeight  = static_cast<float>(getHeight())*0.2f;
124         const float yellowBaseHeight = static_cast<float>(getHeight())*0.4f;
125         const float baseBaseHeight   = static_cast<float>(getHeight())*0.6f;
126 
127         // create gradients
128         Paint fGradient1 = linearGradient(0.0f, 0.0f,            0.0f, redYellowHeight,  kColorRed,    kColorYellow);
129         Paint fGradient2 = linearGradient(0.0f, redYellowHeight, 0.0f, yellowBaseHeight, kColorYellow, fColor);
130 
131         // paint left meter
132         beginPath();
133         rect(0.0f, 0.0f, halfWidth-1.0f, redYellowHeight);
134         fillPaint(fGradient1);
135         fill();
136         closePath();
137 
138         beginPath();
139         rect(0.0f, redYellowHeight-0.5f, halfWidth-1.0f, yellowBaseHeight);
140         fillPaint(fGradient2);
141         fill();
142         closePath();
143 
144         beginPath();
145         rect(0.0f, redYellowHeight+yellowBaseHeight-1.5f, halfWidth-1.0f, baseBaseHeight);
146         fillColor(fColor);
147         fill();
148         closePath();
149 
150         // paint left black matching output level
151         beginPath();
152         rect(0.0f, 0.0f, halfWidth-1.0f, (1.0f-outLeft)*getHeight());
153         fillColor(kColorBlack);
154         fill();
155         closePath();
156 
157         // paint right meter
158         beginPath();
159         rect(halfWidth+1.0f, 0.0f, halfWidth-2.0f, redYellowHeight);
160         fillPaint(fGradient1);
161         fill();
162         closePath();
163 
164         beginPath();
165         rect(halfWidth+1.0f, redYellowHeight-0.5f, halfWidth-2.0f, yellowBaseHeight);
166         fillPaint(fGradient2);
167         fill();
168         closePath();
169 
170         beginPath();
171         rect(halfWidth+1.0f, redYellowHeight+yellowBaseHeight-1.5f, halfWidth-2.0f, baseBaseHeight);
172         fillColor(fColor);
173         fill();
174         closePath();
175 
176         // paint right black matching output level
177         beginPath();
178         rect(halfWidth+1.0f, 0.0f, halfWidth-2.0f, (1.0f-outRight)*getHeight());
179         fillColor(kColorBlack);
180         fill();
181         closePath();
182     }
183 
184    /**
185       Mouse press event.
186       This UI will change color when clicked.
187     */
188     bool onMouse(const MouseEvent& ev) override
189     {
190         // Test for left-clicked + pressed first.
191         if (ev.button != 1 || ! ev.press)
192             return false;
193 
194         const int newColor(fColorValue == 0 ? 1 : 0);
195         updateColor(newColor);
196         setParameterValue(0, newColor);
197 
198         return true;
199     }
200 
201     // -------------------------------------------------------------------------------------------------------
202 
203 private:
204    /**
205       Color and its matching parameter value.
206     */
207     Color fColor;
208     int   fColorValue;
209 
210    /**
211       Meter values.
212       These are the parameter outputs from the DSP side.
213     */
214     float fOutLeft, fOutRight;
215 
216    /**
217       Update color if needed.
218     */
219     void updateColor(const int color)
220     {
221         if (fColorValue == color)
222             return;
223 
224         fColorValue = color;
225 
226         switch (color)
227         {
228         case METER_COLOR_GREEN:
229             fColor = Color(93, 231, 61);
230             break;
231         case METER_COLOR_BLUE:
232             fColor = Color(82, 238, 248);
233             break;
234         }
235 
236         repaint();
237     }
238 
239    /**
240       Set our UI class as non-copyable and add a leak detector just in case.
241     */
242     DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ExampleUIMeters)
243 };
244 
245 /* ------------------------------------------------------------------------------------------------------------
246  * UI entry point, called by DPF to create a new UI instance. */
247 
248 UI* createUI()
249 {
250     return new ExampleUIMeters();
251 }
252 
253 // -----------------------------------------------------------------------------------------------------------
254 
255 END_NAMESPACE_DISTRHO
256