1 /*
2  * Carla Native Plugins
3  * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of
8  * the License, or any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * For a full copy of the GNU General Public License see the doc/GPL.txt file.
16  */
17 
18 #include "CarlaDefines.h"
19 
20 #include "CarlaMathUtils.hpp"
21 #include "CarlaNativeExtUI.hpp"
22 
23 #include "water/maths/MathsFunctions.h"
24 
25 using water::roundToIntAccurate;
26 
27 // -----------------------------------------------------------------------
28 
29 class BigMeterPlugin : public NativePluginAndUiClass
30 {
31 public:
BigMeterPlugin(const NativeHostDescriptor * const host)32     BigMeterPlugin(const NativeHostDescriptor* const host)
33         : NativePluginAndUiClass(host, "bigmeter-ui"),
34           fColor(1),
35           fStyle(1),
36           fOutLeft(0.0f),
37           fOutRight(0.0f),
38           fInlineDisplay() {}
39 
40 protected:
41     // -------------------------------------------------------------------
42     // Plugin parameter calls
43 
getParameterCount() const44     uint32_t getParameterCount() const override
45     {
46         return 4;
47     }
48 
getParameterInfo(const uint32_t index) const49     const NativeParameter* getParameterInfo(const uint32_t index) const override
50     {
51         CARLA_SAFE_ASSERT_RETURN(index < 4, nullptr);
52 
53         static NativeParameter param;
54         static NativeParameterScalePoint scalePoints[3];
55 
56         int hints = NATIVE_PARAMETER_IS_ENABLED|NATIVE_PARAMETER_IS_AUTOMABLE;
57 
58         param.name = nullptr;
59         param.unit = nullptr;
60         param.ranges.def       = 0.0f;
61         param.ranges.min       = 0.0f;
62         param.ranges.max       = 1.0f;
63         param.ranges.step      = 1.0f;
64         param.ranges.stepSmall = 1.0f;
65         param.ranges.stepLarge = 1.0f;
66         param.scalePointCount  = 0;
67         param.scalePoints      = nullptr;
68 
69         switch (index)
70         {
71         case 0:
72             hints |= NATIVE_PARAMETER_IS_INTEGER|NATIVE_PARAMETER_USES_SCALEPOINTS;
73             param.name = "Color";
74             param.ranges.def = 1.0f;
75             param.ranges.min = 1.0f;
76             param.ranges.max = 2.0f;
77             scalePoints[0].value = 1.0f;
78             scalePoints[0].label = "Green";
79             scalePoints[1].value = 2.0f;
80             scalePoints[1].label = "Blue";
81             param.scalePointCount = 2;
82             param.scalePoints     = scalePoints;
83             break;
84         case 1:
85             hints |= NATIVE_PARAMETER_IS_INTEGER|NATIVE_PARAMETER_USES_SCALEPOINTS;
86             param.name = "Style";
87             param.ranges.def = 1.0f;
88             param.ranges.min = 1.0f;
89             param.ranges.max = 3.0f;
90             scalePoints[0].value = 1.0f;
91             scalePoints[0].label = "Default";
92             scalePoints[1].value = 2.0f;
93             scalePoints[1].label = "OpenAV";
94             scalePoints[2].value = 3.0f;
95             scalePoints[2].label = "RNCBC";
96             param.scalePointCount = 3;
97             param.scalePoints     = scalePoints;
98             break;
99         case 2:
100             hints |= NATIVE_PARAMETER_IS_OUTPUT;
101             param.name = "Out Left";
102             break;
103         case 3:
104             hints |= NATIVE_PARAMETER_IS_OUTPUT;
105             param.name = "Out Right";
106             break;
107         }
108 
109         param.hints = static_cast<NativeParameterHints>(hints);
110 
111         return &param;
112     }
113 
getParameterValue(const uint32_t index) const114     float getParameterValue(const uint32_t index) const override
115     {
116         switch (index)
117         {
118         case 0:
119             return float(fColor);
120         case 1:
121             return float(fStyle);
122         case 2:
123             return fOutLeft;
124         case 3:
125             return fOutRight;
126         default:
127             return 0.0f;
128         }
129     }
130 
131     // -------------------------------------------------------------------
132     // Plugin state calls
133 
setParameterValue(const uint32_t index,const float value)134     void setParameterValue(const uint32_t index, const float value) override
135     {
136         switch (index)
137         {
138         case 0:
139             fColor = roundToIntAccurate(value);
140             break;
141         case 1:
142             fStyle = roundToIntAccurate(value);
143             break;
144         default:
145             break;
146         }
147     }
148 
149     // -------------------------------------------------------------------
150     // Plugin process calls
151 
activate()152     void activate() override
153     {
154         fOutLeft  = 0.0f;
155         fOutRight = 0.0f;
156     }
157 
process(const float * const * inputs,float **,const uint32_t frames,const NativeMidiEvent * const,const uint32_t)158     void process(const float* const* inputs, float**, const uint32_t frames,
159                  const NativeMidiEvent* const, const uint32_t) override
160     {
161         fOutLeft  = carla_findMaxNormalizedFloat(inputs[0], frames);
162         fOutRight = carla_findMaxNormalizedFloat(inputs[1], frames);
163 
164         bool needsInlineRender = fInlineDisplay.pending < 0;
165 
166         if (carla_isNotEqual(fOutLeft, fInlineDisplay.lastLeft))
167         {
168             fInlineDisplay.lastLeft = fOutLeft;
169             needsInlineRender = true;
170         }
171 
172         if (carla_isNotEqual(fOutRight, fInlineDisplay.lastRight))
173         {
174             fInlineDisplay.lastRight = fOutRight;
175             needsInlineRender = true;
176         }
177 
178         if (needsInlineRender && fInlineDisplay.pending != 1 && fInlineDisplay.pending != 2)
179         {
180             fInlineDisplay.pending = 1;
181             hostRequestIdle();
182         }
183     }
184 
185     // -------------------------------------------------------------------
186     // Plugin dispatcher calls
187 
idle()188     void idle() override
189     {
190         if (fInlineDisplay.pending == 1)
191         {
192             fInlineDisplay.pending = 2;
193             hostQueueDrawInlineDisplay();
194         }
195     }
196 
renderInlineDisplay(const uint32_t rwidth,const uint32_t height)197     const NativeInlineDisplayImageSurface* renderInlineDisplay(const uint32_t rwidth, const uint32_t height) override
198     {
199         CARLA_SAFE_ASSERT_RETURN(rwidth > 0 && height > 0, nullptr);
200 
201         const uint32_t width = rwidth == height ? height / 6 : rwidth;
202         const size_t stride = width * 4;
203         const size_t dataSize = stride * height;
204 
205         uchar* data = fInlineDisplay.data;
206 
207         if (fInlineDisplay.dataSize < dataSize || data == nullptr)
208         {
209             delete[] data;
210             data = new uchar[dataSize];
211             std::memset(data, 0, dataSize);
212             fInlineDisplay.data = data;
213             fInlineDisplay.dataSize = dataSize;
214         }
215 
216         std::memset(data, 0, dataSize);
217 
218         fInlineDisplay.width = static_cast<int>(width);
219         fInlineDisplay.height = static_cast<int>(height);
220         fInlineDisplay.stride = static_cast<int>(stride);
221 
222         const uint heightValueLeft = static_cast<uint>(fInlineDisplay.lastLeft * static_cast<float>(height));
223         const uint heightValueRight = static_cast<uint>(fInlineDisplay.lastRight * static_cast<float>(height));
224 
225         for (uint h=0; h < height; ++h)
226         {
227             for (uint w=0; w < width; ++w)
228             {
229 //                 data[h * stride + w * 4 + 0] = 0;
230 //                 data[h * stride + w * 4 + 1] = 255;
231 //                 data[h * stride + w * 4 + 2] = 0;
232                 data[h * stride + w * 4 + 3] = 160;
233             }
234         }
235 
236         for (uint h=0; h < heightValueLeft; ++h)
237         {
238             const uint h2 = height - h - 1;
239 
240             for (uint w=0; w < width / 2; ++w)
241             {
242                 data[h2 * stride + w * 4 + 0] = 200;
243                 data[h2 * stride + w * 4 + 1] = 0;
244                 data[h2 * stride + w * 4 + 2] = 0;
245                 data[h2 * stride + w * 4 + 3] = 255;
246             }
247         }
248 
249         for (uint h=0; h < heightValueRight; ++h)
250         {
251             const uint h2 = height - h - 1;
252 
253             for (uint w=width / 2; w < width; ++w)
254             {
255                 data[h2 * stride + w * 4 + 0] = 200;
256                 data[h2 * stride + w * 4 + 1] = 0;
257                 data[h2 * stride + w * 4 + 2] = 0;
258                 data[h2 * stride + w * 4 + 3] = 255;
259             }
260         }
261 
262         // draw 1px border
263         for (uint w=0; w < width; ++w)
264         {
265 //             data[w * 4 + 0] = 0;
266 //             data[w * 4 + 1] = 0;
267 //             data[w * 4 + 2] = 255;
268               data[w * 4 + 3] = 120;
269 
270 //             data[(height - 1) * stride + w * 4 + 0] = 0;
271 //             data[(height - 1) * stride + w * 4 + 1] = 0;
272 //             data[(height - 1) * stride + w * 4 + 2] = 255;
273             data[(height - 1) * stride + w * 4 + 3] = 120;
274         }
275 
276         for (uint h=0; h < height; ++h)
277         {
278 //             data[h * stride + 0] = 0;
279 //             data[h * stride + 1] = 0;
280 //             data[h * stride + 2] = 255;
281             data[h * stride + 3] = 120;
282 
283             data[h * stride + (width / 2) * 4 + 0] = 0;
284             data[h * stride + (width / 2) * 4 + 1] = 0;
285             data[h * stride + (width / 2) * 4 + 2] = 0;
286             data[h * stride + (width / 2) * 4 + 3] = 160;
287 
288 //             data[h * stride + (width - 1) * 4 + 0] = 0;
289 //             data[h * stride + (width - 1) * 4 + 1] = 0;
290 //             data[h * stride + (width - 1) * 4 + 2] = 255;
291             data[h * stride + (width - 1) * 4 + 3] = 120;
292         }
293 
294         fInlineDisplay.pending = rwidth == height ? -1 : 0;
295         return (NativeInlineDisplayImageSurface*)(NativeInlineDisplayImageSurfaceCompat*)&fInlineDisplay;
296     }
297 
298 private:
299     int fColor, fStyle;
300     float fOutLeft, fOutRight;
301 
302     struct InlineDisplay : NativeInlineDisplayImageSurfaceCompat {
303         float lastLeft;
304         float lastRight;
305         volatile int pending;
306 
InlineDisplayBigMeterPlugin::InlineDisplay307         InlineDisplay()
308             : NativeInlineDisplayImageSurfaceCompat(),
309               lastLeft(0.0f),
310               lastRight(0.0f),
311               pending(0) {}
312 
~InlineDisplayBigMeterPlugin::InlineDisplay313         ~InlineDisplay()
314         {
315             if (data != nullptr)
316             {
317                 delete[] data;
318                 data = nullptr;
319             }
320         }
321 
322         CARLA_DECLARE_NON_COPY_STRUCT(InlineDisplay)
323         CARLA_PREVENT_HEAP_ALLOCATION
324     } fInlineDisplay;
325 
326     PluginClassEND(BigMeterPlugin)
327     CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(BigMeterPlugin)
328 };
329 
330 // -----------------------------------------------------------------------
331 
332 static const NativePluginDescriptor bigmeterDesc = {
333     /* category  */ NATIVE_PLUGIN_CATEGORY_UTILITY,
334     /* hints     */ static_cast<NativePluginHints>(NATIVE_PLUGIN_IS_RTSAFE
335                                                   |NATIVE_PLUGIN_HAS_INLINE_DISPLAY
336                                                   |NATIVE_PLUGIN_HAS_UI
337                                                   |NATIVE_PLUGIN_REQUESTS_IDLE),
338     /* supports  */ NATIVE_PLUGIN_SUPPORTS_NOTHING,
339     /* audioIns  */ 2,
340     /* audioOuts */ 0,
341     /* midiIns   */ 0,
342     /* midiOuts  */ 0,
343     /* paramIns  */ 2,
344     /* paramOuts */ 2,
345     /* name      */ "Big Meter",
346     /* label     */ "bigmeter",
347     /* maker     */ "falkTX",
348     /* copyright */ "GNU GPL v2+",
349     PluginDescriptorFILL(BigMeterPlugin)
350 };
351 
352 // -----------------------------------------------------------------------
353 
354 CARLA_EXPORT
355 void carla_register_native_plugin_bigmeter();
356 
357 CARLA_EXPORT
carla_register_native_plugin_bigmeter()358 void carla_register_native_plugin_bigmeter()
359 {
360     carla_register_native_plugin(&bigmeterDesc);
361 }
362 
363 // -----------------------------------------------------------------------
364