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