FastLED 3.9.7
Loading...
Searching...
No Matches
Painter.cpp
1
2
3#include <Arduino.h>
4
5#include "./Painter.h"
6#include "./led_layout_array.h"
7#include "./dprint.h"
8#include "./Keyboard.h"
9#include "fl/math_macros.h"
10#include <math.h>
11
12namespace {
13
14float LuminanceDecay(float time) {
15 typedef InterpData<float, float> Datum;
16 static const Datum kData[] = {
17 Datum(0, 0),
18 Datum(1, 0),
19 Datum(10, 0),
20 Datum(47, 60),
21 Datum(120, 100),
22 Datum(230, 160),
23 Datum(250, 255),
24 Datum(254, 255),
25 Datum(255, 64),
26 };
27
28 const float key = time * 255.f;
29 static const int n = sizeof(kData) / sizeof(kData[0]);
30 float approx_val = Interp(key, kData, n);
31
32
33 static const float k = (1.0f / 255.f);
34 const float out = approx_val * k;
35 return out;
36}
37
38float CalcLuminance(float time_delta_ms,
39 bool sustain_pedal_on,
40 const Key& key,
41 int key_idx) {
42
43 if (key.curr_color_.v_ <= 0.0) {
44 return 0.0;
45 }
46
47 const bool dampened_key = (key_idx < kFirstNoteNoDamp);
48
49 const float decay_factor = CalcDecayFactor(sustain_pedal_on,
50 key.on_,
51 key_idx,
52 key.velocity_ * (1.f/127.f), // Normalizing
53 dampened_key,
54 time_delta_ms);
55
56 if (key.on_) {
57 //const float brigthness_factor = sin(key.orig_color_.v_ * PI / 2.0);
58 float brigthness_factor = 0.0f;
59
60 if (kUseLedCurtin) {
61 brigthness_factor = sqrt(sqrt(key.orig_color_.v_));
62 } else {
63 //brigthness_factor = key.orig_color_.v_ * key.orig_color_.v_;
64 brigthness_factor = key.orig_color_.v_;
65 }
66 return LuminanceDecay(decay_factor) * brigthness_factor;
67 //return 1.0f;
68 } else {
69 return decay_factor * key.orig_color_.v_;
70 }
71}
72
73float CalcSaturation(float time_delta_ms, const ColorHSV& color, bool key_on) {
74 if (color.v_ <= 0.0) {
75 return color.s_;
76 }
77 if (!key_on) {
78 return 1.0f;
79 }
80 static const float kDefaultSaturationTime = 0.05f * 1000.f;
81
82 // At time = 0.0 the saturation_factor will be at 0.0 and then transition to 1.0
83 float saturation_factor = mapf(time_delta_ms,
84 0.0f, kDefaultSaturationTime,
85 0.0f, 1.0f);
86 // As time increases the saturation factor will continue
87 // to grow past 1.0. We use min to clamp it back to 1.0.
88 saturation_factor = MIN(1.0f, saturation_factor);
89 // TODO - make the saturation interpolate between the original
90 // color and the unsaturated state.
91 return saturation_factor;
92}
93
94} // namespace.
95
96
97void Painter::Paint(uint32_t now_ms,
98 uint32_t delta_ms,
99 VisState vis_state,
100 KeyboardState* keyboard,
101 LedRopeInterface* light_rope) {
102 for (int i = 0; i < KeyboardState::kNumKeys; ++i) {
103 Key& key = keyboard->keys_[i];
104
105 const float time_delta_ms = static_cast<float>(now_ms - key.event_time_);
106
107 const float lum = CalcLuminance(time_delta_ms, keyboard->sustain_pedal_, key, i);
108 const float sat = CalcSaturation(time_delta_ms, key.curr_color_, key.on_);
109
110 //if (key.idx_ == 56) {
111 // dprint("lum: "); dprint(lum*255.f); dprint(" sat:"); dprintln(sat*255.f);
112 //}
113
114 key.curr_color_.v_ = lum;
115 key.curr_color_.s_ = sat;
116
117 // Removing this line breaks one of the visualizers...
118 // TODO: Figure out a cleaner solution.
119 light_rope->Set(i, key.curr_color_.ToRGB());
120 }
121
122 LedColumns led_columns = LedLayoutArray();
123
124 switch (vis_state) {
125 case Painter::kBlockNote: {
126 light_rope->DrawSequentialRepeat(kNumLightsPerNote);
127 break;
128 }
129 case Painter::kColumnNote: {
130 light_rope->DrawRepeat(led_columns.array, kNumKeys);
131 break;
132 }
133 case Painter::kVUNote: {
134 PaintVuNotes(now_ms, *keyboard, led_columns.array, kNumKeys, light_rope);
135 break;
136 }
137 case Painter::kVUMidNote: {
138 PaintVuMidNotesFade(delta_ms, *keyboard, led_columns.array, kNumKeys, light_rope);
139 break;
140 }
141
142 case Painter::kVegas: { // aka "vegas mode?"
143 VegasVisualizer(*keyboard, led_columns.array, kNumKeys, light_rope);
144 break;
145 }
146
147 case Painter::kBrightSurprise: {
148 PaintBrightSurprise(*keyboard, led_columns.array, kNumKeys, light_rope);
149 break;
150 }
151
152 case Painter::kVUSpaceInvaders: {
153 PaintVuSpaceInvaders(now_ms, *keyboard, led_columns.array, kNumKeys, light_rope);
154 break;
155 }
156
157 default:
158 dprint("Unknown mode: "); dprint(vis_state); dprint(".\n");
159 break;
160 }
161}
162
163void Painter::PaintVuNotes(uint32_t /*now_ms*/,
164 const KeyboardState& keyboard,
165 const int* led_column_table, int led_column_table_length,
166 LedRopeInterface* led_rope) {
167
168 led_rope->RawBeginDraw();
169
170 for (int i = 0; i < led_column_table_length; ++i) {
171 const Key& key = keyboard.keys_[i];
172
173
174 // Map the white keys to the bottom and the black keys to the top.
175 bool black_key = false;
176 switch (key.idx_ % 12) {
177 case 1:
178 case 4:
179 case 6:
180 case 9:
181 case 11:
182 black_key = true;
183 break;
184 }
185
186 const int pixel_count = led_column_table[i];
187 const int draw_pixel_count = ceil(pixel_count * sqrt(key.curr_color_.v_));
188
189 const int black_pixel_count = pixel_count - draw_pixel_count;
190 const Color3i& c = *led_rope->GetIterator(i);
191
192
193 const bool reverse_correct = black_key == (key.idx_ % 2);
194
195 if (reverse_correct) {
196 for (int j = 0; j < draw_pixel_count; ++j) {
197 if (j < draw_pixel_count - 1) {
198 led_rope->RawDrawPixel(c);
199 } else {
200 // Last pixel.
201 ColorHSV hsv(random(512) / 512.f, random(512) / 512.f, 1.0);
202 led_rope->RawDrawPixel(hsv.ToRGB());
203 }
204 }
205
206 for (int j = 0; j < black_pixel_count; ++j) {
207 led_rope->RawDrawPixel(Color3i::Black());
208 }
209 } else {
210 for (int j = 0; j < black_pixel_count; ++j) {
211 led_rope->RawDrawPixel(Color3i::Black());
212 }
213
214 for (int j = draw_pixel_count - 1; j >= 0; --j) {
215 if (j < draw_pixel_count - 1) {
216 led_rope->RawDrawPixel(c);
217 } else {
218 // Last pixel.
219 ColorHSV hsv(random(512) / 512.f, random(512) / 512.f, 1.0);
220 led_rope->RawDrawPixel(hsv.ToRGB());
221 }
222 }
223 }
224 }
225 led_rope->RawCommitDraw();
226}
227
228void Painter::PaintVuMidNotesFade(uint32_t /*delta_ms*/,
229 const KeyboardState& keyboard,
230 const int* led_column_table, int led_column_table_length,
231 LedRopeInterface* led_rope) {
232 struct DrawPoints {
233 int n_black0;
234 int n_fade0;
235 int n_fill;
236 int n_fade1;
237 int n_black1;
238 float fade_factor; // 0->1.0
239
240 float SumBrightness() const {
241 float out = 0;
242 out += n_fill;
243 out += (fade_factor * n_fade0);
244 out += (fade_factor * n_fade1);
245 return out;
246 }
247 };
248
249 // Generator for the DrawPoints struct above.
250 // n_led: How many led's there are in total.
251 // factor: 0->1, indicates % of led's "on".
252 struct F {
253 static DrawPoints Generate(int n_led, float factor) {
254 DrawPoints out;
255 memset(&out, 0, sizeof(out));
256 if (n_led == 0 || factor == 0.0f) {
257 out.n_black0 = n_led;
258 return out;
259 }
260 const int is_odd = (n_led % 2);
261 const int n_half_lights = n_led / 2 + is_odd;
262 const float f_half_fill = n_half_lights * factor;
263 const int n_half_fill = static_cast<int>(f_half_fill); // Truncates float.
264
265 float fade_pix_perc = f_half_fill - static_cast<float>(n_half_fill);
266 int n_fade_pix = fade_pix_perc < 1.0f;
267 if (n_half_fill == 0) {
268 n_fade_pix = 1;
269 }
270 int n_half_black = n_half_lights - n_half_fill - n_fade_pix;
271
272 int n_fill_pix = 0;
273 if (n_half_fill > 0) {
274 n_fill_pix = n_half_fill * 2 + (is_odd ? -1 : 0);
275 }
276
277 out.n_black0 = n_half_black;
278 out.n_fade0 = n_fade_pix;
279 out.n_fill = n_fill_pix;
280 out.n_fade1 = n_fade_pix;
281 if (!n_fill_pix && is_odd) {
282 out.n_fade1 = 0;
283 }
284 out.n_black1 = n_half_black;
285 out.fade_factor = fade_pix_perc;
286 return out;
287 }
288 };
289
290
291 led_rope->RawBeginDraw();
292
293 for (int i = 0; i < led_column_table_length; ++i) {
294 const Key& key = keyboard.keys_[i];
295
296 float active_lights_factor = key.IntensityFactor();
297
298 //if (key.curr_color_.v_ <= 0.f) {
299 // active_lights_factor = 0.0;
300 //}
301
302 const int n_led = led_column_table[i];
303
304 if (active_lights_factor > 0.0f) {
305 DrawPoints dp = F::Generate(n_led, active_lights_factor);
306
307 ColorHSV hsv = key.curr_color_;
308 hsv.v_ = 1.0;
309 Color3i color = hsv.ToRGB();
310 // Now figure out optional fade color
311 Color3i fade_col;
312 ColorHSV c = key.curr_color_;
313 c.v_ = dp.fade_factor;
314 fade_col = c.ToRGB();
315
316 // Output to graphics.
317 led_rope->RawDrawPixels(Color3i::Black(), dp.n_black0);
318 led_rope->RawDrawPixels(fade_col, dp.n_fade0);
319 led_rope->RawDrawPixels(color, dp.n_fill);
320 led_rope->RawDrawPixels(fade_col, dp.n_fade1);
321 led_rope->RawDrawPixels(Color3i::Black(), dp.n_black1);
322
323#ifdef DEBUG_PAINTER
324 if (active_lights_factor > 0.0) {
325 int total_lights_on = dp.SumBrightness();
326 //dprint("total_lights_on: "); dprint(total_lights_on);
327 //dprint(", total lights written: "); dprintln(total_lights_on + dp.n_black0 + dp.n_black1);
328
329 //float total = (dp.n_fade0 * dp.fade_factor) + (dp.n_fade1 * dp.fade_factor) + static_cast<float>(dp.n_fill);
330 #define P(X) dprint(", "#X ": "); dprint(X);
331
332 //dprint("active_lights_factor: "); dprintln(active_lights_factor);
333
334 //P(dp.n_black0); P(dp.n_fade0); P(dp.n_fill); P(dp.n_fade1); P(dp.n_black1); P(dp.fade_factor);
335 P(total_lights_on);
336 P(active_lights_factor);
337 //P(total);
338 dprintln("");
339 }
340#endif
341 } else {
342 led_rope->RawDrawPixels(Color3i::Black(), n_led);
343 }
344
345
346
347 }
348
349
350 led_rope->RawCommitDraw();
351}
352
353void Painter::VegasVisualizer(const KeyboardState& keyboard,
354 const int* led_column_table, int led_column_table_length,
355 LedRopeInterface* led_rope) {
356
357 led_rope->RawBeginDraw();
358
359 uint32_t skipped_lights = 0;
360 for (int i = 0; i < led_column_table_length; ++i) {
361 const Key& key = keyboard.keys_[i];
362 uint32_t painted_lights = 0;
363
364 // % of lights that are active.
365 const float active_lights_factor = led_column_table[i] * sqrt(key.curr_color_.v_);
366 const float inactive_lights_factor = 1.0f - active_lights_factor;
367 const float taper_point_1 = inactive_lights_factor / 2.0f;
368 const float taper_point_2 = taper_point_1 + active_lights_factor;
369
370 const int taper_idx_1 = static_cast<int>(floor(taper_point_1 * led_column_table[i]));
371 const int taper_idx_2 = static_cast<int>(floor(taper_point_2 * led_column_table[i]));
372
373 const Color3i c = key.curr_color_.ToRGB();
374
375 for (int i = 0; i < taper_idx_1 / 2; ++i) {
376 led_rope->RawDrawPixel(Color3i::Black());
377 painted_lights++;
378 }
379
380 int length = taper_idx_2 - taper_idx_1;
381 for (int i = 0; i < min(200, length); ++i) {
382 led_rope->RawDrawPixel(c);
383 painted_lights++;
384 }
385
386 length = led_column_table[i] - taper_idx_2;
387 for (int i = 0; i < length; ++i) {
388 led_rope->RawDrawPixel(Color3i::Black());
389 painted_lights++;
390 }
391 skipped_lights += MAX(0, static_cast<int32_t>(led_column_table[i]) - static_cast<int32_t>(painted_lights));
392 }
393
394 for (uint32_t i = 0; i < skipped_lights; ++i) {
395 led_rope->RawDrawPixel(Color3i::Black());
396 }
397
398 led_rope->RawCommitDraw();
399}
400
401void Painter::PaintBrightSurprise(
402 const KeyboardState& keyboard,
403 const int* led_column_table, int led_column_table_length,
404 LedRopeInterface* led_rope) {
405
406 led_rope->RawBeginDraw();
407 int total_counted = 0;
408
409 float r, g, b;
410 r = g = b = 0;
411
412 for (int i = 0; i < KeyboardState::kNumKeys; ++i) {
413 const Key& key = keyboard.keys_[i];
414
415
416 if (key.curr_color_.v_ > 0.0f) {
417 const Color3i rgb = key.curr_color_.ToRGB();
418 r += rgb.r_;
419 g += rgb.g_;
420 b += rgb.b_;
421 ++total_counted;
422 }
423 }
424
425 float denom = total_counted ? total_counted : 1;
426 r /= denom;
427 g /= denom;
428 b /= denom;
429
430
431 const Color3i rgb(r, g, b);
432
433 for (int i = 0; i < led_column_table_length; ++i) {
434 const int n = led_column_table[i];
435 for (int i = 0; i < n; ++i) {
436 led_rope->RawDrawPixel(rgb);
437 }
438 }
439 led_rope->RawCommitDraw();
440}
441
442void Painter::PaintVuSpaceInvaders(uint32_t /*now_ms*/,
443 const KeyboardState& keyboard,
444 const int* led_column_table, int led_column_table_length,
445 LedRopeInterface* led_rope) {
446 led_rope->RawBeginDraw();
447
448 Color3i black = Color3i::Black();
449
450 for (int i = 0; i < led_column_table_length; ++i) {
451 const Key& key = keyboard.keys_[i];
452
453 const int pixel_count = led_column_table[i];
454 const int draw_pixel_count = ceil(pixel_count * sqrt(key.curr_color_.v_));
455
456 const int black_pixel_count = pixel_count - draw_pixel_count;
457
458 // If i is even
459 if (i % 2 == 0) {
460 for (int j = 0; j < black_pixel_count; ++j) {
461 led_rope->RawDrawPixel(*led_rope->GetIterator(i));
462 }
463 for (int j = 0; j < draw_pixel_count; ++j) {
464 led_rope->RawDrawPixel(black);
465 }
466 } else {
467
468 for (int j = 0; j < draw_pixel_count; ++j) {
469 led_rope->RawDrawPixel(black);
470 }
471
472 for (int j = 0; j < black_pixel_count; ++j) {
473 led_rope->RawDrawPixel(*led_rope->GetIterator(i));
474 }
475 }
476 }
477 led_rope->RawCommitDraw();
478}
#define P(x)
Reads a single byte from the p array.
Definition noise.cpp:42
Definition color.h:8
Definition Keyboard.h:22